summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndroid Build Coastguard Worker <android-build-coastguard-worker@google.com>2024-05-10 18:09:36 +0000
committerGerrit Code Review <noreply-gerritcodereview@google.com>2024-05-10 18:09:36 +0000
commite8ab430b10f5753e8c51877cf7fb67331f04acf4 (patch)
tree982d69bdb901c83cb0742e4b0914918776645b53
parente38c0e715a54f5fe7297effab05dee2dc7432164 (diff)
parent71908d88be9d88ba6fbec426651d3263a688c962 (diff)
downloadsecurity-busytown-mac-infra-release.tar.gz
Merge "Snap for 11819167 from 99986d19bbf7c7245759e9ff130035b0e9c18797 to busytown-mac-infra-release" into busytown-mac-infra-releasebusytown-mac-infra-release
-rw-r--r--OWNERS2
-rw-r--r--diced/OWNERS3
-rw-r--r--diced/TEST_MAPPING22
-rw-r--r--diced/open_dice/Android.bp256
-rw-r--r--diced/open_dice/bindgen/android/bcc.h17
-rw-r--r--diced/open_dice/bindgen/dice.h18
-rw-r--r--diced/open_dice/src/bcc.rs192
-rw-r--r--diced/open_dice/src/dice.rs270
-rw-r--r--diced/open_dice/src/error.rs59
-rw-r--r--diced/open_dice/src/lib.rs45
-rw-r--r--diced/open_dice/src/ops.rs142
-rw-r--r--diced/open_dice/src/retry.rs160
-rw-r--r--diced/open_dice/tests/api_test.rs107
-rw-r--r--diced/open_dice_cbor/lib.rs145
-rw-r--r--diced/src/sample_inputs.rs179
-rw-r--r--diced/src/utils.rs169
-rw-r--r--fsverity/fsverity_manifest_generator.py4
-rw-r--r--fsverity/libfsverity_rs/Android.bp17
-rw-r--r--fsverity/libfsverity_rs/lib.rs68
-rw-r--r--fsverity/libfsverity_rs/sys.rs58
-rw-r--r--fsverity_init/Android.bp35
-rw-r--r--fsverity_init/flags.aconfig10
-rw-r--r--fsverity_init/fsverity_init.cpp79
-rw-r--r--fsverity_init/include/fsverity_init.h21
-rw-r--r--fsverity_init/main.cpp62
-rw-r--r--identity/Android.bp64
-rw-r--r--identity/CredentialData.cpp10
-rw-r--r--identity/CredentialStore.cpp79
-rw-r--r--identity/CredentialStore.h3
-rw-r--r--identity/fuzzers/credstore_service_fuzzer.cpp62
-rw-r--r--identity/util/Android.bp1
-rw-r--r--keystore-engine/Android.bp9
-rw-r--r--keystore/Android.bp7
-rw-r--r--keystore/KeyAttestationApplicationId.cpp54
-rw-r--r--keystore/KeyAttestationPackageInfo.cpp59
-rw-r--r--keystore/Signature.cpp38
-rw-r--r--keystore/include/keystore/ExportResult.h40
-rw-r--r--keystore/include/keystore/KeyAttestationApplicationId.h58
-rw-r--r--keystore/include/keystore/KeyAttestationPackageInfo.h63
-rw-r--r--keystore/include/keystore/KeyCharacteristics.h49
-rw-r--r--keystore/include/keystore/KeymasterArguments.h48
-rw-r--r--keystore/include/keystore/KeymasterBlob.h40
-rw-r--r--keystore/include/keystore/KeymasterCertificateChain.h43
-rw-r--r--keystore/include/keystore/KeystoreResponse.h58
-rw-r--r--keystore/include/keystore/OperationResult.h48
-rw-r--r--keystore/include/keystore/Signature.h47
-rw-r--r--keystore/include/keystore/keymaster_types.h109
-rw-r--r--keystore/include/keystore/keystore.h85
-rw-r--r--keystore/include/keystore/keystore_attestation_id.h6
-rw-r--r--keystore/include/keystore/keystore_client.h191
-rw-r--r--keystore/include/keystore/keystore_client_impl.h127
-rw-r--r--keystore/include/keystore/keystore_client_mock.h88
-rw-r--r--keystore/include/keystore/keystore_concurrency.h114
-rw-r--r--keystore/include/keystore/keystore_hidl_support.h150
-rw-r--r--keystore/include/keystore/keystore_promises.h72
-rw-r--r--keystore/include/keystore/keystore_return_types.h193
-rw-r--r--keystore/include/keystore/utils.h101
-rw-r--r--keystore/keystore_attestation_id.cpp64
-rw-r--r--keystore/keystore_get.cpp46
-rw-r--r--keystore/keystore_keymaster_enforcement.h107
-rw-r--r--keystore/keystore_utils.cpp179
-rw-r--r--keystore/keystore_utils.h70
-rwxr-xr-xkeystore/test-keystore273
-rw-r--r--keystore/tests/Android.bp14
-rw-r--r--keystore/tests/Makefile125
-rw-r--r--keystore/tests/aaid_truncation_test.cpp65
-rw-r--r--keystore/tests/auth_token_formatting_test.cpp157
-rw-r--r--keystore/tests/auth_token_table_test.cpp520
-rw-r--r--keystore/tests/confirmationui_rate_limiting_test.cpp274
-rw-r--r--keystore/tests/fuzzer/Android.bp26
-rw-r--r--keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp35
-rw-r--r--keystore/tests/fuzzer/keystoreCommon.h20
-rw-r--r--keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp9
-rw-r--r--keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp10
-rwxr-xr-xkeystore/tests/list_auth_bound_keys_test.sh77
-rw-r--r--keystore/user_state.cpp311
-rw-r--r--keystore/user_state.h131
-rw-r--r--keystore2/Android.bp85
-rw-r--r--keystore2/OWNERS9
-rw-r--r--keystore2/TEST_MAPPING9
-rw-r--r--keystore2/aaid/Android.bp3
-rw-r--r--keystore2/aconfig/flags.aconfig26
-rw-r--r--keystore2/aidl/Android.bp71
-rw-r--r--keystore2/aidl/android/security/apc/IConfirmationCallback.aidl4
-rw-r--r--keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl13
-rw-r--r--keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl130
-rw-r--r--keystore2/aidl/android/security/authorization/LockScreenEvent.aidl22
-rw-r--r--keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl77
-rw-r--r--keystore2/aidl/android/security/maintenance/UserState.aidl23
-rw-r--r--keystore2/aidl/android/security/metrics/AtomID.aidl4
-rw-r--r--keystore2/aidl/android/security/metrics/KeystoreAtom.aidl2
-rw-r--r--keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl4
-rw-r--r--keystore2/aidl/android/security/metrics/PoolStatus.aidl30
-rw-r--r--keystore2/aidl/android/security/metrics/RkpPoolStats.aidl32
-rw-r--r--keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl45
-rw-r--r--keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl148
-rw-r--r--keystore2/aidl/android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.aidl49
-rw-r--r--keystore2/aidl/android/security/remoteprovisioning/ImplInfo.aidl37
-rw-r--r--keystore2/aidl/android/security/remoteprovisioning/RemotelyProvisionedKey.aidl42
-rw-r--r--keystore2/aidl/android/security/remoteprovisioning/ResponseCode.aidl34
-rw-r--r--keystore2/android.system.keystore2-service.xml2
-rw-r--r--keystore2/apc_compat/Android.bp10
-rw-r--r--keystore2/apc_compat/apc_compat.cpp3
-rw-r--r--keystore2/apc_compat/apc_compat.rs4
-rw-r--r--keystore2/keystore2.rc2
-rw-r--r--keystore2/legacykeystore/Android.bp5
-rw-r--r--keystore2/legacykeystore/lib.rs35
-rw-r--r--keystore2/message_macro/Android.bp (renamed from diced/open_dice_cbor/Android.bp)28
-rw-r--r--keystore2/message_macro/src/lib.rs (renamed from keystore2/src/ks_err.rs)10
-rw-r--r--keystore2/rkpd_client/Android.bp (renamed from diced/Android.bp)51
-rw-r--r--keystore2/rkpd_client/src/lib.rs (renamed from keystore2/src/rkpd_client.rs)291
-rw-r--r--keystore2/selinux/src/concurrency_test.rs2
-rw-r--r--keystore2/selinux/src/lib.rs8
-rw-r--r--keystore2/src/apc.rs2
-rw-r--r--keystore2/src/attestation_key_utils.rs48
-rw-r--r--keystore2/src/audit_log.rs36
-rw-r--r--keystore2/src/authorization.rs252
-rw-r--r--keystore2/src/crypto/Android.bp49
-rw-r--r--keystore2/src/crypto/crypto.cpp6
-rw-r--r--keystore2/src/crypto/crypto.hpp3
-rw-r--r--keystore2/src/crypto/lib.rs69
-rw-r--r--keystore2/src/crypto/tests/certificate_utils_test.cpp10
-rw-r--r--keystore2/src/crypto/zvec.rs14
-rw-r--r--keystore2/src/database.rs1444
-rw-r--r--keystore2/src/database/perboot.rs19
-rw-r--r--keystore2/src/database/versioning.rs80
-rw-r--r--keystore2/src/enforcements.rs105
-rw-r--r--keystore2/src/entropy.rs2
-rw-r--r--keystore2/src/error.rs175
-rw-r--r--keystore2/src/fuzzers/Android.bp17
-rw-r--r--keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs3
-rw-r--r--keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs17
-rw-r--r--keystore2/src/globals.rs162
-rw-r--r--keystore2/src/hal_instance_names/Android.bp (renamed from keystore2/src/vintf/Android.bp)28
-rw-r--r--keystore2/src/hal_instance_names/hal_names.cpp35
-rw-r--r--keystore2/src/hal_instance_names/hal_names.hpp (renamed from keystore2/src/vintf/vintf.hpp)2
-rw-r--r--keystore2/src/hal_instance_names/lib.rs (renamed from keystore2/src/vintf/lib.rs)6
-rw-r--r--keystore2/src/id_rotation.rs134
-rw-r--r--keystore2/src/key_parameter.rs15
-rw-r--r--keystore2/src/keystore2_main.rs49
-rw-r--r--keystore2/src/km_compat.rs32
-rw-r--r--keystore2/src/km_compat/Android.bp4
-rw-r--r--keystore2/src/km_compat/km_compat.cpp47
-rw-r--r--keystore2/src/km_compat/km_compat.h3
-rw-r--r--keystore2/src/km_compat/lib.rs1
-rw-r--r--keystore2/src/legacy_blob.rs8
-rw-r--r--keystore2/src/legacy_importer.rs16
-rw-r--r--keystore2/src/lib.rs7
-rw-r--r--keystore2/src/maintenance.rs179
-rw-r--r--keystore2/src/metrics_store.rs125
-rw-r--r--keystore2/src/operation.rs12
-rw-r--r--keystore2/src/permission.rs13
-rw-r--r--keystore2/src/raw_device.rs36
-rw-r--r--keystore2/src/remote_provisioning.rs1017
-rw-r--r--keystore2/src/security_level.rs168
-rw-r--r--keystore2/src/service.rs69
-rw-r--r--keystore2/src/shared_secret_negotiation.rs40
-rw-r--r--keystore2/src/super_key.rs1369
-rw-r--r--keystore2/src/sw_keyblob.rs1036
-rw-r--r--keystore2/src/utils.rs487
-rw-r--r--keystore2/src/vintf/vintf.cpp44
-rw-r--r--keystore2/src/watchdog_helper.rs64
-rw-r--r--keystore2/test_utils/Android.bp109
-rw-r--r--keystore2/test_utils/authorizations.rs199
-rw-r--r--keystore2/test_utils/ffi_test_utils.cpp752
-rw-r--r--keystore2/test_utils/ffi_test_utils.hpp16
-rw-r--r--keystore2/test_utils/ffi_test_utils.rs (renamed from keystore2/tests/ffi_test_utils.rs)72
-rw-r--r--keystore2/test_utils/key_generations.rs482
-rw-r--r--keystore2/test_utils/lib.rs8
-rw-r--r--keystore2/test_utils/run_as.rs35
-rw-r--r--keystore2/tests/Android.bp67
-rw-r--r--keystore2/tests/AndroidTest.xml1
-rw-r--r--keystore2/tests/ffi_test_utils.cpp366
-rw-r--r--keystore2/tests/ffi_test_utils.hpp11
-rw-r--r--keystore2/tests/keystore2_client_attest_key_tests.rs196
-rw-r--r--keystore2/tests/keystore2_client_authorizations_tests.rs985
-rw-r--r--keystore2/tests/keystore2_client_device_unique_attestation_tests.rs412
-rw-r--r--keystore2/tests/keystore2_client_ec_key_tests.rs45
-rw-r--r--keystore2/tests/keystore2_client_grant_key_tests.rs19
-rw-r--r--keystore2/tests/keystore2_client_import_keys_tests.rs15
-rw-r--r--keystore2/tests/keystore2_client_keystore_engine_tests.rs305
-rw-r--r--keystore2/tests/keystore2_client_list_entries_tests.rs476
-rw-r--r--keystore2/tests/keystore2_client_operation_tests.rs65
-rw-r--r--keystore2/tests/keystore2_client_test_utils.rs197
-rw-r--r--keystore2/tests/keystore2_client_tests.rs4
-rw-r--r--keystore2/tests/keystore2_client_update_subcomponent_tests.rs3
-rw-r--r--keystore2/tests/legacy_blobs/Android.bp12
-rw-r--r--keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs71
-rw-r--r--keystore2/watchdog/Android.bp49
-rw-r--r--keystore2/watchdog/src/lib.rs (renamed from keystore2/src/watchdog.rs)2
-rw-r--r--ondevice-signing/Android.bp2
-rw-r--r--ondevice-signing/CertUtils.cpp270
-rw-r--r--ondevice-signing/OWNERS2
-rw-r--r--ondevice-signing/VerityUtils.cpp41
-rw-r--r--ondevice-signing/include/CertUtils.h23
-rw-r--r--ondevice-signing/include/VerityUtils.h3
-rw-r--r--ondevice-signing/odsign.rc11
-rw-r--r--ondevice-signing/tests/Android.bp39
-rw-r--r--prng_seeder/Android.bp5
-rw-r--r--prng_seeder/OWNERS2
-rw-r--r--prng_seeder/src/conditioner.rs5
-rw-r--r--prng_seeder/src/cutils_socket.rs4
-rw-r--r--prng_seeder/src/drbg.rs14
-rw-r--r--prng_seeder/src/main.rs7
-rw-r--r--provisioner/Android.bp62
-rw-r--r--provisioner/binder/android/security/provisioner/IProvisionerService.aidl27
-rw-r--r--provisioner/rkp_factory_extraction_lib.cpp83
-rw-r--r--provisioner/rkp_factory_extraction_lib.h3
-rw-r--r--provisioner/rkp_factory_extraction_lib_test.cpp15
-rw-r--r--provisioner/rkp_factory_extraction_tool.cpp51
-rw-r--r--provisioner/support/Android.bp64
-rw-r--r--provisioner/support/TEST_MAPPING7
-rw-r--r--provisioner/support/include/rkp/support/rkpd_client.h (renamed from identity/RemotelyProvisionedKey.h)17
-rw-r--r--provisioner/support/rkpd_client.cpp (renamed from identity/RemotelyProvisionedKey.cpp)76
-rw-r--r--provisioner/support/test.cpp69
215 files changed, 9876 insertions, 11678 deletions
diff --git a/OWNERS b/OWNERS
index 03e57691..6fdb550a 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,5 +1,4 @@
alanstokes@google.com
-cbrubaker@google.com
drysdale@google.com
eranm@google.com
hasinitg@google.com
@@ -8,4 +7,5 @@ jeffv@google.com
kroot@google.com
sethmo@google.com
swillden@google.com
+trong@google.com
zeuthen@google.com
diff --git a/diced/OWNERS b/diced/OWNERS
deleted file mode 100644
index 387cd934..00000000
--- a/diced/OWNERS
+++ /dev/null
@@ -1,3 +0,0 @@
-alanstokes@google.com
-aliceywang@google.com
-ascull@google.com
diff --git a/diced/TEST_MAPPING b/diced/TEST_MAPPING
deleted file mode 100644
index 1b078a42..00000000
--- a/diced/TEST_MAPPING
+++ /dev/null
@@ -1,22 +0,0 @@
-{
- "presubmit": [
- {
- "name": "libdiced_open_dice.integration_test"
- },
- {
- "name": "libdiced_open_dice_nostd.integration_test"
- },
- {
- "name": "libopen_dice_cbor_bindgen_test"
- },
- {
- "name": "libopen_dice_bcc_bindgen_test"
- },
- {
- "name": "diced_utils_test"
- },
- {
- "name": "diced_sample_inputs_test"
- }
- ]
-}
diff --git a/diced/open_dice/Android.bp b/diced/open_dice/Android.bp
deleted file mode 100644
index 2505b426..00000000
--- a/diced/open_dice/Android.bp
+++ /dev/null
@@ -1,256 +0,0 @@
-package {
- default_visibility: [":__subpackages__"],
- default_applicable_licenses: ["Android-Apache-2.0"],
-}
-
-rust_defaults {
- name: "libdiced_open_dice_defaults",
- crate_name: "diced_open_dice",
- srcs: ["src/lib.rs"],
- static_libs: [
- "libopen_dice_cbor",
- ],
- vendor_available: true,
- apex_available: [
- "//apex_available:platform",
- "com.android.virt",
- ],
-}
-
-rust_library_rlib {
- name: "libdiced_open_dice_nostd",
- defaults: ["libdiced_open_dice_defaults"],
- rustlibs: [
- "libopen_dice_bcc_bindgen_nostd",
- "libopen_dice_cbor_bindgen_nostd",
- "libzeroize_nostd",
- ],
- whole_static_libs: [
- "libopen_dice_cbor",
- "libcrypto_baremetal",
- ],
- visibility: [
- "//packages/modules/Virtualization:__subpackages__",
- ],
-}
-
-rust_library {
- name: "libdiced_open_dice",
- defaults: ["libdiced_open_dice_defaults"],
- rustlibs: [
- "libopen_dice_bcc_bindgen",
- "libopen_dice_cbor_bindgen",
- "libzeroize",
- ],
- features: [
- "std",
- ],
- shared_libs: [
- "libcrypto",
- ],
- whole_static_libs: [
- "libopen_dice_bcc",
- ],
- visibility: [
- "//system/security/diced:__subpackages__",
- "//packages/modules/Virtualization:__subpackages__",
- "//hardware/interfaces/security/dice/aidl:__subpackages__",
- ],
-}
-
-rust_defaults {
- name: "libdiced_open_dice_test_defaults",
- crate_name: "diced_open_dice_test",
- srcs: ["tests/*.rs"],
- test_suites: ["general-tests"],
-}
-
-rust_test {
- name: "libdiced_open_dice.integration_test",
- defaults: ["libdiced_open_dice_test_defaults"],
- rustlibs: [
- "libdiced_open_dice",
- ],
-}
-
-rust_test {
- name: "libdiced_open_dice_nostd.integration_test",
- defaults: ["libdiced_open_dice_test_defaults"],
- rustlibs: [
- "libdiced_open_dice_nostd",
- ],
-}
-
-rust_defaults {
- name: "libopen_dice_bindgen_nostd.rust_defaults",
- bindgen_flags: [
- "--use-core",
- "--ctypes-prefix=core::ffi",
- "--raw-line=#![no_std]",
- ],
- no_stdlibs: true,
- prefer_rlib: true,
- stdlibs: [
- "libcore.rust_sysroot",
- "libcompiler_builtins.rust_sysroot",
- ],
- target: {
- musl: {
- enabled: false,
- },
- glibc: {
- enabled: false,
- },
- darwin: {
- enabled: false,
- },
- },
-}
-
-rust_defaults {
- name: "libopen_dice.rust_defaults",
- host_supported: true,
- vendor_available: true,
- apex_available: [
- "//apex_available:platform",
- "com.android.compos",
- "com.android.virt",
- ],
-}
-
-rust_defaults {
- name: "libopen_dice_cbor_bindgen.rust_defaults",
- defaults: ["libopen_dice.rust_defaults"],
- wrapper_src: "bindgen/dice.h",
- crate_name: "open_dice_cbor_bindgen",
- source_stem: "bindings",
- bindgen_flags: [
- "--size_t-is-usize",
- "--rustified-enum DiceConfigType",
- "--rustified-enum DiceMode",
- "--rustified-enum DiceResult",
-
- // By generating only essential functions, we can make bindings concise and
- // optimize compilation time.
- "--allowlist-function=DiceDeriveCdiPrivateKeySeed",
- "--allowlist-function=DiceDeriveCdiCertificateId",
- "--allowlist-function=DiceMainFlow",
- "--allowlist-function=DiceHash",
- "--allowlist-function=DiceKdf",
- "--allowlist-function=DiceKeypairFromSeed",
- "--allowlist-function=DiceSign",
- "--allowlist-function=DiceVerify",
- "--allowlist-function=DiceGenerateCertificate",
-
- // We also need some constants in addition to the functions.
- "--allowlist-var=DICE_CDI_SIZE",
- "--allowlist-var=DICE_HASH_SIZE",
- "--allowlist-var=DICE_HIDDEN_SIZE",
- "--allowlist-var=DICE_INLINE_CONFIG_SIZE",
- "--allowlist-var=DICE_PRIVATE_KEY_SEED_SIZE",
- "--allowlist-var=DICE_ID_SIZE",
- "--allowlist-var=DICE_PUBLIC_KEY_SIZE",
- "--allowlist-var=DICE_PRIVATE_KEY_SIZE",
- "--allowlist-var=DICE_SIGNATURE_SIZE",
- ],
-}
-
-rust_bindgen {
- name: "libopen_dice_cbor_bindgen",
- defaults: ["libopen_dice_cbor_bindgen.rust_defaults"],
- whole_static_libs: ["libopen_dice_cbor"],
-}
-
-rust_bindgen {
- name: "libopen_dice_cbor_bindgen_nostd",
- defaults: [
- "libopen_dice_cbor_bindgen.rust_defaults",
- "libopen_dice_bindgen_nostd.rust_defaults",
- ],
- whole_static_libs: ["libopen_dice_cbor_baremetal"],
-}
-
-rust_defaults {
- name: "libopen_dice_bcc_bindgen.rust_defaults",
- defaults: ["libopen_dice.rust_defaults"],
- wrapper_src: "bindgen/android/bcc.h",
- crate_name: "open_dice_bcc_bindgen",
- source_stem: "bindings",
- bindgen_flags: [
- "--size_t-is-usize",
-
- // By generating only essential functions, we can make bindings concise and
- // optimize compilation time.
- "--allowlist-function=BccFormatConfigDescriptor",
- "--allowlist-function=BccMainFlow",
- "--allowlist-function=BccHandoverMainFlow",
- "--allowlist-function=BccHandoverParse",
-
- // We also need some constants in addition to the functions.
- "--allowlist-var=BCC_INPUT_COMPONENT_NAME",
- "--allowlist-var=BCC_INPUT_COMPONENT_VERSION",
- "--allowlist-var=BCC_INPUT_RESETTABLE",
-
- // Prevent DiceInputValues from being generated a second time and
- // import it instead from open_dice_cbor_bindgen.
- "--blocklist-type=DiceInputValues_",
- "--blocklist-type=DiceInputValues",
- "--raw-line",
- "pub use open_dice_cbor_bindgen::DiceInputValues;",
-
- // Prevent DiceResult from being generated a second time and
- // import it instead from open_dice_cbor_bindgen.
- "--blocklist-type=DiceResult",
- "--raw-line",
- "pub use open_dice_cbor_bindgen::DiceResult;",
- ],
-
-}
-
-rust_bindgen {
- name: "libopen_dice_bcc_bindgen",
- defaults: ["libopen_dice_bcc_bindgen.rust_defaults"],
- rustlibs: [
- "libopen_dice_cbor_bindgen",
- ],
- whole_static_libs: ["libopen_dice_bcc"],
-}
-
-rust_bindgen {
- name: "libopen_dice_bcc_bindgen_nostd",
- defaults: [
- "libopen_dice_bcc_bindgen.rust_defaults",
- "libopen_dice_bindgen_nostd.rust_defaults",
- ],
- rustlibs: [
- "libopen_dice_cbor_bindgen_nostd",
- ],
- whole_static_libs: ["libopen_dice_bcc_baremetal"],
-}
-
-rust_test {
- name: "libopen_dice_cbor_bindgen_test",
- srcs: [
- ":libopen_dice_cbor_bindgen",
- ],
- crate_name: "open_dice_cbor_bindgen_test",
- test_suites: ["general-tests"],
- auto_gen_config: true,
- clippy_lints: "none",
- lints: "none",
-}
-
-rust_test {
- name: "libopen_dice_bcc_bindgen_test",
- srcs: [
- ":libopen_dice_bcc_bindgen",
- ],
- crate_name: "open_dice_bcc_bindgen_test",
- rustlibs: [
- "libopen_dice_cbor_bindgen",
- ],
- test_suites: ["general-tests"],
- auto_gen_config: true,
- clippy_lints: "none",
- lints: "none",
-}
diff --git a/diced/open_dice/bindgen/android/bcc.h b/diced/open_dice/bindgen/android/bcc.h
deleted file mode 100644
index 4dfc8626..00000000
--- a/diced/open_dice/bindgen/android/bcc.h
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright 2021 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#pragma once
-
-#include <dice/android/bcc.h>
diff --git a/diced/open_dice/bindgen/dice.h b/diced/open_dice/bindgen/dice.h
deleted file mode 100644
index 47fe9119..00000000
--- a/diced/open_dice/bindgen/dice.h
+++ /dev/null
@@ -1,18 +0,0 @@
-// Copyright 2021 Google LLC
-//
-// Licensed under the Apache License, Version 2.0 (the "License"); you may not
-// use this file except in compliance with the License. You may obtain a copy of
-// the License at
-//
-// https://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-// License for the specific language governing permissions and limitations under
-// the License.
-
-#pragma once
-
-#include <dice/dice.h>
-#include <dice/ops.h>
diff --git a/diced/open_dice/src/bcc.rs b/diced/open_dice/src/bcc.rs
deleted file mode 100644
index 1575113e..00000000
--- a/diced/open_dice/src/bcc.rs
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright 2023, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! This module mirrors the content in open-dice/include/dice/android/bcc.h
-
-use crate::dice::{Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE};
-use crate::error::{check_result, DiceError, Result};
-use open_dice_bcc_bindgen::{
- BccConfigValues, BccFormatConfigDescriptor, BccHandoverMainFlow, BccHandoverParse, BccMainFlow,
- BCC_INPUT_COMPONENT_NAME, BCC_INPUT_COMPONENT_VERSION, BCC_INPUT_RESETTABLE,
-};
-use std::{ffi::CStr, ptr};
-
-/// Formats a configuration descriptor following the BCC's specification.
-/// See https://cs.android.com/android/platform/superproject/+/master:hardware/interfaces/security/rkp/aidl/android/hardware/security/keymint/ProtectedData.aidl
-pub fn bcc_format_config_descriptor(
- name: Option<&CStr>,
- version: Option<u64>,
- resettable: bool,
- buffer: &mut [u8],
-) -> Result<usize> {
- let mut inputs = 0;
- if name.is_some() {
- inputs |= BCC_INPUT_COMPONENT_NAME;
- }
- if version.is_some() {
- inputs |= BCC_INPUT_COMPONENT_VERSION;
- }
- if resettable {
- inputs |= BCC_INPUT_RESETTABLE;
- }
-
- let values = BccConfigValues {
- inputs,
- component_name: name.map_or(ptr::null(), |p| p.as_ptr()),
- component_version: version.unwrap_or(0),
- };
-
- let mut buffer_size = 0;
- // SAFETY: The function writes to the buffer, within the given bounds, and only reads the
- // input values. It writes its result to buffer_size.
- check_result(unsafe {
- BccFormatConfigDescriptor(&values, buffer.len(), buffer.as_mut_ptr(), &mut buffer_size)
- })?;
- Ok(buffer_size)
-}
-
-/// Executes the main BCC flow.
-///
-/// Given a full set of input values along with the current BCC and CDI values,
-/// computes the next CDI values and matching updated BCC.
-pub fn bcc_main_flow(
- current_cdi_attest: &Cdi,
- current_cdi_seal: &Cdi,
- current_bcc: &[u8],
- input_values: &InputValues,
- next_cdi_values: &mut CdiValues,
- next_bcc: &mut [u8],
-) -> Result<usize> {
- let mut next_bcc_size = 0;
- // SAFETY: `BccMainFlow` only reads the current `bcc` and CDI values and writes
- // to `next_bcc` and next CDI values within its bounds. It also reads
- // `input_values` as a constant input and doesn't store any pointer.
- // The first argument can be null and is not used in the current implementation.
- check_result(unsafe {
- BccMainFlow(
- ptr::null_mut(), // context
- current_cdi_attest.as_ptr(),
- current_cdi_seal.as_ptr(),
- current_bcc.as_ptr(),
- current_bcc.len(),
- input_values.as_ptr(),
- next_bcc.len(),
- next_bcc.as_mut_ptr(),
- &mut next_bcc_size,
- next_cdi_values.cdi_attest.as_mut_ptr(),
- next_cdi_values.cdi_seal.as_mut_ptr(),
- )
- })?;
- Ok(next_bcc_size)
-}
-
-/// Executes the main BCC handover flow.
-///
-/// A BCC handover combines the BCC and CDIs in a single CBOR object.
-/// This function takes the current boot stage's BCC handover bundle and produces a
-/// bundle for the next stage.
-pub fn bcc_handover_main_flow(
- current_bcc_handover: &[u8],
- input_values: &InputValues,
- next_bcc_handover: &mut [u8],
-) -> Result<usize> {
- let mut next_bcc_handover_size = 0;
- // SAFETY - The function only reads `current_bcc_handover` and writes to `next_bcc_handover`
- // within its bounds,
- // It also reads `input_values` as a constant input and doesn't store any pointer.
- // The first argument can be null and is not used in the current implementation.
- check_result(unsafe {
- BccHandoverMainFlow(
- ptr::null_mut(), // context
- current_bcc_handover.as_ptr(),
- current_bcc_handover.len(),
- input_values.as_ptr(),
- next_bcc_handover.len(),
- next_bcc_handover.as_mut_ptr(),
- &mut next_bcc_handover_size,
- )
- })?;
-
- Ok(next_bcc_handover_size)
-}
-
-/// A BCC handover combines the BCC and CDIs in a single CBOR object.
-/// This struct is used as return of the function `bcc_handover_parse`, its lifetime is tied
-/// to the lifetime of the raw BCC handover slice.
-#[derive(Debug)]
-pub struct BccHandover<'a> {
- /// Attestation CDI.
- cdi_attest: &'a [u8; CDI_SIZE],
- /// Sealing CDI.
- cdi_seal: &'a [u8; CDI_SIZE],
- /// Boot Certificate Chain.
- bcc: Option<&'a [u8]>,
-}
-
-impl<'a> DiceArtifacts for BccHandover<'a> {
- fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
- self.cdi_attest
- }
-
- fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
- self.cdi_seal
- }
-
- fn bcc(&self) -> Option<&[u8]> {
- self.bcc
- }
-}
-
-/// A BCC handover combines the BCC and CDIs in a single CBOR object.
-/// This function parses the `bcc_handover` to extracts the BCC and CDIs.
-/// The lifetime of the returned `BccHandover` is tied to the given `bcc_handover` slice.
-pub fn bcc_handover_parse(bcc_handover: &[u8]) -> Result<BccHandover> {
- let mut cdi_attest: *const u8 = ptr::null();
- let mut cdi_seal: *const u8 = ptr::null();
- let mut bcc: *const u8 = ptr::null();
- let mut bcc_size = 0;
- // SAFETY: The `bcc_handover` is only read and never stored and the returned pointers should all
- // point within the address range of the `bcc_handover` or be NULL.
- check_result(unsafe {
- BccHandoverParse(
- bcc_handover.as_ptr(),
- bcc_handover.len(),
- &mut cdi_attest,
- &mut cdi_seal,
- &mut bcc,
- &mut bcc_size,
- )
- })?;
- let cdi_attest = sub_slice(bcc_handover, cdi_attest, CDI_SIZE)?;
- let cdi_seal = sub_slice(bcc_handover, cdi_seal, CDI_SIZE)?;
- let bcc = sub_slice(bcc_handover, bcc, bcc_size).ok();
- Ok(BccHandover {
- cdi_attest: cdi_attest.try_into().map_err(|_| DiceError::PlatformError)?,
- cdi_seal: cdi_seal.try_into().map_err(|_| DiceError::PlatformError)?,
- bcc,
- })
-}
-
-/// Gets a slice the `addr` points to and of length `len`.
-/// The slice should be contained in the buffer.
-fn sub_slice(buffer: &[u8], addr: *const u8, len: usize) -> Result<&[u8]> {
- if addr.is_null() || !buffer.as_ptr_range().contains(&addr) {
- return Err(DiceError::PlatformError);
- }
- // SAFETY: This is safe because addr is not null and is within the range of the buffer.
- let start: usize = unsafe {
- addr.offset_from(buffer.as_ptr()).try_into().map_err(|_| DiceError::PlatformError)?
- };
- start.checked_add(len).and_then(|end| buffer.get(start..end)).ok_or(DiceError::PlatformError)
-}
diff --git a/diced/open_dice/src/dice.rs b/diced/open_dice/src/dice.rs
deleted file mode 100644
index 9266b6fc..00000000
--- a/diced/open_dice/src/dice.rs
+++ /dev/null
@@ -1,270 +0,0 @@
-// Copyright 2023, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Structs and functions about the types used in DICE.
-//! This module mirrors the content in open-dice/include/dice/dice.h
-
-use crate::error::{check_result, Result};
-pub use open_dice_cbor_bindgen::DiceMode;
-use open_dice_cbor_bindgen::{
- DiceConfigType, DiceDeriveCdiCertificateId, DiceDeriveCdiPrivateKeySeed, DiceInputValues,
- DiceMainFlow, DICE_CDI_SIZE, DICE_HASH_SIZE, DICE_HIDDEN_SIZE, DICE_ID_SIZE,
- DICE_INLINE_CONFIG_SIZE, DICE_PRIVATE_KEY_SEED_SIZE, DICE_PRIVATE_KEY_SIZE,
- DICE_PUBLIC_KEY_SIZE, DICE_SIGNATURE_SIZE,
-};
-use std::ptr;
-use zeroize::{Zeroize, ZeroizeOnDrop};
-
-/// The size of a DICE hash.
-pub const HASH_SIZE: usize = DICE_HASH_SIZE as usize;
-/// The size of the DICE hidden value.
-pub const HIDDEN_SIZE: usize = DICE_HIDDEN_SIZE as usize;
-/// The size of a DICE inline config.
-const INLINE_CONFIG_SIZE: usize = DICE_INLINE_CONFIG_SIZE as usize;
-/// The size of a CDI.
-pub const CDI_SIZE: usize = DICE_CDI_SIZE as usize;
-/// The size of a private key seed.
-pub const PRIVATE_KEY_SEED_SIZE: usize = DICE_PRIVATE_KEY_SEED_SIZE as usize;
-/// The size of a private key.
-pub const PRIVATE_KEY_SIZE: usize = DICE_PRIVATE_KEY_SIZE as usize;
-/// The size of a public key.
-pub const PUBLIC_KEY_SIZE: usize = DICE_PUBLIC_KEY_SIZE as usize;
-/// The size of a signature.
-pub const SIGNATURE_SIZE: usize = DICE_SIGNATURE_SIZE as usize;
-/// The size of an ID.
-pub const ID_SIZE: usize = DICE_ID_SIZE as usize;
-
-/// Array type of hashes used by DICE.
-pub type Hash = [u8; HASH_SIZE];
-/// Array type of additional input.
-pub type Hidden = [u8; HIDDEN_SIZE];
-/// Array type of inline configuration values.
-pub type InlineConfig = [u8; INLINE_CONFIG_SIZE];
-/// Array type of CDIs.
-pub type Cdi = [u8; CDI_SIZE];
-/// Array type of the public key.
-pub type PublicKey = [u8; PUBLIC_KEY_SIZE];
-/// Array type of the signature.
-pub type Signature = [u8; SIGNATURE_SIZE];
-/// Array type of DICE ID.
-pub type DiceId = [u8; ID_SIZE];
-
-/// A trait for types that represent Dice artifacts, which include:
-///
-/// - Attestation CDI
-/// - Sealing CDI
-/// - Boot Certificate Chain
-///
-/// Types that implement this trait provide an access these artifacts.
-pub trait DiceArtifacts {
- /// Returns a reference to the attestation CDI.
- fn cdi_attest(&self) -> &[u8; CDI_SIZE];
-
- /// Returns a reference to the sealing CDI.
- fn cdi_seal(&self) -> &[u8; CDI_SIZE];
-
- /// Returns a reference to the Boot Certificate Chain, if present.
- fn bcc(&self) -> Option<&[u8]>;
-}
-
-/// TODO(b/268587826): Clean up the memory cache after zeroing out the memory
-/// for sensitive data like CDI values and private key.
-/// CDI Values.
-#[derive(Debug, Zeroize, ZeroizeOnDrop, Default)]
-pub struct CdiValues {
- /// Attestation CDI.
- pub cdi_attest: [u8; CDI_SIZE],
- /// Sealing CDI.
- pub cdi_seal: [u8; CDI_SIZE],
-}
-
-/// Private key seed. The data is zeroed out when the struct is dropped.
-#[derive(Zeroize, ZeroizeOnDrop, Default)]
-pub struct PrivateKeySeed([u8; PRIVATE_KEY_SEED_SIZE]);
-
-impl PrivateKeySeed {
- /// Returns an array reference of the private key seed.
- pub fn as_array(&self) -> &[u8; PRIVATE_KEY_SEED_SIZE] {
- &self.0
- }
-
- /// Returns a mutable pointer to the slice buffer of the private key seed.
- pub fn as_mut_ptr(&mut self) -> *mut u8 {
- self.0.as_mut_ptr()
- }
-}
-
-/// Private key. The data is zeroed out when the struct is dropped.
-#[derive(Zeroize, ZeroizeOnDrop)]
-pub struct PrivateKey([u8; PRIVATE_KEY_SIZE]);
-
-impl Default for PrivateKey {
- /// Creates a new `PrivateKey` instance with all bytes set to 0.
- ///
- /// Since the size of the private key array is too large to be initialized
- /// with a default value, this implementation sets all the bytes in the array
- /// to 0 using the `[0u8; PRIVATE_KEY_SIZE]` syntax.
- fn default() -> Self {
- Self([0u8; PRIVATE_KEY_SIZE])
- }
-}
-
-impl PrivateKey {
- /// Returns an array reference of the private key.
- pub fn as_array(&self) -> &[u8; PRIVATE_KEY_SIZE] {
- &self.0
- }
-
- /// Returns a mutable pointer to the slice buffer of the private key.
- pub fn as_mut_ptr(&mut self) -> *mut u8 {
- self.0.as_mut_ptr()
- }
-}
-
-/// Configuration descriptor for DICE input values.
-#[derive(Debug, Clone, PartialEq, Eq)]
-pub enum Config<'a> {
- /// Reference to an inline descriptor.
- Inline(&'a InlineConfig),
- /// Reference to a free form descriptor that will be hashed by the implementation.
- Descriptor(&'a [u8]),
-}
-
-impl Config<'_> {
- fn dice_config_type(&self) -> DiceConfigType {
- match self {
- Self::Inline(_) => DiceConfigType::kDiceConfigTypeInline,
- Self::Descriptor(_) => DiceConfigType::kDiceConfigTypeDescriptor,
- }
- }
-
- fn inline_config(&self) -> InlineConfig {
- match self {
- Self::Inline(inline) => **inline,
- Self::Descriptor(_) => [0u8; INLINE_CONFIG_SIZE],
- }
- }
-
- fn descriptor_ptr(&self) -> *const u8 {
- match self {
- Self::Descriptor(descriptor) => descriptor.as_ptr(),
- _ => ptr::null(),
- }
- }
-
- fn descriptor_size(&self) -> usize {
- match self {
- Self::Descriptor(descriptor) => descriptor.len(),
- _ => 0,
- }
- }
-}
-
-/// Wrap of `DiceInputValues`.
-#[derive(Clone, Debug)]
-pub struct InputValues(DiceInputValues);
-
-impl InputValues {
- /// Creates a new `InputValues`.
- pub fn new(
- code_hash: Hash,
- config: Config,
- authority_hash: Hash,
- mode: DiceMode,
- hidden: Hidden,
- ) -> Self {
- Self(DiceInputValues {
- code_hash,
- code_descriptor: ptr::null(),
- code_descriptor_size: 0,
- config_type: config.dice_config_type(),
- config_value: config.inline_config(),
- config_descriptor: config.descriptor_ptr(),
- config_descriptor_size: config.descriptor_size(),
- authority_hash,
- authority_descriptor: ptr::null(),
- authority_descriptor_size: 0,
- mode,
- hidden,
- })
- }
-
- /// Returns a raw pointer to the wrapped `DiceInputValues`.
- pub fn as_ptr(&self) -> *const DiceInputValues {
- &self.0 as *const DiceInputValues
- }
-}
-
-/// Derives a CDI private key seed from a `cdi_attest` value.
-pub fn derive_cdi_private_key_seed(cdi_attest: &Cdi) -> Result<PrivateKeySeed> {
- let mut seed = PrivateKeySeed::default();
- // SAFETY: The function writes to the buffer within the given bounds, and only reads the
- // input values. The first argument context is not used in this function.
- check_result(unsafe {
- DiceDeriveCdiPrivateKeySeed(
- ptr::null_mut(), // context
- cdi_attest.as_ptr(),
- seed.as_mut_ptr(),
- )
- })?;
- Ok(seed)
-}
-
-/// Derives an ID from the given `cdi_public_key` value.
-pub fn derive_cdi_certificate_id(cdi_public_key: &[u8]) -> Result<DiceId> {
- let mut id = [0u8; ID_SIZE];
- // SAFETY: The function writes to the buffer within the given bounds, and only reads the
- // input values. The first argument context is not used in this function.
- check_result(unsafe {
- DiceDeriveCdiCertificateId(
- ptr::null_mut(), // context
- cdi_public_key.as_ptr(),
- cdi_public_key.len(),
- id.as_mut_ptr(),
- )
- })?;
- Ok(id)
-}
-
-/// Executes the main DICE flow.
-///
-/// Given a full set of input values and the current CDI values, computes the
-/// next CDI values and a matching certificate.
-/// Returns the actual size of the next CDI certificate.
-pub fn dice_main_flow(
- current_cdi_attest: &Cdi,
- current_cdi_seal: &Cdi,
- input_values: &InputValues,
- next_cdi_certificate: &mut [u8],
- next_cdi_values: &mut CdiValues,
-) -> Result<usize> {
- let mut next_cdi_certificate_actual_size = 0;
- // SAFETY: The function only reads the current CDI values and inputs and writes
- // to `next_cdi_certificate` and next CDI values within its bounds.
- // The first argument can be null and is not used in the current implementation.
- check_result(unsafe {
- DiceMainFlow(
- ptr::null_mut(), // context
- current_cdi_attest.as_ptr(),
- current_cdi_seal.as_ptr(),
- input_values.as_ptr(),
- next_cdi_certificate.len(),
- next_cdi_certificate.as_mut_ptr(),
- &mut next_cdi_certificate_actual_size,
- next_cdi_values.cdi_attest.as_mut_ptr(),
- next_cdi_values.cdi_seal.as_mut_ptr(),
- )
- })?;
- Ok(next_cdi_certificate_actual_size)
-}
diff --git a/diced/open_dice/src/error.rs b/diced/open_dice/src/error.rs
deleted file mode 100644
index 4c673354..00000000
--- a/diced/open_dice/src/error.rs
+++ /dev/null
@@ -1,59 +0,0 @@
-// Copyright 2023, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Errors and relating functions thrown in this library.
-
-use open_dice_cbor_bindgen::DiceResult;
-use std::{fmt, result};
-
-#[cfg(feature = "std")]
-use std::error::Error;
-
-/// Error type used by DICE.
-#[derive(Debug)]
-pub enum DiceError {
- /// Provided input was invalid.
- InvalidInput,
- /// Provided buffer was too small.
- BufferTooSmall,
- /// Platform error.
- PlatformError,
-}
-
-/// This makes `DiceError` accepted by anyhow.
-#[cfg(feature = "std")]
-impl Error for DiceError {}
-
-impl fmt::Display for DiceError {
- fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
- match self {
- Self::InvalidInput => write!(f, "invalid input"),
- Self::BufferTooSmall => write!(f, "buffer too small"),
- Self::PlatformError => write!(f, "platform error"),
- }
- }
-}
-
-/// DICE result type.
-pub type Result<T> = result::Result<T, DiceError>;
-
-/// Checks the given `DiceResult`. Returns an error if it's not OK.
-pub fn check_result(result: DiceResult) -> Result<()> {
- match result {
- DiceResult::kDiceResultOk => Ok(()),
- DiceResult::kDiceResultInvalidInput => Err(DiceError::InvalidInput),
- DiceResult::kDiceResultBufferTooSmall => Err(DiceError::BufferTooSmall),
- DiceResult::kDiceResultPlatformError => Err(DiceError::PlatformError),
- }
-}
diff --git a/diced/open_dice/src/lib.rs b/diced/open_dice/src/lib.rs
deleted file mode 100644
index e7ec56be..00000000
--- a/diced/open_dice/src/lib.rs
+++ /dev/null
@@ -1,45 +0,0 @@
-// Copyright 2023, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! Implements safe wrappers around the public API of libopen-dice for
-//! both std and nostd usages.
-
-#![cfg_attr(not(feature = "std"), no_std)]
-
-#[cfg(not(feature = "std"))]
-extern crate core as std;
-
-mod bcc;
-mod dice;
-mod error;
-mod ops;
-#[cfg(feature = "std")]
-mod retry;
-
-pub use bcc::{
- bcc_format_config_descriptor, bcc_handover_main_flow, bcc_handover_parse, bcc_main_flow,
- BccHandover,
-};
-pub use dice::{
- derive_cdi_certificate_id, derive_cdi_private_key_seed, dice_main_flow, Cdi, CdiValues, Config,
- DiceArtifacts, DiceMode, Hash, Hidden, InlineConfig, InputValues, PrivateKey, PrivateKeySeed,
- PublicKey, Signature, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
-};
-pub use error::{check_result, DiceError, Result};
-pub use ops::{generate_certificate, hash, kdf, keypair_from_seed, sign, verify};
-#[cfg(feature = "std")]
-pub use retry::{
- retry_bcc_format_config_descriptor, retry_bcc_main_flow, retry_dice_main_flow,
- retry_generate_certificate, OwnedDiceArtifacts,
-};
diff --git a/diced/open_dice/src/ops.rs b/diced/open_dice/src/ops.rs
deleted file mode 100644
index 8222b266..00000000
--- a/diced/open_dice/src/ops.rs
+++ /dev/null
@@ -1,142 +0,0 @@
-// Copyright 2023, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! This module mirrors the content in open-dice/include/dice/ops.h
-//! It contains the set of functions that implement various operations that the
-//! main DICE functions depend on.
-
-use crate::dice::{
- Hash, InputValues, PrivateKey, PublicKey, Signature, HASH_SIZE, PRIVATE_KEY_SEED_SIZE,
- PRIVATE_KEY_SIZE, PUBLIC_KEY_SIZE, SIGNATURE_SIZE,
-};
-use crate::error::{check_result, Result};
-use open_dice_cbor_bindgen::{
- DiceGenerateCertificate, DiceHash, DiceKdf, DiceKeypairFromSeed, DiceSign, DiceVerify,
-};
-use std::ptr;
-
-/// Hashes the provided input using DICE's hash function `DiceHash`.
-pub fn hash(input: &[u8]) -> Result<Hash> {
- let mut output: Hash = [0; HASH_SIZE];
- // SAFETY: DiceHash takes a sized input buffer and writes to a constant-sized output buffer.
- // The first argument context is not used in this function.
- check_result(unsafe {
- DiceHash(
- ptr::null_mut(), // context
- input.as_ptr(),
- input.len(),
- output.as_mut_ptr(),
- )
- })?;
- Ok(output)
-}
-
-/// An implementation of HKDF-SHA512. Derives a key of `derived_key.len()` bytes from `ikm`, `salt`,
-/// and `info`. The derived key is written to the `derived_key`.
-pub fn kdf(ikm: &[u8], salt: &[u8], info: &[u8], derived_key: &mut [u8]) -> Result<()> {
- // SAFETY: The function writes to the `derived_key`, within the given bounds, and only reads the
- // input values. The first argument context is not used in this function.
- check_result(unsafe {
- DiceKdf(
- ptr::null_mut(), // context
- derived_key.len(),
- ikm.as_ptr(),
- ikm.len(),
- salt.as_ptr(),
- salt.len(),
- info.as_ptr(),
- info.len(),
- derived_key.as_mut_ptr(),
- )
- })
-}
-
-/// Deterministically generates a public and private key pair from `seed`.
-/// Since this is deterministic, `seed` is as sensitive as a private key and can
-/// be used directly as the private key.
-pub fn keypair_from_seed(seed: &[u8; PRIVATE_KEY_SEED_SIZE]) -> Result<(PublicKey, PrivateKey)> {
- let mut public_key = [0u8; PUBLIC_KEY_SIZE];
- let mut private_key = PrivateKey::default();
- // SAFETY: The function writes to the `public_key` and `private_key` within the given bounds,
- // and only reads the `seed`. The first argument context is not used in this function.
- check_result(unsafe {
- DiceKeypairFromSeed(
- ptr::null_mut(), // context
- seed.as_ptr(),
- public_key.as_mut_ptr(),
- private_key.as_mut_ptr(),
- )
- })?;
- Ok((public_key, private_key))
-}
-
-/// Signs the `message` with the give `private_key` using `DiceSign`.
-pub fn sign(message: &[u8], private_key: &[u8; PRIVATE_KEY_SIZE]) -> Result<Signature> {
- let mut signature = [0u8; SIGNATURE_SIZE];
- // SAFETY: The function writes to the `signature` within the given bounds, and only reads the
- // message and the private key. The first argument context is not used in this function.
- check_result(unsafe {
- DiceSign(
- ptr::null_mut(), // context
- message.as_ptr(),
- message.len(),
- private_key.as_ptr(),
- signature.as_mut_ptr(),
- )
- })?;
- Ok(signature)
-}
-
-/// Verifies the `signature` of the `message` with the given `public_key` using `DiceVerify`.
-pub fn verify(message: &[u8], signature: &Signature, public_key: &PublicKey) -> Result<()> {
- // SAFETY: only reads the messages, signature and public key as constant values.
- // The first argument context is not used in this function.
- check_result(unsafe {
- DiceVerify(
- ptr::null_mut(), // context
- message.as_ptr(),
- message.len(),
- signature.as_ptr(),
- public_key.as_ptr(),
- )
- })
-}
-
-/// Generates an X.509 certificate from the given `subject_private_key_seed` and
-/// `input_values`, and signed by `authority_private_key_seed`.
-/// The subject private key seed is supplied here so the implementation can choose
-/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
-/// Returns the actual size of the generated certificate.
-pub fn generate_certificate(
- subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- input_values: &InputValues,
- certificate: &mut [u8],
-) -> Result<usize> {
- let mut certificate_actual_size = 0;
- // SAFETY: The function writes to the `certificate` within the given bounds, and only reads the
- // input values and the key seeds. The first argument context is not used in this function.
- check_result(unsafe {
- DiceGenerateCertificate(
- ptr::null_mut(), // context
- subject_private_key_seed.as_ptr(),
- authority_private_key_seed.as_ptr(),
- input_values.as_ptr(),
- certificate.len(),
- certificate.as_mut_ptr(),
- &mut certificate_actual_size,
- )
- })?;
- Ok(certificate_actual_size)
-}
diff --git a/diced/open_dice/src/retry.rs b/diced/open_dice/src/retry.rs
deleted file mode 100644
index 76a214c9..00000000
--- a/diced/open_dice/src/retry.rs
+++ /dev/null
@@ -1,160 +0,0 @@
-// Copyright 2023, The Android Open Source Project
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//! This module implements a retry version for multiple DICE functions that
-//! require preallocated output buffer. As the retry functions require
-//! memory allocation on heap, currently we only expose these functions in
-//! std environment.
-
-use crate::bcc::{bcc_format_config_descriptor, bcc_main_flow};
-use crate::dice::{
- dice_main_flow, Cdi, CdiValues, DiceArtifacts, InputValues, CDI_SIZE, PRIVATE_KEY_SEED_SIZE,
-};
-use crate::error::{DiceError, Result};
-use crate::ops::generate_certificate;
-use std::ffi::CStr;
-
-/// Artifacts stores a set of dice artifacts comprising CDI_ATTEST, CDI_SEAL,
-/// and the BCC formatted attestation certificate chain.
-/// As we align with the DICE standards today, this is the certificate chain
-/// is also called DICE certificate chain.
-#[derive(Debug)]
-pub struct OwnedDiceArtifacts {
- /// CDI Values.
- cdi_values: CdiValues,
- /// Boot Certificate Chain.
- bcc: Vec<u8>,
-}
-
-impl DiceArtifacts for OwnedDiceArtifacts {
- fn cdi_attest(&self) -> &[u8; CDI_SIZE] {
- &self.cdi_values.cdi_attest
- }
-
- fn cdi_seal(&self) -> &[u8; CDI_SIZE] {
- &self.cdi_values.cdi_seal
- }
-
- fn bcc(&self) -> Option<&[u8]> {
- Some(&self.bcc)
- }
-}
-
-/// Retries the given function with bigger output buffer size.
-fn retry_with_bigger_buffer<F>(mut f: F) -> Result<Vec<u8>>
-where
- F: FnMut(&mut Vec<u8>) -> Result<usize>,
-{
- const INITIAL_BUFFER_SIZE: usize = 256;
- const MAX_BUFFER_SIZE: usize = 64 * 1024 * 1024;
-
- let mut buffer = vec![0u8; INITIAL_BUFFER_SIZE];
- while buffer.len() <= MAX_BUFFER_SIZE {
- match f(&mut buffer) {
- Err(DiceError::BufferTooSmall) => {
- let new_size = buffer.len() * 2;
- buffer.resize(new_size, 0);
- }
- Err(e) => return Err(e),
- Ok(actual_size) => {
- if actual_size > buffer.len() {
- panic!(
- "actual_size larger than buffer size: open-dice function
- may have written past the end of the buffer."
- );
- }
- buffer.truncate(actual_size);
- return Ok(buffer);
- }
- }
- }
- Err(DiceError::PlatformError)
-}
-
-/// Formats a configuration descriptor following the BCC's specification.
-pub fn retry_bcc_format_config_descriptor(
- name: Option<&CStr>,
- version: Option<u64>,
- resettable: bool,
-) -> Result<Vec<u8>> {
- retry_with_bigger_buffer(|buffer| {
- bcc_format_config_descriptor(name, version, resettable, buffer)
- })
-}
-
-/// Executes the main BCC flow.
-///
-/// Given a full set of input values along with the current BCC and CDI values,
-/// computes the next CDI values and matching updated BCC.
-pub fn retry_bcc_main_flow(
- current_cdi_attest: &Cdi,
- current_cdi_seal: &Cdi,
- bcc: &[u8],
- input_values: &InputValues,
-) -> Result<OwnedDiceArtifacts> {
- let mut next_cdi_values = CdiValues::default();
- let next_bcc = retry_with_bigger_buffer(|next_bcc| {
- bcc_main_flow(
- current_cdi_attest,
- current_cdi_seal,
- bcc,
- input_values,
- &mut next_cdi_values,
- next_bcc,
- )
- })?;
- Ok(OwnedDiceArtifacts { cdi_values: next_cdi_values, bcc: next_bcc })
-}
-
-/// Executes the main DICE flow.
-///
-/// Given a full set of input values and the current CDI values, computes the
-/// next CDI values and a matching certificate.
-pub fn retry_dice_main_flow(
- current_cdi_attest: &Cdi,
- current_cdi_seal: &Cdi,
- input_values: &InputValues,
-) -> Result<(CdiValues, Vec<u8>)> {
- let mut next_cdi_values = CdiValues::default();
- let next_cdi_certificate = retry_with_bigger_buffer(|next_cdi_certificate| {
- dice_main_flow(
- current_cdi_attest,
- current_cdi_seal,
- input_values,
- next_cdi_certificate,
- &mut next_cdi_values,
- )
- })?;
- Ok((next_cdi_values, next_cdi_certificate))
-}
-
-/// Generates an X.509 certificate from the given `subject_private_key_seed` and
-/// `input_values`, and signed by `authority_private_key_seed`.
-/// The subject private key seed is supplied here so the implementation can choose
-/// between asymmetric mechanisms, for example ECDSA vs Ed25519.
-/// Returns the generated certificate.
-pub fn retry_generate_certificate(
- subject_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- authority_private_key_seed: &[u8; PRIVATE_KEY_SEED_SIZE],
- input_values: &InputValues,
-) -> Result<Vec<u8>> {
- retry_with_bigger_buffer(|certificate| {
- generate_certificate(
- subject_private_key_seed,
- authority_private_key_seed,
- input_values,
- certificate,
- )
- })
-}
diff --git a/diced/open_dice/tests/api_test.rs b/diced/open_dice/tests/api_test.rs
deleted file mode 100644
index a47265b3..00000000
--- a/diced/open_dice/tests/api_test.rs
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2023 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-use diced_open_dice::{
- derive_cdi_certificate_id, derive_cdi_private_key_seed, hash, kdf, keypair_from_seed, sign,
- verify, CDI_SIZE, HASH_SIZE, ID_SIZE, PRIVATE_KEY_SEED_SIZE,
-};
-
-#[test]
-fn hash_succeeds() {
- const EXPECTED_HASH: [u8; HASH_SIZE] = [
- 0x30, 0x9e, 0xcc, 0x48, 0x9c, 0x12, 0xd6, 0xeb, 0x4c, 0xc4, 0x0f, 0x50, 0xc9, 0x02, 0xf2,
- 0xb4, 0xd0, 0xed, 0x77, 0xee, 0x51, 0x1a, 0x7c, 0x7a, 0x9b, 0xcd, 0x3c, 0xa8, 0x6d, 0x4c,
- 0xd8, 0x6f, 0x98, 0x9d, 0xd3, 0x5b, 0xc5, 0xff, 0x49, 0x96, 0x70, 0xda, 0x34, 0x25, 0x5b,
- 0x45, 0xb0, 0xcf, 0xd8, 0x30, 0xe8, 0x1f, 0x60, 0x5d, 0xcf, 0x7d, 0xc5, 0x54, 0x2e, 0x93,
- 0xae, 0x9c, 0xd7, 0x6f,
- ];
- assert_eq!(EXPECTED_HASH, hash(b"hello world").expect("hash failed"));
-}
-
-#[test]
-fn kdf_succeeds() {
- let mut derived_key = [0u8; PRIVATE_KEY_SEED_SIZE];
- kdf(b"myInitialKeyMaterial", b"mySalt", b"myInfo", &mut derived_key).unwrap();
- const EXPECTED_DERIVED_KEY: [u8; PRIVATE_KEY_SEED_SIZE] = [
- 0x91, 0x9b, 0x8d, 0x29, 0xc4, 0x1b, 0x93, 0xd7, 0xeb, 0x09, 0xfa, 0xd7, 0xc9, 0x87, 0xb0,
- 0xd1, 0xcc, 0x26, 0xef, 0x07, 0x83, 0x42, 0xcf, 0xa3, 0x45, 0x0a, 0x57, 0xe9, 0x19, 0x86,
- 0xef, 0x48,
- ];
- assert_eq!(EXPECTED_DERIVED_KEY, derived_key);
-}
-
-#[test]
-fn derive_cdi_certificate_id_succeeds() {
- const EXPECTED_ID: [u8; ID_SIZE] = [
- 0x7a, 0x36, 0x45, 0x2c, 0x02, 0xf6, 0x2b, 0xec, 0xf9, 0x80, 0x06, 0x75, 0x87, 0xa5, 0xc1,
- 0x44, 0x0c, 0xd3, 0xc0, 0x6d,
- ];
- assert_eq!(EXPECTED_ID, derive_cdi_certificate_id(b"MyPubKey").unwrap());
-}
-
-const EXPECTED_SEED: &[u8] = &[
- 0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba, 0xaa,
- 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5, 0x3a, 0x08,
- 0x84, 0x8a, 0x98, 0x85, 0x6d, 0xf5, 0x69, 0x21, 0x03, 0xcd, 0x09, 0xc3, 0x28, 0xd6, 0x06, 0xa7,
- 0x57, 0xbd, 0x48, 0x4b, 0x0f, 0x79, 0x0f, 0xf8, 0x2f, 0xf0, 0x0a, 0x41, 0x94, 0xd8, 0x8c, 0xa8,
-];
-
-const EXPECTED_CDI_ATTEST: &[u8] = &[
- 0xfa, 0x3c, 0x2f, 0x58, 0x37, 0xf5, 0x8e, 0x96, 0x16, 0x09, 0xf5, 0x22, 0xa1, 0xf1, 0xba, 0xaa,
- 0x19, 0x95, 0x01, 0x79, 0x2e, 0x60, 0x56, 0xaf, 0xf6, 0x41, 0xe7, 0xff, 0x48, 0xf5, 0x3a, 0x08,
-];
-
-const EXPECTED_CDI_PRIVATE_KEY_SEED: &[u8] = &[
- 0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe, 0x0d,
- 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72, 0x02, 0x6e,
-];
-
-const EXPECTED_PUB_KEY: &[u8] = &[
- 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b, 0xfc, 0x23, 0xc9,
- 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52, 0xf1, 0x61, 0x06, 0x37,
-];
-const EXPECTED_PRIV_KEY: &[u8] = &[
- 0x5f, 0xcc, 0x8e, 0x1a, 0xd1, 0xc2, 0xb3, 0xe9, 0xfb, 0xe1, 0x68, 0xf0, 0xf6, 0x98, 0xfe, 0x0d,
- 0xee, 0xd4, 0xb5, 0x18, 0xcb, 0x59, 0x70, 0x2d, 0xee, 0x06, 0xe5, 0x70, 0xf1, 0x72, 0x02, 0x6e,
- 0x47, 0x42, 0x4b, 0xbd, 0xd7, 0x23, 0xb4, 0xcd, 0xca, 0xe2, 0x8e, 0xdc, 0x6b, 0xfc, 0x23, 0xc9,
- 0x21, 0x5c, 0x48, 0x21, 0x47, 0xee, 0x5b, 0xfa, 0xaf, 0x88, 0x9a, 0x52, 0xf1, 0x61, 0x06, 0x37,
-];
-
-const EXPECTED_SIGNATURE: &[u8] = &[
- 0x44, 0xae, 0xcc, 0xe2, 0xb9, 0x96, 0x18, 0x39, 0x0e, 0x61, 0x0f, 0x53, 0x07, 0xbf, 0xf2, 0x32,
- 0x3d, 0x44, 0xd4, 0xf2, 0x07, 0x23, 0x30, 0x85, 0x32, 0x18, 0xd2, 0x69, 0xb8, 0x29, 0x3c, 0x26,
- 0xe6, 0x0d, 0x9c, 0xa5, 0xc2, 0x73, 0xcd, 0x8c, 0xb8, 0x3c, 0x3e, 0x5b, 0xfd, 0x62, 0x8d, 0xf6,
- 0xc4, 0x27, 0xa6, 0xe9, 0x11, 0x06, 0x5a, 0xb2, 0x2b, 0x64, 0xf7, 0xfc, 0xbb, 0xab, 0x4a, 0x0e,
-];
-
-#[test]
-fn hash_derive_sign_verify() {
- let seed = hash(b"MySeedString").unwrap();
- assert_eq!(seed, EXPECTED_SEED);
- let cdi_attest = &seed[..CDI_SIZE];
- assert_eq!(cdi_attest, EXPECTED_CDI_ATTEST);
- let cdi_private_key_seed = derive_cdi_private_key_seed(cdi_attest.try_into().unwrap()).unwrap();
- assert_eq!(cdi_private_key_seed.as_array(), EXPECTED_CDI_PRIVATE_KEY_SEED);
- let (pub_key, priv_key) = keypair_from_seed(cdi_private_key_seed.as_array()).unwrap();
- assert_eq!(&pub_key, EXPECTED_PUB_KEY);
- assert_eq!(priv_key.as_array(), EXPECTED_PRIV_KEY);
- let mut signature = sign(b"MyMessage", priv_key.as_array()).unwrap();
- assert_eq!(&signature, EXPECTED_SIGNATURE);
- assert!(verify(b"MyMessage", &signature, &pub_key).is_ok());
- assert!(verify(b"MyMessage_fail", &signature, &pub_key).is_err());
- signature[0] += 1;
- assert!(verify(b"MyMessage", &signature, &pub_key).is_err());
-}
diff --git a/diced/open_dice_cbor/lib.rs b/diced/open_dice_cbor/lib.rs
deleted file mode 100644
index 1650d936..00000000
--- a/diced/open_dice_cbor/lib.rs
+++ /dev/null
@@ -1,145 +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.
-
-// TODO(b/267575445): Move the tests to other target and remove the folder `open_dice_cbor` completely.
-#[cfg(test)]
-mod test {
- use diced_open_dice::DiceArtifacts;
- use diced_sample_inputs::make_sample_bcc_and_cdis;
-
- 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, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x3e,
- 0x85, 0xe5, 0x72, 0x75, 0x55, 0xe5, 0x1e, 0xe7, 0xf3, 0x35, 0x94, 0x8e, 0xbb, 0xbd, 0x74,
- 0x1e, 0x1d, 0xca, 0x49, 0x9c, 0x97, 0x39, 0x77, 0x06, 0xd3, 0xc8, 0x6e, 0x8b, 0xd7, 0x33,
- 0xf9, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9, 0x01, 0x78, 0x28, 0x34,
- 0x32, 0x64, 0x38, 0x38, 0x36, 0x34, 0x66, 0x39, 0x37, 0x62, 0x36, 0x35, 0x34, 0x37, 0x61,
- 0x35, 0x30, 0x63, 0x31, 0x65, 0x30, 0x61, 0x37, 0x34, 0x39, 0x66, 0x38, 0x65, 0x66, 0x38,
- 0x62, 0x38, 0x31, 0x65, 0x63, 0x36, 0x32, 0x61, 0x66, 0x02, 0x78, 0x28, 0x31, 0x66, 0x36,
- 0x39, 0x36, 0x66, 0x30, 0x37, 0x32, 0x35, 0x32, 0x66, 0x32, 0x39, 0x65, 0x39, 0x33, 0x66,
- 0x65, 0x34, 0x64, 0x65, 0x31, 0x39, 0x65, 0x65, 0x33, 0x32, 0x63, 0x64, 0x38, 0x31, 0x64,
- 0x63, 0x34, 0x30, 0x34, 0x65, 0x37, 0x36, 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x16,
- 0x48, 0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38, 0x63, 0x26,
- 0x0f, 0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c, 0x6d, 0xa2, 0xbe,
- 0x25, 0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2, 0xb3, 0x91, 0x4d,
- 0xd3, 0xfb, 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba, 0x30, 0xf7,
- 0x15, 0x98, 0x14, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x56, 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71,
- 0x63, 0x41, 0x42, 0x4c, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x01, 0x3a, 0x00, 0x01, 0x11, 0x73,
- 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x47, 0xae, 0x42, 0x27, 0x4c, 0xcb, 0x65,
- 0x4d, 0xee, 0x74, 0x2d, 0x05, 0x78, 0x2a, 0x08, 0x2a, 0xa5, 0xf0, 0xcf, 0xea, 0x3e, 0x60,
- 0xee, 0x97, 0x11, 0x4b, 0x5b, 0xe6, 0x05, 0x0c, 0xe8, 0x90, 0xf5, 0x22, 0xc4, 0xc6, 0x67,
- 0x7a, 0x22, 0x27, 0x17, 0xb3, 0x79, 0xcc, 0x37, 0x64, 0x5e, 0x19, 0x4f, 0x96, 0x37, 0x67,
- 0x3c, 0xd0, 0xc5, 0xed, 0x0f, 0xdd, 0xe7, 0x2e, 0x4f, 0x70, 0x97, 0x30, 0x3a, 0x00, 0x47,
- 0x44, 0x54, 0x58, 0x40, 0xf9, 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3,
- 0x97, 0x4a, 0xcb, 0x3c, 0xe7, 0x6b, 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59,
- 0x15, 0xb1, 0x23, 0xe6, 0xc8, 0xdf, 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc,
- 0x5b, 0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd, 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6,
- 0x29, 0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01,
- 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02,
- 0x20, 0x06, 0x21, 0x58, 0x20, 0xb1, 0x02, 0xcc, 0x2c, 0xb2, 0x6a, 0x3b, 0xe9, 0xc1, 0xd3,
- 0x95, 0x10, 0xa0, 0xe1, 0xff, 0x51, 0xde, 0x57, 0xd5, 0x65, 0x28, 0xfd, 0x7f, 0xeb, 0xd4,
- 0xca, 0x15, 0xf3, 0xca, 0xdf, 0x37, 0x88, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58,
- 0x40, 0x58, 0xd8, 0x03, 0x24, 0x53, 0x60, 0x57, 0xa9, 0x09, 0xfa, 0xab, 0xdc, 0x57, 0x1e,
- 0xf0, 0xe5, 0x1e, 0x51, 0x6f, 0x9e, 0xa3, 0x42, 0xe6, 0x6a, 0x8c, 0xaa, 0xad, 0x08, 0x48,
- 0xde, 0x7f, 0x4f, 0x6e, 0x2f, 0x7f, 0x39, 0x6c, 0xa1, 0xf8, 0x42, 0x71, 0xfe, 0x17, 0x3d,
- 0xca, 0x31, 0x83, 0x92, 0xed, 0xbb, 0x40, 0xb8, 0x10, 0xe0, 0xf2, 0x5a, 0x99, 0x53, 0x38,
- 0x46, 0x33, 0x97, 0x78, 0x05, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0, 0x59, 0x01, 0x8a, 0xa9,
- 0x01, 0x78, 0x28, 0x31, 0x66, 0x36, 0x39, 0x36, 0x66, 0x30, 0x37, 0x32, 0x35, 0x32, 0x66,
- 0x32, 0x39, 0x65, 0x39, 0x33, 0x66, 0x65, 0x34, 0x64, 0x65, 0x31, 0x39, 0x65, 0x65, 0x33,
- 0x32, 0x63, 0x64, 0x38, 0x31, 0x64, 0x63, 0x34, 0x30, 0x34, 0x65, 0x37, 0x36, 0x02, 0x78,
- 0x28, 0x32, 0x35, 0x39, 0x34, 0x38, 0x39, 0x65, 0x36, 0x39, 0x37, 0x34, 0x38, 0x37, 0x30,
- 0x35, 0x64, 0x65, 0x33, 0x65, 0x32, 0x66, 0x34, 0x34, 0x32, 0x36, 0x37, 0x65, 0x61, 0x34,
- 0x39, 0x33, 0x38, 0x66, 0x66, 0x36, 0x61, 0x35, 0x37, 0x32, 0x35, 0x3a, 0x00, 0x47, 0x44,
- 0x50, 0x58, 0x40, 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43,
- 0x83, 0x7f, 0x46, 0x8d, 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9,
- 0x56, 0xb3, 0xbf, 0x2f, 0xfa, 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b, 0x18,
- 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f, 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9, 0x71, 0xd2,
- 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7, 0x3a, 0x00, 0x47, 0x44, 0x53, 0x56, 0xa3, 0x3a,
- 0x00, 0x01, 0x11, 0x71, 0x63, 0x41, 0x56, 0x42, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x01, 0x3a,
- 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x93, 0x17, 0xe1,
- 0x11, 0x27, 0x59, 0xd0, 0xef, 0x75, 0x0b, 0x2b, 0x1c, 0x0f, 0x5f, 0x52, 0xc3, 0x29, 0x23,
- 0xb5, 0x2a, 0xe6, 0x12, 0x72, 0x6f, 0x39, 0x86, 0x65, 0x2d, 0xf2, 0xe4, 0xe7, 0xd0, 0xaf,
- 0x0e, 0xa7, 0x99, 0x16, 0x89, 0x97, 0x21, 0xf7, 0xdc, 0x89, 0xdc, 0xde, 0xbb, 0x94, 0x88,
- 0x1f, 0xda, 0xe2, 0xf3, 0xe0, 0x54, 0xf9, 0x0e, 0x29, 0xb1, 0xbd, 0xe1, 0x0c, 0x0b, 0xd7,
- 0xf6, 0x3a, 0x00, 0x47, 0x44, 0x54, 0x58, 0x40, 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa,
- 0x55, 0x6f, 0xac, 0x56, 0xd9, 0x02, 0x35, 0x2b, 0xaa, 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a,
- 0x86, 0xf5, 0xd4, 0xc2, 0xf1, 0xf9, 0x35, 0x7d, 0xe4, 0x43, 0x13, 0xbf, 0xfe, 0xd3, 0x36,
- 0xd8, 0x1c, 0x12, 0x78, 0x5c, 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab, 0x3d, 0x0f, 0x89, 0xa4,
- 0x6f, 0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98, 0x3a, 0x00, 0x47,
- 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00, 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03,
- 0x27, 0x04, 0x81, 0x02, 0x20, 0x06, 0x21, 0x58, 0x20, 0x96, 0x6d, 0x96, 0x42, 0xda, 0x64,
- 0x51, 0xad, 0xfa, 0x00, 0xbc, 0xbc, 0x95, 0x8a, 0xb0, 0xb9, 0x76, 0x01, 0xe6, 0xbd, 0xc0,
- 0x26, 0x79, 0x26, 0xfc, 0x0f, 0x1d, 0x87, 0x65, 0xf1, 0xf3, 0x99, 0x3a, 0x00, 0x47, 0x44,
- 0x58, 0x41, 0x20, 0x58, 0x40, 0x10, 0x7f, 0x77, 0xad, 0x70, 0xbd, 0x52, 0x81, 0x28, 0x8d,
- 0x24, 0x81, 0xb4, 0x3f, 0x21, 0x68, 0x9f, 0xc3, 0x80, 0x68, 0x86, 0x55, 0xfb, 0x2e, 0x6d,
- 0x96, 0xe1, 0xe1, 0xb7, 0x28, 0x8d, 0x63, 0x85, 0xba, 0x2a, 0x01, 0x33, 0x87, 0x60, 0x63,
- 0xbb, 0x16, 0x3f, 0x2f, 0x3d, 0xf4, 0x2d, 0x48, 0x5b, 0x87, 0xed, 0xda, 0x34, 0xeb, 0x9c,
- 0x4d, 0x14, 0xac, 0x65, 0xf4, 0xfa, 0xef, 0x45, 0x0b, 0x84, 0x43, 0xa1, 0x01, 0x27, 0xa0,
- 0x59, 0x01, 0x8f, 0xa9, 0x01, 0x78, 0x28, 0x32, 0x35, 0x39, 0x34, 0x38, 0x39, 0x65, 0x36,
- 0x39, 0x37, 0x34, 0x38, 0x37, 0x30, 0x35, 0x64, 0x65, 0x33, 0x65, 0x32, 0x66, 0x34, 0x34,
- 0x32, 0x36, 0x37, 0x65, 0x61, 0x34, 0x39, 0x33, 0x38, 0x66, 0x66, 0x36, 0x61, 0x35, 0x37,
- 0x32, 0x35, 0x02, 0x78, 0x28, 0x35, 0x64, 0x34, 0x65, 0x64, 0x37, 0x66, 0x34, 0x31, 0x37,
- 0x61, 0x39, 0x35, 0x34, 0x61, 0x31, 0x38, 0x31, 0x34, 0x30, 0x37, 0x62, 0x35, 0x38, 0x38,
- 0x35, 0x61, 0x66, 0x64, 0x37, 0x32, 0x61, 0x35, 0x62, 0x66, 0x34, 0x30, 0x64, 0x61, 0x36,
- 0x3a, 0x00, 0x47, 0x44, 0x50, 0x58, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0x00, 0x47, 0x44,
- 0x53, 0x58, 0x1a, 0xa3, 0x3a, 0x00, 0x01, 0x11, 0x71, 0x67, 0x41, 0x6e, 0x64, 0x72, 0x6f,
- 0x69, 0x64, 0x3a, 0x00, 0x01, 0x11, 0x72, 0x0c, 0x3a, 0x00, 0x01, 0x11, 0x73, 0xf6, 0x3a,
- 0x00, 0x47, 0x44, 0x52, 0x58, 0x40, 0x26, 0x1a, 0xbd, 0x26, 0xd8, 0x37, 0x8f, 0x4a, 0xf2,
- 0x9e, 0x49, 0x4d, 0x93, 0x23, 0xc4, 0x6e, 0x02, 0xda, 0xe0, 0x00, 0x02, 0xe7, 0xed, 0x29,
- 0xdf, 0x2b, 0xb3, 0x69, 0xf3, 0x55, 0x0e, 0x4c, 0x22, 0xdc, 0xcf, 0xf5, 0x92, 0xc9, 0xfa,
- 0x78, 0x98, 0xf1, 0x0e, 0x55, 0x5f, 0xf4, 0x45, 0xed, 0xc0, 0x0a, 0x72, 0x2a, 0x7a, 0x3a,
- 0xd2, 0xb1, 0xf7, 0x76, 0xfe, 0x2a, 0x6b, 0x7b, 0x2a, 0x53, 0x3a, 0x00, 0x47, 0x44, 0x54,
- 0x58, 0x40, 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30,
- 0x03, 0xb8, 0xd6, 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37,
- 0x68, 0x4e, 0x1d, 0xc0, 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43, 0xd2,
- 0x9c, 0xfc, 0x12, 0x9e, 0x77, 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7, 0x10, 0xd5,
- 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f, 0x3a, 0x00, 0x47, 0x44, 0x56, 0x41, 0x01, 0x3a, 0x00,
- 0x47, 0x44, 0x57, 0x58, 0x2d, 0xa5, 0x01, 0x01, 0x03, 0x27, 0x04, 0x81, 0x02, 0x20, 0x06,
- 0x21, 0x58, 0x20, 0xdb, 0xe7, 0x5b, 0x3f, 0xa3, 0x42, 0xb0, 0x9c, 0xf8, 0x40, 0x8c, 0xb0,
- 0x9c, 0xf0, 0x0a, 0xaf, 0xdf, 0x6f, 0xe5, 0x09, 0x21, 0x11, 0x92, 0xe1, 0xf8, 0xc5, 0x09,
- 0x02, 0x3d, 0x1f, 0xb7, 0xc5, 0x3a, 0x00, 0x47, 0x44, 0x58, 0x41, 0x20, 0x58, 0x40, 0xc4,
- 0xc1, 0xd7, 0x1c, 0x2d, 0x26, 0x89, 0x22, 0xcf, 0xa6, 0x99, 0x77, 0x30, 0x84, 0x86, 0x27,
- 0x59, 0x8f, 0xd8, 0x08, 0x75, 0xe0, 0xb2, 0xef, 0xf9, 0xfa, 0xa5, 0x40, 0x8c, 0xd3, 0xeb,
- 0xbb, 0xda, 0xf2, 0xc8, 0xae, 0x41, 0x22, 0x50, 0x9c, 0xe8, 0xb2, 0x9c, 0x9b, 0x3f, 0x8a,
- 0x78, 0x76, 0xab, 0xd0, 0xbe, 0xfc, 0xe4, 0x79, 0xcb, 0x1b, 0x2b, 0xaa, 0x4d, 0xdd, 0x15,
- 0x61, 0x42, 0x06,
- ];
-
- // 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 dice_artifacts = make_sample_bcc_and_cdis().unwrap();
- assert_eq!(dice_artifacts.cdi_attest(), SAMPLE_CDI_ATTEST_TEST_VECTOR);
- assert_eq!(dice_artifacts.cdi_seal(), SAMPLE_CDI_SEAL_TEST_VECTOR);
- assert_eq!(dice_artifacts.bcc(), Some(SAMPLE_BCC_TEST_VECTOR));
- }
-}
diff --git a/diced/src/sample_inputs.rs b/diced/src/sample_inputs.rs
deleted file mode 100644
index 8b914f3c..00000000
--- a/diced/src/sample_inputs.rs
+++ /dev/null
@@ -1,179 +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 anyhow::{anyhow, Context, Result};
-use ciborium::{de, ser, value::Value};
-use coset::{iana, Algorithm, AsCborValue, CoseKey, KeyOperation, KeyType, Label};
-use diced_open_dice::{
- derive_cdi_private_key_seed, keypair_from_seed, retry_bcc_format_config_descriptor,
- retry_bcc_main_flow, retry_dice_main_flow, Config, DiceArtifacts, DiceMode, InputValues,
- OwnedDiceArtifacts, CDI_SIZE, HASH_SIZE, HIDDEN_SIZE,
-};
-use std::ffi::CStr;
-
-/// Sample UDS used to perform the root dice flow by `make_sample_bcc_and_cdis`.
-pub const UDS: &[u8; CDI_SIZE] = &[
- 0x65, 0x4f, 0xab, 0xa9, 0xa5, 0xad, 0x0f, 0x5e, 0x15, 0xc3, 0x12, 0xf7, 0x77, 0x45, 0xfa, 0x55,
- 0x18, 0x6a, 0xa6, 0x34, 0xb6, 0x7c, 0x82, 0x7b, 0x89, 0x4c, 0xc5, 0x52, 0xd3, 0x27, 0x35, 0x8e,
-];
-
-const CODE_HASH_ABL: [u8; HASH_SIZE] = [
- 0x16, 0x48, 0xf2, 0x55, 0x53, 0x23, 0xdd, 0x15, 0x2e, 0x83, 0x38, 0xc3, 0x64, 0x38, 0x63, 0x26,
- 0x0f, 0xcf, 0x5b, 0xd1, 0x3a, 0xd3, 0x40, 0x3e, 0x23, 0xf8, 0x34, 0x4c, 0x6d, 0xa2, 0xbe, 0x25,
- 0x1c, 0xb0, 0x29, 0xe8, 0xc3, 0xfb, 0xb8, 0x80, 0xdc, 0xb1, 0xd2, 0xb3, 0x91, 0x4d, 0xd3, 0xfb,
- 0x01, 0x0f, 0xe4, 0xe9, 0x46, 0xa2, 0xc0, 0x26, 0x57, 0x5a, 0xba, 0x30, 0xf7, 0x15, 0x98, 0x14,
-];
-const AUTHORITY_HASH_ABL: [u8; HASH_SIZE] = [
- 0xf9, 0x00, 0x9d, 0xc2, 0x59, 0x09, 0xe0, 0xb6, 0x98, 0xbd, 0xe3, 0x97, 0x4a, 0xcb, 0x3c, 0xe7,
- 0x6b, 0x24, 0xc3, 0xe4, 0x98, 0xdd, 0xa9, 0x6a, 0x41, 0x59, 0x15, 0xb1, 0x23, 0xe6, 0xc8, 0xdf,
- 0xfb, 0x52, 0xb4, 0x52, 0xc1, 0xb9, 0x61, 0xdd, 0xbc, 0x5b, 0x37, 0x0e, 0x12, 0x12, 0xb2, 0xfd,
- 0xc1, 0x09, 0xb0, 0xcf, 0x33, 0x81, 0x4c, 0xc6, 0x29, 0x1b, 0x99, 0xea, 0xae, 0xfd, 0xaa, 0x0d,
-];
-const HIDDEN_ABL: [u8; HIDDEN_SIZE] = [
- 0xa2, 0x01, 0xd0, 0xc0, 0xaa, 0x75, 0x3c, 0x06, 0x43, 0x98, 0x6c, 0xc3, 0x5a, 0xb5, 0x5f, 0x1f,
- 0x0f, 0x92, 0x44, 0x3b, 0x0e, 0xd4, 0x29, 0x75, 0xe3, 0xdb, 0x36, 0xda, 0xc8, 0x07, 0x97, 0x4d,
- 0xff, 0xbc, 0x6a, 0xa4, 0x8a, 0xef, 0xc4, 0x7f, 0xf8, 0x61, 0x7d, 0x51, 0x4d, 0x2f, 0xdf, 0x7e,
- 0x8c, 0x3d, 0xa3, 0xfc, 0x63, 0xd4, 0xd4, 0x74, 0x8a, 0xc4, 0x14, 0x45, 0x83, 0x6b, 0x12, 0x7e,
-];
-const CODE_HASH_AVB: [u8; HASH_SIZE] = [
- 0xa4, 0x0c, 0xcb, 0xc1, 0xbf, 0xfa, 0xcc, 0xfd, 0xeb, 0xf4, 0xfc, 0x43, 0x83, 0x7f, 0x46, 0x8d,
- 0xd8, 0xd8, 0x14, 0xc1, 0x96, 0x14, 0x1f, 0x6e, 0xb3, 0xa0, 0xd9, 0x56, 0xb3, 0xbf, 0x2f, 0xfa,
- 0x88, 0x70, 0x11, 0x07, 0x39, 0xa4, 0xd2, 0xa9, 0x6b, 0x18, 0x28, 0xe8, 0x29, 0x20, 0x49, 0x0f,
- 0xbb, 0x8d, 0x08, 0x8c, 0xc6, 0x54, 0xe9, 0x71, 0xd2, 0x7e, 0xa4, 0xfe, 0x58, 0x7f, 0xd3, 0xc7,
-];
-const AUTHORITY_HASH_AVB: [u8; HASH_SIZE] = [
- 0xb2, 0x69, 0x05, 0x48, 0x56, 0xb5, 0xfa, 0x55, 0x6f, 0xac, 0x56, 0xd9, 0x02, 0x35, 0x2b, 0xaa,
- 0x4c, 0xba, 0x28, 0xdd, 0x82, 0x3a, 0x86, 0xf5, 0xd4, 0xc2, 0xf1, 0xf9, 0x35, 0x7d, 0xe4, 0x43,
- 0x13, 0xbf, 0xfe, 0xd3, 0x36, 0xd8, 0x1c, 0x12, 0x78, 0x5c, 0x9c, 0x3e, 0xf6, 0x66, 0xef, 0xab,
- 0x3d, 0x0f, 0x89, 0xa4, 0x6f, 0xc9, 0x72, 0xee, 0x73, 0x43, 0x02, 0x8a, 0xef, 0xbc, 0x05, 0x98,
-];
-const HIDDEN_AVB: [u8; HIDDEN_SIZE] = [
- 0x5b, 0x3f, 0xc9, 0x6b, 0xe3, 0x95, 0x59, 0x40, 0x5e, 0x64, 0xe5, 0x64, 0x3f, 0xfd, 0x21, 0x09,
- 0x9d, 0xf3, 0xcd, 0xc7, 0xa4, 0x2a, 0xe2, 0x97, 0xdd, 0xe2, 0x4f, 0xb0, 0x7d, 0x7e, 0xf5, 0x8e,
- 0xd6, 0x4d, 0x84, 0x25, 0x54, 0x41, 0x3f, 0x8f, 0x78, 0x64, 0x1a, 0x51, 0x27, 0x9d, 0x55, 0x8a,
- 0xe9, 0x90, 0x35, 0xab, 0x39, 0x80, 0x4b, 0x94, 0x40, 0x84, 0xa2, 0xfd, 0x73, 0xeb, 0x35, 0x7a,
-];
-const AUTHORITY_HASH_ANDROID: [u8; HASH_SIZE] = [
- 0x04, 0x25, 0x5d, 0x60, 0x5f, 0x5c, 0x45, 0x0d, 0xf2, 0x9a, 0x6e, 0x99, 0x30, 0x03, 0xb8, 0xd6,
- 0xe1, 0x99, 0x71, 0x1b, 0xf8, 0x44, 0xfa, 0xb5, 0x31, 0x79, 0x1c, 0x37, 0x68, 0x4e, 0x1d, 0xc0,
- 0x24, 0x74, 0x68, 0xf8, 0x80, 0x20, 0x3e, 0x44, 0xb1, 0x43, 0xd2, 0x9c, 0xfc, 0x12, 0x9e, 0x77,
- 0x0a, 0xde, 0x29, 0x24, 0xff, 0x2e, 0xfa, 0xc7, 0x10, 0xd5, 0x73, 0xd4, 0xc6, 0xdf, 0x62, 0x9f,
-];
-
-fn ed25519_public_key_to_cbor_value(public_key: &[u8]) -> Result<Value> {
- let key = CoseKey {
- kty: KeyType::Assigned(iana::KeyType::OKP),
- alg: Some(Algorithm::Assigned(iana::Algorithm::EdDSA)),
- key_ops: vec![KeyOperation::Assigned(iana::KeyOperation::Verify)].into_iter().collect(),
- params: vec![
- (
- Label::Int(iana::Ec2KeyParameter::Crv as i64),
- Value::from(iana::EllipticCurve::Ed25519 as u64),
- ),
- (Label::Int(iana::Ec2KeyParameter::X as i64), Value::Bytes(public_key.to_vec())),
- ],
- ..Default::default()
- };
- key.to_cbor_value()
- .map_err(|e| anyhow!(format!("Failed to serialize the key to CBOR data. Error: {e}")))
-}
-
-/// Makes a DICE chain (BCC) from the sample input.
-///
-/// The DICE chain is of the following format:
-/// public key derived from UDS -> ABL certificate -> AVB certificate -> Android certificate
-pub fn make_sample_bcc_and_cdis() -> Result<OwnedDiceArtifacts> {
- let private_key_seed = derive_cdi_private_key_seed(UDS)
- .context("In make_sample_bcc_and_cdis: Trying to derive private key seed.")?;
-
- // Gets the root public key in DICE chain (BCC).
- let (public_key, _) = keypair_from_seed(private_key_seed.as_array())
- .context("In make_sample_bcc_and_cids: Failed to generate key pair.")?;
- let ed25519_public_key_value = ed25519_public_key_to_cbor_value(&public_key)?;
-
- // Gets the ABL certificate to as the root certificate of DICE chain.
- let config_descriptor = retry_bcc_format_config_descriptor(
- Some(CStr::from_bytes_with_nul(b"ABL\0").unwrap()),
- Some(1), // version
- true,
- )?;
- let input_values = InputValues::new(
- CODE_HASH_ABL,
- Config::Descriptor(config_descriptor.as_slice()),
- AUTHORITY_HASH_ABL,
- DiceMode::kDiceModeNormal,
- HIDDEN_ABL,
- );
- let (cdi_values, cert) = retry_dice_main_flow(UDS, UDS, &input_values)
- .context("In make_sample_bcc_and_cdis: Trying to run first main flow.")?;
- let bcc_value = Value::Array(vec![
- ed25519_public_key_value,
- de::from_reader(&cert[..]).context("Deserialize root DICE certificate failed")?,
- ]);
- let mut bcc: Vec<u8> = vec![];
- ser::into_writer(&bcc_value, &mut bcc)?;
-
- // Appends AVB certificate to DICE chain.
- let config_descriptor = retry_bcc_format_config_descriptor(
- Some(CStr::from_bytes_with_nul(b"AVB\0").unwrap()),
- Some(1), // version
- true,
- )?;
- let input_values = InputValues::new(
- CODE_HASH_AVB,
- Config::Descriptor(config_descriptor.as_slice()),
- AUTHORITY_HASH_AVB,
- DiceMode::kDiceModeNormal,
- HIDDEN_AVB,
- );
- let dice_artifacts =
- retry_bcc_main_flow(&cdi_values.cdi_attest, &cdi_values.cdi_seal, &bcc, &input_values)
- .context("In make_sample_bcc_and_cdis: Trying to run first bcc main flow.")?;
-
- // Appends Android certificate to DICE chain.
- let config_descriptor = retry_bcc_format_config_descriptor(
- Some(CStr::from_bytes_with_nul(b"Android\0").unwrap()),
- Some(12), // version
- true,
- )?;
- let input_values = InputValues::new(
- [0u8; HASH_SIZE], // code_hash
- Config::Descriptor(config_descriptor.as_slice()),
- AUTHORITY_HASH_ANDROID,
- DiceMode::kDiceModeNormal,
- [0u8; HIDDEN_SIZE], // hidden
- );
- retry_bcc_main_flow(
- dice_artifacts.cdi_attest(),
- dice_artifacts.cdi_seal(),
- dice_artifacts.bcc().ok_or_else(|| anyhow!("bcc is none"))?,
- &input_values,
- )
- .context("In make_sample_bcc_and_cdis: Trying to run second bcc main flow.")
-}
-
-#[cfg(test)]
-mod test {
- use super::*;
-
- // This simple test checks if the invocation succeeds, essentially it tests
- // if the initial bcc is accepted by `diced_open_dice::retry_bcc_main_flow`.
- #[test]
- fn make_sample_bcc_and_cdis_test() {
- make_sample_bcc_and_cdis().unwrap();
- }
-}
diff --git a/diced/src/utils.rs b/diced/src/utils.rs
deleted file mode 100644
index 0931e685..00000000
--- a/diced/src/utils.rs
+++ /dev/null
@@ -1,169 +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.
-
-/// 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 << 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 << 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 << 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 << 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 << 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)).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/fsverity_manifest_generator.py b/fsverity/fsverity_manifest_generator.py
index 181758aa..ca7ac5cb 100644
--- a/fsverity/fsverity_manifest_generator.py
+++ b/fsverity/fsverity_manifest_generator.py
@@ -35,7 +35,7 @@ def _digest(fsverity_path, input_file):
return bytes(bytearray.fromhex(out))
if __name__ == '__main__':
- p = argparse.ArgumentParser()
+ p = argparse.ArgumentParser(fromfile_prefix_chars='@')
p.add_argument(
'--output',
help='Path to the output manifest',
@@ -52,7 +52,7 @@ if __name__ == '__main__':
'inputs',
nargs='*',
help='input file for the build manifest')
- args = p.parse_args(sys.argv[1:])
+ args = p.parse_args()
digests = FSVerityDigests()
for f in sorted(args.inputs):
diff --git a/fsverity/libfsverity_rs/Android.bp b/fsverity/libfsverity_rs/Android.bp
new file mode 100644
index 00000000..91b12486
--- /dev/null
+++ b/fsverity/libfsverity_rs/Android.bp
@@ -0,0 +1,17 @@
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_library {
+ name: "libfsverity_rs",
+ crate_name: "fsverity",
+ srcs: ["lib.rs"],
+ edition: "2021",
+ rustlibs: [
+ "libnix",
+ ],
+ apex_available: [
+ "com.android.compos",
+ "com.android.virt",
+ ],
+}
diff --git a/fsverity/libfsverity_rs/lib.rs b/fsverity/libfsverity_rs/lib.rs
new file mode 100644
index 00000000..473b2d5c
--- /dev/null
+++ b/fsverity/libfsverity_rs/lib.rs
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! A wrapper library to use fs-verity
+
+mod sys;
+
+use crate::sys::*;
+use std::io;
+use std::os::fd::AsRawFd;
+use std::os::unix::io::BorrowedFd;
+
+fn read_metadata(fd: i32, metadata_type: u64, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
+ let mut arg = fsverity_read_metadata_arg {
+ metadata_type,
+ offset,
+ length: buf.len() as u64,
+ buf_ptr: buf.as_mut_ptr() as u64,
+ __reserved: 0,
+ };
+ // SAFETY: the ioctl doesn't change the sematics in the current process
+ Ok(unsafe { read_verity_metadata(fd, &mut arg) }? as usize)
+}
+
+/// Read the raw Merkle tree from the fd, if it exists. The API semantics is similar to a regular
+/// pread(2), and may not return full requested buffer.
+pub fn read_merkle_tree(fd: i32, offset: u64, buf: &mut [u8]) -> io::Result<usize> {
+ read_metadata(fd, FS_VERITY_METADATA_TYPE_MERKLE_TREE, offset, buf)
+}
+
+/// Read the fs-verity signature from the fd (if exists). The returned signature should be complete.
+pub fn read_signature(fd: i32, buf: &mut [u8]) -> io::Result<usize> {
+ read_metadata(fd, FS_VERITY_METADATA_TYPE_SIGNATURE, 0 /* offset */, buf)
+}
+
+/// Enable fs-verity to the `fd`, with sha256 hash algorithm and 4KB block size.
+pub fn enable(fd: BorrowedFd) -> io::Result<()> {
+ let arg = fsverity_enable_arg {
+ version: 1,
+ hash_algorithm: FS_VERITY_HASH_ALG_SHA256,
+ block_size: 4096,
+ salt_size: 0,
+ salt_ptr: 0,
+ sig_size: 0,
+ __reserved1: 0,
+ sig_ptr: 0,
+ __reserved2: [0; 11],
+ };
+ // SAFETY: the ioctl doesn't change the sematics in the current process
+ if unsafe { enable_verity(fd.as_raw_fd(), &arg) } == Ok(0) {
+ Ok(())
+ } else {
+ Err(io::Error::last_os_error())
+ }
+}
diff --git a/fsverity/libfsverity_rs/sys.rs b/fsverity/libfsverity_rs/sys.rs
new file mode 100644
index 00000000..8ce0836d
--- /dev/null
+++ b/fsverity/libfsverity_rs/sys.rs
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//! Stable API definition copied from uapi/linux/fsverity.h
+
+use nix::{ioctl_readwrite, ioctl_write_ptr};
+
+const FS_IOCTL_MAGIC: u8 = b'f';
+const FS_IOC_ENABLE_VERITY: u8 = 133;
+const FS_IOCTL_READ_VERITY_METADATA: u8 = 135;
+
+pub const FS_VERITY_HASH_ALG_SHA256: u32 = 1;
+pub const FS_VERITY_METADATA_TYPE_MERKLE_TREE: u64 = 1;
+pub const FS_VERITY_METADATA_TYPE_SIGNATURE: u64 = 3;
+
+#[repr(C)]
+pub struct fsverity_read_metadata_arg {
+ pub metadata_type: u64,
+ pub offset: u64,
+ pub length: u64,
+ pub buf_ptr: u64,
+ pub __reserved: u64,
+}
+
+ioctl_readwrite!(
+ read_verity_metadata,
+ FS_IOCTL_MAGIC,
+ FS_IOCTL_READ_VERITY_METADATA,
+ fsverity_read_metadata_arg
+);
+
+#[repr(C)]
+pub struct fsverity_enable_arg {
+ pub version: u32,
+ pub hash_algorithm: u32,
+ pub block_size: u32,
+ pub salt_size: u32,
+ pub salt_ptr: u64,
+ pub sig_size: u32,
+ pub __reserved1: u32,
+ pub sig_ptr: u64,
+ pub __reserved2: [u64; 11],
+}
+
+ioctl_write_ptr!(enable_verity, FS_IOCTL_MAGIC, FS_IOC_ENABLE_VERITY, fsverity_enable_arg);
diff --git a/fsverity_init/Android.bp b/fsverity_init/Android.bp
index 83c59457..55884937 100644
--- a/fsverity_init/Android.bp
+++ b/fsverity_init/Android.bp
@@ -10,11 +10,11 @@ package {
cc_binary {
name: "fsverity_init",
srcs: [
- "main.cpp",
+ "fsverity_init.cpp",
],
static_libs: [
+ "aconfig_fsverity_init_c_lib",
"libc++fs",
- "libfsverity_init",
"libmini_keyctl_static",
],
shared_libs: [
@@ -22,22 +22,21 @@ cc_binary {
"libkeyutils",
"liblog",
],
- cflags: ["-Werror", "-Wall", "-Wextra"],
+ 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,
+aconfig_declarations {
+ name: "aconfig_fsverity_init",
+ package: "android.security.flag",
+ container: "system",
+ srcs: ["flags.aconfig"],
+}
+
+cc_aconfig_library {
+ name: "aconfig_fsverity_init_c_lib",
+ aconfig_declarations: "aconfig_fsverity_init",
}
diff --git a/fsverity_init/flags.aconfig b/fsverity_init/flags.aconfig
new file mode 100644
index 00000000..495c71c4
--- /dev/null
+++ b/fsverity_init/flags.aconfig
@@ -0,0 +1,10 @@
+package: "android.security.flag"
+container: "system"
+
+flag {
+ name: "deprecate_fsverity_init"
+ namespace: "hardware_backed_security"
+ description: "Feature flag for deprecate fsverity_init"
+ bug: "290064770"
+ is_fixed_read_only: true
+}
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index 61f84ddf..717beebc 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -14,6 +14,25 @@
* limitations under the License.
*/
+//
+// fsverity_init is a tool for loading X.509 certificates into the kernel keyring used by the
+// fsverity builtin signature verification kernel feature
+// (https://www.kernel.org/doc/html/latest/filesystems/fsverity.html#built-in-signature-verification).
+// Starting in Android 14, Android has actually stopped using this feature, as it was too inflexible
+// and caused problems. It has been replaced by userspace signature verification. Also, some uses
+// of fsverity in Android are now for integrity-only use cases.
+//
+// Regardless, there may exist fsverity files on-disk that were created by Android 13 or earlier.
+// These files still have builtin signatures. If the kernel is an older kernel that still has
+// CONFIG_FS_VERITY_BUILTIN_SIGNATURES enabled, these files cannot be opened unless the
+// corresponding key is in the ".fs-verity" keyring. Therefore, this tool still has to exist and be
+// used to load keys into the kernel, even though this has no security purpose anymore.
+//
+// This tool can be removed as soon as all supported kernels are guaranteed to have
+// CONFIG_FS_VERITY_BUILTIN_SIGNATURES disabled, or alternatively as soon as support for upgrades
+// from Android 13 or earlier is no longer required.
+//
+
#define LOG_TAG "fsverity_init"
#include <sys/types.h>
@@ -23,33 +42,11 @@
#include <android-base/file.h>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android-base/strings.h>
+#include <android_security_flag.h>
#include <log/log.h>
#include <mini_keyctl_utils.h>
-bool LoadKeyToKeyring(key_serial_t keyring_id, const char* desc, const char* data, size_t size) {
- key_serial_t key = add_key("asymmetric", desc, data, size, keyring_id);
- if (key < 0) {
- PLOG(ERROR) << "Failed to add key";
- return false;
- }
- return true;
-}
-
-bool 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;
- }
- 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) {
LOG(INFO) << "LoadKeyFromFile path=" << path << " keyname=" << keyname;
std::string content;
@@ -57,8 +54,8 @@ void LoadKeyFromFile(key_serial_t keyring_id, const char* keyname, const std::st
LOG(ERROR) << "Failed to read key from " << path;
return;
}
- if (!LoadKeyToKeyring(keyring_id, keyname, content.c_str(), content.size())) {
- LOG(ERROR) << "Failed to load key from " << path;
+ if (add_key("asymmetric", keyname, content.c_str(), content.size(), keyring_id) < 0) {
+ PLOG(ERROR) << "Failed to add key from " << path;
}
}
@@ -81,3 +78,35 @@ 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 (android::security::flag::deprecate_fsverity_init()) {
+ // Don't load keys to the built-in fs-verity keyring in kernel. This will make existing
+ // files not readable. We expect to only enable the flag when there are no such files or
+ // when failure is ok (e.g. with a fallback).
+ return 0;
+ }
+
+ if (argc < 2) {
+ LOG(ERROR) << "Not enough arguments";
+ return -1;
+ }
+
+ key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+ if (keyring_id < 0) {
+ // This is expected on newer kernels. See comment at the beginning of this file.
+ LOG(DEBUG) << "no initialization required";
+ return 0;
+ }
+
+ const std::string_view command = argv[1];
+
+ if (command == "--load-verified-keys") {
+ LoadKeyFromVerifiedPartitions(keyring_id);
+ } 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 b502b91c..00000000
--- a/fsverity_init/main.cpp
+++ /dev/null
@@ -1,62 +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") {
- 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 4f203e6a..a5635321 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -1,4 +1,5 @@
package {
+ default_team: "trendy_team_android_hardware_backed_security",
// 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"
@@ -17,55 +18,63 @@ cc_defaults {
"-Wno-deprecated-declarations",
],
sanitize: {
- misc_undefined : ["integer"],
+ misc_undefined: ["integer"],
},
}
-cc_binary {
- name: "credstore",
+cc_defaults {
+ name: "credstore_defaults",
defaults: [
"identity_defaults",
"identity_use_latest_hal_aidl_cpp_static",
"keymint_use_latest_hal_aidl_ndk_shared",
"keymint_use_latest_hal_aidl_cpp_static",
+ "android.hardware.identity-support-lib-deps",
],
-
srcs: [
"Credential.cpp",
"CredentialData.cpp",
"CredentialStore.cpp",
"CredentialStoreFactory.cpp",
- "RemotelyProvisionedKey.cpp",
"Session.cpp",
"Util.cpp",
"WritableCredential.cpp",
- "main.cpp",
],
- init_rc: ["credstore.rc"],
shared_libs: [
- "android.hardware.identity-support-lib",
"android.hardware.keymaster@4.0",
"android.security.authorization-ndk",
- "android.security.remoteprovisioning-cpp",
"libbase",
"libbinder",
"libbinder_ndk",
- "libcredstore_aidl",
"libcrypto",
"libhidlbase",
- "libkeymaster4support",
- "libkeystore-attestation-application-id",
+ "liblog",
"libutils",
"libutilscallstack",
- "libvintf",
+ "libkeystore-attestation-application-id",
],
static_libs: [
- "android.hardware.security.rkp-V3-cpp",
"android.hardware.keymaster-V3-cpp",
+ "android.hardware.identity-support-lib",
+ "android.hardware.security.rkp-V3-cpp",
"android.security.rkp_aidl-cpp",
- "libcppbor_external",
+ "libcppbor",
+ "libcredstore_aidl",
+ "libkeymaster4support",
+ "librkp_support",
+ ],
+}
+
+cc_binary {
+ name: "credstore",
+ defaults: [
+ "credstore_defaults",
+ ],
+ srcs: [
+ "main.cpp",
],
+ init_rc: ["credstore.rc"],
}
filegroup {
@@ -90,11 +99,11 @@ filegroup {
path: "binder",
}
-cc_library_shared {
+cc_library_static {
name: "libcredstore_aidl",
srcs: [
":credstore_aidl",
- ],
+ ],
aidl: {
export_aidl_headers: true,
include_dirs: [
@@ -104,9 +113,30 @@ cc_library_shared {
shared_libs: [
"libbinder",
"libutils",
+ ],
+ static_libs: [
"libkeymaster4support",
],
export_shared_lib_headers: [
"libbinder",
],
}
+
+cc_fuzz {
+ name: "credstore_service_fuzzer",
+ defaults: [
+ "credstore_defaults",
+ "service_fuzzer_defaults",
+ "fuzzer_disable_leaks",
+ ],
+ srcs: [
+ "fuzzers/credstore_service_fuzzer.cpp",
+ ],
+ fuzz_config: {
+ triage_assignee: "waghpawan@google.com",
+ cc: [
+ "trong@google.com",
+ "zeuthen@google.com",
+ ],
+ },
+}
diff --git a/identity/CredentialData.cpp b/identity/CredentialData.cpp
index 1bf1527b..ecf2258c 100644
--- a/identity/CredentialData.cpp
+++ b/identity/CredentialData.cpp
@@ -520,10 +520,13 @@ AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys,
bool allowUsingExpiredKeys) {
AuthKeyData* candidate = nullptr;
- int64_t nowMilliSeconds =
- std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()) * 1000;
+ time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
+ int64_t nowMilliSeconds;
+ if (__builtin_mul_overflow(int64_t(now), int64_t(1000), &nowMilliSeconds)) {
+ LOG(ERROR) << "Overflow converting " << now << " to milliseconds";
+ return nullptr;
+ }
- int n = 0;
for (AuthKeyData& data : authKeyDatas_) {
if (nowMilliSeconds > data.expirationDateMillisSinceEpoch) {
if (!allowUsingExpiredKeys) {
@@ -536,7 +539,6 @@ AuthKeyData* CredentialData::findAuthKey_(bool allowUsingExhaustedKeys,
candidate = &data;
}
}
- n++;
}
if (candidate == nullptr) {
diff --git a/identity/CredentialStore.cpp b/identity/CredentialStore.cpp
index e2b3cf46..57361c03 100644
--- a/identity/CredentialStore.cpp
+++ b/identity/CredentialStore.cpp
@@ -20,19 +20,15 @@
#include <optional>
#include <android-base/logging.h>
-#include <android-base/properties.h>
#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <android/hardware/security/keymint/RpcHardwareInfo.h>
-#include <android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.h>
-#include <android/security/remoteprovisioning/RemotelyProvisionedKey.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
-#include <vintf/VintfObject.h>
+#include <rkp/support/rkpd_client.h>
#include "Credential.h"
#include "CredentialData.h"
#include "CredentialStore.h"
-#include "RemotelyProvisionedKey.h"
#include "Session.h"
#include "Util.h"
#include "WritableCredential.h"
@@ -42,13 +38,8 @@ namespace security {
namespace identity {
namespace {
-using ::android::security::remoteprovisioning::IRemotelyProvisionedKeyPool;
-using ::android::security::rkp::IRemoteProvisioning;
-
-bool useRkpd() {
- return android::base::GetBoolProperty("remote_provisioning.enable_rkpd",
- /*default_value=*/true);
-}
+using ::android::security::rkp::RemotelyProvisionedKey;
+using ::android::security::rkp::support::getRpcKey;
} // namespace
@@ -189,61 +180,23 @@ Status CredentialStore::setRemotelyProvisionedAttestationKey(
std::vector<uint8_t> encodedCertChain;
Status status;
- if (useRkpd()) {
- LOG(INFO) << "Fetching attestation key from RKPD";
-
- uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
- auto rpcKeyFuture = getRpcKeyFuture(rpc_, callingUid);
- if (!rpcKeyFuture) {
- return Status::fromServiceSpecificError(ERROR_GENERIC, "Error in getRpcKeyFuture()");
- }
-
- if (rpcKeyFuture->wait_for(std::chrono::seconds(10)) != std::future_status::ready) {
- return Status::fromServiceSpecificError(
- ERROR_GENERIC, "Waiting for remotely provisioned attestation key timed out");
- }
-
- std::optional<::android::security::rkp::RemotelyProvisionedKey> key = rpcKeyFuture->get();
- if (!key) {
- return Status::fromServiceSpecificError(
- ERROR_GENERIC, "Failed to get remotely provisioned attestation key");
- }
-
- if (key->keyBlob.empty()) {
- return Status::fromServiceSpecificError(
- ERROR_GENERIC, "Remotely provisioned attestation key blob is empty");
- }
-
- keyBlob = std::move(key->keyBlob);
- encodedCertChain = std::move(key->encodedCertChain);
- } else {
- LOG(INFO) << "Fetching attestation key from remotely provisioned key pool.";
-
- sp<IRemotelyProvisionedKeyPool> keyPool =
- android::waitForService<IRemotelyProvisionedKeyPool>(
- IRemotelyProvisionedKeyPool::descriptor);
- if (!keyPool) {
- return Status::fromServiceSpecificError(
- ERROR_GENERIC, "Error getting IRemotelyProvisionedKeyPool HAL");
- }
-
- std::optional<std::string> rpcId = getRpcId(rpc_);
- if (!rpcId) {
- return Status::fromServiceSpecificError(
- ERROR_GENERIC, "Error getting remotely provisioned component id");
- }
+ LOG(INFO) << "Fetching attestation key from RKPD";
- uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
- ::android::security::remoteprovisioning::RemotelyProvisionedKey key;
- Status status = keyPool->getAttestationKey(callingUid, *rpcId, &key);
- if (!status.isOk()) {
- return status;
- }
+ uid_t callingUid = android::IPCThreadState::self()->getCallingUid();
+ std::optional<RemotelyProvisionedKey> key = getRpcKey(rpc_, callingUid);
+ if (!key) {
+ return Status::fromServiceSpecificError(
+ ERROR_GENERIC, "Failed to get remotely provisioned attestation key");
+ }
- keyBlob = std::move(key.keyBlob);
- encodedCertChain = std::move(key.encodedCertChain);
+ if (key->keyBlob.empty()) {
+ return Status::fromServiceSpecificError(
+ ERROR_GENERIC, "Remotely provisioned attestation key blob is empty");
}
+ keyBlob = std::move(key->keyBlob);
+ encodedCertChain = std::move(key->encodedCertChain);
+
status = halWritableCredential->setRemotelyProvisionedAttestationKey(keyBlob, encodedCertChain);
if (!status.isOk()) {
LOG(ERROR) << "Error setting remotely provisioned attestation key on credential";
diff --git a/identity/CredentialStore.h b/identity/CredentialStore.h
index 57c94e04..8bc02e8e 100644
--- a/identity/CredentialStore.h
+++ b/identity/CredentialStore.h
@@ -22,8 +22,6 @@
#include <android/hardware/identity/IIdentityCredentialStore.h>
#include <android/security/identity/BnCredentialStore.h>
-#include <android/security/remoteprovisioning/IRemotelyProvisionedKeyPool.h>
-#include <android/security/rkp/IRemoteProvisioning.h>
namespace android {
namespace security {
@@ -41,7 +39,6 @@ using ::android::hardware::identity::IIdentityCredentialStore;
using ::android::hardware::identity::IPresentationSession;
using ::android::hardware::identity::IWritableIdentityCredential;
using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
-using ::android::security::remoteprovisioning::IRemotelyProvisionedKeyPool;
class CredentialStore : public BnCredentialStore {
public:
diff --git a/identity/fuzzers/credstore_service_fuzzer.cpp b/identity/fuzzers/credstore_service_fuzzer.cpp
new file mode 100644
index 00000000..008cb0d8
--- /dev/null
+++ b/identity/fuzzers/credstore_service_fuzzer.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/logging.h>
+#include <fuzzbinder/libbinder_driver.h>
+#include <sys/stat.h>
+
+#include "CredentialStoreFactory.h"
+
+using android::security::identity::CredentialStoreFactory;
+using namespace android;
+
+void clearDirectory(const char* dirpath, bool recursive) {
+ DIR* dir = opendir(dirpath);
+ CHECK(dir != nullptr);
+ dirent* e;
+ struct stat s;
+ while ((e = readdir(dir)) != nullptr) {
+ if ((strcmp(e->d_name, ".") == 0) || (strcmp(e->d_name, "..") == 0)) {
+ continue;
+ }
+ std::string filename(dirpath);
+ filename.push_back('/');
+ filename.append(e->d_name);
+ int stat_result = lstat(filename.c_str(), &s);
+ CHECK_EQ(0, stat_result) << "unable to stat " << filename;
+ if (S_ISDIR(s.st_mode)) {
+ if (recursive) {
+ clearDirectory(filename.c_str(), true);
+ int rmdir_result = rmdir(filename.c_str());
+ CHECK_EQ(0, rmdir_result) << filename;
+ }
+ } else {
+ int unlink_result = unlink(filename.c_str());
+ CHECK_EQ(0, unlink_result) << filename;
+ }
+ }
+ closedir(dir);
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ std::string dataDir = "/data/cred_store_fuzzer";
+ mkdir(dataDir.c_str(), 0700);
+ sp<CredentialStoreFactory> service = sp<CredentialStoreFactory>::make(dataDir);
+ fuzzService(service, FuzzedDataProvider(data, size));
+ clearDirectory(dataDir.c_str(), true);
+ rmdir(dataDir.c_str());
+ return 0;
+}
diff --git a/identity/util/Android.bp b/identity/util/Android.bp
index 71d77187..771fe792 100644
--- a/identity/util/Android.bp
+++ b/identity/util/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_android_hardware_backed_security",
default_applicable_licenses: ["Android-Apache-2.0"],
}
diff --git a/keystore-engine/Android.bp b/keystore-engine/Android.bp
index cb75cde7..7fbfe53a 100644
--- a/keystore-engine/Android.bp
+++ b/keystore-engine/Android.bp
@@ -21,9 +21,14 @@ package {
default_applicable_licenses: ["system_security_license"],
}
-cc_library_shared {
+// This is expected to be cc_test_library but due to issue mentioned in b/298668920, b/314110490
+// we are creating cc_library and using static library to link with `keystore_client_tests`.
+cc_library {
name: "libkeystore-engine",
+ defaults: [
+ "keystore2_use_latest_aidl_ndk_shared",
+ ],
srcs: [
"android_engine.cpp",
"keystore2_engine.cpp",
@@ -36,7 +41,7 @@ cc_library_shared {
],
shared_libs: [
- "android.system.keystore2-V1-ndk",
+ "android.system.keystore2-V4-ndk",
"libbinder_ndk",
"libcrypto",
"libcutils",
diff --git a/keystore/Android.bp b/keystore/Android.bp
index 221ead9b..c79d00ba 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -69,19 +69,16 @@ cc_library {
defaults: ["keystore_defaults"],
srcs: [
- ":IKeyAttestationApplicationIdProvider.aidl",
"keystore_attestation_id.cpp",
- "KeyAttestationApplicationId.cpp",
- "KeyAttestationPackageInfo.cpp",
- "Signature.cpp",
],
shared_libs: [
+ "android.security.aaid_aidl-cpp",
"libbase",
"libbinder",
+ "libcrypto",
"libhidlbase",
"liblog",
"libutils",
- "libcrypto",
],
export_include_dirs: ["include"],
diff --git a/keystore/KeyAttestationApplicationId.cpp b/keystore/KeyAttestationApplicationId.cpp
deleted file mode 100644
index 1838b07d..00000000
--- a/keystore/KeyAttestationApplicationId.cpp
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
-**
-** Copyright 2016, 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 "include/keystore/KeyAttestationApplicationId.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-KeyAttestationApplicationId::KeyAttestationApplicationId() = default;
-
-KeyAttestationApplicationId::KeyAttestationApplicationId(
- std::optional<KeyAttestationPackageInfo> package)
- : packageInfos_(new std::vector<std::optional<KeyAttestationPackageInfo>>()) {
- packageInfos_->push_back(std::move(package));
-}
-
-KeyAttestationApplicationId::KeyAttestationApplicationId(PackageInfoVector packages)
- : packageInfos_(std::make_shared<PackageInfoVector>(std::move(packages))) {}
-
-status_t KeyAttestationApplicationId::writeToParcel(Parcel* parcel) const {
- return parcel->writeParcelableVector(packageInfos_);
-}
-
-status_t KeyAttestationApplicationId::readFromParcel(const Parcel* parcel) {
- std::optional<std::vector<std::optional<KeyAttestationPackageInfo>>> temp_vector;
- auto rc = parcel->readParcelableVector(&temp_vector);
- if (rc != NO_ERROR) return rc;
- packageInfos_.reset();
- if (temp_vector) {
- packageInfos_ = std::make_shared<PackageInfoVector>(std::move(*temp_vector));
- }
- return NO_ERROR;
-}
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
diff --git a/keystore/KeyAttestationPackageInfo.cpp b/keystore/KeyAttestationPackageInfo.cpp
deleted file mode 100644
index 8e9a36a2..00000000
--- a/keystore/KeyAttestationPackageInfo.cpp
+++ /dev/null
@@ -1,59 +0,0 @@
-/*
-**
-** Copyright 2016, 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 "include/keystore/KeyAttestationPackageInfo.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-KeyAttestationPackageInfo::KeyAttestationPackageInfo() = default;
-
-KeyAttestationPackageInfo::KeyAttestationPackageInfo(const String16& packageName,
- int64_t versionCode,
- SharedSignaturesVector signatures)
- : packageName_(packageName), versionCode_(versionCode), signatures_(signatures) {}
-
-status_t KeyAttestationPackageInfo::writeToParcel(Parcel* parcel) const {
- auto rc = parcel->writeString16(packageName_);
- if (rc != NO_ERROR) return rc;
- rc = parcel->writeInt64(versionCode_);
- if (rc != NO_ERROR) return rc;
- return parcel->writeParcelableVector(signatures_);
-}
-
-status_t KeyAttestationPackageInfo::readFromParcel(const Parcel* parcel) {
- auto rc = parcel->readString16(&packageName_);
- if (rc != NO_ERROR) return rc;
- rc = parcel->readInt64(&versionCode_);
- if (rc != NO_ERROR) return rc;
-
- std::optional<SignaturesVector> temp_vector;
- rc = parcel->readParcelableVector(&temp_vector);
- if (rc != NO_ERROR) return rc;
- signatures_.reset();
- if (temp_vector) {
- signatures_ = std::make_shared<SignaturesVector>(std::move(*temp_vector));
- }
- return NO_ERROR;
-}
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
diff --git a/keystore/Signature.cpp b/keystore/Signature.cpp
deleted file mode 100644
index 284f358c..00000000
--- a/keystore/Signature.cpp
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
-**
-** Copyright 2016, 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 "include/keystore/Signature.h"
-
-#include <binder/Parcel.h>
-
-namespace android {
-namespace content {
-namespace pm {
-
-status_t Signature::writeToParcel(Parcel* parcel) const {
- return parcel->writeByteVector(sig_data_);
-}
-
-status_t Signature::readFromParcel(const Parcel* parcel) {
- return parcel->readByteVector(&sig_data_);
-}
-
-Signature::Signature(std::vector<uint8_t> signature_data) : sig_data_(std::move(signature_data)) {}
-
-} // namespace pm
-} // namespace content
-} // namespace android
diff --git a/keystore/include/keystore/ExportResult.h b/keystore/include/keystore/ExportResult.h
deleted file mode 100644
index b5489427..00000000
--- a/keystore/include/keystore/ExportResult.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 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 KEYSTORE_INCLUDE_KEYSTORE_EXPORTRESULT_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_EXPORTRESULT_H_
-
-#include <binder/Parcelable.h>
-
-#include "keystore_return_types.h"
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-struct ExportResult : public ::android::Parcelable {
- ExportResult();
- ~ExportResult();
- status_t readFromParcel(const Parcel* in) override;
- status_t writeToParcel(Parcel* out) const override;
-
- ::keystore::KeyStoreServiceReturnCode resultCode;
- hardware::hidl_vec<uint8_t> exportData;
-};
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_EXPORTRESULT_H_
diff --git a/keystore/include/keystore/KeyAttestationApplicationId.h b/keystore/include/keystore/KeyAttestationApplicationId.h
deleted file mode 100644
index 0bf1aad0..00000000
--- a/keystore/include/keystore/KeyAttestationApplicationId.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2016 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 KEYSTORE_INCLUDE_KEYSTORE_KEYATTESTATIONAPPLICATIONID_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYATTESTATIONAPPLICATIONID_H_
-
-#include <memory>
-#include <optional>
-#include <vector>
-
-#include <binder/Parcelable.h>
-
-#include "KeyAttestationPackageInfo.h"
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-class KeyAttestationApplicationId : public Parcelable {
- public:
- typedef SharedNullableIterator<const KeyAttestationPackageInfo, std::vector>
- ConstKeyAttestationPackageInfoIterator;
- typedef std::vector<std::optional<KeyAttestationPackageInfo>> PackageInfoVector;
- KeyAttestationApplicationId();
- // Following c'tors are for initializing instances containing test data.
- explicit KeyAttestationApplicationId(std::optional<KeyAttestationPackageInfo> package);
- explicit KeyAttestationApplicationId(PackageInfoVector packages);
-
- status_t writeToParcel(Parcel*) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- ConstKeyAttestationPackageInfoIterator pinfos_begin() const {
- return ConstKeyAttestationPackageInfoIterator(packageInfos_);
- }
- ConstKeyAttestationPackageInfoIterator pinfos_end() const {
- return ConstKeyAttestationPackageInfoIterator();
- }
-
- private:
- std::shared_ptr<PackageInfoVector> packageInfos_;
-};
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYATTESTATIONAPPLICATIONID_H_
diff --git a/keystore/include/keystore/KeyAttestationPackageInfo.h b/keystore/include/keystore/KeyAttestationPackageInfo.h
deleted file mode 100644
index fa638f9a..00000000
--- a/keystore/include/keystore/KeyAttestationPackageInfo.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2016 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 KEYSTORE_INCLUDE_KEYSTORE_KEYATTESTATIONPACKAGEINFO_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYATTESTATIONPACKAGEINFO_H_
-
-#include <stdint.h>
-
-#include <memory>
-#include <optional>
-#include <vector>
-
-#include <binder/Parcelable.h>
-
-#include "Signature.h"
-#include "utils.h"
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-class KeyAttestationPackageInfo : public Parcelable {
- public:
- typedef SharedNullableIterator<const content::pm::Signature, std::vector>
- ConstSignatureIterator;
- typedef std::vector<std::optional<content::pm::Signature>> SignaturesVector;
- typedef std::shared_ptr<SignaturesVector> SharedSignaturesVector;
-
- KeyAttestationPackageInfo(const String16& packageName, int64_t versionCode,
- SharedSignaturesVector signatures);
- KeyAttestationPackageInfo();
-
- status_t writeToParcel(Parcel*) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- const std::optional<String16>& package_name() const { return packageName_; }
- int64_t version_code() const { return versionCode_; }
-
- ConstSignatureIterator sigs_begin() const { return ConstSignatureIterator(signatures_); }
- ConstSignatureIterator sigs_end() const { return ConstSignatureIterator(); }
-
- private:
- std::optional<String16> packageName_;
- int64_t versionCode_;
- SharedSignaturesVector signatures_;
-};
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYATTESTATIONPACKAGEINFO_H_
diff --git a/keystore/include/keystore/KeyCharacteristics.h b/keystore/include/keystore/KeyCharacteristics.h
deleted file mode 100644
index 9c90b8a1..00000000
--- a/keystore/include/keystore/KeyCharacteristics.h
+++ /dev/null
@@ -1,49 +0,0 @@
-// Copyright 2017 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 KEYSTORE_INCLUDE_KEYSTORE_KEYCHARACTERISTICS_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYCHARACTERISTICS_H_
-
-#include <binder/Parcelable.h>
-
-#include "KeymasterArguments.h"
-#include "keymaster_types.h"
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-// Parcelable version of keystore::KeyCharacteristics
-struct KeyCharacteristics : public ::android::Parcelable {
- KeyCharacteristics(){};
- explicit KeyCharacteristics(::keystore::KeyCharacteristics&& other) {
- softwareEnforced = std::move(other.softwareEnforced);
- hardwareEnforced = std::move(other.hardwareEnforced);
- }
- explicit KeyCharacteristics(const ::keystore::KeyCharacteristics& other) {
- softwareEnforced = KeymasterArguments(other.softwareEnforced);
- hardwareEnforced = KeymasterArguments(other.hardwareEnforced);
- }
- status_t readFromParcel(const Parcel* in) override;
- status_t writeToParcel(Parcel* out) const override;
-
- KeymasterArguments softwareEnforced;
- KeymasterArguments hardwareEnforced;
-};
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYCHARACTERISTICS_H_
diff --git a/keystore/include/keystore/KeymasterArguments.h b/keystore/include/keystore/KeymasterArguments.h
deleted file mode 100644
index 3d22f5f1..00000000
--- a/keystore/include/keystore/KeymasterArguments.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 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 KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERARGUMENTS_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERARGUMENTS_H_
-
-#include <binder/Parcelable.h>
-
-#include <keystore/keymaster_types.h>
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-// struct for serializing/deserializing a list of KeyParameters
-struct KeymasterArguments : public Parcelable {
- KeymasterArguments(){};
- // NOLINTNEXTLINE(google-explicit-constructor)
- KeymasterArguments(hardware::hidl_vec<::keystore::KeyParameter>&& other);
- explicit KeymasterArguments(const hardware::hidl_vec<::keystore::KeyParameter>& other);
-
- status_t readFromParcel(const Parcel* in) override;
- status_t writeToParcel(Parcel* out) const override;
-
- const inline hardware::hidl_vec<::keystore::KeyParameter>& getParameters() const {
- return data_;
- }
-
- private:
- hardware::hidl_vec<::keystore::KeyParameter> data_;
-};
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERARGUMENTS_H_
diff --git a/keystore/include/keystore/KeymasterBlob.h b/keystore/include/keystore/KeymasterBlob.h
deleted file mode 100644
index fc849bd0..00000000
--- a/keystore/include/keystore/KeymasterBlob.h
+++ /dev/null
@@ -1,40 +0,0 @@
-// Copyright 2017 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 KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERBLOB_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERBLOB_H_
-
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-// Parcelable which wraps hardware::hidl_vec<uint8_t>
-struct KeymasterBlob : public ::android::Parcelable {
- KeymasterBlob(){};
- explicit KeymasterBlob(hardware::hidl_vec<uint8_t> data) : data_(data) {}
- status_t readFromParcel(const Parcel* in) override;
- status_t writeToParcel(Parcel* out) const override;
- const hardware::hidl_vec<uint8_t>& getData() const { return data_; }
-
- private:
- hardware::hidl_vec<uint8_t> data_;
-};
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERBLOB_H_
diff --git a/keystore/include/keystore/KeymasterCertificateChain.h b/keystore/include/keystore/KeymasterCertificateChain.h
deleted file mode 100644
index f251d084..00000000
--- a/keystore/include/keystore/KeymasterCertificateChain.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2017 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 KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERCERTIFICATECHAIN_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERCERTIFICATECHAIN_H_
-
-#include <binder/Parcelable.h>
-#include <keystore/keymaster_types.h>
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-// struct for serializing keymaster_cert_chain_t's
-struct KeymasterCertificateChain : public ::android::Parcelable {
- KeymasterCertificateChain(){};
- explicit KeymasterCertificateChain(hardware::hidl_vec<hardware::hidl_vec<uint8_t>> other)
- : chain(std::move(other)) {}
-
- status_t readFromParcel(const Parcel* in) override;
- status_t writeToParcel(Parcel* out) const override;
-
- private:
- // The structure is only used as output and doesn't have getter.
- hardware::hidl_vec<hardware::hidl_vec<uint8_t>> chain;
-};
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYMASTERCERTIFICATECHAIN_H_
diff --git a/keystore/include/keystore/KeystoreResponse.h b/keystore/include/keystore/KeystoreResponse.h
deleted file mode 100644
index 4a7ef0df..00000000
--- a/keystore/include/keystore/KeystoreResponse.h
+++ /dev/null
@@ -1,58 +0,0 @@
-// Copyright 2018 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 KEYSTORE_INCLUDE_KEYSTORE_RESPONSE_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_RESPONSE_H_
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-#include <utils/String8.h>
-
-#include "keystore_return_types.h"
-
-namespace android {
-namespace security {
-namespace keystore {
-
-// struct for holding response code and optionally an error message for keystore
-// AIDL callbacks
-struct KeystoreResponse : public ::android::Parcelable {
- public:
- KeystoreResponse() = default;
- explicit KeystoreResponse(const int response_code, const String16& error_msg)
- : response_code_(response_code), error_msg_(error_msg) {}
- explicit KeystoreResponse(const int response_code)
- : response_code_(response_code), error_msg_() {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- KeystoreResponse(const ::keystore::KeyStoreServiceReturnCode& rc)
- : response_code_(rc.getErrorCode()), error_msg_() {}
- KeystoreResponse(const KeystoreResponse& other) = default;
- KeystoreResponse(KeystoreResponse&& other) = default;
-
- status_t readFromParcel(const Parcel* in) override;
- status_t writeToParcel(Parcel* out) const override;
-
- int response_code() const { return response_code_; }
- const std::optional<String16>& error_msg() const { return error_msg_; }
-
- private:
- int response_code_;
- std::optional<String16> error_msg_;
-};
-
-} // namespace keystore
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_RESPONSE_H_
diff --git a/keystore/include/keystore/OperationResult.h b/keystore/include/keystore/OperationResult.h
deleted file mode 100644
index caa7cdbf..00000000
--- a/keystore/include/keystore/OperationResult.h
+++ /dev/null
@@ -1,48 +0,0 @@
-// Copyright 2017 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 KEYSTORE_INCLUDE_KEYSTORE_OPERATIONRESULT_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_OPERATIONRESULT_H_
-
-#include <binder/Parcel.h>
-#include <binder/Parcelable.h>
-
-#include "keymaster_types.h"
-#include "keystore_return_types.h"
-
-namespace android {
-namespace security {
-namespace keymaster {
-
-struct OperationResult : public ::android::Parcelable {
- OperationResult();
- status_t readFromParcel(const Parcel* in) override;
- status_t writeToParcel(Parcel* out) const override;
-
- // Native code may need to use KeyStoreNativeReturnCode
- ::keystore::KeyStoreServiceReturnCode resultCode;
- sp<IBinder> token;
- uint64_t handle;
- int inputConsumed;
- ::keystore::hidl_vec<uint8_t> data;
- ::keystore::hidl_vec<::keystore::KeyParameter> outParams;
-};
-
-OperationResult operationFailed(const ::keystore::KeyStoreServiceReturnCode& error);
-
-} // namespace keymaster
-} // namespace security
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_OPERATIONRESULT_H_
diff --git a/keystore/include/keystore/Signature.h b/keystore/include/keystore/Signature.h
deleted file mode 100644
index f39acecf..00000000
--- a/keystore/include/keystore/Signature.h
+++ /dev/null
@@ -1,47 +0,0 @@
-// Copyright 2016 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 KEYSTORE_INCLUDE_KEYSTORE_SIGNATURE_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_SIGNATURE_H_
-
-#include <vector>
-
-#include <binder/Parcelable.h>
-
-namespace android {
-namespace content {
-namespace pm {
-
-class Signature : public Parcelable {
- public:
- Signature() = default;
- // Intended for initializing instances containing test data.
- explicit Signature(std::vector<uint8_t> signature_data);
-
- status_t writeToParcel(Parcel*) const override;
- status_t readFromParcel(const Parcel* parcel) override;
-
- const std::vector<uint8_t>& data() const & { return sig_data_; }
- std::vector<uint8_t>& data() & { return sig_data_; }
- std::vector<uint8_t>&& data() && { return std::move(sig_data_); }
-
- private:
- std::vector<uint8_t> sig_data_;
-};
-
-} // namespace pm
-} // namespace content
-} // namespace android
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_SIGNATURE_H_
diff --git a/keystore/include/keystore/keymaster_types.h b/keystore/include/keystore/keymaster_types.h
deleted file mode 100644
index 8da9682a..00000000
--- a/keystore/include/keystore/keymaster_types.h
+++ /dev/null
@@ -1,109 +0,0 @@
-// Copyright 2017 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 SECURITY_KEYSTORE_INCLUDE_KEYSTORE_KEYMASTER_TYPES_H_
-#define SECURITY_KEYSTORE_INCLUDE_KEYSTORE_KEYMASTER_TYPES_H_
-
-#include <android/hardware/keymaster/3.0/types.h>
-#include <android/hardware/keymaster/4.1/IKeymasterDevice.h>
-#include <android/hardware/keymaster/4.1/types.h>
-
-#include <keymasterV4_1/authorization_set.h>
-#include <keymasterV4_1/keymaster_tags.h>
-
-/**
- * This header lifts the types from the current Keymaster version into the keystore namespace.
- */
-
-namespace keystore {
-
-// Changing this namespace alias will change the keymaster version.
-namespace keymaster = ::android::hardware::keymaster::V4_1;
-
-using android::hardware::hidl_vec;
-using android::hardware::Return;
-
-using keymaster::IKeymasterDevice;
-using keymaster::SecurityLevel;
-
-using keymaster::AuthorizationSet;
-using keymaster::AuthorizationSetBuilder;
-
-// It's more convenient to use the V4.0 error and tag types by default.
-using ::android::hardware::keymaster::V4_0::ErrorCode;
-using ::android::hardware::keymaster::V4_0::Tag;
-
-using V4_1_ErrorCode = ::android::hardware::keymaster::V4_1::ErrorCode;
-using V4_1_Tag = ::android::hardware::keymaster::V4_1::Tag;
-
-using keymaster::Algorithm;
-using keymaster::BlockMode;
-using keymaster::Digest;
-using keymaster::EcCurve;
-using keymaster::HardwareAuthenticatorType;
-using keymaster::HardwareAuthToken;
-using keymaster::HmacSharingParameters;
-using keymaster::KeyCharacteristics;
-using keymaster::KeyFormat;
-using keymaster::KeyParameter;
-using keymaster::KeyPurpose;
-using keymaster::OperationHandle;
-using keymaster::PaddingMode;
-using keymaster::SecurityLevel;
-using keymaster::TagType;
-using keymaster::VerificationToken;
-
-using keymaster::TAG_ACTIVE_DATETIME;
-using keymaster::TAG_ALGORITHM;
-using keymaster::TAG_ALLOW_WHILE_ON_BODY;
-using keymaster::TAG_APPLICATION_DATA;
-using keymaster::TAG_APPLICATION_ID;
-using keymaster::TAG_ATTESTATION_APPLICATION_ID;
-using keymaster::TAG_AUTH_TIMEOUT;
-using keymaster::TAG_BLOB_USAGE_REQUIREMENTS;
-using keymaster::TAG_BLOCK_MODE;
-using keymaster::TAG_DIGEST;
-using keymaster::TAG_EC_CURVE;
-using keymaster::TAG_KEY_SIZE;
-using keymaster::TAG_MAC_LENGTH;
-using keymaster::TAG_MAX_USES_PER_BOOT;
-using keymaster::TAG_MIN_MAC_LENGTH;
-using keymaster::TAG_MIN_SECONDS_BETWEEN_OPS;
-using keymaster::TAG_NO_AUTH_REQUIRED;
-using keymaster::TAG_NONCE;
-using keymaster::TAG_ORIGIN;
-using keymaster::TAG_ORIGINATION_EXPIRE_DATETIME;
-using keymaster::TAG_PADDING;
-using keymaster::TAG_PURPOSE;
-using keymaster::TAG_RESET_SINCE_ID_ROTATION;
-using keymaster::TAG_RSA_PUBLIC_EXPONENT;
-using keymaster::TAG_USAGE_EXPIRE_DATETIME;
-using keymaster::TAG_USER_AUTH_TYPE;
-using keymaster::TAG_USER_ID;
-using keymaster::TAG_USER_SECURE_ID;
-
-using keymaster::NullOr;
-
-using Km3HardwareAuthToken = ::android::hardware::keymaster::V3_0::HardwareAuthToken;
-using Km3HardwareAuthenticatorType =
- ::android::hardware::keymaster::V3_0::HardwareAuthenticatorType;
-
-// The following create the numeric values that KM_TAG_PADDING and KM_TAG_DIGEST used to have. We
-// need these old values to be able to support old keys that use them.
-constexpr int32_t KM_TAG_DIGEST_OLD = static_cast<int32_t>(TagType::ENUM) | 5;
-constexpr int32_t KM_TAG_PADDING_OLD = static_cast<int32_t>(TagType::ENUM) | 7;
-
-} // namespace keystore
-
-#endif // SYSTEM_SECURITY_KEYSTORE_KM4_AUTHORIZATION_SET_H_
diff --git a/keystore/include/keystore/keystore.h b/keystore/include/keystore/keystore.h
deleted file mode 100644
index ab6c6829..00000000
--- a/keystore/include/keystore/keystore.h
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2009 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 __KEYSTORE_H__
-#define __KEYSTORE_H__
-
-#include <stdint.h>
-
-// note state values overlap with ResponseCode for the purposes of the state() API
-enum State {
- STATE_NO_ERROR = 1,
- STATE_LOCKED = 2,
- STATE_UNINITIALIZED = 3,
-};
-
-// must be in sync with KeyStore.java,
-enum class ResponseCode : int32_t {
- NO_ERROR = STATE_NO_ERROR, // 1
- LOCKED = STATE_LOCKED, // 2
- UNINITIALIZED = STATE_UNINITIALIZED, // 3
- SYSTEM_ERROR = 4,
- PROTOCOL_ERROR = 5,
- PERMISSION_DENIED = 6,
- KEY_NOT_FOUND = 7,
- VALUE_CORRUPTED = 8,
- UNDEFINED_ACTION = 9,
- WRONG_PASSWORD_0 = 10,
- WRONG_PASSWORD_1 = 11,
- WRONG_PASSWORD_2 = 12,
- WRONG_PASSWORD_3 = 13, // MAX_RETRY = 4
- SIGNATURE_INVALID = 14,
- OP_AUTH_NEEDED = 15, // Auth is needed for this operation before it can be used.
- KEY_ALREADY_EXISTS = 16,
- KEY_PERMANENTLY_INVALIDATED = 17,
-
- /**
- * Following three response codes are for logging purposes only.
- * The operations are logged at the end of the life cycle of an operation handle,
- * along with the reason for the end of the operation handle. For the operations
- * that fail in update and finish, the reason for failure is available with
- * the above response codes.
- * For the operations that are aborted in three different ways, the reason
- * for aborting is not available. The following enum values define the
- * three ways an operation can get aborted.
- */
- ABORT_CALLED = 18,
- PRUNED = 19,
- BINDER_DIED = 20,
-};
-
-/*
- * All the flags for import and insert calls.
- */
-enum KeyStoreFlag : uint8_t {
- KEYSTORE_FLAG_NONE = 0,
- KEYSTORE_FLAG_ENCRYPTED = 1 << 0,
- KEYSTORE_FLAG_FALLBACK = 1 << 1,
- // KEYSTORE_FLAG_SUPER_ENCRYPTED is for blobs that are already encrypted by keymaster but have
- // an additional layer of password-based encryption applied. The same encryption scheme is used
- // as KEYSTORE_FLAG_ENCRYPTED, but it's safe to remove super-encryption when the password is
- // cleared, rather than deleting blobs, and the error returned when attempting to use a
- // super-encrypted blob while keystore is locked is different.
- KEYSTORE_FLAG_SUPER_ENCRYPTED = 1 << 2,
- // KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION is for blobs that are part of device encryption
- // flow so it receives special treatment from keystore. For example this blob will not be super
- // encrypted, and it will be stored separately under an unique UID instead. This flag should
- // only be available to system uid.
- KEYSTORE_FLAG_CRITICAL_TO_DEVICE_ENCRYPTION = 1 << 3,
- KEYSTORE_FLAG_STRONGBOX = 1 << 4,
-};
-
-#endif
diff --git a/keystore/include/keystore/keystore_attestation_id.h b/keystore/include/keystore/keystore_attestation_id.h
index 238f4b12..a0d43ad8 100644
--- a/keystore/include/keystore/keystore_attestation_id.h
+++ b/keystore/include/keystore/keystore_attestation_id.h
@@ -25,11 +25,11 @@ namespace security {
constexpr size_t KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE = 1024;
-namespace keymaster {
+namespace keystore {
class KeyAttestationApplicationId;
-} // namespace keymaster
+} // namespace keystore
template <typename T> class StatusOr {
public:
@@ -77,7 +77,7 @@ StatusOr<std::vector<uint8_t>> gather_attestation_application_id(uid_t uid);
*/
StatusOr<std::vector<uint8_t>> build_attestation_application_id(
- const ::android::security::keymaster::KeyAttestationApplicationId& key_attestation_id);
+ const ::android::security::keystore::KeyAttestationApplicationId& key_attestation_id);
} // namespace security
} // namespace android
diff --git a/keystore/include/keystore/keystore_client.h b/keystore/include/keystore/keystore_client.h
deleted file mode 100644
index cb27268e..00000000
--- a/keystore/include/keystore/keystore_client.h
+++ /dev/null
@@ -1,191 +0,0 @@
-// Copyright 2015 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 KEYSTORE_KEYSTORE_CLIENT_H_
-#define KEYSTORE_KEYSTORE_CLIENT_H_
-
-#include <memory>
-#include <optional>
-#include <set>
-#include <string>
-#include <vector>
-
-#include <android-base/macros.h>
-
-#include "keymaster_types.h"
-#include "keystore.h"
-#include "keystore_return_types.h"
-
-namespace keystore {
-
-// An abstract class providing a convenient interface to keystore services. This
-// interface is designed to:
-// - hide details of the IPC mechanism (e.g. binder)
-// - use std data types
-// - encourage the use of keystore::AuthorizationSet[Builder]
-// - be convenient for native services integrating with keystore
-// - be safely mocked for unit testing (e.g. pure virtual methods)
-//
-// Example usage:
-// KeystoreClient* keystore = new KeyStoreClientImpl();
-// keystore->AddRandomNumberGeneratorEntropy("unpredictable");
-//
-// Notes on error codes:
-// Keystore binder methods return a variety of values including ResponseCode
-// values defined in keystore.h, keymaster_error_t values defined in
-// keymaster_defs.h, or just 0 or -1 (both of which conflict with
-// keymaster_error_t). The methods in this class converge on a single success
-// indicator for convenience. KM_ERROR_OK was chosen over ::NO_ERROR for two
-// reasons:
-// 1) KM_ERROR_OK is 0, which is a common convention for success, is the gmock
-// default, and allows error checks like 'if (error) {...'.
-// 2) Although both pollute the global namespace, KM_ERROR_OK has a prefix per
-// C convention and hopefully clients can use this interface without
-// needing to include 'keystore.h' directly.
-class KeystoreClient {
- public:
- KeystoreClient() = default;
- virtual ~KeystoreClient() = default;
-
- // Encrypts and authenticates |data| with minimal configuration for local
- // decryption. If a key identified by |key_name| does not already exist it
- // will be generated. On success returns true and populates |encrypted_data|.
- // Note: implementations may generate more than one key but they will always
- // have |key_name| as a prefix.
- virtual bool encryptWithAuthentication(const std::string& key_name, const std::string& data,
- int32_t flags, std::string* encrypted_data) = 0;
-
- // Decrypts and authenticates |encrypted_data| as output by
- // EncryptWithAuthentication using the key(s) identified by |key_name|. On
- // success returns true and populates |data|.
- virtual bool decryptWithAuthentication(const std::string& key_name,
- const std::string& encrypted_data,
- std::string* data) = 0;
-
- // Performs a Begin/Update/Finish sequence for an operation. The |purpose|,
- // |key_name|, |input_parameters|, and |output_parameters| are as in
- // BeginOperation. The |input_data| is as in UpdateOperation. The
- // |signature_to_verify| and |output_data| are as in FinishOperation. On
- // success returns true.
- virtual bool oneShotOperation(KeyPurpose purpose, const std::string& key_name,
- const keystore::AuthorizationSet& input_parameters,
- const std::string& input_data,
- const std::string& signature_to_verify,
- keystore::AuthorizationSet* output_parameters,
- std::string* output_data) = 0;
-
- // Adds |entropy| to the random number generator. Returns KM_ERROR_OK on
- // success and a Keystore ResponseCode or keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode addRandomNumberGeneratorEntropy(const std::string& entropy,
- int32_t flags) = 0;
-
- // Generates a key according to the given |key_parameters| and stores it with
- // the given |key_name|. The [hardware|software]_enforced_characteristics of
- // the key are provided on success. Returns KM_ERROR_OK on success. Returns
- // KM_ERROR_OK on success and a Keystore ResponseCode or keymaster_error_t on
- // failure.
- virtual KeyStoreNativeReturnCode
- generateKey(const std::string& key_name, const keystore::AuthorizationSet& key_parameters,
- int32_t flags, keystore::AuthorizationSet* hardware_enforced_characteristics,
- keystore::AuthorizationSet* software_enforced_characteristics) = 0;
-
- // Provides the [hardware|software]_enforced_characteristics of a key
- // identified by |key_name|. Returns KM_ERROR_OK on success and a Keystore
- // ResponseCode or keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode
- getKeyCharacteristics(const std::string& key_name,
- keystore::AuthorizationSet* hardware_enforced_characteristics,
- keystore::AuthorizationSet* software_enforced_characteristics) = 0;
-
- // Imports |key_data| in the given |key_format|, applies the given
- // |key_parameters|, and stores it with the given |key_name|. The
- // [hardware|software]_enforced_characteristics of the key are provided on
- // success. Returns KM_ERROR_OK on success and a Keystore ResponseCode or
- // keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode
- importKey(const std::string& key_name, const keystore::AuthorizationSet& key_parameters,
- KeyFormat key_format, const std::string& key_data,
- keystore::AuthorizationSet* hardware_enforced_characteristics,
- keystore::AuthorizationSet* software_enforced_characteristics) = 0;
-
- // Exports the public key identified by |key_name| to |export_data| using
- // |export_format|. Returns KM_ERROR_OK on success and a Keystore ResponseCode
- // or keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode exportKey(KeyFormat export_format, const std::string& key_name,
- std::string* export_data) = 0;
-
- // Deletes the key identified by |key_name|. Returns KM_ERROR_OK on success
- // and a Keystore ResponseCode or keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode deleteKey(const std::string& key_name) = 0;
-
- // Deletes all keys owned by the caller. Returns KM_ERROR_OK on success and a
- // Keystore ResponseCode or keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode deleteAllKeys() = 0;
-
- // Begins a cryptographic operation (e.g. encrypt, sign) identified by
- // |purpose| using the key identified by |key_name| and the given
- // |input_parameters|. On success, any |output_parameters| and an operation
- // |handle| are populated. Returns KM_ERROR_OK on success and a Keystore
- // ResponseCode or keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode
- beginOperation(KeyPurpose purpose, const std::string& key_name,
- const keystore::AuthorizationSet& input_parameters,
- keystore::AuthorizationSet* output_parameters, uint64_t* handle) = 0;
-
- // Continues the operation associated with |handle| using the given
- // |input_parameters| and |input_data|. On success, the
- // |num_input_bytes_consumed| and any |output_parameters| are populated. Any
- // |output_data| will be appended. Returns KM_ERROR_OK on success and a
- // Keystore ResponseCode or keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode
- updateOperation(uint64_t handle, const keystore::AuthorizationSet& input_parameters,
- const std::string& input_data, size_t* num_input_bytes_consumed,
- keystore::AuthorizationSet* output_parameters, std::string* output_data) = 0;
-
- // Finishes the operation associated with |handle| using the given
- // |input_parameters| and, if necessary, a |signature_to_verify|. On success,
- // any |output_parameters| are populated and |output_data| is appended.
- // Returns KM_ERROR_OK on success and a Keystore ResponseCode or
- // keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode
- finishOperation(uint64_t handle, const keystore::AuthorizationSet& input_parameters,
- const std::string& input_data, const std::string& signature_to_verify,
- keystore::AuthorizationSet* output_parameters, std::string* output_data) = 0;
-
- // Aborts the operation associated with |handle|. Returns KM_ERROR_OK on
- // success and a Keystore ResponseCode or keymaster_error_t on failure.
- virtual KeyStoreNativeReturnCode abortOperation(uint64_t handle) = 0;
-
- // Returns true if a key identified by |key_name| exists in the caller's
- // key store. Returns false if an error occurs.
- virtual bool doesKeyExist(const std::string& key_name) = 0;
-
- // Provides a |key_name_list| containing all existing key names in the
- // caller's key store starting with |prefix|. Returns true on success.
- virtual bool listKeys(const std::string& prefix, std::vector<std::string>* key_name_list) = 0;
-
- // Provides a |key_name_list| containing all existing key names in the
- // caller's key store starting with |prefix|. Returns true on success.
- virtual bool listKeysOfUid(const std::string& prefix, int uid,
- std::vector<std::string>* key_name_list) = 0;
-
- virtual std::optional<std::vector<uint8_t>> getKey(const std::string& alias, int uid) = 0;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(KeystoreClient);
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_KEYSTORE_CLIENT_H_
diff --git a/keystore/include/keystore/keystore_client_impl.h b/keystore/include/keystore/keystore_client_impl.h
deleted file mode 100644
index ed8ac446..00000000
--- a/keystore/include/keystore/keystore_client_impl.h
+++ /dev/null
@@ -1,127 +0,0 @@
-// Copyright 2015 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 KEYSTORE_KEYSTORE_CLIENT_IMPL_H_
-#define KEYSTORE_KEYSTORE_CLIENT_IMPL_H_
-
-#include "keystore_client.h"
-
-#include <future>
-#include <map>
-#include <optional>
-#include <string>
-#include <vector>
-
-#include <android/security/keystore/IKeystoreService.h>
-#include <binder/IBinder.h>
-#include <binder/IServiceManager.h>
-#include <utils/StrongPointer.h>
-
-namespace keystore {
-
-class KeystoreClientImpl : public KeystoreClient {
- public:
- KeystoreClientImpl();
- ~KeystoreClientImpl() override = default;
-
- // KeystoreClient methods.
- bool encryptWithAuthentication(const std::string& key_name, const std::string& data,
- int32_t flags, std::string* encrypted_data) override;
- bool decryptWithAuthentication(const std::string& key_name, const std::string& encrypted_data,
- std::string* data) override;
- bool oneShotOperation(KeyPurpose purpose, const std::string& key_name,
- const keystore::AuthorizationSet& input_parameters,
- const std::string& input_data, const std::string& signature_to_verify,
- keystore::AuthorizationSet* output_parameters,
- std::string* output_data) override;
- KeyStoreNativeReturnCode addRandomNumberGeneratorEntropy(const std::string& entropy,
- int32_t flags) override;
- KeyStoreNativeReturnCode
- generateKey(const std::string& key_name, const keystore::AuthorizationSet& key_parameters,
- int32_t flags, keystore::AuthorizationSet* hardware_enforced_characteristics,
- keystore::AuthorizationSet* software_enforced_characteristics) override;
- KeyStoreNativeReturnCode
- getKeyCharacteristics(const std::string& key_name,
- keystore::AuthorizationSet* hardware_enforced_characteristics,
- keystore::AuthorizationSet* software_enforced_characteristics) override;
- KeyStoreNativeReturnCode
- importKey(const std::string& key_name, const keystore::AuthorizationSet& key_parameters,
- KeyFormat key_format, const std::string& key_data,
- keystore::AuthorizationSet* hardware_enforced_characteristics,
- keystore::AuthorizationSet* software_enforced_characteristics) override;
- KeyStoreNativeReturnCode exportKey(KeyFormat export_format, const std::string& key_name,
- std::string* export_data) override;
- KeyStoreNativeReturnCode deleteKey(const std::string& key_name) override;
- KeyStoreNativeReturnCode deleteAllKeys() override;
- KeyStoreNativeReturnCode beginOperation(KeyPurpose purpose, const std::string& key_name,
- const keystore::AuthorizationSet& input_parameters,
- keystore::AuthorizationSet* output_parameters,
- uint64_t* handle) override;
- KeyStoreNativeReturnCode updateOperation(uint64_t handle,
- const keystore::AuthorizationSet& input_parameters,
- const std::string& input_data,
- size_t* num_input_bytes_consumed,
- keystore::AuthorizationSet* output_parameters,
- std::string* output_data) override;
- KeyStoreNativeReturnCode finishOperation(uint64_t handle,
- const keystore::AuthorizationSet& input_parameters,
- const std::string& input_data,
- const std::string& signature_to_verify,
- keystore::AuthorizationSet* output_parameters,
- std::string* output_data) override;
- KeyStoreNativeReturnCode abortOperation(uint64_t handle) override;
- bool doesKeyExist(const std::string& key_name) override;
- bool listKeys(const std::string& prefix, std::vector<std::string>* key_name_list) override;
- bool listKeysOfUid(const std::string& prefix, int uid,
- std::vector<std::string>* key_name_list) override;
- std::optional<std::vector<uint8_t>> getKey(const std::string& alias, int uid) override;
-
- private:
- // Returns an available virtual operation handle.
- uint64_t getNextVirtualHandle();
-
- // Maps a keystore error code to a code where all success cases use
- // KM_ERROR_OK (not keystore's NO_ERROR).
- // int32_t mapKeystoreError(int32_t keystore_error);
-
- // Creates an encryption key suitable for EncryptWithAuthentication or
- // verifies attributes if the key already exists. Returns true on success.
- bool createOrVerifyEncryptionKey(const std::string& key_name, int32_t flags);
-
- // Creates an authentication key suitable for EncryptWithAuthentication or
- // verifies attributes if the key already exists. Returns true on success.
- bool createOrVerifyAuthenticationKey(const std::string& key_name, int32_t flags);
-
- // Verifies attributes of an encryption key suitable for
- // EncryptWithAuthentication. Returns true on success and populates |verified|
- // with the result of the verification.
- bool verifyEncryptionKeyAttributes(const std::string& key_name, bool* verified);
-
- // Verifies attributes of an authentication key suitable for
- // EncryptWithAuthentication. Returns true on success and populates |verified|
- // with the result of the verification.
- bool verifyAuthenticationKeyAttributes(const std::string& key_name, bool* verified);
-
- android::sp<android::IServiceManager> service_manager_;
- android::sp<android::IBinder> keystore_binder_;
- android::sp<android::security::keystore::IKeystoreService> keystore_;
- uint64_t next_virtual_handle_ = 1;
- std::map<uint64_t, android::sp<android::IBinder>> active_operations_;
-
- DISALLOW_COPY_AND_ASSIGN(KeystoreClientImpl);
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_KEYSTORE_CLIENT_IMPL_H_
diff --git a/keystore/include/keystore/keystore_client_mock.h b/keystore/include/keystore/keystore_client_mock.h
deleted file mode 100644
index b16367fb..00000000
--- a/keystore/include/keystore/keystore_client_mock.h
+++ /dev/null
@@ -1,88 +0,0 @@
-// Copyright 2015 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 KEYSTORE_KEYSTORE_CLIENT_MOCK_H_
-#define KEYSTORE_KEYSTORE_CLIENT_MOCK_H_
-
-#include "keystore/keystore_client.h"
-#include "gmock/gmock.h"
-
-using testing::_;
-
-namespace keystore {
-
-// A mock implementation of KeystoreClient. By default all methods do nothing
-// and return KM_ERROR_OK (or false).
-class KeystoreClientMock : public KeystoreClient {
- public:
- KeystoreClientMock() = default;
- ~KeystoreClientMock() = default;
-
- MOCK_METHOD3(encryptWithAuthentication,
- bool(const std::string& key_name, const std::string& data,
- std::string* encrypted_data));
- MOCK_METHOD3(decryptWithAuthentication,
- bool(const std::string& key_name, const std::string& encrypted_data,
- std::string* data));
- MOCK_METHOD7(oneShotOperation,
- bool(keymaster_purpose_t purpose, const std::string& key_name,
- const keymaster::AuthorizationSet& input_parameters,
- const std::string& input_data, const std::string& signature_to_verify,
- keymaster::AuthorizationSet* output_parameters, std::string* output_data));
- MOCK_METHOD1(addRandomNumberGeneratorEntropy, int32_t(const std::string& entropy));
- MOCK_METHOD4(generateKey,
- int32_t(const std::string& key_name,
- const keymaster::AuthorizationSet& key_parameters,
- keymaster::AuthorizationSet* hardware_enforced_characteristics,
- keymaster::AuthorizationSet* software_enforced_characteristics));
- MOCK_METHOD3(getKeyCharacteristics,
- int32_t(const std::string& key_name,
- keymaster::AuthorizationSet* hardware_enforced_characteristics,
- keymaster::AuthorizationSet* software_enforced_characteristics));
- MOCK_METHOD6(importKey,
- int32_t(const std::string& key_name,
- const keymaster::AuthorizationSet& key_parameters,
- keymaster_key_format_t key_format, const std::string& key_data,
- keymaster::AuthorizationSet* hardware_enforced_characteristics,
- keymaster::AuthorizationSet* software_enforced_characteristics));
- MOCK_METHOD3(exportKey, int32_t(keymaster_key_format_t export_format,
- const std::string& key_name, std::string* export_data));
- MOCK_METHOD1(deleteKey, int32_t(const std::string& key_name));
- MOCK_METHOD0(deleteAllKeys, int32_t());
- MOCK_METHOD5(beginOperation, int32_t(keymaster_purpose_t purpose, const std::string& key_name,
- const keymaster::AuthorizationSet& input_parameters,
- keymaster::AuthorizationSet* output_parameters,
- keymaster_operation_handle_t* handle));
- MOCK_METHOD6(updateOperation,
- int32_t(keymaster_operation_handle_t handle,
- const keymaster::AuthorizationSet& input_parameters,
- const std::string& input_data, size_t* num_input_bytes_consumed,
- keymaster::AuthorizationSet* output_parameters, std::string* output_data));
- MOCK_METHOD5(finishOperation,
- int32_t(keymaster_operation_handle_t handle,
- const keymaster::AuthorizationSet& input_parameters,
- const std::string& signature_to_verify,
- keymaster::AuthorizationSet* output_parameters, std::string* output_data));
- MOCK_METHOD1(abortOperation, int32_t(keymaster_operation_handle_t handle));
- MOCK_METHOD1(doesKeyExist, bool(const std::string& key_name));
- MOCK_METHOD2(listKeys,
- bool(const std::string& prefix, std::vector<std::string>* key_name_list));
-
- private:
- DISALLOW_COPY_AND_ASSIGN(KeystoreClientMock);
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_KEYSTORE_CLIENT_MOCK_H_
diff --git a/keystore/include/keystore/keystore_concurrency.h b/keystore/include/keystore/keystore_concurrency.h
deleted file mode 100644
index 039ca313..00000000
--- a/keystore/include/keystore/keystore_concurrency.h
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
-**
-** Copyright 2018, 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 KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_CONCURRENCY_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_CONCURRENCY_H_
-
-#include <type_traits>
-
-namespace keystore {
-
-template <typename LockedType> class UnlockProxyLockHelper {
- private:
- std::function<void(LockedType*)> unlock_;
- LockedType* value_;
-
- public:
- using lockedType = LockedType;
- UnlockProxyLockHelper() : value_(nullptr) {}
- UnlockProxyLockHelper(LockedType* value, std::function<void(LockedType*)>&& unlock)
- : unlock_(std::move(unlock)), value_(value) {}
- ~UnlockProxyLockHelper() {
- if (unlock_) unlock_(value_);
- }
- UnlockProxyLockHelper(UnlockProxyLockHelper&& rhs)
- : unlock_(std::move(rhs.unlock_)), value_(rhs.value_) {
- rhs.value_ = nullptr;
- rhs.unlock_ = {};
- }
- UnlockProxyLockHelper& operator=(UnlockProxyLockHelper&& rhs) {
- if (this != &rhs) {
- UnlockProxyLockHelper dummy(std::move(*this));
- unlock_ = std::move(rhs.unlock_);
- value_ = std::move(rhs.value_);
- rhs.value_ = nullptr;
- rhs.unlock_ = {};
- }
- return *this;
- }
- UnlockProxyLockHelper(const UnlockProxyLockHelper& rhs) = delete;
- UnlockProxyLockHelper& operator=(const UnlockProxyLockHelper& rhs) = delete;
-
- template <typename T = LockedType>
- std::enable_if_t<!std::is_const<LockedType>::value, T*> value() {
- return value_;
- }
- const LockedType* value() const { return value_; }
-};
-
-template <typename LockedType, typename MutexType, template <typename> class GuardType>
-class MutexProxyLockHelper {
- private:
- GuardType<MutexType> lock_;
- LockedType* value_;
-
- public:
- using lockedType = LockedType;
- MutexProxyLockHelper() : value_(nullptr) {}
- MutexProxyLockHelper(LockedType* value, GuardType<MutexType>&& lock)
- : lock_(std::move(lock)), value_(value) {}
-
- template <typename T = LockedType>
- std::enable_if_t<!std::is_const<LockedType>::value, T*> value() {
- return value_;
- }
- const LockedType* value() const { return value_; }
-};
-
-template <typename Implementation> class ProxyLock {
- private:
- Implementation impl_;
-
- public:
- ProxyLock() : impl_() {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- template <typename... Args> ProxyLock(Args&&... args) : impl_{std::forward<Args>(args)...} {}
- explicit ProxyLock(Implementation&& impl) : impl_(std::move(impl)) {}
- explicit operator bool() const { return impl_.value() != nullptr; }
-
- template <typename T = typename Implementation::lockedType>
- std::enable_if_t<!std::is_const<typename Implementation::lockedType>::value, T*> operator->() {
- return impl_.value();
- }
-
- template <typename T = typename Implementation::lockedType>
- std::enable_if_t<!std::is_const<typename Implementation::lockedType>::value, T&> operator*() {
- return *impl_.value();
- }
-
- const std::remove_const_t<typename Implementation::lockedType>* operator->() const {
- return impl_.value();
- }
-
- const std::remove_const_t<typename Implementation::lockedType>& operator*() const {
- return *impl_.value();
- }
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_CONCURRENCY_H_
diff --git a/keystore/include/keystore/keystore_hidl_support.h b/keystore/include/keystore/keystore_hidl_support.h
deleted file mode 100644
index d1d7f165..00000000
--- a/keystore/include/keystore/keystore_hidl_support.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*
- **
- ** Copyright 2016, 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 KEYSTORE_KEYSTORE_HIDL_SUPPORT_H_
-#define KEYSTORE_KEYSTORE_HIDL_SUPPORT_H_
-
-#include <ostream>
-#include <sstream>
-#include <string>
-
-#include <android-base/logging.h>
-#include <android/hardware/keymaster/3.0/IKeymasterDevice.h>
-#include <hardware/hw_auth_token.h>
-#include <hidl/Status.h>
-#include <keymasterV4_0/keymaster_utils.h>
-
-#include <keystore/keymaster_types.h>
-
-namespace keystore {
-
-using android::hardware::keymaster::V4_0::support::blob2hidlVec;
-using android::hardware::keymaster::V4_0::support::hidlVec2AuthToken;
-using android::hardware::keymaster::V4_0::support::authToken2HidlVec;
-
-inline static std::ostream& formatArgs(std::ostream& out) {
- return out;
-}
-
-template <typename First, typename... Args>
-inline static std::ostream& formatArgs(std::ostream& out, First&& first, Args&&... args) {
- out << first;
- return formatArgs(out, args...);
-}
-
-template <typename... Args> inline static std::string argsToString(Args&&... args) {
- std::stringstream s;
- formatArgs(s, args...);
- return s.str();
-}
-
-template <typename KMDevice, typename... Msgs>
-inline static ErrorCode ksHandleHidlError(KMDevice dev, const Return<ErrorCode>& error,
- Msgs&&... msgs) {
- if (!error.isOk()) {
- LOG(ERROR) << "HIDL call failed with " << error.description().c_str() << " @ "
- << argsToString(msgs...);
- return ErrorCode::UNKNOWN_ERROR;
- }
- auto ec = ErrorCode(error);
- dev->logIfKeymasterVendorError(ec);
- return ec;
-}
-template <typename KMDevice, typename... Msgs>
-inline static ErrorCode ksHandleHidlError(KMDevice, const Return<void>& error, Msgs&&... msgs) {
- if (!error.isOk()) {
- ALOGE("HIDL call failed with %s @ %s", error.description().c_str(),
- argsToString(msgs...).c_str());
- return ErrorCode::UNKNOWN_ERROR;
- }
- return ErrorCode::OK;
-}
-
-#define KS_HANDLE_HIDL_ERROR(dev, rc) \
- ::keystore::ksHandleHidlError(dev, rc, __FILE__, ":", __LINE__, ":", __PRETTY_FUNCTION__)
-
-template <typename T, typename OutIter>
-inline static OutIter copy_bytes_to_iterator(const T& value, OutIter dest) {
- const uint8_t* value_ptr = reinterpret_cast<const uint8_t*>(&value);
- return std::copy(value_ptr, value_ptr + sizeof(value), dest);
-}
-
-constexpr size_t kHmacSize = 32;
-
-inline static hidl_vec<uint8_t> authToken2HidlVec(const Km3HardwareAuthToken& token) {
- static_assert(std::is_same<decltype(token.hmac),
- ::android::hardware::hidl_array<uint8_t, kHmacSize>>::value,
- "This function assumes token HMAC is 32 bytes, but it might not be.");
- static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
- sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
- sizeof(token.timestamp) + kHmacSize ==
- sizeof(hw_auth_token_t),
- "HardwareAuthToken content size does not match hw_auth_token_t size");
-
- hidl_vec<uint8_t> result;
- result.resize(sizeof(hw_auth_token_t));
- auto pos = result.begin();
- *pos++ = 0; // Version byte
- pos = copy_bytes_to_iterator(token.challenge, pos);
- pos = copy_bytes_to_iterator(token.userId, pos);
- pos = copy_bytes_to_iterator(token.authenticatorId, pos);
- pos = copy_bytes_to_iterator(token.authenticatorType, pos);
- pos = copy_bytes_to_iterator(token.timestamp, pos);
- pos = std::copy(token.hmac.data(), token.hmac.data() + token.hmac.size(), pos);
-
- return result;
-}
-
-template <typename T, typename InIter>
-inline static InIter copy_bytes_from_iterator(T* value, InIter src) {
- uint8_t* value_ptr = reinterpret_cast<uint8_t*>(value);
- std::copy(src, src + sizeof(T), value_ptr);
- return src + sizeof(T);
-}
-
-inline static Km3HardwareAuthToken hidlVec2Km3AuthToken(const hidl_vec<uint8_t>& buffer) {
- Km3HardwareAuthToken token;
- static_assert(std::is_same<decltype(token.hmac),
- ::android::hardware::hidl_array<uint8_t, kHmacSize>>::value,
- "This function assumes token HMAC is 32 bytes, but it might not be.");
- static_assert(1 /* version size */ + sizeof(token.challenge) + sizeof(token.userId) +
- sizeof(token.authenticatorId) + sizeof(token.authenticatorType) +
- sizeof(token.timestamp) + kHmacSize ==
- sizeof(hw_auth_token_t),
- "HardwareAuthToken content size does not match hw_auth_token_t size");
-
- if (buffer.size() != sizeof(hw_auth_token_t)) return {};
-
- auto pos = buffer.begin();
- ++pos; // skip first byte
- pos = copy_bytes_from_iterator(&token.challenge, pos);
- pos = copy_bytes_from_iterator(&token.userId, pos);
- pos = copy_bytes_from_iterator(&token.authenticatorId, pos);
- pos = copy_bytes_from_iterator(&token.authenticatorType, pos);
- pos = copy_bytes_from_iterator(&token.timestamp, pos);
- pos = std::copy(pos, pos + token.hmac.size(), &token.hmac[0]);
-
- return token;
-}
-
-inline std::string hidlVec2String(const hidl_vec<uint8_t>& value) {
- return std::string(reinterpret_cast<const std::string::value_type*>(&value[0]), value.size());
-}
-
-} // namespace keystore
-
-#endif // KEYSTORE_KEYSTORE_HIDL_SUPPORT_H_
diff --git a/keystore/include/keystore/keystore_promises.h b/keystore/include/keystore/keystore_promises.h
deleted file mode 100644
index 3d45016e..00000000
--- a/keystore/include/keystore/keystore_promises.h
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
-**
-** Copyright 2018, 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 KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_PROMISES_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_PROMISES_H_
-
-#include <android/security/keystore/BnKeystoreCertificateChainCallback.h>
-#include <android/security/keystore/BnKeystoreExportKeyCallback.h>
-#include <android/security/keystore/BnKeystoreKeyCharacteristicsCallback.h>
-#include <android/security/keystore/BnKeystoreOperationResultCallback.h>
-#include <android/security/keystore/BnKeystoreResponseCallback.h>
-#include <future>
-
-namespace keystore {
-
-template <typename BnInterface, typename Result>
-class CallbackPromise : public BnInterface, public std::promise<Result> {
- public:
- ::android::binder::Status onFinished(const Result& result) override {
- this->set_value(result);
- return ::android::binder::Status::ok();
- }
-};
-
-template <typename BnInterface, typename... Results>
-class CallbackPromise<BnInterface, std::tuple<Results...>>
- : public BnInterface, public std::promise<std::tuple<Results...>> {
- public:
- ::android::binder::Status onFinished(const Results&... results) override {
- this->set_value({results...});
- return ::android::binder::Status::ok();
- }
-};
-
-using OperationResultPromise =
- CallbackPromise<::android::security::keystore::BnKeystoreOperationResultCallback,
- ::android::security::keymaster::OperationResult>;
-
-using KeystoreResponsePromise =
- CallbackPromise<::android::security::keystore::BnKeystoreResponseCallback,
- ::android::security::keystore::KeystoreResponse>;
-
-using KeyCharacteristicsPromise =
- CallbackPromise<::android::security::keystore::BnKeystoreKeyCharacteristicsCallback,
- std::tuple<::android::security::keystore::KeystoreResponse,
- ::android::security::keymaster::KeyCharacteristics>>;
-using KeystoreExportPromise =
- CallbackPromise<::android::security::keystore::BnKeystoreExportKeyCallback,
- ::android::security::keymaster::ExportResult>;
-
-using KeyCertChainPromise =
- CallbackPromise<::android::security::keystore::BnKeystoreCertificateChainCallback,
- std::tuple<::android::security::keystore::KeystoreResponse,
- ::android::security::keymaster::KeymasterCertificateChain>>;
-
-} // namespace keystore
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_PROMISES_H_
diff --git a/keystore/include/keystore/keystore_return_types.h b/keystore/include/keystore/keystore_return_types.h
deleted file mode 100644
index 2762f8d1..00000000
--- a/keystore/include/keystore/keystore_return_types.h
+++ /dev/null
@@ -1,193 +0,0 @@
-/*
-**
-** Copyright 2016, 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 KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_RETURN_TYPES_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_RETURN_TYPES_H_
-
-#include "keymaster_types.h"
-#include "keystore.h"
-
-namespace keystore {
-
-class KeyStoreServiceReturnCode;
-class KeyStoreNativeReturnCode;
-
-/**
- * The keystore service return code is a bit tricky. It can return error codes from two name spaces:
- * ErrorCode, which has negative error codes and use 0 for ERROR_OK;
- * ResponseCode, which has positive error codes and uses 1 for NO_ERROR.
- * This class can be initialized by both. And when accessed through the operator int32_t () it
- * always returns ResponseCode::NO_ERROR (1) on success, even if it was initialized with
- * ErrorCode::OK (0), because this is what (java) clients expect.
- *
- * !!! Do not confuse this with KeyStoreNativeReturnCode which always converts to 0 on success. !!!
- */
-class KeyStoreServiceReturnCode {
- public:
- KeyStoreServiceReturnCode() : errorCode_(0) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- KeyStoreServiceReturnCode(const ErrorCode& errorCode) : errorCode_(int32_t(errorCode)) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- KeyStoreServiceReturnCode(const ResponseCode& errorCode) : errorCode_(int32_t(errorCode)) {}
- KeyStoreServiceReturnCode(const KeyStoreServiceReturnCode& errorCode)
- : errorCode_(errorCode.errorCode_) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- KeyStoreServiceReturnCode(const KeyStoreNativeReturnCode& errorCode);
- explicit inline KeyStoreServiceReturnCode(const int32_t& errorCode) : errorCode_(errorCode) {}
- inline KeyStoreServiceReturnCode& operator=(const ErrorCode& errorCode) {
- errorCode_ = int32_t(errorCode);
- return *this;
- }
- inline KeyStoreServiceReturnCode& operator=(const ResponseCode& errorCode) {
- errorCode_ = int32_t(errorCode);
- return *this;
- }
- inline KeyStoreServiceReturnCode& operator=(const KeyStoreServiceReturnCode& errorCode) {
- errorCode_ = errorCode.errorCode_;
- return *this;
- }
- inline bool isOk() const {
- return errorCode_ == static_cast<int32_t>(ResponseCode::NO_ERROR) ||
- errorCode_ == static_cast<int32_t>(ErrorCode::OK);
- }
-
- inline int32_t getErrorCode() const {
- if (!errorCode_) return static_cast<int32_t>(ResponseCode::NO_ERROR /* 1 */);
- return errorCode_;
- }
- inline bool operator==(const ResponseCode& rhs) const {
- return (rhs == ResponseCode::NO_ERROR &&
- errorCode_ == static_cast<int32_t>(ErrorCode::OK)) ||
- errorCode_ == int32_t(rhs);
- }
- inline bool operator==(const ErrorCode& rhs) const {
- return (rhs == ErrorCode::OK &&
- errorCode_ == static_cast<int32_t>(ResponseCode::NO_ERROR)) ||
- errorCode_ == int32_t(rhs);
- }
- inline bool operator!=(const ResponseCode& rhs) const { return !(*this == rhs); }
- inline bool operator!=(const ErrorCode& rhs) const { return !(*this == rhs); }
-
- private:
- int32_t errorCode_;
-};
-
-inline bool operator==(const ResponseCode& lhs, const KeyStoreServiceReturnCode& rhs) {
- return rhs == lhs;
-}
-inline bool operator==(const ErrorCode& lhs, const KeyStoreServiceReturnCode& rhs) {
- return rhs == lhs;
-}
-inline bool operator!=(const ResponseCode& lhs, const KeyStoreServiceReturnCode& rhs) {
- return rhs != lhs;
-}
-inline bool operator!=(const ErrorCode& lhs, const KeyStoreServiceReturnCode& rhs) {
- return rhs != lhs;
-}
-
-inline std::ostream& operator<<(std::ostream& out, const KeyStoreServiceReturnCode& error) {
- return out << error.getErrorCode();
-}
-
-/**
- * The keystore native return code is a bit tricky. It can return error codes from two name spaces:
- * ErrorCode, which has negative error codes and use 0 for ERROR_OK;
- * ResponseCode, which has positive error codes and uses 1 for NO_ERROR.
- * This class can be initialized by both. And when accessed through the operator int32_t () it
- * always returns ErrorCode::OK (0) on success, even if it was initialized with
- * ResponseCode::NO_ERROR (1), because this is what (native) clients expect.
- *
- * !!! Do not this confuse with KeyStoreServiceReturnCode which always converts to 1 on success. !!!
- */
-class KeyStoreNativeReturnCode {
- public:
- KeyStoreNativeReturnCode() : errorCode_(0) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- KeyStoreNativeReturnCode(const ErrorCode& errorCode) : errorCode_(int32_t(errorCode)) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- KeyStoreNativeReturnCode(const ResponseCode& errorCode) : errorCode_(int32_t(errorCode)) {}
- KeyStoreNativeReturnCode(const KeyStoreNativeReturnCode& errorCode)
- : errorCode_(errorCode.errorCode_) {}
- explicit inline KeyStoreNativeReturnCode(const int32_t& errorCode) : errorCode_(errorCode) {}
- // NOLINTNEXTLINE(google-explicit-constructor)
- KeyStoreNativeReturnCode(const KeyStoreServiceReturnCode& errorcode);
- inline KeyStoreNativeReturnCode& operator=(const ErrorCode& errorCode) {
- errorCode_ = int32_t(errorCode);
- return *this;
- }
- inline KeyStoreNativeReturnCode& operator=(const ResponseCode& errorCode) {
- errorCode_ = int32_t(errorCode);
- return *this;
- }
- inline KeyStoreNativeReturnCode& operator=(const KeyStoreNativeReturnCode& errorCode) {
- errorCode_ = errorCode.errorCode_;
- return *this;
- }
- inline bool isOk() const {
- return errorCode_ == static_cast<int32_t>(ResponseCode::NO_ERROR) ||
- errorCode_ == static_cast<int32_t>(ErrorCode::OK);
- }
- inline int32_t getErrorCode() const {
- if (errorCode_ == static_cast<int32_t>(ResponseCode::NO_ERROR) /* 1 */) {
- return static_cast<int32_t>(ErrorCode::OK) /* 0 */;
- }
- return errorCode_;
- }
- inline bool operator==(const ResponseCode& rhs) const {
- return (rhs == ResponseCode::NO_ERROR &&
- errorCode_ == static_cast<int32_t>(ErrorCode::OK)) ||
- errorCode_ == int32_t(rhs);
- }
- inline bool operator==(const ErrorCode& rhs) const {
- return (rhs == ErrorCode::OK &&
- errorCode_ == static_cast<int32_t>(ResponseCode::NO_ERROR)) ||
- errorCode_ == int32_t(rhs);
- }
- inline bool operator!=(const ResponseCode& rhs) const { return !(*this == rhs); }
- inline bool operator!=(const ErrorCode& rhs) const { return !(*this == rhs); }
-
- private:
- int32_t errorCode_;
-};
-
-inline bool operator==(const ResponseCode& lhs, const KeyStoreNativeReturnCode& rhs) {
- return rhs == lhs;
-}
-inline bool operator==(const ErrorCode& lhs, const KeyStoreNativeReturnCode& rhs) {
- return rhs == lhs;
-}
-inline bool operator!=(const ResponseCode& lhs, const KeyStoreNativeReturnCode& rhs) {
- return rhs != lhs;
-}
-inline bool operator!=(const ErrorCode& lhs, const KeyStoreNativeReturnCode& rhs) {
- return rhs != lhs;
-}
-
-inline KeyStoreNativeReturnCode::KeyStoreNativeReturnCode(
- const KeyStoreServiceReturnCode& errorCode)
- : errorCode_(errorCode.getErrorCode()) {}
-inline KeyStoreServiceReturnCode::KeyStoreServiceReturnCode(
- const KeyStoreNativeReturnCode& errorCode)
- : errorCode_(errorCode.getErrorCode()) {}
-
-inline std::ostream& operator<<(std::ostream& out, const KeyStoreNativeReturnCode& error) {
- return out << error.getErrorCode();
-}
-
-} // namespace keystore
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_KEYSTORE_RETURN_TYPES_H_
diff --git a/keystore/include/keystore/utils.h b/keystore/include/keystore/utils.h
deleted file mode 100644
index 2143d3af..00000000
--- a/keystore/include/keystore/utils.h
+++ /dev/null
@@ -1,101 +0,0 @@
-// TODO: Insert description here. (generated by jdanis)
-
-#ifndef KEYSTORE_INCLUDE_KEYSTORE_UTILS_H_
-#define KEYSTORE_INCLUDE_KEYSTORE_UTILS_H_
-
-#include <iterator>
-#include <memory>
-#include <optional>
-#include <vector>
-
-namespace android {
-namespace security {
-
-/*
- * This iterator abstracts from a collection of the form
- * std::shared_ptr<COLLECTION_TYPE<std::optional<T>>>
- * such that it is defined both for nulled outer pointer and
- * nulled entries. If shared_ptr(nullptr) is passed in, the iterator behaves
- * like the end iterator yielding an empty collection. Nulled
- * entries are skipped so that the iterator is always dereferencable unless
- * it is equal to end.
- * The default constructor always yields an iterator equal to end.
- * The same iterator invalidation rules apply as they do for the iterators
- * of the corresponding collection.
- */
-template <typename T, template <typename...> class Coll = std::vector>
-class SharedNullableIterator {
- public:
- typedef Coll<std::optional<typename std::remove_const<T>::type>> CollectionType;
- typedef std::shared_ptr<CollectionType> CollectionPtr;
-
- SharedNullableIterator() {}
- explicit SharedNullableIterator(const std::shared_ptr<CollectionType>& coll) : coll_(coll) {
- init();
- }
- explicit SharedNullableIterator(std::shared_ptr<CollectionType>&& coll) : coll_(coll) {
- init();
- }
-
- SharedNullableIterator(const SharedNullableIterator& other)
- : coll_(other.coll_), cur_(other.cur_) {}
- SharedNullableIterator(SharedNullableIterator&& other) noexcept
- : coll_(std::move(other.coll_)), cur_(std::move(other.cur_)) {}
-
- SharedNullableIterator& operator++() {
- inc();
- return *this;
- }
- SharedNullableIterator operator++(int) {
- SharedNullableIterator retval(*this);
- ++(*this);
- return retval;
- }
- T& operator*() const { return **cur_; }
-
- T* operator->() const { return &**cur_; }
-
- bool operator==(const SharedNullableIterator& other) const {
- return cur_ == other.cur_ || (is_end() && other.is_end());
- }
- bool operator!=(const SharedNullableIterator& other) const { return !(*this == other); }
-
- SharedNullableIterator& operator=(const SharedNullableIterator&) = default;
- SharedNullableIterator& operator=(SharedNullableIterator&&) noexcept = default;
-
- private:
- inline bool is_end() const { return !coll_ || cur_ == coll_->end(); }
- inline void inc() {
- if (!is_end()) {
- do {
- ++cur_;
- // move forward to the next non null member or stay at end
- } while (cur_ != coll_->end() && !(*cur_));
- }
- }
- void init() {
- if (coll_) {
- // move forward to the first non null member
- for (cur_ = coll_->begin(); cur_ != coll_->end() && !(*cur_); ++cur_) {
- }
- }
- }
-
- CollectionPtr coll_;
- typename CollectionType::iterator cur_;
-};
-
-} // namespace security
-} // namespace android
-
-namespace std {
-template <typename T, template <typename...> class COLL>
-struct iterator_traits<android::security::SharedNullableIterator<T, COLL>> {
- typedef T& reference;
- typedef T value_type;
- typedef T* pointer;
- typedef forward_iterator_tag iterator_category;
-};
-}
-
-#endif // KEYSTORE_INCLUDE_KEYSTORE_UTILS_H_
diff --git a/keystore/keystore_attestation_id.cpp b/keystore/keystore_attestation_id.cpp
index ccd38085..1534be16 100644
--- a/keystore/keystore_attestation_id.cpp
+++ b/keystore/keystore_attestation_id.cpp
@@ -29,11 +29,11 @@
#include <binder/Parcelable.h>
#include <binder/PersistableBundle.h>
-#include <android/security/keymaster/BpKeyAttestationApplicationIdProvider.h>
-#include <android/security/keymaster/IKeyAttestationApplicationIdProvider.h>
-#include <keystore/KeyAttestationApplicationId.h>
-#include <keystore/KeyAttestationPackageInfo.h>
-#include <keystore/Signature.h>
+#include <android/security/keystore/BpKeyAttestationApplicationIdProvider.h>
+#include <android/security/keystore/IKeyAttestationApplicationIdProvider.h>
+#include <android/security/keystore/KeyAttestationApplicationId.h>
+#include <android/security/keystore/KeyAttestationPackageInfo.h>
+#include <android/security/keystore/Signature.h>
#include <private/android_filesystem_config.h> /* for AID_SYSTEM */
@@ -50,13 +50,13 @@ namespace {
constexpr const char* kAttestationSystemPackageName = "AndroidSystem";
constexpr const char* kUnknownPackageName = "UnknownPackage";
-std::vector<uint8_t> signature2SHA256(const content::pm::Signature& sig) {
+std::vector<uint8_t> signature2SHA256(const security::keystore::Signature& sig) {
std::vector<uint8_t> digest_buffer(SHA256_DIGEST_LENGTH);
- SHA256(sig.data().data(), sig.data().size(), digest_buffer.data());
+ SHA256(sig.data.data(), sig.data.size(), digest_buffer.data());
return digest_buffer;
}
-using ::android::security::keymaster::BpKeyAttestationApplicationIdProvider;
+using ::android::security::keystore::BpKeyAttestationApplicationIdProvider;
class KeyAttestationApplicationIdProvider : public BpKeyAttestationApplicationIdProvider {
public:
@@ -74,8 +74,8 @@ KeyAttestationApplicationIdProvider& KeyAttestationApplicationIdProvider::get()
}
KeyAttestationApplicationIdProvider::KeyAttestationApplicationIdProvider()
- : BpKeyAttestationApplicationIdProvider(
- android::defaultServiceManager()->getService(String16("sec_key_att_app_id_provider"))) {}
+ : BpKeyAttestationApplicationIdProvider(android::defaultServiceManager()->waitForService(
+ String16("sec_key_att_app_id_provider"))) {}
DECLARE_STACK_OF(ASN1_OCTET_STRING);
@@ -141,8 +141,8 @@ namespace android {
namespace security {
namespace {
-using ::android::security::keymaster::KeyAttestationApplicationId;
-using ::android::security::keymaster::KeyAttestationPackageInfo;
+using ::android::security::keystore::KeyAttestationApplicationId;
+using ::android::security::keystore::KeyAttestationPackageInfo;
status_t build_attestation_package_info(const KeyAttestationPackageInfo& pinfo,
std::unique_ptr<KM_ATTESTATION_PACKAGE_INFO>* attestation_package_info_ptr) {
@@ -153,12 +153,12 @@ status_t build_attestation_package_info(const KeyAttestationPackageInfo& pinfo,
attestation_package_info.reset(KM_ATTESTATION_PACKAGE_INFO_new());
if (!attestation_package_info.get()) return NO_MEMORY;
- if (!pinfo.package_name()) {
+ if (!pinfo.packageName) {
ALOGE("Key attestation package info lacks package name");
return BAD_VALUE;
}
- std::string pkg_name(String8(*pinfo.package_name()).string());
+ std::string pkg_name(String8(pinfo.packageName).c_str());
if (!ASN1_OCTET_STRING_set(attestation_package_info->package_name,
reinterpret_cast<const unsigned char*>(pkg_name.data()),
pkg_name.size())) {
@@ -169,7 +169,7 @@ status_t build_attestation_package_info(const KeyAttestationPackageInfo& pinfo,
if (bn_version == nullptr) {
return NO_MEMORY;
}
- if (BN_set_u64(bn_version, static_cast<uint64_t>(pinfo.version_code())) != 1) {
+ if (BN_set_u64(bn_version, static_cast<uint64_t>(pinfo.versionCode)) != 1) {
BN_free(bn_version);
return UNKNOWN_ERROR;
}
@@ -201,15 +201,16 @@ build_attestation_application_id(const KeyAttestationApplicationId& key_attestat
auto attestation_pinfo_stack = reinterpret_cast<_STACK*>(attestation_id->package_infos);
- if (key_attestation_id.pinfos_begin() == key_attestation_id.pinfos_end()) return BAD_VALUE;
+ if (key_attestation_id.packageInfos.begin() == key_attestation_id.packageInfos.end())
+ return BAD_VALUE;
- for (auto pinfo = key_attestation_id.pinfos_begin(); pinfo != key_attestation_id.pinfos_end();
- ++pinfo) {
- if (!pinfo->package_name()) {
+ for (auto pinfo = key_attestation_id.packageInfos.begin();
+ pinfo != key_attestation_id.packageInfos.end(); ++pinfo) {
+ if (!pinfo->packageName) {
ALOGE("Key attestation package info lacks package name");
return BAD_VALUE;
}
- std::string package_name(String8(*pinfo->package_name()).string());
+ std::string package_name(String8(pinfo->packageName).c_str());
std::unique_ptr<KM_ATTESTATION_PACKAGE_INFO> attestation_package_info;
auto rc = build_attestation_package_info(*pinfo, &attestation_package_info);
if (rc != NO_ERROR) {
@@ -231,10 +232,10 @@ build_attestation_application_id(const KeyAttestationApplicationId& key_attestat
* signature field actually holds the signing certificate, rather than a signature, we can
* simply use the set of signature digests of the first package info.
*/
- const auto& pinfo = *key_attestation_id.pinfos_begin();
+ const auto& pinfo = *key_attestation_id.packageInfos.begin();
std::vector<std::vector<uint8_t>> signature_digests;
- for (auto sig = pinfo.sigs_begin(); sig != pinfo.sigs_end(); ++sig) {
+ for (auto sig = pinfo.signatures.begin(); sig != pinfo.signatures.end(); ++sig) {
signature_digests.push_back(signature2SHA256(*sig));
}
@@ -271,10 +272,10 @@ StatusOr<std::vector<uint8_t>> gather_attestation_application_id(uid_t uid) {
if (uid == AID_SYSTEM) {
/* Use a fixed ID for system callers */
- auto pinfo = std::make_optional<KeyAttestationPackageInfo>(
- String16(kAttestationSystemPackageName), 1 /* version code */,
- std::make_shared<KeyAttestationPackageInfo::SignaturesVector>());
- key_attestation_id = KeyAttestationApplicationId(std::move(pinfo));
+ auto pinfo = KeyAttestationPackageInfo();
+ pinfo.packageName = String16(kAttestationSystemPackageName);
+ pinfo.versionCode = 1;
+ key_attestation_id.packageInfos.push_back(std::move(pinfo));
} else {
/* Get the attestation application ID from package manager */
auto& pm = KeyAttestationApplicationIdProvider::get();
@@ -283,11 +284,12 @@ StatusOr<std::vector<uint8_t>> gather_attestation_application_id(uid_t uid) {
// caller is unknown.
if (!status.isOk()) {
ALOGW("package manager request for key attestation ID failed with: %s %d",
- status.exceptionMessage().string(), status.exceptionCode());
- auto pinfo = std::make_optional<KeyAttestationPackageInfo>(
- String16(kUnknownPackageName), 1 /* version code */,
- std::make_shared<KeyAttestationPackageInfo::SignaturesVector>());
- key_attestation_id = KeyAttestationApplicationId(std::move(pinfo));
+ status.exceptionMessage().c_str(), status.exceptionCode());
+
+ auto pinfo = KeyAttestationPackageInfo();
+ pinfo.packageName = String16(kUnknownPackageName);
+ pinfo.versionCode = 1;
+ key_attestation_id.packageInfos.push_back(std::move(pinfo));
}
}
diff --git a/keystore/keystore_get.cpp b/keystore/keystore_get.cpp
deleted file mode 100644
index a6f87557..00000000
--- a/keystore/keystore_get.cpp
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2012 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/security/keystore/IKeystoreService.h>
-#include <binder/IServiceManager.h>
-
-#include <keystore/keystore_get.h>
-#include <vector>
-
-using namespace android;
-using namespace keystore;
-
-ssize_t keystore_get(const char* key, size_t keyLength, uint8_t** value) {
- sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder = sm->getService(String16("android.security.keystore"));
- sp<android::security::keystore::IKeystoreService> service =
- interface_cast<android::security::keystore::IKeystoreService>(binder);
-
- if (service == nullptr) {
- return -1;
- }
-
- ::std::vector<uint8_t> result;
- auto ret = service->get(String16(key, keyLength), -1, &result);
- if (!ret.isOk()) return -1;
-
- if (value) {
- *value = reinterpret_cast<uint8_t*>(malloc(result.size()));
- if (!*value) return -1;
- memcpy(*value, &result[0], result.size());
- }
- return result.size();
-}
diff --git a/keystore/keystore_keymaster_enforcement.h b/keystore/keystore_keymaster_enforcement.h
deleted file mode 100644
index b0dae48e..00000000
--- a/keystore/keystore_keymaster_enforcement.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (C) 2015 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 KEYSTORE_KEYSTORE_KEYMASTER_ENFORCEMENT_H_
-#define KEYSTORE_KEYSTORE_KEYMASTER_ENFORCEMENT_H_
-
-#include <time.h>
-
-#include "keymaster_enforcement.h"
-
-namespace keystore {
-/**
- * This is a specialization of the KeymasterEnforcement class to be used by Keystore to enforce
- * keymaster requirements on all key operation.
- */
-class KeystoreKeymasterEnforcement : public KeymasterEnforcement {
- public:
- KeystoreKeymasterEnforcement() : KeymasterEnforcement(64, 64) {}
-
- uint32_t get_current_time() const override {
- struct timespec tp;
- int err = clock_gettime(CLOCK_MONOTONIC, &tp);
- if (err || tp.tv_sec < 0)
- return 0;
- return static_cast<uint32_t>(tp.tv_sec);
- }
-
- bool activation_date_valid(uint64_t activation_date) const override {
- time_t now = time(nullptr);
- if (now == static_cast<time_t>(-1)) {
- // Failed to obtain current time -- fail safe: activation_date hasn't yet occurred.
- return false;
- } else if (now < 0) {
- // Current time is prior to start of the epoch -- activation_date hasn't yet occurred.
- return false;
- }
-
- // time(NULL) returns seconds since epoch and "loses" milliseconds information. We thus add
- // 999 ms to now_date to avoid a situation where an activation_date of up to 999ms in the
- // past may still be considered to still be in the future. This can be removed once
- // time(NULL) is replaced by a millisecond-precise source of time.
- uint64_t now_date = static_cast<uint64_t>(now) * 1000 + 999;
- return now_date >= activation_date;
- }
-
- bool expiration_date_passed(uint64_t expiration_date) const override {
- time_t now = time(nullptr);
- if (now == static_cast<time_t>(-1)) {
- // Failed to obtain current time -- fail safe: expiration_date has passed.
- return true;
- } else if (now < 0) {
- // Current time is prior to start of the epoch: expiration_date hasn't yet occurred.
- return false;
- }
-
- // time(NULL) returns seconds since epoch and "loses" milliseconds information. As a result,
- // expiration_date of up to 999 ms in the past may still be considered in the future. This
- // is OK.
- uint64_t now_date = static_cast<uint64_t>(now) * 1000;
- return now_date > expiration_date;
- }
-
- bool auth_token_timed_out(const HardwareAuthToken&, uint32_t) const {
- // Assume the token has not timed out, because AuthTokenTable would not have returned it if
- // the timeout were past. Secure hardware will also check timeouts if it supports them.
- return false;
- }
-
- bool ValidateTokenSignature(const HardwareAuthToken&) const override {
- // Non-secure world cannot validate token signatures because it doesn't have access to the
- // signing key. Assume the token is good.
- return true;
- }
-
- bool is_device_locked(int32_t userId) const override {
- std::lock_guard<std::mutex> lock(is_device_locked_for_user_map_lock_);
- // If we haven't had a set call for this user yet, assume the device is locked.
- if (mIsDeviceLockedForUser.count(userId) == 0) return true;
- return mIsDeviceLockedForUser.find(userId)->second;
- }
-
- void set_device_locked(bool isLocked, int32_t userId) {
- std::lock_guard<std::mutex> lock(is_device_locked_for_user_map_lock_);
- mIsDeviceLockedForUser[userId] = isLocked;
- }
-
- private:
- mutable std::mutex is_device_locked_for_user_map_lock_;
- std::map<int32_t, bool> mIsDeviceLockedForUser;
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_KEYSTORE_KEYMASTER_ENFORCEMENT_H_
diff --git a/keystore/keystore_utils.cpp b/keystore/keystore_utils.cpp
deleted file mode 100644
index f0f60982..00000000
--- a/keystore/keystore_utils.cpp
+++ /dev/null
@@ -1,179 +0,0 @@
-/*
- * Copyright (C) 2016 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 "keystore"
-
-#include "keystore_utils.h"
-
-#include <errno.h>
-#include <string.h>
-#include <unistd.h>
-
-#include <log/log.h>
-#include <private/android_filesystem_config.h>
-#include <private/android_logger.h>
-
-#include <log/log_event_list.h>
-
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_client.h>
-
-#include <android-base/logging.h>
-#include <android-base/unique_fd.h>
-
-#include "blob.h"
-
-size_t readFully(int fd, uint8_t* data, size_t size) {
- size_t remaining = size;
- while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(read(fd, data, remaining));
- if (n <= 0) {
- return size - remaining;
- }
- data += n;
- remaining -= n;
- }
- return size;
-}
-
-size_t writeFully(int fd, uint8_t* data, size_t size) {
- size_t remaining = size;
- while (remaining > 0) {
- ssize_t n = TEMP_FAILURE_RETRY(write(fd, data, remaining));
- if (n < 0) {
- ALOGW("write failed: %s", strerror(errno));
- return size - remaining;
- }
- data += n;
- remaining -= n;
- }
- if (TEMP_FAILURE_RETRY(fsync(fd)) == -1) {
- ALOGW("fsync failed: %s", strerror(errno));
- return -1;
- }
- return size;
-}
-
-std::string getContainingDirectory(const std::string& filename) {
- std::string containing_dir;
- size_t last_pos;
- size_t pos = std::string::npos;
-
- __builtin_add_overflow(filename.size(), -1, &last_pos);
-
- // strip all trailing '/'
- while ((pos = filename.find_last_of('/', last_pos)) == last_pos && pos != 0) {
- --last_pos;
- }
-
- if (pos == 0) {
- containing_dir = "/";
- } else if (pos == std::string::npos) {
- containing_dir = ".";
- } else {
- containing_dir = filename.substr(0, pos);
- }
-
- return containing_dir;
-}
-
-void fsyncDirectory(const std::string& path) {
- android::base::unique_fd dir_fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_DIRECTORY | O_RDONLY)));
-
- if (dir_fd < 0) {
- LOG(WARNING) << "Could not open dir: " << path << " error: " << strerror(errno);
- return;
- }
-
- if (TEMP_FAILURE_RETRY(fsync(dir_fd)) == -1) {
- LOG(WARNING) << "Failed to fsync the directory " << path << " error: " << strerror(errno);
- }
-
- return;
-}
-
-void add_legacy_key_authorizations(int keyType, keystore::AuthorizationSet* params) {
- using namespace keystore;
- params->push_back(TAG_PURPOSE, KeyPurpose::SIGN);
- params->push_back(TAG_PURPOSE, KeyPurpose::VERIFY);
- params->push_back(TAG_PURPOSE, KeyPurpose::ENCRYPT);
- params->push_back(TAG_PURPOSE, KeyPurpose::DECRYPT);
- params->push_back(TAG_PADDING, PaddingMode::NONE);
- if (keyType == EVP_PKEY_RSA) {
- params->push_back(TAG_PADDING, PaddingMode::RSA_PKCS1_1_5_SIGN);
- params->push_back(TAG_PADDING, PaddingMode::RSA_PKCS1_1_5_ENCRYPT);
- params->push_back(TAG_PADDING, PaddingMode::RSA_PSS);
- params->push_back(TAG_PADDING, PaddingMode::RSA_OAEP);
- }
- params->push_back(TAG_DIGEST, Digest::NONE);
- params->push_back(TAG_DIGEST, Digest::MD5);
- params->push_back(TAG_DIGEST, Digest::SHA1);
- params->push_back(TAG_DIGEST, Digest::SHA_2_224);
- params->push_back(TAG_DIGEST, Digest::SHA_2_256);
- params->push_back(TAG_DIGEST, Digest::SHA_2_384);
- params->push_back(TAG_DIGEST, Digest::SHA_2_512);
- params->push_back(TAG_NO_AUTH_REQUIRED);
- params->push_back(TAG_ORIGINATION_EXPIRE_DATETIME, LLONG_MAX);
- params->push_back(TAG_USAGE_EXPIRE_DATETIME, LLONG_MAX);
- params->push_back(TAG_ACTIVE_DATETIME, 0);
-}
-
-uid_t get_app_id(uid_t uid) {
- return uid % AID_USER;
-}
-
-uid_t get_user_id(uid_t uid) {
- return uid / AID_USER;
-}
-
-void log_key_integrity_violation(const char* name, uid_t uid) {
- if (!__android_log_security()) return;
- android_log_event_list(SEC_TAG_KEY_INTEGRITY_VIOLATION)
- << name << int32_t(uid) << LOG_ID_SECURITY;
-}
-
-namespace keystore {
-
-hidl_vec<uint8_t> blob2hidlVec(const Blob& blob) {
- hidl_vec<uint8_t> result(blob.getValue(), blob.getValue() + blob.getLength());
- return result;
-}
-
-SecurityLevel flagsToSecurityLevel(int32_t flags) {
- switch (flags & (KEYSTORE_FLAG_FALLBACK | KEYSTORE_FLAG_STRONGBOX)) {
- case KEYSTORE_FLAG_FALLBACK:
- // treating Strongbox flag as "don't care" if Fallback is set
- case (KEYSTORE_FLAG_FALLBACK | KEYSTORE_FLAG_STRONGBOX):
- return SecurityLevel::SOFTWARE;
- case KEYSTORE_FLAG_STRONGBOX:
- return SecurityLevel::STRONGBOX;
- default:
- return SecurityLevel::TRUSTED_ENVIRONMENT;
- }
-}
-
-uint32_t securityLevelToFlags(SecurityLevel secLevel) {
- switch (secLevel) {
- case SecurityLevel::SOFTWARE:
- return KEYSTORE_FLAG_FALLBACK;
- case SecurityLevel::STRONGBOX:
- return KEYSTORE_FLAG_STRONGBOX;
- default:
- return 0;
- }
-}
-
-} // namespace keystore
diff --git a/keystore/keystore_utils.h b/keystore/keystore_utils.h
deleted file mode 100644
index ce64d423..00000000
--- a/keystore/keystore_utils.h
+++ /dev/null
@@ -1,70 +0,0 @@
-/*
- * Copyright (C) 2016 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 KEYSTORE_KEYSTORE_UTILS_H_
-#define KEYSTORE_KEYSTORE_UTILS_H_
-
-#include <cstdint>
-#include <string>
-#include <vector>
-
-#include <openssl/evp.h>
-#include <openssl/pem.h>
-
-#include <memory>
-
-#include <keystore/keymaster_types.h>
-
-size_t readFully(int fd, uint8_t* data, size_t size);
-size_t writeFully(int fd, uint8_t* data, size_t size);
-std::string getContainingDirectory(const std::string& filename);
-void fsyncDirectory(const std::string& path);
-
-void add_legacy_key_authorizations(int keyType, keystore::AuthorizationSet* params);
-
-/**
- * Returns the app ID (in the Android multi-user sense) for the current
- * UNIX UID.
- */
-uid_t get_app_id(uid_t uid);
-
-/**
- * Returns the user ID (in the Android multi-user sense) for the current
- * UNIX UID.
- */
-uid_t get_user_id(uid_t uid);
-
-class Blob;
-
-// Tags for audit logging. Be careful and don't log sensitive data.
-// Should be in sync with frameworks/base/core/java/android/app/admin/SecurityLogTags.logtags
-constexpr int SEC_TAG_KEY_DESTROYED = 210026;
-constexpr int SEC_TAG_KEY_INTEGRITY_VIOLATION = 210032;
-constexpr int SEC_TAG_AUTH_KEY_GENERATED = 210024;
-constexpr int SEC_TAG_KEY_IMPORTED = 210025;
-
-void log_key_integrity_violation(const char* name, uid_t uid);
-
-namespace keystore {
-
-hidl_vec<uint8_t> blob2hidlVec(const Blob& blob);
-
-SecurityLevel flagsToSecurityLevel(int32_t flags);
-uint32_t securityLevelToFlags(SecurityLevel secLevel);
-
-} // namespace keystore
-
-#endif // KEYSTORE_KEYSTORE_UTILS_H_
diff --git a/keystore/test-keystore b/keystore/test-keystore
deleted file mode 100755
index 3be51b3e..00000000
--- a/keystore/test-keystore
+++ /dev/null
@@ -1,273 +0,0 @@
-#!/bin/bash
-#
-# Copyright 2011, 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.
-
-set -e
-
-prefix=$0
-log_file=$prefix.log
-baseline_file=$prefix.baseline
-
-function cleanup_output() {
- rm -f $log_file
- rm -f $baseline_file
-}
-
-function log() {
- echo "$@"
- append $log_file \# "$@"
- append $baseline_file \# "$@"
-}
-
-function expect() {
- append $baseline_file "$@"
-}
-
-function append() {
- declare -r file=$1
- shift
- echo "$@" >> $file
-}
-
-function run() {
- # strip out carriage returns from adb
- # strip out date/time from ls -l
- "$@" | tr --delete '\r' | sed -E 's/[0-9]{4}-[0-9]{2}-[0-9]{2} +[0-9]{1,2}:[0-9]{2} //' >> $log_file
-}
-
-function keystore() {
- declare -r user=$1
- shift
- run adb shell su $user keystore_cli "$@"
-}
-
-function list_keystore_directory() {
- run adb shell ls -al /data/misc/keystore
-}
-
-function compare() {
- log "comparing $baseline_file and $log_file"
- diff $baseline_file $log_file || (log $tag FAILED && exit 1)
-}
-
-function test_basic() {
-
- #
- # reset
- #
- log "reset keystore as system user"
- keystore system r
- expect "1 No error"
- list_keystore_directory
-
- #
- # basic tests as system/root
- #
- log "root does not have permission to run test"
- keystore root t
- expect "6 Permission denied"
-
- log "but system user does"
- keystore system t
- expect "3 Uninitialized"
- list_keystore_directory
-
- log "password is now bar"
- keystore system p bar
- expect "1 No error"
- list_keystore_directory
- expect "-rw------- keystore keystore 84 .masterkey"
-
- log "no error implies initialized and unlocked"
- keystore system t
- expect "1 No error"
-
- log "saw with no argument"
- keystore system s
- expect "5 Protocol error"
-
- log "saw nothing"
- keystore system s ""
- expect "1 No error"
-
- log "add key baz"
- keystore system i baz quux
- expect "1 No error"
-
- log "1000 is uid of system"
- list_keystore_directory
- expect "-rw------- keystore keystore 84 .masterkey"
- expect "-rw------- keystore keystore 52 1000_baz"
-
- log "saw baz"
- keystore system s ""
- expect "1 No error"
- expect "baz"
-
- log "get baz"
- keystore system g baz
- expect "1 No error"
- expect "quux"
-
- log "root can read system user keys (as can wifi or vpn users)"
- keystore root g baz
- expect "1 No error"
- expect "quux"
-
- #
- # app user tests
- #
-
- # app_0 has uid 10000, as seen below
- log "other uses cannot see the system keys"
- keystore app_0 g baz
- expect "7 Key not found"
-
- log "app user cannot use reset, password, lock, unlock"
- keystore app_0 r
- expect "6 Permission denied"
- keystore app_0 p
- expect "6 Permission denied"
- keystore app_0 l
- expect "6 Permission denied"
- keystore app_0 u
- expect "6 Permission denied"
-
- log "install app_0 key"
- keystore app_0 i 0x deadbeef
- expect 1 No error
- list_keystore_directory
- expect "-rw------- keystore keystore 84 .masterkey"
- expect "-rw------- keystore keystore 52 10000_0x"
- expect "-rw------- keystore keystore 52 1000_baz"
-
- log "get with no argument"
- keystore app_0 g
- expect "5 Protocol error"
-
- keystore app_0 g 0x
- expect "1 No error"
- expect "deadbeef"
-
- keystore app_0 i fred barney
- expect "1 No error"
-
- keystore app_0 s ""
- expect "1 No error"
- expect "0x"
- expect "fred"
-
- log "note that saw returns the suffix of prefix matches"
- keystore app_0 s fr # fred
- expect "1 No error"
- expect "ed" # fred
-
- #
- # lock tests
- #
- log "lock the store as system"
- keystore system l
- expect "1 No error"
- keystore system t
- expect "2 Locked"
-
- log "saw works while locked"
- keystore app_0 s ""
- expect "1 No error"
- expect "0x"
- expect "fred"
-
- log "...but cannot read keys..."
- keystore app_0 g 0x
- expect "2 Locked"
-
- log "...but they can be deleted."
- keystore app_0 e 0x
- expect "1 No error"
- keystore app_0 d 0x
- expect "1 No error"
- keystore app_0 e 0x
- expect "7 Key not found"
-
- #
- # password
- #
- log "wrong password"
- keystore system u foo
- expect "13 Wrong password (4 tries left)"
- log "right password"
- keystore system u bar
- expect "1 No error"
-
- log "make the password foo"
- keystore system p foo
- expect "1 No error"
-
- #
- # final reset
- #
- log "reset wipes everything for all users"
- keystore system r
- expect "1 No error"
- list_keystore_directory
-
- keystore system t
- expect "3 Uninitialized"
-
-}
-
-function test_4599735() {
- # http://b/4599735
- log "start regression test for b/4599735"
- keystore system r
- expect "1 No error"
-
- keystore system p foo
- expect "1 No error"
-
- keystore system i baz quux
- expect "1 No error"
-
- keystore root g baz
- expect "1 No error"
- expect "quux"
-
- keystore system l
- expect "1 No error"
-
- keystore system p foo
- expect "1 No error"
-
- log "after unlock, regression led to result of '8 Value corrupted'"
- keystore root g baz
- expect "1 No error"
- expect "quux"
-
- keystore system r
- expect "1 No error"
- log "end regression test for b/4599735"
-}
-
-function main() {
- cleanup_output
- log $tag START
- test_basic
- test_4599735
- compare
- log $tag PASSED
- cleanup_output
-}
-
-main
diff --git a/keystore/tests/Android.bp b/keystore/tests/Android.bp
index f51cc2f5..c3a9e660 100644
--- a/keystore/tests/Android.bp
+++ b/keystore/tests/Android.bp
@@ -1,6 +1,7 @@
// Unit test for AuthTokenTable
package {
+ default_team: "trendy_team_android_hardware_backed_security",
// 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"
@@ -35,14 +36,15 @@ cc_test {
"libutils",
],
shared_libs: [
+ "android.security.aaid_aidl-cpp",
"libbinder",
"libkeymaster_messages",
"libkeystore-attestation-application-id",
"libvndksupport",
],
- sanitize: {
- cfi: false,
- }
+ sanitize: {
+ cfi: false,
+ },
}
cc_test {
@@ -67,7 +69,7 @@ cc_test {
shared_libs: [
"libbinder_ndk",
],
- sanitize: {
- cfi: false,
- }
+ sanitize: {
+ cfi: false,
+ },
}
diff --git a/keystore/tests/Makefile b/keystore/tests/Makefile
deleted file mode 100644
index b50b94a4..00000000
--- a/keystore/tests/Makefile
+++ /dev/null
@@ -1,125 +0,0 @@
-##########
-# This makefile builds local unit tests that run locally on the development machine. Note
-# that it may be necessary to install some libraries on the dev maching to make the tests
-# build.
-#
-# The same unit tests are also built by Android.mk to run on the target device. The tests
-# should always build and pass in both places. The on-device test is what really matters,
-# of course, but debugging is often somewhat easier on the dev platform.
-##########
-
-BASE=../../../..
-SUBS=system/core \
- system/keymaster\
- hardware/libhardware \
- external/gtest
-GTEST=$(BASE)/external/gtest
-KEYMASTER=$(BASE)/system/keymaster
-
-INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
- -I $(GTEST) -Iinclude
-
-# Add USE_CLANG=1 to the make command line to build with clang, which has better error
-# reporting and diagnoses some conditions that GCC doesn't.
-ifdef USE_CLANG
-CC=/usr/bin/clang
-CXX=/usr/bin/clang
-CLANG_TEST_DEFINE=-DKEYMASTER_CLANG_TEST_BUILD
-COMPILER_SPECIFIC_ARGS=-std=c++11 $(CLANG_TEST_DEFINE)
-else
-COMPILER_SPECIFIC_ARGS=-std=c++0x -fprofile-arcs
-endif
-
-CPPFLAGS=$(INCLUDES) -g -O0 -MD -DHOST_BUILD
-CXXFLAGS=-Wall -Werror -Wno-unused -Winit-self -Wpointer-arith -Wunused-parameter \
- -Werror=sign-compare -Wmissing-declarations -ftest-coverage -fno-permissive \
- -Wno-deprecated-declarations -fno-exceptions -DKEYMASTER_NAME_TAGS \
- $(COMPILER_SPECIFIC_ARGS)
-
-# Uncomment to enable debug logging.
-# CXXFLAGS += -DDEBUG
-
-LDLIBS=-lpthread -lstdc++ -lgcov
-
-# This list of sources is used for dependency generation and cleanup. Add each new source
-# file here (not headers).
-CPPSRCS=\
- ../auth_token_table.cpp \
- auth_token_table_test.cpp \
- gtest_main.cpp \
- $(KEYMASTER)/authorization_set.cpp \
- $(KEYMASTER)/keymaster_tags.cpp \
- $(KEYMASTER)/logger.cpp \
- $(KEYMASTER)/serializable.cpp
-
-CCSRCS=$(GTEST)/src/gtest-all.cc
-
-# This list of binaries determes what gets built and run. Add each new test binary here.
-BINARIES=\
- auth_token_table_test
-
-.PHONY: coverage memcheck massif clean run
-
-%.run: %
- ./$<
- touch $@
-
-run: $(BINARIES:=.run)
-
-GTEST_OBJS = $(GTEST)/src/gtest-all.o gtest_main.o
-
-auth_token_table_test: auth_token_table_test.o \
- ../auth_token_table.o \
- $(GTEST_OBJS) \
- $(KEYMASTER)/authorization_set.o \
- $(KEYMASTER)/keymaster_tags.o \
- $(KEYMASTER)/logger.o \
- $(KEYMASTER)/serializable.o
-
-coverage: coverage.info
- genhtml coverage.info --output-directory coverage
-
-coverage.info: run
- lcov --capture --directory=. --directory=.. -b . --output-file coverage.info
-
-%.coverage : %
- $(MAKE) clean && $(MAKE) $<
- ./$<
- lcov --capture --directory=. --output-file coverage.info
- genhtml coverage.info --output-directory coverage
-#UNINIT_OPTS=--track-origins=yes
-UNINIT_OPTS=--undef-value-errors=no
-
-MEMCHECK_OPTS=--leak-check=full \
- --show-reachable=yes \
- --vgdb=full \
- $(UNINIT_OPTS) \
- --error-exitcode=1
-
-MASSIF_OPTS=--tool=massif \
- --stacks=yes
-
-%.memcheck : %
- valgrind $(MEMCHECK_OPTS) ./$< && \
- touch $@
-
-%.massif : %
- valgrind $(MASSIF_OPTS) --massif-out-file=$@ ./$<
-
-memcheck: $(BINARIES:=.memcheck)
-
-massif: $(BINARIES:=.massif)
-
-OBJS=$(CPPSRCS:.cpp=.o)
-DEPS=$(CPPSRCS:.cpp=.d)
-GCOV=$(CPPSRCS:.cpp=.gcov) $(CPPSRCS:.cpp=.gcda) $(CPPSRCS:.cpp=.gcno)
-
-clean:
- rm -f $(OBJS) $(DEPS) $(BINARIES) $(GCOV) \
- $(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \
- *gcov *gcno *gcda coverage.info
- rm -rf coverage
-
--include $(CPPSRCS:.cpp=.d)
--include $(CCSRCS:.cc=.d)
-
diff --git a/keystore/tests/aaid_truncation_test.cpp b/keystore/tests/aaid_truncation_test.cpp
index fa4d769a..3a94ec1c 100644
--- a/keystore/tests/aaid_truncation_test.cpp
+++ b/keystore/tests/aaid_truncation_test.cpp
@@ -22,14 +22,14 @@
#include <keymaster/logger.h>
#include <keystore/keystore_attestation_id.h>
-#include <keystore/KeyAttestationApplicationId.h>
-#include <keystore/KeyAttestationPackageInfo.h>
-#include <keystore/Signature.h>
+#include <android/security/keystore/KeyAttestationApplicationId.h>
+#include <android/security/keystore/KeyAttestationPackageInfo.h>
+#include <android/security/keystore/Signature.h>
using ::android::String16;
using ::android::security::KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE;
-using ::android::security::keymaster::KeyAttestationApplicationId;
-using ::android::security::keymaster::KeyAttestationPackageInfo;
+using ::android::security::keystore::KeyAttestationApplicationId;
+using ::android::security::keystore::KeyAttestationPackageInfo;
using std::vector;
namespace keystore {
@@ -72,24 +72,27 @@ constexpr const size_t kTooManySignatures = 35;
} // namespace
-using ::android::content::pm::Signature;
using ::android::security::build_attestation_application_id;
+using ::android::security::keystore::Signature;
-std::optional<KeyAttestationPackageInfo>
-make_package_info_with_signatures(const char* package_name,
- KeyAttestationPackageInfo::SignaturesVector signatures) {
- return std::make_optional<KeyAttestationPackageInfo>(
- String16(package_name), 1 /* version code */,
- std::make_shared<KeyAttestationPackageInfo::SignaturesVector>(std::move(signatures)));
+KeyAttestationPackageInfo make_package_info_with_signatures(const char* package_name,
+ std::vector<Signature> signatures) {
+ auto pInfo = KeyAttestationPackageInfo();
+ pInfo.packageName = String16(package_name);
+ pInfo.versionCode = 1;
+ std::move(signatures.begin(), signatures.end(), std::back_inserter(pInfo.signatures));
+
+ return pInfo;
}
-std::optional<KeyAttestationPackageInfo> make_package_info(const char* package_name) {
- return make_package_info_with_signatures(package_name,
- KeyAttestationPackageInfo::SignaturesVector());
+KeyAttestationPackageInfo make_package_info(const char* package_name) {
+ return make_package_info_with_signatures(package_name, std::vector<Signature>());
}
TEST(AaidTruncationTest, shortPackageInfoTest) {
- KeyAttestationApplicationId app_id(make_package_info(kDummyPackageName));
+ KeyAttestationApplicationId app_id;
+ auto pInfo = make_package_info(kDummyPackageName);
+ app_id.packageInfos.push_back(std::move(pInfo));
auto result = build_attestation_application_id(app_id);
ASSERT_TRUE(result.isOk());
@@ -98,7 +101,9 @@ TEST(AaidTruncationTest, shortPackageInfoTest) {
}
TEST(AaidTruncationTest, tooLongPackageNameTest) {
- KeyAttestationApplicationId app_id(make_package_info(kLongPackageName));
+ KeyAttestationApplicationId app_id;
+ auto pInfo = make_package_info(kLongPackageName);
+ app_id.packageInfos.push_back(std::move(pInfo));
auto result = build_attestation_application_id(app_id);
ASSERT_TRUE(result.isOk());
@@ -108,14 +113,17 @@ TEST(AaidTruncationTest, tooLongPackageNameTest) {
TEST(AaidTruncationTest, tooManySignaturesTest) {
std::vector<uint8_t> dummy_sig_data(kDummySignature, kDummySignature + 32);
- KeyAttestationPackageInfo::SignaturesVector signatures;
+ std::vector<Signature> signatures;
// Add 35 signatures which will surely exceed the 1K limit.
for (size_t i = 0; i < kTooManySignatures; ++i) {
- signatures.push_back(std::make_optional<Signature>(dummy_sig_data));
+ auto sign = Signature();
+ sign.data = dummy_sig_data;
+ signatures.push_back(std::move(sign));
}
- KeyAttestationApplicationId app_id(
- make_package_info_with_signatures(kDummyPackageName, std::move(signatures)));
+ auto pInfo = make_package_info_with_signatures(kDummyPackageName, std::move(signatures));
+ KeyAttestationApplicationId app_id;
+ app_id.packageInfos.push_back(std::move(pInfo));
auto result = build_attestation_application_id(app_id);
ASSERT_TRUE(result.isOk());
@@ -125,19 +133,22 @@ TEST(AaidTruncationTest, tooManySignaturesTest) {
TEST(AaidTruncationTest, combinedPackagesAndSignaturesTest) {
std::vector<uint8_t> dummy_sig_data(kDummySignature, kDummySignature + 32);
- KeyAttestationApplicationId::PackageInfoVector packages;
+ ::std::vector<KeyAttestationPackageInfo> packages;
for (size_t i = 0; i < kTooManyPackages; ++i) {
- KeyAttestationPackageInfo::SignaturesVector signatures;
+ std::vector<Signature> signatures;
// Add a few signatures for each package
for (int j = 0; j < 3; ++j) {
- signatures.push_back(std::make_optional<Signature>(dummy_sig_data));
+ auto sign = Signature();
+ sign.data = dummy_sig_data;
+ signatures.push_back(std::move(sign));
}
- packages.push_back(
- make_package_info_with_signatures(kReasonablePackageName, std::move(signatures)));
+ packages.push_back(std::move(
+ make_package_info_with_signatures(kReasonablePackageName, std::move(signatures))));
}
+ KeyAttestationApplicationId app_id;
+ std::move(packages.begin(), packages.end(), std::back_inserter(app_id.packageInfos));
- KeyAttestationApplicationId app_id(std::move(packages));
auto result = build_attestation_application_id(app_id);
ASSERT_TRUE(result.isOk());
std::vector<uint8_t>& encoded_app_id = result;
diff --git a/keystore/tests/auth_token_formatting_test.cpp b/keystore/tests/auth_token_formatting_test.cpp
deleted file mode 100644
index 0ecc4cca..00000000
--- a/keystore/tests/auth_token_formatting_test.cpp
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * Copyright (C) 2017 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 <gtest/gtest.h>
-
-#include <endian.h>
-#include <hidl/HidlSupport.h>
-#include <keymaster/logger.h>
-#include <keymasterV4_0/keymaster_utils.h>
-
-#include <keystore/keymaster_types.h>
-#include <keystore/keystore_hidl_support.h>
-
-#include "../auth_token_table.h"
-
-using std::vector;
-
-namespace keystore {
-
-using android::hardware::hidl_array;
-using android::hardware::hidl_vec;
-
-namespace test {
-
-namespace {
-
-class StdoutLogger : public ::keymaster::Logger {
- public:
- StdoutLogger() { set_instance(this); }
-
- int log_msg(LogLevel level, const char* fmt, va_list args) const {
- int output_len = 0;
- switch (level) {
- case DEBUG_LVL:
- output_len = printf("DEBUG: ");
- break;
- case INFO_LVL:
- output_len = printf("INFO: ");
- break;
- case WARNING_LVL:
- output_len = printf("WARNING: ");
- break;
- case ERROR_LVL:
- output_len = printf("ERROR: ");
- break;
- case SEVERE_LVL:
- output_len = printf("SEVERE: ");
- break;
- }
-
- output_len += vprintf(fmt, args);
- output_len += printf("\n");
- return output_len;
- }
-};
-
-StdoutLogger logger;
-
-} // namespace
-
-constexpr const uint8_t test_token[69] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
- 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b,
- 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29,
- 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
- 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44};
-
-constexpr const uint8_t test_hmac_data[] = {
- 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x34,
- 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44};
-
-static const Km3HardwareAuthToken km3_hidl_test_token_little_endian = {
- UINT64_C(0x0807060504030201), UINT64_C(0x100f0e0d0c0b0a09),
- UINT64_C(0x1817161514131211), UINT32_C(0x1c1b1a19),
- UINT64_C(0x24232221201f1e1d), hidl_array<uint8_t, 32>(test_hmac_data)};
-
-static const HardwareAuthToken km4_hidl_test_token = {
- UINT64_C(0x0807060504030201), UINT64_C(0x100f0e0d0c0b0a09),
- UINT64_C(0x1817161514131211), static_cast<HardwareAuthenticatorType>(UINT32_C(0x191a1b1c)),
- UINT64_C(0x1d1e1f2021222324), hidl_vec<uint8_t>(test_hmac_data, test_hmac_data + 32)};
-
-TEST(AuthenticationTokenFormattingTest, hidlVec2Km3AuthToken) {
- static_assert(sizeof(hw_auth_token_t) == sizeof(test_token), "test_token has wrong size");
- hidl_vec<uint8_t> hidl_test_token;
- hidl_test_token.setToExternal(const_cast<unsigned char*>(test_token), sizeof(test_token));
- ASSERT_EQ(km3_hidl_test_token_little_endian, hidlVec2Km3AuthToken(hidl_test_token));
-}
-
-TEST(AuthenticationTokenFormattingTest, hidlVec2Km4AuthToken) {
- static_assert(sizeof(hw_auth_token_t) == sizeof(test_token), "test_token has wrong size");
- hidl_vec<uint8_t> hidl_test_token;
- hidl_test_token.setToExternal(const_cast<unsigned char*>(test_token), sizeof(test_token));
- ASSERT_EQ(km4_hidl_test_token, hidlVec2AuthToken(hidl_test_token));
-}
-
-TEST(AuthenticationTokenFormattingTest, km3AuthToken2HidlVec) {
- static_assert(sizeof(hw_auth_token_t) == sizeof(test_token), "test_token has wrong size");
- hidl_vec<uint8_t> hidl_test_token;
- hidl_test_token.setToExternal(const_cast<unsigned char*>(test_token), sizeof(test_token));
- ASSERT_EQ(hidl_test_token, authToken2HidlVec(km3_hidl_test_token_little_endian));
-}
-
-TEST(AuthenticationTokenFormattingTest, km4AuthToken2HidlVec) {
- static_assert(sizeof(hw_auth_token_t) == sizeof(test_token), "test_token has wrong size");
- hidl_vec<uint8_t> hidl_test_token;
- hidl_test_token.setToExternal(const_cast<unsigned char*>(test_token), sizeof(test_token));
- ASSERT_EQ(hidl_test_token, authToken2HidlVec(km4_hidl_test_token));
-}
-
-TEST(AuthenticationTokenFormattingTest, backAndForth) {
- static_assert(sizeof(hw_auth_token_t) == sizeof(test_token), "test_token has wrong size");
- hidl_vec<uint8_t> hidl_test_token;
- hidl_test_token.setToExternal(const_cast<unsigned char*>(test_token), sizeof(test_token));
- ASSERT_EQ(km3_hidl_test_token_little_endian,
- hidlVec2Km3AuthToken(authToken2HidlVec(km3_hidl_test_token_little_endian)));
- ASSERT_EQ(km4_hidl_test_token, hidlVec2AuthToken(authToken2HidlVec(km4_hidl_test_token)));
-}
-
-TEST(AuthenticationTokenFormattingTest, forthAndBack) {
- static_assert(sizeof(hw_auth_token_t) == sizeof(test_token), "test_token has wrong size");
- hidl_vec<uint8_t> hidl_test_token;
- hidl_test_token.setToExternal(const_cast<unsigned char*>(test_token), sizeof(test_token));
- ASSERT_EQ(hidl_test_token, authToken2HidlVec(hidlVec2Km3AuthToken(hidl_test_token)));
- ASSERT_EQ(hidl_test_token, authToken2HidlVec(hidlVec2Km3AuthToken(hidl_test_token)));
-}
-
-TEST(AuthenticationTokenFormattingTest, roundAndRound) {
- static_assert(sizeof(hw_auth_token_t) == sizeof(test_token), "test_token has wrong size");
- hidl_vec<uint8_t> hidl_test_token;
- hidl_test_token.setToExternal(const_cast<unsigned char*>(test_token), sizeof(test_token));
- HardwareAuthToken km4_from_hidl = hidlVec2AuthToken(hidl_test_token);
- hidl_vec<uint8_t> hidl_from_km4 = authToken2HidlVec(km4_from_hidl);
- Km3HardwareAuthToken km3_from_hidl = hidlVec2Km3AuthToken(hidl_from_km4);
- hidl_vec<uint8_t> hidl_from_km3 = authToken2HidlVec(km3_from_hidl);
-
- ASSERT_EQ(hidl_from_km4, hidl_test_token);
- ASSERT_EQ(hidl_from_km3, hidl_test_token);
- ASSERT_NE(km4_from_hidl.timestamp, km3_from_hidl.timestamp);
- ASSERT_NE(static_cast<uint32_t>(km4_from_hidl.authenticatorType),
- km3_from_hidl.authenticatorType);
-}
-
-} // namespace test
-} // namespace keystore
diff --git a/keystore/tests/auth_token_table_test.cpp b/keystore/tests/auth_token_table_test.cpp
deleted file mode 100644
index f6ce10ed..00000000
--- a/keystore/tests/auth_token_table_test.cpp
+++ /dev/null
@@ -1,520 +0,0 @@
-/*
- * Copyright (C) 2015 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 <gtest/gtest.h>
-
-#include <endian.h>
-#include <keymaster/logger.h>
-
-#include "../auth_token_table.h"
-
-using std::vector;
-
-namespace keystore {
-namespace test {
-
-class StdoutLogger : public ::keymaster::Logger {
- public:
- StdoutLogger() { set_instance(this); }
-
- int log_msg(LogLevel level, const char* fmt, va_list args) const {
- int output_len = 0;
- switch (level) {
- case DEBUG_LVL:
- output_len = printf("DEBUG: ");
- break;
- case INFO_LVL:
- output_len = printf("INFO: ");
- break;
- case WARNING_LVL:
- output_len = printf("WARNING: ");
- break;
- case ERROR_LVL:
- output_len = printf("ERROR: ");
- break;
- case SEVERE_LVL:
- output_len = printf("SEVERE: ");
- break;
- }
-
- output_len += vprintf(fmt, args);
- output_len += printf("\n");
- return output_len;
- }
-};
-
-StdoutLogger logger;
-
-TEST(AuthTokenTableTest, Create) {
- AuthTokenTable table;
-}
-
-static HardwareAuthToken make_token(uint64_t rsid, uint64_t ssid = 0, uint64_t challenge = 0,
- uint64_t timestamp = 0) {
- HardwareAuthToken token;
- token.userId = rsid;
- token.authenticatorId = ssid;
- token.authenticatorType = HardwareAuthenticatorType::PASSWORD;
- token.challenge = challenge;
- token.timestamp = timestamp;
- return token;
-}
-
-static AuthorizationSet make_set(uint64_t rsid, uint32_t timeout = 10000) {
- AuthorizationSetBuilder builder;
- builder.Authorization(TAG_USER_AUTH_TYPE, HardwareAuthenticatorType::PASSWORD)
- .Authorization(TAG_USER_SECURE_ID, rsid);
- // Use timeout == 0 to indicate tags that require auth per operation.
- if (timeout != 0) builder.Authorization(TAG_AUTH_TIMEOUT, timeout);
- return std::move(builder);
-}
-
-// Tests obviously run so fast that a real-time clock with a one-second granularity rarely changes
-// output during a test run. This test clock "ticks" one second every time it's called.
-static time_t monotonic_clock() {
- static time_t time = 0;
- return time++;
-}
-
-TEST(AuthTokenTableTest, SimpleAddAndFindTokens) {
- AuthTokenTable table;
-
- table.AddAuthenticationToken(make_token(1, 2));
- table.AddAuthenticationToken(make_token(3, 4));
- EXPECT_EQ(2U, table.size());
-
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- ASSERT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(1U, found.userId);
- EXPECT_EQ(2U, found.authenticatorId);
-
- ASSERT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(1U, found.userId);
- EXPECT_EQ(2U, found.authenticatorId);
-
- ASSERT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(3U, found.userId);
- EXPECT_EQ(4U, found.authenticatorId);
-
- ASSERT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(4), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(3U, found.userId);
- EXPECT_EQ(4U, found.authenticatorId);
-
- ASSERT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(5), KeyPurpose::SIGN, 0), rc));
-}
-
-TEST(AuthTokenTableTest, FlushTable) {
- AuthTokenTable table(3, monotonic_clock);
-
- table.AddAuthenticationToken(make_token(1));
- table.AddAuthenticationToken(make_token(2));
- table.AddAuthenticationToken(make_token(3));
-
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- // All three should be in the table.
- EXPECT_EQ(3U, table.size());
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0), rc));
-
- table.Clear();
- EXPECT_EQ(0U, table.size());
-}
-
-TEST(AuthTokenTableTest, TableOverflow) {
- AuthTokenTable table(3, monotonic_clock);
-
- table.AddAuthenticationToken(make_token(1));
- table.AddAuthenticationToken(make_token(2));
- table.AddAuthenticationToken(make_token(3));
-
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- // All three should be in the table.
- EXPECT_EQ(3U, table.size());
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0), rc));
-
- table.AddAuthenticationToken(make_token(4));
-
- // Oldest should be gone.
- EXPECT_EQ(3U, table.size());
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0), rc));
-
- // Others should be there, including the new one (4). Search for it first, then the others, so
- // 4 becomes the least recently used.
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(4), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0), rc));
-
- table.AddAuthenticationToken(make_token(5));
-
- // 5 should have replaced 4.
- EXPECT_EQ(3U, table.size());
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(4), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(5), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0), rc));
-
- table.AddAuthenticationToken(make_token(6));
- table.AddAuthenticationToken(make_token(7));
-
- // 2 and 5 should be gone
- EXPECT_EQ(3U, table.size());
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(5), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(6), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(7), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0), rc));
-
- table.AddAuthenticationToken(make_token(8));
- table.AddAuthenticationToken(make_token(9));
- table.AddAuthenticationToken(make_token(10));
-
- // Only the three most recent should be there.
- EXPECT_EQ(3U, table.size());
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(3), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(4), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(5), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(6), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(7), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(8), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(9), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(10), KeyPurpose::SIGN, 0), rc));
-}
-
-TEST(AuthTokenTableTest, AuthenticationNotRequired) {
- AuthTokenTable table;
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- EXPECT_EQ(AuthTokenTable::AUTH_NOT_REQUIRED,
- (std::tie(rc, found) = table.FindAuthorization(
- AuthorizationSetBuilder().Authorization(TAG_NO_AUTH_REQUIRED), KeyPurpose::SIGN,
- 0 /* no challenge */),
- rc));
-}
-
-TEST(AuthTokenTableTest, OperationHandleNotFound) {
- AuthTokenTable table;
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- table.AddAuthenticationToken(make_token(1, 0, 1, 5));
- EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) =
- table.FindAuthorization(make_set(1, 0 /* no timeout */), KeyPurpose::SIGN,
- 2 /* non-matching challenge */),
- rc));
- EXPECT_EQ(AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(
- make_set(1, 0 /* no timeout */), KeyPurpose::SIGN, 1 /* matching challenge */),
- rc));
- table.MarkCompleted(1);
- EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(
- make_set(1, 0 /* no timeout */), KeyPurpose::SIGN, 1 /* used challenge */),
- rc));
-}
-
-TEST(AuthTokenTableTest, OperationHandleRequired) {
- AuthTokenTable table;
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- table.AddAuthenticationToken(make_token(1));
- EXPECT_EQ(AuthTokenTable::OP_HANDLE_REQUIRED,
- (std::tie(rc, found) = table.FindAuthorization(
- make_set(1, 0 /* no timeout */), KeyPurpose::SIGN, 0 /* no op handle */),
- rc));
-}
-
-TEST(AuthTokenTableTest, AuthSidChanged) {
- AuthTokenTable table;
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- table.AddAuthenticationToken(make_token(1, 3, /* op handle */ 1));
- EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_WRONG_SID,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2, 0 /* no timeout */),
- KeyPurpose::SIGN, 1 /* op handle */),
- rc));
-}
-
-TEST(AuthTokenTableTest, TokenExpired) {
- AuthTokenTable table(5, monotonic_clock);
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- auto key_info = make_set(1, 5 /* five second timeout */);
-
- // monotonic_clock "ticks" one second each time it's called, which is once per request, so the
- // sixth request should fail, since key_info says the key is good for five seconds.
- //
- // Note that this tests the decision of the AuthTokenTable to reject a request it knows is
- // expired. An additional check of the secure timestamp (in the token) will be made by
- // keymaster when the found token is passed to it.
- table.AddAuthenticationToken(make_token(1, 0));
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- key_info, KeyPurpose::SIGN, 0 /* no op handle */),
- rc));
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- key_info, KeyPurpose::SIGN, 0 /* no op handle */),
- rc));
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- key_info, KeyPurpose::SIGN, 0 /* no op handle */),
- rc));
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- key_info, KeyPurpose::SIGN, 0 /* no op handle */),
- rc));
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- key_info, KeyPurpose::SIGN, 0 /* no op handle */),
- rc));
- EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_EXPIRED,
- (std::tie(rc, found) =
- table.FindAuthorization(key_info, KeyPurpose::SIGN, 0 /* no op handle */),
- rc));
-}
-
-TEST(AuthTokenTableTest, MarkNonexistentEntryCompleted) {
- AuthTokenTable table;
- // Marking a nonexistent entry completed is ignored. This test is mainly for code coverage.
- table.MarkCompleted(1);
-}
-
-TEST(AuthTokenTableTest, SupersededEntries) {
- AuthTokenTable table;
- AuthTokenTable::Error rc;
- HardwareAuthToken found;
-
- // Add two identical tokens, without challenges. The second should supersede the first, based
- // on timestamp (fourth arg to make_token).
- table.AddAuthenticationToken(make_token(1, 0, 0, 0));
- table.AddAuthenticationToken(make_token(1, 0, 0, 1));
- EXPECT_EQ(1U, table.size());
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(1U, found.timestamp);
-
- // Add a third token, this with a different RSID. It should not be superseded.
- table.AddAuthenticationToken(make_token(2, 0, 0, 2));
- EXPECT_EQ(2U, table.size());
-
- // Add two more, superseding each of the two in the table.
- table.AddAuthenticationToken(make_token(1, 0, 0, 3));
- table.AddAuthenticationToken(make_token(2, 0, 0, 4));
- EXPECT_EQ(2U, table.size());
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(3U, found.timestamp);
- EXPECT_EQ(
- AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(2), KeyPurpose::SIGN, 0), rc));
- EXPECT_EQ(4U, found.timestamp);
-
- // Add another, this one with a challenge value. It should supersede the old one since it is
- // newer, and matches other than the challenge.
- table.AddAuthenticationToken(make_token(1, 0, 1, 5));
- EXPECT_EQ(2U, table.size());
-
- // And another, also with a challenge. Because of the challenge values, the one just added
- // cannot be superseded.
- table.AddAuthenticationToken(make_token(1, 0, 2, 6));
- EXPECT_EQ(3U, table.size());
-
- // Should be able to find each of them, by specifying their challenge, with a key that is not
- // timed (timed keys don't care about challenges).
- EXPECT_EQ(AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1, 0 /* no timeout*/),
- KeyPurpose::SIGN, 1 /* challenge */),
- rc));
- EXPECT_EQ(5U, found.timestamp);
- EXPECT_EQ(AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1, 0 /* no timeout */),
- KeyPurpose::SIGN, 2 /* challenge */),
- rc));
- EXPECT_EQ(6U, found.timestamp);
-
- // Add another, without a challenge, and the same timestamp as the last one. This new one
- // actually could be considered already-superseded, but the table doesn't handle that case,
- // since it seems unlikely to occur in practice.
- table.AddAuthenticationToken(make_token(1, 0, 0, 6));
- EXPECT_EQ(4U, table.size());
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- make_set(1), KeyPurpose::SIGN, 0 /* challenge */),
- rc));
- EXPECT_EQ(6U, found.timestamp);
-
- // Add another without a challenge but an increased timestamp. This should supersede the
- // previous challenge-free entry.
- table.AddAuthenticationToken(make_token(1, 0, 0, 7));
- EXPECT_EQ(4U, table.size());
- EXPECT_EQ(AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1, 0 /* no timeout */),
- KeyPurpose::SIGN, 2 /* challenge */),
- rc));
- EXPECT_EQ(6U, found.timestamp);
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- make_set(1), KeyPurpose::SIGN, 0 /* challenge */),
- rc));
- EXPECT_EQ(7U, found.timestamp);
-
- // Mark the entry with challenge 2 as complete. Since there's a newer challenge-free entry, the
- // challenge entry will be superseded.
- table.MarkCompleted(2);
- EXPECT_EQ(3U, table.size());
- EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1, 0 /* no timeout */),
- KeyPurpose::SIGN, 2 /* challenge */),
- rc));
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- make_set(1), KeyPurpose::SIGN, 0 /* challenge */),
- rc));
- EXPECT_EQ(7U, found.timestamp);
-
- // Add another SID 1 entry with a challenge. It supersedes the previous SID 1 entry with
- // no challenge (timestamp 7), but not the one with challenge 1 (timestamp 5).
- table.AddAuthenticationToken(make_token(1, 0, 3, 8));
- EXPECT_EQ(3U, table.size());
-
- EXPECT_EQ(AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1, 0 /* no timeout */),
- KeyPurpose::SIGN, 1 /* challenge */),
- rc));
- EXPECT_EQ(5U, found.timestamp);
-
- EXPECT_EQ(AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1, 0 /* no timeout */),
- KeyPurpose::SIGN, 3 /* challenge */),
- rc));
- EXPECT_EQ(8U, found.timestamp);
-
- // SID 2 entry is still there.
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- make_set(2), KeyPurpose::SIGN, 0 /* challenge */),
- rc));
- EXPECT_EQ(4U, found.timestamp);
-
- // Mark the entry with challenge 3 as complete. Since the older challenge 1 entry is
- // incomplete, nothing is superseded.
- table.MarkCompleted(3);
- EXPECT_EQ(3U, table.size());
-
- EXPECT_EQ(AuthTokenTable::OK,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1, 0 /* no timeout */),
- KeyPurpose::SIGN, 1 /* challenge */),
- rc));
- EXPECT_EQ(5U, found.timestamp);
-
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- make_set(1), KeyPurpose::SIGN, 0 /* challenge */),
- rc));
- EXPECT_EQ(8U, found.timestamp);
-
- // Mark the entry with challenge 1 as complete. Since there's a newer one (with challenge 3,
- // completed), the challenge 1 entry is superseded and removed.
- table.MarkCompleted(1);
- EXPECT_EQ(2U, table.size());
- EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
- (std::tie(rc, found) = table.FindAuthorization(make_set(1, 0 /* no timeout */),
- KeyPurpose::SIGN, 1 /* challenge */),
- rc));
- EXPECT_EQ(AuthTokenTable::OK, (std::tie(rc, found) = table.FindAuthorization(
- make_set(1), KeyPurpose::SIGN, 0 /* challenge */),
- rc));
- EXPECT_EQ(8U, found.timestamp);
-}
-
-} // namespace test
-} // namespace keystore
diff --git a/keystore/tests/confirmationui_rate_limiting_test.cpp b/keystore/tests/confirmationui_rate_limiting_test.cpp
deleted file mode 100644
index f56b5099..00000000
--- a/keystore/tests/confirmationui_rate_limiting_test.cpp
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2018 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 <gtest/gtest.h>
-
-#include "../confirmationui_rate_limiting.h"
-#include <keymaster/logger.h>
-
-using std::vector;
-
-namespace keystore {
-
-namespace test {
-
-namespace {
-
-class StdoutLogger : public ::keymaster::Logger {
- public:
- StdoutLogger() { set_instance(this); }
-
- int log_msg(LogLevel level, const char* fmt, va_list args) const {
- int output_len = 0;
- switch (level) {
- case DEBUG_LVL:
- output_len = printf("DEBUG: ");
- break;
- case INFO_LVL:
- output_len = printf("INFO: ");
- break;
- case WARNING_LVL:
- output_len = printf("WARNING: ");
- break;
- case ERROR_LVL:
- output_len = printf("ERROR: ");
- break;
- case SEVERE_LVL:
- output_len = printf("SEVERE: ");
- break;
- }
-
- output_len += vprintf(fmt, args);
- output_len += printf("\n");
- return output_len;
- }
-};
-
-StdoutLogger logger;
-
-class FakeClock : public std::chrono::steady_clock {
- private:
- static time_point sNow;
-
- public:
- static void setNow(time_point newNow) { sNow = newNow; }
- static time_point now() noexcept { return sNow; }
-};
-
-FakeClock::time_point FakeClock::sNow;
-
-} // namespace
-
-/*
- * Test that there are no residual slots when various apps receive successful confirmations.
- */
-TEST(ConfirmationUIRateLimitingTest, noPenaltyTest) {
- auto now = std::chrono::steady_clock::now();
- RateLimiting<FakeClock> rateLimiting;
- FakeClock::setNow(now);
-
- for (int i = 0; i < 10000; ++i) {
- ASSERT_TRUE(rateLimiting.tryPrompt(rand()));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- ASSERT_EQ(0U, rateLimiting.usedSlots());
-}
-
-TEST(ConfirmationUIRateLimitingTest, policyTest) {
- using namespace std::chrono_literals;
- auto now = std::chrono::steady_clock::now();
- RateLimiting<FakeClock> rateLimiting;
- FakeClock::setNow(now);
-
- // first three tries are free
- for (int i = 0; i < 3; ++i) {
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
- }
-
- // throw in a couple of successful confirmations by other apps to make sure there
- // is not cross talk
- for (int i = 0; i < 10000; ++i) {
- uid_t id = rand();
- if (id == 20) continue;
- ASSERT_TRUE(rateLimiting.tryPrompt(id));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- // the next three tries get a 30s penalty
- for (int i = 3; i < 6; ++i) {
- FakeClock::setNow(FakeClock::now() + 29s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
- }
-
- // throw in a couple of successful confirmations by other apps to make sure there
- // is not cross talk
- for (int i = 0; i < 10000; ++i) {
- uid_t id = rand();
- if (id == 20) continue;
- ASSERT_TRUE(rateLimiting.tryPrompt(id));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- // there after the penalty doubles with each cancellation
- for (int i = 6; i < 17; ++i) {
- FakeClock::setNow((FakeClock::now() + 60s * (1ULL << (i - 6))) - 1s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
- }
-
- // throw in a couple of successful confirmations by other apps to make sure there
- // is not cross talk
- for (int i = 0; i < 10000; ++i) {
- uid_t id = rand();
- if (id == 20) continue;
- ASSERT_TRUE(rateLimiting.tryPrompt(id));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- ASSERT_EQ(1U, rateLimiting.usedSlots());
-
- FakeClock::setNow(FakeClock::now() + 24h - 1s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
-
- // after 24h the counter is forgotten
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
-
- // throw in a couple of successful confirmations by other apps to make sure there
- // is not cross talk
- for (int i = 0; i < 10000; ++i) {
- uid_t id = rand();
- if (id == 20) continue;
- ASSERT_TRUE(rateLimiting.tryPrompt(id));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- for (int i = 1; i < 3; ++i) {
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
- }
-
- // throw in a couple of successful confirmations by other apps to make sure there
- // is not cross talk
- for (int i = 0; i < 10000; ++i) {
- uid_t id = rand();
- if (id == 20) continue;
- ASSERT_TRUE(rateLimiting.tryPrompt(id));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- for (int i = 3; i < 6; ++i) {
- FakeClock::setNow(FakeClock::now() + 29s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
- }
-
- // throw in a couple of successful confirmations by other apps to make sure there
- // is not cross talk
- for (int i = 0; i < 10000; ++i) {
- uid_t id = rand();
- if (id == 20) continue;
- ASSERT_TRUE(rateLimiting.tryPrompt(id));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- for (int i = 6; i < 17; ++i) {
- FakeClock::setNow((FakeClock::now() + 60s * (1ULL << (i - 6))) - 1s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
- }
-
- // throw in a couple of successful confirmations by other apps to make sure there
- // is not cross talk
- for (int i = 0; i < 10000; ++i) {
- uid_t id = rand();
- if (id == 20) continue;
- ASSERT_TRUE(rateLimiting.tryPrompt(id));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- ASSERT_EQ(1U, rateLimiting.usedSlots());
-}
-
-TEST(ConfirmationUIRateLimitingTest, rewindTest) {
- using namespace std::chrono_literals;
- auto now = std::chrono::steady_clock::now();
- RateLimiting<FakeClock> rateLimiting;
-
- // first three tries are free
- for (int i = 0; i < 3; ++i) {
- FakeClock::setNow(now);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
- }
-
- for (int i = 3; i < 6; ++i) {
- FakeClock::setNow(FakeClock::now() + 29s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Canceled);
- }
-
- FakeClock::setNow(FakeClock::now() + 59s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::Aborted);
-
- FakeClock::setNow(FakeClock::now() - 1s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::SystemError);
-
- // throw in a couple of successful confirmations by other apps to make sure there
- // is not cross talk
- for (int i = 0; i < 10000; ++i) {
- uid_t id = rand();
- if (id == 20) continue;
- ASSERT_TRUE(rateLimiting.tryPrompt(id));
- rateLimiting.processResult(ConfirmationResponseCode::OK);
- }
-
- FakeClock::setNow(FakeClock::now() - 1s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
- rateLimiting.processResult(ConfirmationResponseCode::UIError);
-
- FakeClock::setNow(FakeClock::now() - 1s);
- ASSERT_FALSE(rateLimiting.tryPrompt(20));
- FakeClock::setNow(FakeClock::now() + 1s);
- ASSERT_TRUE(rateLimiting.tryPrompt(20));
-
- ASSERT_EQ(1U, rateLimiting.usedSlots());
-}
-
-} // namespace test
-} // namespace keystore
diff --git a/keystore/tests/fuzzer/Android.bp b/keystore/tests/fuzzer/Android.bp
index 4116ae14..55d8f83e 100644
--- a/keystore/tests/fuzzer/Android.bp
+++ b/keystore/tests/fuzzer/Android.bp
@@ -15,6 +15,7 @@
*/
package {
+ default_team: "trendy_team_android_hardware_backed_security",
// 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"
@@ -40,9 +41,17 @@ cc_fuzz {
],
fuzz_config: {
cc: [
- "android-media-fuzzing-reports@google.com",
+ "android-hardware-security@google.com",
],
- componentid: 155276,
+ componentid: 1084732,
+ hotlists: [
+ "4593311",
+ ],
+ description: "The fuzzer targets the APIs of libkeystore-wifi-hidl",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
@@ -55,15 +64,24 @@ cc_defaults {
"libhidlbase",
],
shared_libs: [
+ "android.security.aaid_aidl-cpp",
"libbinder",
"libcrypto",
"libutils",
],
fuzz_config: {
cc: [
- "android-media-fuzzing-reports@google.com",
+ "android-hardware-security@google.com",
+ ],
+ componentid: 1084732,
+ hotlists: [
+ "4593311",
],
- componentid: 155276,
+ description: "The fuzzer targets the APIs of libkeystore-attestation-application-id",
+ vector: "local_no_privileges_required",
+ service_privilege: "privileged",
+ users: "multi_user",
+ fuzzed_code_usage: "shipped",
},
}
diff --git a/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
index 0eddb9a8..9388001c 100644
--- a/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
+++ b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
@@ -15,9 +15,9 @@
*/
#include "keystoreCommon.h"
-#include <keystore/KeyAttestationApplicationId.h>
+#include <android/security/keystore/KeyAttestationApplicationId.h>
-using ::security::keymaster::KeyAttestationApplicationId;
+using ::android::security::keystore::KeyAttestationApplicationId;
constexpr size_t kPackageVectorSizeMin = 1;
constexpr size_t kPackageVectorSizeMax = 10;
@@ -33,26 +33,37 @@ class KeystoreApplicationId {
};
void KeystoreApplicationId::invokeApplicationId() {
- std::optional<KeyAttestationApplicationId> applicationId;
+ KeyAttestationApplicationId applicationId;
bool shouldUsePackageInfoVector = mFdp->ConsumeBool();
if (shouldUsePackageInfoVector) {
- KeyAttestationApplicationId::PackageInfoVector packageInfoVector;
+ ::std::vector<KeyAttestationPackageInfo> 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));
+ auto pInfo = KeyAttestationPackageInfo();
+ pInfo.packageName = String16((packageInfoData.packageName).c_str());
+ pInfo.versionCode = packageInfoData.versionCode;
+ std::move(packageInfoData.sharedSignaturesVector->begin(),
+ packageInfoData.sharedSignaturesVector->end(),
+ std::back_inserter(pInfo.signatures));
+
+ packageInfoVector.push_back(std::move(pInfo));
}
- applicationId = KeyAttestationApplicationId(std::move(packageInfoVector));
+
+ std::move(packageInfoVector.begin(), packageInfoVector.end(),
+ std::back_inserter(applicationId.packageInfos));
} else {
auto packageInfoData = initPackageInfoData(mFdp.get());
- applicationId = KeyAttestationApplicationId(make_optional<KeyAttestationPackageInfo>(
- String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
- packageInfoData.sharedSignaturesVector));
+ auto pInfo = KeyAttestationPackageInfo();
+ pInfo.packageName = String16((packageInfoData.packageName).c_str());
+ pInfo.versionCode = packageInfoData.versionCode;
+ std::move(packageInfoData.sharedSignaturesVector->begin(),
+ packageInfoData.sharedSignaturesVector->end(),
+ std::back_inserter(pInfo.signatures));
+ applicationId.packageInfos.push_back(std::move(pInfo));
}
- invokeReadWriteParcel(&applicationId.value());
+ invokeReadWriteParcel(&applicationId);
}
void KeystoreApplicationId::process(const uint8_t* data, size_t size) {
diff --git a/keystore/tests/fuzzer/keystoreCommon.h b/keystore/tests/fuzzer/keystoreCommon.h
index 7af3ba8c..77d39e0a 100644
--- a/keystore/tests/fuzzer/keystoreCommon.h
+++ b/keystore/tests/fuzzer/keystoreCommon.h
@@ -16,18 +16,18 @@
#ifndef KEYSTORECOMMON_H
#define KEYSTORECOMMON_H
+#include <android/security/keystore/KeyAttestationPackageInfo.h>
+#include <android/security/keystore/Signature.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;
+using ::android::security::keystore::KeyAttestationPackageInfo;
+using ::android::security::keystore::Signature;
constexpr size_t kSignatureSizeMin = 1;
constexpr size_t kSignatureSizeMax = 1000;
@@ -38,7 +38,7 @@ constexpr size_t kSignatureVectorSizeMax = 1000;
struct PackageInfoData {
string packageName;
int64_t versionCode;
- KeyAttestationPackageInfo::SharedSignaturesVector sharedSignaturesVector;
+ std::shared_ptr<std::vector<Signature>> sharedSignaturesVector;
};
inline void invokeReadWriteParcel(Parcelable* obj) {
@@ -60,18 +60,20 @@ inline PackageInfoData initPackageInfoData(FuzzedDataProvider* fdp) {
packageInfoData.versionCode = fdp->ConsumeIntegral<int64_t>();
size_t signatureVectorSize =
fdp->ConsumeIntegralInRange(kSignatureVectorSizeMin, kSignatureVectorSizeMax);
- KeyAttestationPackageInfo::SignaturesVector signatureVector;
+ std::vector<Signature> 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));
+ auto sign = Signature();
+ sign.data = signatureData;
+ signatureVector.push_back(std::move(sign));
} else {
- signatureVector.push_back(std::nullopt);
+ signatureVector.push_back(Signature());
}
}
packageInfoData.sharedSignaturesVector =
- make_shared<KeyAttestationPackageInfo::SignaturesVector>(move(signatureVector));
+ make_shared<std::vector<Signature>>(std::move(signatureVector));
return packageInfoData;
}
#endif // KEYSTORECOMMON_H
diff --git a/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
index 63899ff8..f1e42041 100644
--- a/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
+++ b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
@@ -28,9 +28,12 @@ class KeystorePackageInfoFuzzer {
void KeystorePackageInfoFuzzer::invokePackageInfo() {
auto packageInfoData = initPackageInfoData(mFdp.get());
- KeyAttestationPackageInfo packageInfo(String16((packageInfoData.packageName).c_str()),
- packageInfoData.versionCode,
- packageInfoData.sharedSignaturesVector);
+ auto packageInfo = KeyAttestationPackageInfo();
+ packageInfo.packageName = String16((packageInfoData.packageName).c_str());
+ packageInfo.versionCode = packageInfoData.versionCode;
+ std::move(packageInfoData.sharedSignaturesVector->begin(),
+ packageInfoData.sharedSignaturesVector->end(),
+ std::back_inserter(packageInfo.signatures));
invokeReadWriteParcel(&packageInfo);
}
diff --git a/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
index b8f8a73e..aab1f251 100644
--- a/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
+++ b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
@@ -14,7 +14,9 @@
* limitations under the License.
*/
#include "keystoreCommon.h"
-#include <keystore/Signature.h>
+#include <android/security/keystore/Signature.h>
+
+using ::android::security::keystore::Signature;
class KeystoreSignatureFuzzer {
public:
@@ -27,15 +29,15 @@ class KeystoreSignatureFuzzer {
};
void KeystoreSignatureFuzzer::invokeSignature() {
- std::optional<Signature> signature;
+ Signature signature;
bool shouldUseParameterizedConstructor = mFdp->ConsumeBool();
if (shouldUseParameterizedConstructor) {
std::vector<uint8_t> signatureData = initSignatureData(mFdp.get());
- signature = Signature(signatureData);
+ signature.data = signatureData;
} else {
signature = Signature();
}
- invokeReadWriteParcel(&signature.value());
+ invokeReadWriteParcel(&signature);
}
void KeystoreSignatureFuzzer::process(const uint8_t* data, size_t size) {
diff --git a/keystore/tests/list_auth_bound_keys_test.sh b/keystore/tests/list_auth_bound_keys_test.sh
deleted file mode 100755
index f609b346..00000000
--- a/keystore/tests/list_auth_bound_keys_test.sh
+++ /dev/null
@@ -1,77 +0,0 @@
-#!/usr/bin/env bash
-
-#
-# Copyright (C) 2018 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.
-#
-#
-# Simple adb based test for keystore method list_auth_bound_keys
-# Depends on keystore_cli_v2 tool and root
-#
-
-set -e
-
-ROOT_ID=0
-USER1_ID=10901
-USER2_ID=10902
-SYSTEM_ID=1000
-
-function cli {
- adb shell "su $1 keystore_cli_v2 $2"
-}
-
-#start as root
-adb root
-
-# generate keys as user
-echo "generating keys"
-cli $USER1_ID "delete --name=no_auth_key" || true
-cli $USER1_ID "generate --name=no_auth_key"
-cli $USER2_ID "delete --name=auth_key" || true
-if ! cli $USER2_ID "generate --name=auth_key --auth_bound"; then
- echo "Unable to generate auth bound key, make sure device/emulator has a pin/password set."
- echo "$ adb shell locksettings set-pin 1234"
- exit 1
-fi
-
-# try to list keys as user
-if cli $USER2_ID list-apps-with-keys; then
- echo "Error: list-apps-with-keys succeeded as user, this is not expected!"
- exit 1
-fi
-
-# try to list keys as root
-if cli $ROOT_ID "list-apps-with-keys"; then
- echo "Error: list-apps-with-keys succeeded as root, this is not expected!"
- exit 1
-fi
-
-# try to list keys as system
-success=false
-while read -r line; do
- echo $line
- if [ "$line" == "$USER2_ID" ]; then
- success=true
- fi
- if [ "$line" == "$USER1_ID" ]; then
- echo "Error: User1 id not expected in list"
- exit 1
- fi
-done <<< $(cli $SYSTEM_ID "list-apps-with-keys")
-if [ $success = true ]; then
- echo "Success!"
-else
- echo "Error: User2 id not in list"
- exit 1
-fi \ No newline at end of file
diff --git a/keystore/user_state.cpp b/keystore/user_state.cpp
deleted file mode 100644
index 30dfe3c3..00000000
--- a/keystore/user_state.cpp
+++ /dev/null
@@ -1,311 +0,0 @@
-/*
- * Copyright (C) 2016 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 "keystore"
-
-#include "user_state.h"
-
-#include <dirent.h>
-#include <fcntl.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <sys/stat.h>
-
-#include <openssl/digest.h>
-#include <openssl/evp.h>
-#include <openssl/rand.h>
-
-#include <log/log.h>
-
-#include "blob.h"
-#include "keystore_utils.h"
-
-namespace keystore {
-
-UserState::UserState(uid_t userId)
- : mMasterKeyEntry(".masterkey", "user_" + std::to_string(userId), userId, /* masterkey */ true),
- mUserId(userId), mState(STATE_UNINITIALIZED) {}
-
-bool UserState::operator<(const UserState& rhs) const {
- return getUserId() < rhs.getUserId();
-}
-
-bool UserState::operator<(uid_t userId) const {
- return getUserId() < userId;
-}
-
-bool operator<(uid_t userId, const UserState& rhs) {
- return userId < rhs.getUserId();
-}
-
-bool UserState::initialize() {
- if ((mkdir(mMasterKeyEntry.user_dir().c_str(), S_IRUSR | S_IWUSR | S_IXUSR) < 0) &&
- (errno != EEXIST)) {
- ALOGE("Could not create directory '%s'", mMasterKeyEntry.user_dir().c_str());
- return false;
- }
-
- if (mMasterKeyEntry.hasKeyBlob()) {
- setState(STATE_LOCKED);
- } else {
- setState(STATE_UNINITIALIZED);
- }
-
- return true;
-}
-
-void UserState::setState(State state) {
- mState = state;
-}
-
-void UserState::zeroizeMasterKeysInMemory() {
- memset(mMasterKey.data(), 0, mMasterKey.size());
- memset(mSalt, 0, sizeof(mSalt));
-}
-
-bool UserState::deleteMasterKey() {
- setState(STATE_UNINITIALIZED);
- zeroizeMasterKeysInMemory();
- return unlink(mMasterKeyEntry.getKeyBlobPath().c_str()) == 0 || errno == ENOENT;
-}
-
-ResponseCode UserState::initialize(const android::String8& pw) {
- if (!generateMasterKey()) {
- return ResponseCode::SYSTEM_ERROR;
- }
- ResponseCode response = writeMasterKey(pw);
- if (response != ResponseCode::NO_ERROR) {
- return response;
- }
- setupMasterKeys();
- return ResponseCode::NO_ERROR;
-}
-
-ResponseCode UserState::copyMasterKey(LockedUserState<UserState>* src) {
- if (mState != STATE_UNINITIALIZED) {
- return ResponseCode::SYSTEM_ERROR;
- }
- if ((*src)->getState() != STATE_NO_ERROR) {
- return ResponseCode::SYSTEM_ERROR;
- }
- mMasterKey = (*src)->mMasterKey;
- setupMasterKeys();
- return copyMasterKeyFile(src);
-}
-
-ResponseCode UserState::copyMasterKeyFile(LockedUserState<UserState>* src) {
- /* Copy the master key file to the new user. Unfortunately we don't have the src user's
- * password so we cannot generate a new file with a new salt.
- */
- int in = TEMP_FAILURE_RETRY(open((*src)->getMasterKeyFileName().c_str(), O_RDONLY));
- if (in < 0) {
- return ResponseCode::SYSTEM_ERROR;
- }
- blobv3 rawBlob;
- size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
- if (close(in) != 0) {
- return ResponseCode::SYSTEM_ERROR;
- }
- int out = TEMP_FAILURE_RETRY(open(mMasterKeyEntry.getKeyBlobPath().c_str(),
- O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IWUSR));
- if (out < 0) {
- return ResponseCode::SYSTEM_ERROR;
- }
- size_t outLength = writeFully(out, (uint8_t*)&rawBlob, length);
- if (close(out) != 0) {
- return ResponseCode::SYSTEM_ERROR;
- }
- if (outLength != length) {
- ALOGW("blob not fully written %zu != %zu", outLength, length);
- unlink(mMasterKeyEntry.getKeyBlobPath().c_str());
- return ResponseCode::SYSTEM_ERROR;
- }
- return ResponseCode::NO_ERROR;
-}
-
-ResponseCode UserState::writeMasterKey(const android::String8& pw) {
- std::vector<uint8_t> passwordKey(mMasterKey.size());
- generateKeyFromPassword(passwordKey, pw, mSalt);
- auto blobType = TYPE_MASTER_KEY_AES256;
- if (mMasterKey.size() == kAes128KeySizeBytes) {
- blobType = TYPE_MASTER_KEY;
- }
- Blob masterKeyBlob(mMasterKey.data(), mMasterKey.size(), mSalt, sizeof(mSalt), blobType);
- auto lockedEntry = LockedKeyBlobEntry::get(mMasterKeyEntry);
- return lockedEntry.writeBlobs(masterKeyBlob, {}, passwordKey, STATE_NO_ERROR);
-}
-
-ResponseCode UserState::readMasterKey(const android::String8& pw) {
-
- auto lockedEntry = LockedKeyBlobEntry::get(mMasterKeyEntry);
-
- int in = TEMP_FAILURE_RETRY(open(mMasterKeyEntry.getKeyBlobPath().c_str(), O_RDONLY));
- if (in < 0) {
- return ResponseCode::SYSTEM_ERROR;
- }
-
- // We read the raw blob to just to get the salt to generate the AES key, then we create the Blob
- // to use with decryptBlob
- blobv3 rawBlob;
- size_t length = readFully(in, (uint8_t*)&rawBlob, sizeof(rawBlob));
- if (close(in) != 0) {
- return ResponseCode::SYSTEM_ERROR;
- }
- // find salt at EOF if present, otherwise we have an old file
- uint8_t* salt;
- if (length > SALT_SIZE && rawBlob.info == SALT_SIZE) {
- salt = (uint8_t*)&rawBlob + length - SALT_SIZE;
- } else {
- salt = nullptr;
- }
-
- size_t masterKeySize = MASTER_KEY_SIZE_BYTES;
- if (rawBlob.type == TYPE_MASTER_KEY) {
- masterKeySize = kAes128KeySizeBytes;
- }
-
- std::vector<uint8_t> passwordKey(masterKeySize);
- generateKeyFromPassword(passwordKey, pw, salt);
- Blob masterKeyBlob, dummyBlob;
- ResponseCode response;
- std::tie(response, masterKeyBlob, dummyBlob) =
- lockedEntry.readBlobs(passwordKey, STATE_NO_ERROR);
- if (response == ResponseCode::SYSTEM_ERROR) {
- return response;
- }
-
- size_t masterKeyBlobLength = static_cast<size_t>(masterKeyBlob.getLength());
-
- if (response == ResponseCode::NO_ERROR && masterKeyBlobLength == masterKeySize) {
- // If salt was missing, generate one and write a new master key file with the salt.
- if (salt == nullptr) {
- if (!generateSalt()) {
- return ResponseCode::SYSTEM_ERROR;
- }
- response = writeMasterKey(pw);
- }
- if (response == ResponseCode::NO_ERROR) {
- mMasterKey = std::vector<uint8_t>(masterKeyBlob.getValue(),
- masterKeyBlob.getValue() + masterKeyBlob.getLength());
-
- setupMasterKeys();
- }
- return response;
- }
-
- LOG(ERROR) << "Invalid password presented";
- return ResponseCode::WRONG_PASSWORD_0;
-}
-
-bool UserState::reset() {
- DIR* dir = opendir(mMasterKeyEntry.user_dir().c_str());
- if (!dir) {
- // If the directory doesn't exist then nothing to do.
- if (errno == ENOENT) {
- return true;
- }
- ALOGW("couldn't open user directory: %s", strerror(errno));
- return false;
- }
-
- struct dirent* file;
- while ((file = readdir(dir)) != nullptr) {
- // skip . and ..
- if (!strcmp(".", file->d_name) || !strcmp("..", file->d_name)) {
- continue;
- }
-
- unlinkat(dirfd(dir), file->d_name, 0);
- }
- closedir(dir);
- return true;
-}
-
-void UserState::generateKeyFromPassword(std::vector<uint8_t>& key, const android::String8& pw,
- uint8_t* salt) {
- size_t saltSize;
- if (salt != nullptr) {
- saltSize = SALT_SIZE;
- } else {
- // Pre-gingerbread used this hardwired salt, readMasterKey will rewrite these when found
- salt = (uint8_t*)"keystore";
- // sizeof = 9, not strlen = 8
- saltSize = sizeof("keystore");
- }
-
- const EVP_MD* digest = EVP_sha256();
-
- // SHA1 was used prior to increasing the key size
- if (key.size() == kAes128KeySizeBytes) {
- digest = EVP_sha1();
- }
-
- PKCS5_PBKDF2_HMAC(reinterpret_cast<const char*>(pw.string()), pw.length(), salt, saltSize, 8192,
- digest, key.size(), key.data());
-}
-
-bool UserState::generateSalt() {
- return RAND_bytes(mSalt, sizeof(mSalt));
-}
-
-bool UserState::generateMasterKey() {
- mMasterKey.resize(MASTER_KEY_SIZE_BYTES);
- if (!RAND_bytes(mMasterKey.data(), mMasterKey.size())) {
- return false;
- }
- if (!generateSalt()) {
- return false;
- }
- return true;
-}
-
-void UserState::setupMasterKeys() {
- setState(STATE_NO_ERROR);
-}
-
-LockedUserState<UserState> UserStateDB::getUserState(uid_t userId) {
- std::unique_lock<std::mutex> lock(locked_state_mutex_);
- decltype(mMasterKeys.begin()) it;
- bool inserted;
- std::tie(it, inserted) = mMasterKeys.emplace(userId, userId);
- if (inserted) {
- if (!it->second.initialize()) {
- /* There's not much we can do if initialization fails. Trying to
- * unlock the keystore for that user will fail as well, so any
- * subsequent request for this user will just return SYSTEM_ERROR.
- */
- ALOGE("User initialization failed for %u; subsequent operations will fail", userId);
- }
- }
- return get(std::move(lock), &it->second);
-}
-
-LockedUserState<UserState> UserStateDB::getUserStateByUid(uid_t uid) {
- return getUserState(get_user_id(uid));
-}
-
-LockedUserState<const UserState> UserStateDB::getUserState(uid_t userId) const {
- std::unique_lock<std::mutex> lock(locked_state_mutex_);
- auto it = mMasterKeys.find(userId);
- if (it == mMasterKeys.end()) return {};
- return get(std::move(lock), &it->second);
-}
-
-LockedUserState<const UserState> UserStateDB::getUserStateByUid(uid_t uid) const {
- return getUserState(get_user_id(uid));
-}
-
-} // namespace keystore
diff --git a/keystore/user_state.h b/keystore/user_state.h
deleted file mode 100644
index 75d99d91..00000000
--- a/keystore/user_state.h
+++ /dev/null
@@ -1,131 +0,0 @@
-/*
- * Copyright (C) 2016 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 KEYSTORE_USER_STATE_H_
-#define KEYSTORE_USER_STATE_H_
-
-#include <sys/types.h>
-
-#include <openssl/aes.h>
-
-#include <utils/String8.h>
-
-#include <keystore/keystore.h>
-
-#include "blob.h"
-#include "keystore_utils.h"
-
-#include <android-base/logging.h>
-#include <condition_variable>
-#include <keystore/keystore_concurrency.h>
-#include <mutex>
-#include <set>
-#include <vector>
-
-namespace keystore {
-
-class UserState;
-
-template <typename UserState> using LockedUserState = ProxyLock<UnlockProxyLockHelper<UserState>>;
-
-class UserState {
- public:
- explicit UserState(uid_t userId);
-
- bool initialize();
-
- uid_t getUserId() const { return mUserId; }
- const std::string& getUserDirName() const { return mMasterKeyEntry.user_dir(); }
-
- std::string getMasterKeyFileName() const { return mMasterKeyEntry.getKeyBlobPath(); }
-
- void setState(State state);
- State getState() const { return mState; }
-
- void zeroizeMasterKeysInMemory();
- bool deleteMasterKey();
-
- ResponseCode initialize(const android::String8& pw);
-
- ResponseCode copyMasterKey(LockedUserState<UserState>* src);
- ResponseCode copyMasterKeyFile(LockedUserState<UserState>* src);
- ResponseCode writeMasterKey(const android::String8& pw);
- ResponseCode readMasterKey(const android::String8& pw);
-
- const std::vector<uint8_t>& getEncryptionKey() const { return mMasterKey; }
-
- bool reset();
-
- bool operator<(const UserState& rhs) const;
- bool operator<(uid_t userId) const;
-
- private:
- static constexpr int SHA1_DIGEST_SIZE_BYTES = 16;
- static constexpr int SHA256_DIGEST_SIZE_BYTES = 32;
-
- static constexpr int MASTER_KEY_SIZE_BYTES = kAes256KeySizeBytes;
- static constexpr int MASTER_KEY_SIZE_BITS = MASTER_KEY_SIZE_BYTES * 8;
-
- static constexpr size_t SALT_SIZE = 16;
-
- void generateKeyFromPassword(std::vector<uint8_t>& key, const android::String8& pw,
- uint8_t* salt);
- bool generateSalt();
- bool generateMasterKey();
- void setupMasterKeys();
-
- KeyBlobEntry mMasterKeyEntry;
-
- uid_t mUserId;
- State mState;
-
- std::vector<uint8_t> mMasterKey;
- uint8_t mSalt[SALT_SIZE];
-};
-
-bool operator<(uid_t userId, const UserState& rhs);
-
-class UserStateDB {
- public:
- LockedUserState<UserState> getUserState(uid_t userId);
- LockedUserState<UserState> getUserStateByUid(uid_t uid);
- LockedUserState<const UserState> getUserState(uid_t userId) const;
- LockedUserState<const UserState> getUserStateByUid(uid_t uid) const;
-
- private:
- mutable std::set<const UserState*> locked_state_;
- mutable std::mutex locked_state_mutex_;
- mutable std::condition_variable locked_state_mutex_cond_var_;
-
- template <typename UserState>
- LockedUserState<UserState> get(std::unique_lock<std::mutex> lock, UserState* entry) const {
- locked_state_mutex_cond_var_.wait(
- lock, [&] { return locked_state_.find(entry) == locked_state_.end(); });
- locked_state_.insert(entry);
- return {entry, [&](UserState* entry) {
- std::unique_lock<std::mutex> lock(locked_state_mutex_);
- locked_state_.erase(entry);
- lock.unlock();
- locked_state_mutex_cond_var_.notify_all();
- }};
- }
-
- std::map<uid_t, UserState> mMasterKeys;
-};
-
-} // namespace keystore
-
-#endif // KEYSTORE_USER_STATE_H_
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index d8c00814..ed9cd880 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -28,6 +28,7 @@ rust_defaults {
defaults: [
"keymint_use_latest_hal_aidl_rust",
"keystore2_use_latest_aidl_rust",
+ "structured_log_rust_defaults",
],
rustlibs: [
@@ -40,26 +41,30 @@ rust_defaults {
"android.security.compat-rust",
"android.security.maintenance-rust",
"android.security.metrics-rust",
- "android.security.remoteprovisioning-rust",
"android.security.rkp_aidl-rust",
+ "libaconfig_android_hardware_biometrics_rust",
+ "libandroid_security_flags_rust",
"libanyhow",
"libbinder_rs",
"libkeystore2_aaid-rust",
"libkeystore2_apc_compat-rust",
"libkeystore2_crypto_rust",
+ "libkeystore2_flags_rust",
+ "libkeystore2_hal_names_rust",
"libkeystore2_km_compat",
"libkeystore2_selinux",
- "libkeystore2_vintf_rust",
"liblazy_static",
"liblibc",
- "liblog_event_list",
"liblog_rust",
+ "libmessage_macro",
"librand",
+ "librkpd_client",
"librustutils",
"libserde",
"libserde_cbor",
"libthiserror",
"libtokio",
+ "libwatchdog_rs",
],
shared_libs: [
"libcutils",
@@ -79,62 +84,15 @@ rust_library {
}
rust_library {
- name: "libkeystore2_test_utils",
- crate_name: "keystore2_test_utils",
- srcs: ["test_utils/lib.rs"],
- defaults: [
- "keymint_use_latest_hal_aidl_rust",
- "keystore2_use_latest_aidl_rust",
- ],
- rustlibs: [
- "android.hardware.security.rkp-V3-rust",
- "libbinder_rs",
- "libkeystore2_selinux",
- "liblog_rust",
- "libnix",
- "librand",
- "libserde",
- "libserde_cbor",
- "libthiserror",
- "libanyhow",
- ],
-}
-
-rust_library {
name: "libkeystore2_with_test_utils",
defaults: ["libkeystore2_defaults"],
features: [
"keystore2_blob_test_utils",
],
rustlibs: [
+ "libkeystore2_test_utils",
"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",
- "keystore2_use_latest_aidl_rust",
- ],
- test_suites: ["general-tests"],
- require_root: true,
- auto_gen_config: true,
- compile_multilib: "first",
- rustlibs: [
- "android.hardware.security.rkp-V3-rust",
- "libbinder_rs",
- "libkeystore2_selinux",
- "liblog_rust",
- "libnix",
- "librand",
- "libserde",
- "libserde_cbor",
- "libthiserror",
- "libanyhow",
],
}
@@ -147,16 +105,17 @@ rust_test {
defaults: ["libkeystore2_defaults"],
rustlibs: [
"libandroid_logger",
+ "libhex",
"libkeystore2_test_utils",
+ "libkeystore2_with_test_utils",
"liblibsqlite3_sys",
"libnix",
"librusqlite",
- "libkeystore2_with_test_utils",
],
// The test should always include watchdog.
features: [
- "watchdog",
"keystore2_blob_test_utils",
+ "watchdog",
],
require_root: true,
}
@@ -194,3 +153,23 @@ rust_binary {
],
afdo: true,
}
+
+// Keystore Flag definitions
+aconfig_declarations {
+ name: "keystore2_flags",
+ package: "android.security.keystore2",
+ container: "system",
+ srcs: ["aconfig/flags.aconfig"],
+}
+
+rust_aconfig_library {
+ name: "libkeystore2_flags_rust",
+ crate_name: "keystore2_flags",
+ aconfig_declarations: "keystore2_flags",
+}
+
+rust_aconfig_library {
+ name: "libaconfig_android_hardware_biometrics_rust",
+ crate_name: "aconfig_android_hardware_biometrics_rust",
+ aconfig_declarations: "android.hardware.biometrics.flags-aconfig",
+}
diff --git a/keystore2/OWNERS b/keystore2/OWNERS
new file mode 100644
index 00000000..6b1a95bd
--- /dev/null
+++ b/keystore2/OWNERS
@@ -0,0 +1,9 @@
+set noparent
+# Bug component: 1084732
+eranm@google.com
+drysdale@google.com
+hasinitg@google.com
+jbires@google.com
+sethmo@google.com
+trong@google.com
+swillden@google.com
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 5d0a7dd3..57ce78cc 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -30,6 +30,15 @@
"postsubmit": [
{
"name": "CtsKeystorePerformanceTestCases"
+ },
+ {
+ "name": "keystore2_client_tests"
+ },
+ {
+ "name": "librkpd_client.test"
+ },
+ {
+ "name": "libwatchdog_rs.test"
}
]
}
diff --git a/keystore2/aaid/Android.bp b/keystore2/aaid/Android.bp
index 3417960d..3e90a926 100644
--- a/keystore2/aaid/Android.bp
+++ b/keystore2/aaid/Android.bp
@@ -27,7 +27,7 @@ cc_library {
"aaid.cpp",
],
shared_libs: [
- "libkeystore-attestation-application-id"
+ "libkeystore-attestation-application-id",
],
}
@@ -38,7 +38,6 @@ rust_bindgen {
source_stem: "bindings",
bindgen_flags: [
- "--size_t-is-usize",
"--allowlist-function=aaid_keystore_attestation_id",
"--allowlist-var=KEY_ATTESTATION_APPLICATION_ID_MAX_SIZE",
],
diff --git a/keystore2/aconfig/flags.aconfig b/keystore2/aconfig/flags.aconfig
new file mode 100644
index 00000000..b67bc6cb
--- /dev/null
+++ b/keystore2/aconfig/flags.aconfig
@@ -0,0 +1,26 @@
+package: "android.security.keystore2"
+container: "system"
+
+flag {
+ name: "wal_db_journalmode_v3"
+ namespace: "hardware_backed_security"
+ description: "This flag controls changing journalmode to wal"
+ bug: "191777960"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "disable_legacy_keystore_put_v2"
+ namespace: "hardware_backed_security"
+ description: "This flag disables legacy keystore put and makes it so that command returns an error"
+ bug: "307460850"
+ is_fixed_read_only: true
+}
+
+flag {
+ name: "import_previously_emulated_keys"
+ namespace: "hardware_backed_security"
+ description: "Include support for importing keys that were previously software-emulated into KeyMint"
+ bug: "283077822"
+ is_fixed_read_only: true
+} \ No newline at end of file
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index e3961da3..c297a158 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -23,8 +23,8 @@ package {
aidl_interface {
name: "android.security.attestationmanager",
- srcs: [ "android/security/attestationmanager/*.aidl", ],
- imports: [ "android.hardware.security.keymint-V3" ],
+ srcs: ["android/security/attestationmanager/*.aidl"],
+ imports: ["android.hardware.security.keymint-V3"],
unstable: true,
backend: {
java: {
@@ -36,13 +36,13 @@ aidl_interface {
ndk: {
enabled: true,
apps_enabled: false,
- }
+ },
},
}
aidl_interface {
name: "android.security.authorization",
- srcs: [ "android/security/authorization/*.aidl" ],
+ srcs: ["android/security/authorization/*.aidl"],
imports: [
"android.hardware.security.keymint-V3",
"android.hardware.security.secureclock-V1",
@@ -58,13 +58,13 @@ aidl_interface {
ndk: {
enabled: true,
apps_enabled: false,
- }
+ },
},
}
aidl_interface {
name: "android.security.apc",
- srcs: [ "android/security/apc/*.aidl" ],
+ srcs: ["android/security/apc/*.aidl"],
unstable: true,
backend: {
java: {
@@ -75,13 +75,13 @@ aidl_interface {
},
ndk: {
enabled: true,
- }
+ },
},
}
aidl_interface {
name: "android.security.compat",
- srcs: [ "android/security/compat/*.aidl" ],
+ srcs: ["android/security/compat/*.aidl"],
imports: [
"android.hardware.security.keymint-V3",
"android.hardware.security.secureclock-V1",
@@ -103,32 +103,10 @@ aidl_interface {
}
aidl_interface {
- name: "android.security.remoteprovisioning",
- srcs: [ "android/security/remoteprovisioning/*.aidl" ],
- imports: [
- "android.hardware.security.keymint-V3",
- "android.hardware.security.rkp-V3",
- ],
- unstable: true,
- backend: {
- java: {
- platform_apis: true,
- },
- ndk: {
- enabled: true,
- apps_enabled: false,
- },
- rust: {
- enabled: true,
- },
- },
-}
-
-aidl_interface {
name: "android.security.maintenance",
- srcs: [ "android/security/maintenance/*.aidl" ],
+ srcs: ["android/security/maintenance/*.aidl"],
imports: [
- "android.system.keystore2-V3",
+ "android.system.keystore2-V4",
],
unstable: true,
backend: {
@@ -141,13 +119,13 @@ aidl_interface {
ndk: {
enabled: true,
apps_enabled: false,
- }
+ },
},
}
aidl_interface {
name: "android.security.legacykeystore",
- srcs: [ "android/security/legacykeystore/*.aidl" ],
+ srcs: ["android/security/legacykeystore/*.aidl"],
unstable: true,
backend: {
java: {
@@ -159,15 +137,15 @@ aidl_interface {
ndk: {
enabled: true,
apps_enabled: false,
- }
+ },
},
}
aidl_interface {
name: "android.security.metrics",
- srcs: [ "android/security/metrics/*.aidl" ],
+ srcs: ["android/security/metrics/*.aidl"],
imports: [
- "android.system.keystore2-V3",
+ "android.system.keystore2-V4",
],
unstable: true,
backend: {
@@ -180,7 +158,7 @@ aidl_interface {
ndk: {
enabled: true,
apps_enabled: false,
- }
+ },
},
}
@@ -190,21 +168,21 @@ aidl_interface {
java_defaults {
name: "keystore2_use_latest_aidl_java_static",
static_libs: [
- "android.system.keystore2-V3-java-source"
+ "android.system.keystore2-V4-java-source",
],
}
java_defaults {
name: "keystore2_use_latest_aidl_java_shared",
libs: [
- "android.system.keystore2-V3-java-source"
+ "android.system.keystore2-V4-java-source",
],
}
java_defaults {
name: "keystore2_use_latest_aidl_java",
libs: [
- "android.system.keystore2-V3-java"
+ "android.system.keystore2-V4-java",
],
}
@@ -214,38 +192,37 @@ java_defaults {
cc_defaults {
name: "keystore2_use_latest_aidl_ndk_static",
static_libs: [
- "android.system.keystore2-V3-ndk",
+ "android.system.keystore2-V4-ndk",
],
}
cc_defaults {
name: "keystore2_use_latest_aidl_ndk_shared",
shared_libs: [
- "android.system.keystore2-V3-ndk",
+ "android.system.keystore2-V4-ndk",
],
}
cc_defaults {
name: "keystore2_use_latest_aidl_cpp_shared",
shared_libs: [
- "android.system.keystore2-V3-cpp",
+ "android.system.keystore2-V4-cpp",
],
}
cc_defaults {
name: "keystore2_use_latest_aidl_cpp_static",
static_libs: [
- "android.system.keystore2-V3-cpp",
+ "android.system.keystore2-V4-cpp",
],
}
-
// A rust_defaults that includes the latest Keystore2 AIDL library.
// Modules that depend on Keystore2 directly can include this rust_defaults to avoid
// managing dependency versions explicitly.
rust_defaults {
name: "keystore2_use_latest_aidl_rust",
rustlibs: [
- "android.system.keystore2-V3-rust",
+ "android.system.keystore2-V4-rust",
],
}
diff --git a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
index 277b9dd8..5b22be01 100644
--- a/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
+++ b/keystore2/aidl/android/security/apc/IConfirmationCallback.aidl
@@ -27,6 +27,10 @@ interface IConfirmationCallback {
/**
* This callback gets called by the implementing service when a pending confirmation prompt
* gets finalized.
+ * @deprecated Android Protected Confirmation had a low adoption rate among Android device
+ * makers and developers alike. Given the lack of devices supporting the feature,
+ * it is deprecated. Developers can use auth-bound Keystore keys as a partial
+ * replacement.
*
* @param result
* - ResponseCode.OK On success. In this case dataConfirmed must be non null.
diff --git a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
index 3162224f..9f978479 100644
--- a/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
+++ b/keystore2/aidl/android/security/apc/IProtectedConfirmation.aidl
@@ -35,6 +35,10 @@ interface IProtectedConfirmation {
/**
* Present the confirmation prompt. The caller must implement IConfirmationCallback and pass
* it to this function as listener.
+ * @deprecated Android Protected Confirmation had a low adoption rate among Android device
+ * makers and developers alike. Given the lack of devices supporting the
+ * feature, it is deprecated. Developers can use auth-bound Keystore keys
+ * as a partial replacement.
*
* @param listener Must implement IConfirmationCallback. Doubles as session identifier when
* passed to cancelPrompt.
@@ -55,6 +59,11 @@ interface IProtectedConfirmation {
/**
* Cancel an ongoing prompt.
+ * @deprecated Android Protected Confirmation had a low adoption rate among Android device
+ * makers and developers alike. Given the lack of devices supporting the
+ * feature, it is deprecated. Developers can use auth-bound Keystore keys as
+ * a partial replacement.
+ *
*
* @param listener Must implement IConfirmationCallback, although in this context this binder
* token is only used to identify the session that is to be cancelled.
@@ -66,6 +75,10 @@ interface IProtectedConfirmation {
/**
* Returns true if the device supports Android Protected Confirmation.
+ * @deprecated Android Protected Confirmation had a low adoption rate among Android device
+ * makers and developers alike. Given the lack of devices supporting the
+ * feature, it is deprecated. Developers can use auth-bound Keystore keys
+ * as a partial replacement.
*/
boolean isSupported();
}
diff --git a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
index e3b7d11d..fd532f62 100644
--- a/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
+++ b/keystore2/aidl/android/security/authorization/IKeystoreAuthorization.aidl
@@ -15,11 +15,9 @@
package android.security.authorization;
import android.hardware.security.keymint.HardwareAuthToken;
-import android.security.authorization.LockScreenEvent;
+import android.hardware.security.keymint.HardwareAuthenticatorType;
import android.security.authorization.AuthorizationTokens;
-// TODO: mark the interface with @SensitiveData when the annotation is ready (b/176110256).
-
/**
* IKeystoreAuthorization interface exposes the methods for other system components to
* provide keystore with the information required to enforce authorizations on key usage.
@@ -40,41 +38,102 @@ interface IKeystoreAuthorization {
void addAuthToken(in HardwareAuthToken authToken);
/**
- * Unlocks the keystore for the given user id.
+ * Tells Keystore that the device is now unlocked for a user. Requires the 'Unlock' permission.
+ *
+ * This method makes Keystore start allowing the use of the given user's keys that require an
+ * unlocked device, following the device boot or an earlier call to onDeviceLocked() which
+ * disabled the use of such keys. In addition, once per boot, this method must be called with a
+ * password before keys that require user authentication can be used.
+ *
+ * This method does two things to restore access to UnlockedDeviceRequired keys. First, it sets
+ * a flag that indicates the user is unlocked. This is always done, and it makes Keystore's
+ * logical enforcement of UnlockedDeviceRequired start passing. Second, it recovers and caches
+ * the user's UnlockedDeviceRequired super keys. This succeeds only in the following cases:
+ *
+ * - The (correct) password is provided, proving that the user has authenticated using LSKF or
+ * equivalent. This is the most powerful type of unlock. Keystore uses the password to
+ * decrypt the user's UnlockedDeviceRequired super keys from disk. It also uses the password
+ * to decrypt the user's AfterFirstUnlock super key from disk, if not already done.
+ *
+ * - The user's UnlockedDeviceRequired super keys are cached in biometric-encrypted form, and a
+ * matching valid HardwareAuthToken has been added to Keystore. I.e., class 3 biometric
+ * unlock is enabled and the user recently authenticated using a class 3 biometric. The keys
+ * are cached in biometric-encrypted form if onDeviceLocked() was called with a nonempty list
+ * of unlockingSids, and onNonLskfUnlockMethodsExpired() was not called later.
+ *
+ * - The user's UnlockedDeviceRequired super keys are already cached in plaintext. This is the
+ * case if onDeviceLocked() was called with weakUnlockEnabled=true, and
+ * onWeakUnlockMethodsExpired() was not called later. This case provides only
+ * Keystore-enforced logical security for UnlockedDeviceRequired.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'Unlock' permission.
+ * `ResponseCode::VALUE_CORRUPTED` - if a super key can not be decrypted.
+ * `ResponseCode::KEY_NOT_FOUND` - if a super key is not found.
+ * `ResponseCode::SYSTEM_ERROR` - if another error occurred.
+ *
+ * @param userId The Android user ID of the user for which the device is now unlocked
+ * @param password If available, a secret derived from the user's synthetic password
+ */
+ void onDeviceUnlocked(in int userId, in @nullable byte[] password);
+
+ /**
+ * Tells Keystore that the device is now locked for a user. Requires the 'Lock' permission.
+ *
+ * This method makes Keystore stop allowing the use of the given user's keys that require an
+ * unlocked device. This is enforced logically, and when possible it's also enforced
+ * cryptographically by wiping the UnlockedDeviceRequired super keys from memory.
+ *
+ * unlockingSids and weakUnlockEnabled specify the methods by which the device can become
+ * unlocked for the user, in addition to LSKF-equivalent authentication.
+ *
+ * unlockingSids is the list of SIDs of class 3 (strong) biometrics that can unlock. If
+ * unlockingSids is non-empty, then this method saves a copy of the UnlockedDeviceRequired super
+ * keys in memory encrypted by a new AES key that is imported into KeyMint and configured to be
+ * usable only when user authentication has occurred using any of the SIDs. This allows the
+ * keys to be recovered if the device is unlocked using a class 3 biometric.
*
- * Callers require 'Unlock' permission.
+ * weakUnlockEnabled is true if the unlock can happen using a method that does not have an
+ * associated SID, such as a class 1 (convenience) biometric, class 2 (weak) biometric, or trust
+ * agent. These methods don't count as "authentication" from Keystore's perspective. In this
+ * case, Keystore keeps a copy of the UnlockedDeviceRequired super keys in memory in plaintext,
+ * providing only logical security for UnlockedDeviceRequired.
*
- * Super-Encryption Key:
- * When the device is unlocked (and password is non-null), Keystore stores in memory
- * a super-encryption key derived from the password that protects UNLOCKED_DEVICE_REQUIRED
- * keys; this key is wiped from memory when the device is locked.
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'Lock' permission.
*
- * If unlockingSids is non-empty on lock, then before the super-encryption key is wiped from
- * memory, a copy of it is stored in memory encrypted with a fresh AES key. This key is then
- * imported into KM, tagged such that it can be used given a valid, recent auth token for any
- * of the unlockingSids.
+ * @param userId The Android user ID of the user for which the device is now locked
+ * @param unlockingSids SIDs of class 3 biometrics that can unlock the device for the user
+ * @param weakUnlockEnabled Whether a weak unlock method can unlock the device for the user
+ */
+ void onDeviceLocked(in int userId, in long[] unlockingSids, in boolean weakUnlockEnabled);
+
+ /**
+ * Tells Keystore that weak unlock methods can no longer unlock the device for the given user.
+ * This is intended to be called after an earlier call to onDeviceLocked() with
+ * weakUnlockEnabled=true. It upgrades the security level of UnlockedDeviceRequired keys to
+ * that which would have resulted from calling onDeviceLocked() with weakUnlockEnabled=false.
*
- * Options for unlock:
- * - If the password is non-null, the super-encryption key is re-derived as above.
- * - If the password is null, then if a suitable auth token to access the encrypted
- * Super-encryption key stored in KM has been sent to keystore (via addAuthToken), the
- * encrypted super-encryption key is recovered so that UNLOCKED_DEVICE_REQUIRED keys can
- * be used once again.
- * - If neither of these are met, then the operation fails.
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'Lock' permission.
+ *
+ * @param userId The Android user ID of the user for which weak unlock methods have expired
+ */
+ void onWeakUnlockMethodsExpired(in int userId);
+
+ /**
+ * Tells Keystore that non-LSKF-equivalent unlock methods can no longer unlock the device for
+ * the given user. This is intended to be called after an earlier call to onDeviceLocked() with
+ * nonempty unlockingSids. It upgrades the security level of UnlockedDeviceRequired keys to
+ * that which would have resulted from calling onDeviceLocked() with unlockingSids=[] and
+ * weakUnlockEnabled=false.
*
* ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'Unlock' permission.
- * `ResponseCode::SYSTEM_ERROR` - if failed to perform lock/unlock operations due to various
- * `ResponseCode::VALUE_CORRUPTED` - if the super key can not be decrypted.
- * `ResponseCode::KEY_NOT_FOUND` - if the super key is not found.
- *
- * @param lockScreenEvent whether the lock screen locked or unlocked
- * @param userId android user id
- * @param password synthetic password derived from the user's LSKF, must be null on lock
- * @param unlockingSids list of biometric SIDs for this user, ignored on unlock
+ * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the 'Lock' permission.
+ *
+ * @param userId The Android user ID of the user for which non-LSKF unlock methods have expired
*/
- void onLockScreenEvent(in LockScreenEvent lockScreenEvent, in int userId,
- in @nullable byte[] password, in @nullable long[] unlockingSids);
+ void onNonLskfUnlockMethodsExpired(in int userId);
/**
* Allows Credstore to retrieve a HardwareAuthToken and a TimestampToken.
@@ -108,4 +167,13 @@ interface IKeystoreAuthorization {
*/
AuthorizationTokens getAuthTokensForCredStore(in long challenge, in long secureUserId,
in long authTokenMaxAgeMillis);
+
+ /**
+ * Returns the last successful authentication time since boot for the given user with any of the
+ * given authenticator types. This is determined by inspecting the cached auth tokens.
+ *
+ * ## Error conditions:
+ * `ResponseCode::NO_AUTH_TOKEN_FOUND` - if there is no matching authentication token found
+ */
+ long getLastAuthTime(in long secureUserId, in HardwareAuthenticatorType[] authTypes);
}
diff --git a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl b/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
deleted file mode 100644
index c7553a27..00000000
--- a/keystore2/aidl/android/security/authorization/LockScreenEvent.aidl
+++ /dev/null
@@ -1,22 +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.
-
-package android.security.authorization;
-
-/** @hide */
-@Backing(type="int")
-enum LockScreenEvent {
- UNLOCK = 0,
- LOCK = 1,
-}
diff --git a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
index 6a37c786..50e98286 100644
--- a/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
+++ b/keystore2/aidl/android/security/maintenance/IKeystoreMaintenance.aidl
@@ -16,7 +16,6 @@ package android.security.maintenance;
import android.system.keystore2.Domain;
import android.system.keystore2.KeyDescriptor;
-import android.security.maintenance.UserState;
/**
* IKeystoreMaintenance interface exposes the methods for adding/removing users and changing the
@@ -28,10 +27,10 @@ interface IKeystoreMaintenance {
/**
* Allows LockSettingsService to inform keystore about adding a new user.
- * Callers require 'AddUser' permission.
+ * Callers require 'ChangeUser' permission.
*
* ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'AddUser' permission.
+ * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'ChangeUser' permission.
* `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of an existing user with the same
* user id.
*
@@ -40,11 +39,25 @@ interface IKeystoreMaintenance {
void onUserAdded(in int userId);
/**
+ * Allows LockSettingsService to tell Keystore to create a user's superencryption keys and store
+ * them encrypted by the given secret. Requires 'ChangeUser' permission.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if caller does not have the 'ChangeUser' permission
+ * `ResponseCode::SYSTEM_ERROR` - if failed to initialize the user's super keys
+ *
+ * @param userId - Android user id
+ * @param password - a secret derived from the synthetic password of the user
+ * @param allowExisting - if true, then the keys already existing is not considered an error
+ */
+ void initUserSuperKeys(in int userId, in byte[] password, in boolean allowExisting);
+
+ /**
* Allows LockSettingsService to inform keystore about removing a user.
- * Callers require 'RemoveUser' permission.
+ * Callers require 'ChangeUser' permission.
*
* ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'RemoveUser' permission.
+ * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'ChangeUser' permission.
* `ResponseCode::SYSTEM_ERROR` - if failed to delete the keys of the user being deleted.
*
* @param userId - Android user id
@@ -52,6 +65,18 @@ interface IKeystoreMaintenance {
void onUserRemoved(in int userId);
/**
+ * Allows LockSettingsService to tell Keystore that a user's LSKF is being removed, ie the
+ * user's lock screen is changing to Swipe or None. Requires 'ChangePassword' permission.
+ *
+ * ## Error conditions:
+ * `ResponseCode::PERMISSION_DENIED` - if caller does not have the 'ChangePassword' permission
+ * `ResponseCode::SYSTEM_ERROR` - if failed to delete the user's auth-bound keys
+ *
+ * @param userId - Android user id
+ */
+ void onUserLskfRemoved(in int userId);
+
+ /**
* Allows LockSettingsService to inform keystore about password change of a user.
* Callers require 'ChangePassword' permission.
*
@@ -77,19 +102,6 @@ interface IKeystoreMaintenance {
void clearNamespace(Domain domain, long nspace);
/**
- * Allows querying user state, given user id.
- * Callers require 'GetState' permission.
- *
- * ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the callers do not have the 'GetState'
- * permission.
- * `ResponseCode::SYSTEM_ERROR` - if an error occurred when querying the user state.
- *
- * @param userId - Android user id
- */
- UserState getState(in int userId);
-
- /**
* This function notifies the Keymint device of the specified securityLevel that
* early boot has ended, so that they no longer allow early boot keys to be used.
* ## Error conditions:
@@ -100,16 +112,6 @@ interface IKeystoreMaintenance {
void earlyBootEnded();
/**
- * Informs Keystore 2.0 that the an off body event was detected.
- *
- * ## Error conditions:
- * `ResponseCode::PERMISSION_DENIED` - if the caller does not have the `ReportOffBody`
- * permission.
- * `ResponseCode::SYSTEM_ERROR` - if an unexpected error occurred.
- */
- void onDeviceOffBody();
-
- /**
* Migrate a key from one namespace to another. The caller must have use, grant, and delete
* permissions on the source namespace and rebind permissions on the destination namespace.
* The source may be specified by Domain::APP, Domain::SELINUX, or Domain::KEY_ID. The target
@@ -131,4 +133,23 @@ interface IKeystoreMaintenance {
* Tag::ROLLBACK_RESISTANCE may or may not be rendered unusable.
*/
void deleteAllKeys();
+
+ /**
+ * Returns a list of App UIDs that have keys associated with the given SID, under the
+ * given user ID.
+ * When a given user's LSKF is removed or biometric authentication methods are changed
+ * (addition of a fingerprint, for example), authentication-bound keys may be invalidated.
+ * This method allows the platform to find out which apps would be affected (for a given user)
+ * when a given user secure ID is removed.
+ * Callers require the `android.permission.MANAGE_USERS` Android permission
+ * (not SELinux policy).
+ *
+ * @param userId The affected user.
+ * @param sid The user secure ID - identifier of the authentication method.
+ *
+ * @return A list of APP UIDs, in the form of (AID + userId*AID_USER_OFFSET), that have
+ * keys auth-bound to the given SID. These values can be passed into the
+ * PackageManager for resolution.
+ */
+ long[] getAppUidsAffectedBySid(in int userId, in long sid);
}
diff --git a/keystore2/aidl/android/security/maintenance/UserState.aidl b/keystore2/aidl/android/security/maintenance/UserState.aidl
deleted file mode 100644
index 376f4fb0..00000000
--- a/keystore2/aidl/android/security/maintenance/UserState.aidl
+++ /dev/null
@@ -1,23 +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.maintenance;
-
-/** @hide */
-@Backing(type="int")
-enum UserState {
- UNINITIALIZED = 0,
- LSKF_UNLOCKED = 1,
- LSKF_LOCKED = 2,
-} \ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/AtomID.aidl b/keystore2/aidl/android/security/metrics/AtomID.aidl
index 166e7538..3043ed3a 100644
--- a/keystore2/aidl/android/security/metrics/AtomID.aidl
+++ b/keystore2/aidl/android/security/metrics/AtomID.aidl
@@ -23,7 +23,7 @@ package android.security.metrics;
@Backing(type="int")
enum AtomID {
STORAGE_STATS = 10103,
- RKP_POOL_STATS = 10104,
+ // reserved 10104
KEY_CREATION_WITH_GENERAL_INFO = 10118,
KEY_CREATION_WITH_AUTH_INFO = 10119,
KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO = 10120,
@@ -32,4 +32,4 @@ enum AtomID {
KEY_OPERATION_WITH_GENERAL_INFO = 10123,
RKP_ERROR_STATS = 10124,
CRASH_STATS = 10125,
-} \ No newline at end of file
+}
diff --git a/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl b/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
index 266267ac..843e80b2 100644
--- a/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
+++ b/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
@@ -22,7 +22,7 @@ import android.security.metrics.KeystoreAtomPayload;
* Encapsulates a particular atom object of type KeystoreAtomPayload its count. Note that
* the field: count is only relevant for the atom types that are stored in the
* in-memory metrics store. E.g. count field is not relevant for the atom types such as StorageStats
- * and RkpPoolStats that are not stored in the metrics store.
+ * that are not stored in the metrics store.
* @hide
*/
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
diff --git a/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl b/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
index a3e4dd68..2f89a2d1 100644
--- a/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
+++ b/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
@@ -24,14 +24,12 @@ import android.security.metrics.KeyOperationWithPurposeAndModesInfo;
import android.security.metrics.StorageStats;
import android.security.metrics.Keystore2AtomWithOverflow;
import android.security.metrics.RkpErrorStats;
-import android.security.metrics.RkpPoolStats;
import android.security.metrics.CrashStats;
/** @hide */
@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
union KeystoreAtomPayload {
StorageStats storageStats;
- RkpPoolStats rkpPoolStats;
KeyCreationWithGeneralInfo keyCreationWithGeneralInfo;
KeyCreationWithAuthInfo keyCreationWithAuthInfo;
KeyCreationWithPurposeAndModesInfo keyCreationWithPurposeAndModesInfo;
@@ -40,4 +38,4 @@ union KeystoreAtomPayload {
KeyOperationWithGeneralInfo keyOperationWithGeneralInfo;
RkpErrorStats rkpErrorStats;
CrashStats crashStats;
-} \ No newline at end of file
+}
diff --git a/keystore2/aidl/android/security/metrics/PoolStatus.aidl b/keystore2/aidl/android/security/metrics/PoolStatus.aidl
deleted file mode 100644
index 35301639..00000000
--- a/keystore2/aidl/android/security/metrics/PoolStatus.aidl
+++ /dev/null
@@ -1,30 +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.metrics;
-
-/**
- * Status of the remotely provisioned keys, as defined in RkpPoolStats of
- * frameworks/proto_logging/stats/atoms.proto.
- * @hide
- */
-@Backing(type="int")
-enum PoolStatus {
- EXPIRING = 1,
- UNASSIGNED = 2,
- ATTESTED = 3,
- TOTAL = 4,
-} \ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl b/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl
deleted file mode 100644
index 016b6ff3..00000000
--- a/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl
+++ /dev/null
@@ -1,32 +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.metrics;
-
-import android.security.metrics.SecurityLevel;
-
-/**
- * Count of keys in the attestation key pool related to Remote Key Provisioning (RKP).
- * @hide
- */
-@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
-parcelable RkpPoolStats {
- SecurityLevel security_level;
- int expiring;
- int unassigned;
- int attested;
- int total;
-} \ No newline at end of file
diff --git a/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl b/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl
deleted file mode 100644
index 3528b423..00000000
--- a/keystore2/aidl/android/security/remoteprovisioning/AttestationPoolStatus.aidl
+++ /dev/null
@@ -1,45 +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.
- */
-
-package android.security.remoteprovisioning;
-
-/**
- * This parcelable provides information about the state of the attestation key pool.
- * @hide
- */
-parcelable AttestationPoolStatus {
- /**
- * The number of signed attestation certificate chains which will expire when the date provided
- * to keystore to check against is reached.
- */
- int expiring;
- /**
- * The number of signed attestation certificate chains which have not yet been assigned to an
- * app. This should be less than or equal to signed keys. The remainder of `signed` -
- * `unassigned` gives the number of signed keys that have been assigned to an app.
- */
- int unassigned;
- /**
- * The number of signed attestation keys. This should be less than or equal to `total`. The
- * remainder of `total` - `attested` gives the number of keypairs available to be sent off to
- * the server for signing.
- */
- int attested;
- /**
- * The total number of attestation keys.
- */
- int total;
-}
diff --git a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl b/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
deleted file mode 100644
index ecdc7901..00000000
--- a/keystore2/aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
+++ /dev/null
@@ -1,148 +0,0 @@
-/*
- * Copyright (C) 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.
- */
-
-package android.security.remoteprovisioning;
-
-import android.hardware.security.keymint.DeviceInfo;
-import android.hardware.security.keymint.ProtectedData;
-import android.hardware.security.keymint.SecurityLevel;
-import android.security.remoteprovisioning.AttestationPoolStatus;
-import android.security.remoteprovisioning.ImplInfo;
-
-/**
- * `IRemoteProvisioning` is the interface provided to use the remote provisioning functionality
- * provided through KeyStore. The intent is for a higher level system component to use these
- * functions in order to drive the process through which the device can receive functioning
- * attestation certificates.
- *
- * ## Error conditions
- * Error conditions are reported as service specific errors.
- * Positive codes correspond to `android.security.remoteprovisioning.ResponseCode`
- * and indicate error conditions diagnosed by the Keystore 2.0 service.
- * TODO: Remote Provisioning HAL error code info
- *
- * `ResponseCode::PERMISSION_DENIED` if the caller does not have the permissions
- * to use the RemoteProvisioning API. This permission is defined under access_vectors in SEPolicy
- * in the keystore2 class: remotely_provision
- *
- * `ResponseCode::SYSTEM_ERROR` for any unexpected errors like IO or IPC failures.
- *
- * @hide
- */
-interface IRemoteProvisioning {
-
- /**
- * Returns the status of the attestation key pool in the database.
- *
- * @param expiredBy The date as seconds since epoch by which to judge expiration status of
- * certificates.
- *
- * @param secLevel The security level to specify which KM instance to get the pool for.
- *
- * @return The `AttestationPoolStatus` parcelable contains fields communicating information
- * relevant to making decisions about when to generate and provision
- * more attestation keys.
- */
- AttestationPoolStatus getPoolStatus(in long expiredBy, in SecurityLevel secLevel);
-
- /**
- * This is the primary entry point for beginning a remote provisioning flow. The caller
- * specifies how many CSRs should be generated and provides an X25519 ECDH public key along
- * with a challenge to encrypt privacy sensitive portions of the returned CBOR blob and
- * guarantee freshness of the request to the certifying third party.
- *
- * ## Error conditions
- * `ResponseCode::NO_UNSIGNED_KEYS` if there are no unsigned keypairs in the database that can
- * be used for the CSRs.
- *
- * A RemoteProvisioning HAL response code may indicate backend errors such as failed EEK
- * verification.
- *
- * @param testMode Whether or not the TA implementing the Remote Provisioning HAL should accept
- * any EEK (Endpoint Encryption Key), or only one signed by a chain
- * that verifies back to the Root of Trust baked into the TA. True
- * means that any key is accepted.
- *
- * @param numCsr How many certificate signing requests should be generated.
- *
- * @param eek A chain of certificates terminating in an X25519 public key, the Endpoint
- * Encryption Key.
- *
- * @param challenge A challenge to be included and MACed in the returned CBOR blob.
- *
- * @param secLevel The security level to specify which KM instance from which to generate a
- * CSR.
- *
- * @param protectedData The encrypted CBOR blob generated by the remote provisioner
- *
- * @return A CBOR blob composed of various elements required by the server to verify the
- * request.
- */
- byte[] generateCsr(in boolean testMode, in int numCsr, in byte[] eek, in byte[] challenge,
- in SecurityLevel secLevel, out ProtectedData protectedData, out DeviceInfo deviceInfo);
-
- /**
- * This method provides a way for the returned attestation certificate chains to be provisioned
- * to the attestation key database. When an app requests an attesation key, it will be assigned
- * one of these certificate chains along with the corresponding private key.
- *
- * @param publicKey The raw public key encoded in the leaf certificate.
- *
- * @param batchCert The batch certificate corresponding to the attestation key. Separated for
- * the purpose of making Subject lookup for KM attestation easier.
- *
- * @param certs An X.509, DER encoded certificate chain for the attestation key.
- *
- * @param expirationDate The expiration date on the certificate chain, provided by the caller
- * for convenience.
- *
- * @param secLevel The security level representing the KM instance containing the key that this
- * chain corresponds to.
- */
- void provisionCertChain(in byte[] publicKey, in byte[] batchCert, in byte[] certs,
- in long expirationDate, in SecurityLevel secLevel);
-
- /**
- * This method allows the caller to instruct KeyStore to generate and store a key pair to be
- * used for attestation in the `generateCsr` method. The caller should handle spacing out these
- * requests so as not to jam up the KeyStore work queue.
- *
- * @param is_test_mode Instructs the underlying HAL interface to mark the generated key with a
- * tag to indicate that it's for testing.
- *
- * @param secLevel The security level to specify which KM instance should generate a key pair.
- */
- void generateKeyPair(in boolean is_test_mode, in SecurityLevel secLevel);
-
- /**
- * This method returns implementation information for whichever instances of
- * IRemotelyProvisionedComponent are running on the device. The RemoteProvisioner app needs to
- * know which KM instances it should be generating and managing attestation keys for, and which
- * EC curves are supported in those instances.
- *
- * @return The array of ImplInfo parcelables.
- */
- ImplInfo[] getImplementationInfo();
-
- /**
- * This method deletes all remotely provisioned attestation keys in the database, regardless
- * of what state in their life cycle they are in. This is primarily useful to facilitate
- * testing.
- *
- * @return Number of keys deleted
- */
- long deleteAllKeys();
-}
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/ImplInfo.aidl b/keystore2/aidl/android/security/remoteprovisioning/ImplInfo.aidl
deleted file mode 100644
index 9baeb24b..00000000
--- a/keystore2/aidl/android/security/remoteprovisioning/ImplInfo.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.remoteprovisioning;
-
-import android.hardware.security.keymint.SecurityLevel;
-
-/**
- * This parcelable provides information about the underlying IRemotelyProvisionedComponent
- * implementation.
- * @hide
- */
-parcelable ImplInfo {
- /**
- * The security level of the underlying implementation: TEE or StrongBox.
- */
- SecurityLevel secLevel;
- /**
- * An integer denoting which EC curve is supported in the underlying implementation. The current
- * options are either P256 or 25519, with values defined in
- * hardware/interfaces/security/keymint/aidl/.../RpcHardwareInfo.aidl
- */
- int supportedCurve;
-}
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/aidl/android/security/remoteprovisioning/ResponseCode.aidl b/keystore2/aidl/android/security/remoteprovisioning/ResponseCode.aidl
deleted file mode 100644
index c9877db5..00000000
--- a/keystore2/aidl/android/security/remoteprovisioning/ResponseCode.aidl
+++ /dev/null
@@ -1,34 +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.
- */
-
-package android.security.remoteprovisioning;
-
-@Backing(type="int")
-/** @hide */
-enum ResponseCode {
- /**
- * Returned if there are no keys available in the database to be used in a CSR
- */
- NO_UNSIGNED_KEYS = 1,
- /**
- * The caller has imrproper SELinux permissions to access the Remote Provisioning API.
- */
- PERMISSION_DENIED = 2,
- /**
- * An unexpected error occurred, likely with IO or IPC.
- */
- SYSTEM_ERROR = 3,
-}
diff --git a/keystore2/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
index 45f995c8..4d8a756d 100644
--- a/keystore2/android.system.keystore2-service.xml
+++ b/keystore2/android.system.keystore2-service.xml
@@ -1,7 +1,7 @@
<manifest version="1.0" type="framework">
<hal format="aidl">
<name>android.system.keystore2</name>
- <version>3</version>
+ <version>4</version>
<interface>
<name>IKeystoreService</name>
<instance>default</instance>
diff --git a/keystore2/apc_compat/Android.bp b/keystore2/apc_compat/Android.bp
index 61697a85..9ca4612a 100644
--- a/keystore2/apc_compat/Android.bp
+++ b/keystore2/apc_compat/Android.bp
@@ -27,10 +27,10 @@ cc_library {
"apc_compat.cpp",
],
shared_libs: [
- "libbinder_ndk",
- "android.hardware.confirmationui@1.0",
"android.hardware.confirmationui-V1-ndk",
+ "android.hardware.confirmationui@1.0",
"libbase",
+ "libbinder_ndk",
"libhidlbase",
"libutils",
],
@@ -43,12 +43,12 @@ rust_bindgen {
source_stem: "bindings",
bindgen_flags: [
- "--allowlist-function=tryGetUserConfirmationService",
- "--allowlist-function=promptUserConfirmation",
"--allowlist-function=abortUserConfirmation",
"--allowlist-function=closeUserConfirmationService",
- "--allowlist-var=INVALID_SERVICE_HANDLE",
+ "--allowlist-function=promptUserConfirmation",
+ "--allowlist-function=tryGetUserConfirmationService",
"--allowlist-var=APC_COMPAT_.*",
+ "--allowlist-var=INVALID_SERVICE_HANDLE",
],
}
diff --git a/keystore2/apc_compat/apc_compat.cpp b/keystore2/apc_compat/apc_compat.cpp
index 9f60db2e..ffe7595e 100644
--- a/keystore2/apc_compat/apc_compat.cpp
+++ b/keystore2/apc_compat/apc_compat.cpp
@@ -118,8 +118,7 @@ class ConfuiHidlCompatSession : public HidlConfirmationResultCb,
hidl_ui_options);
if (!rc.isOk()) {
LOG(ERROR) << "Communication error: promptUserConfirmation: " << rc.description();
- }
- if (rc == ResponseCode::OK) {
+ } else if (rc == ResponseCode::OK) {
callback_ = callback;
}
return responseCode2Compat(rc.withDefault(ResponseCode::SystemError));
diff --git a/keystore2/apc_compat/apc_compat.rs b/keystore2/apc_compat/apc_compat.rs
index 480f14dd..e97ac59a 100644
--- a/keystore2/apc_compat/apc_compat.rs
+++ b/keystore2/apc_compat/apc_compat.rs
@@ -53,7 +53,10 @@ use std::{ffi::CString, slice};
/// ```
pub struct ApcHal(ApcCompatServiceHandle);
+// SAFETY: This is a wrapper around `ApcCompatSession`, which can be used from any thread.
unsafe impl Send for ApcHal {}
+// SAFETY: `ApcCompatSession` can be called simultaneously from different threads because AIDL and
+// HIDL are thread-safe.
unsafe impl Sync for ApcHal {}
impl Drop for ApcHal {
@@ -120,6 +123,7 @@ impl ApcHal {
// `closeUserConfirmationService` when dropped.
let handle = unsafe { tryGetUserConfirmationService() };
match handle {
+ // SAFETY: This is just a constant.
h if h == unsafe { INVALID_SERVICE_HANDLE } => None,
h => Some(Self(h)),
}
diff --git a/keystore2/keystore2.rc b/keystore2/keystore2.rc
index 6f88dd39..d7d6951c 100644
--- a/keystore2/keystore2.rc
+++ b/keystore2/keystore2.rc
@@ -11,3 +11,5 @@ service keystore2 /system/bin/keystore2 /data/misc/keystore
user keystore
group keystore readproc log
task_profiles ProcessCapacityHigh
+ # The default memlock limit of 65536 bytes is too low for keystore.
+ rlimit memlock unlimited unlimited
diff --git a/keystore2/legacykeystore/Android.bp b/keystore2/legacykeystore/Android.bp
index 505b1653..de2edc20 100644
--- a/keystore2/legacykeystore/Android.bp
+++ b/keystore2/legacykeystore/Android.bp
@@ -31,6 +31,8 @@ rust_defaults {
"android.security.legacykeystore-rust",
"libanyhow",
"libbinder_rs",
+ "libkeystore2_flags_rust",
+ "libkeystore2_flags_rust",
"liblog_rust",
"librusqlite",
"librustutils",
@@ -43,6 +45,7 @@ rust_library {
defaults: ["liblegacykeystore-rust_defaults"],
rustlibs: [
"libkeystore2",
+ "libkeystore2_flags_rust",
"librusqlite",
],
}
@@ -58,6 +61,8 @@ rust_test {
"libanyhow",
"libbinder_rs",
"libkeystore2",
+ "libkeystore2_flags_rust",
+ "libkeystore2_flags_rust",
"libkeystore2_test_utils",
"liblog_rust",
"librusqlite",
diff --git a/keystore2/legacykeystore/lib.rs b/keystore2/legacykeystore/lib.rs
index 464f0a29..db3eff63 100644
--- a/keystore2/legacykeystore/lib.rs
+++ b/keystore2/legacykeystore/lib.rs
@@ -29,9 +29,7 @@ use keystore2::{
legacy_blob::LegacyBlobLoader, maintenance::DeleteListener, maintenance::Domain,
utils::uid_to_android_user, utils::watchdog as wd,
};
-use rusqlite::{
- params, Connection, OptionalExtension, Transaction, TransactionBehavior, NO_PARAMS,
-};
+use rusqlite::{params, Connection, OptionalExtension, Transaction, TransactionBehavior};
use std::sync::Arc;
use std::{
collections::HashSet,
@@ -57,7 +55,7 @@ impl DB {
F: Fn(&Transaction) -> Result<T>,
{
loop {
- match self
+ let result = self
.conn
.transaction_with_behavior(behavior)
.context("In with_transaction.")
@@ -65,7 +63,8 @@ impl DB {
.and_then(|(result, tx)| {
tx.commit().context("In with_transaction: Failed to commit transaction.")?;
Ok(result)
- }) {
+ });
+ match result {
Ok(result) => break Ok(result),
Err(e) => {
if Self::is_locked_error(&e) {
@@ -95,7 +94,7 @@ impl DB {
alias BLOB,
profile BLOB,
UNIQUE(owner, alias));",
- NO_PARAMS,
+ [],
)
.context("Failed to initialize \"profiles\" table.")?;
Ok(())
@@ -123,6 +122,7 @@ impl DB {
}
fn put(&mut self, caller_uid: u32, alias: &str, entry: &[u8]) -> Result<()> {
+ ensure_keystore_put_is_enabled()?;
self.with_transaction(TransactionBehavior::Immediate, |tx| {
tx.execute(
"INSERT OR REPLACE INTO profiles (owner, alias, profile) values (?, ?, ?)",
@@ -203,6 +203,11 @@ impl Error {
pub fn perm() -> Self {
Error::Error(ERROR_PERMISSION_DENIED)
}
+
+ /// Short hand for `Error::Error(ERROR_SYSTEM_ERROR)`
+ pub fn deprecated() -> Self {
+ Error::Error(ERROR_SYSTEM_ERROR)
+ }
}
/// This function should be used by legacykeystore service calls to translate error conditions
@@ -242,6 +247,17 @@ where
)
}
+fn ensure_keystore_put_is_enabled() -> Result<()> {
+ if keystore2_flags::disable_legacy_keystore_put_v2() {
+ Err(Error::deprecated()).context(concat!(
+ "Storing into Keystore's legacy database is ",
+ "no longer supported, store in an app-specific database instead"
+ ))
+ } else {
+ Ok(())
+ }
+}
+
struct LegacyKeystoreDeleteListener {
legacy_keystore: Arc<LegacyKeystore>,
}
@@ -334,6 +350,7 @@ impl LegacyKeystore {
}
fn put(&self, alias: &str, uid: i32, entry: &[u8]) -> Result<()> {
+ ensure_keystore_put_is_enabled()?;
let uid = Self::get_effective_uid(uid).context("In put.")?;
let mut db = self.open_db().context("In put.")?;
db.put(uid, alias, entry).context("In put: Trying to insert entry into DB.")?;
@@ -502,8 +519,10 @@ impl LegacyKeystore {
) -> 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))
+ if let Some(key) = SUPER_KEY
+ .read()
+ .unwrap()
+ .get_after_first_unlock_key_by_user_id(uid_to_android_user(uid))
{
key.decrypt(ciphertext, iv, tag)
} else {
diff --git a/diced/open_dice_cbor/Android.bp b/keystore2/message_macro/Android.bp
index a2534dcf..f1fbad76 100644
--- a/diced/open_dice_cbor/Android.bp
+++ b/keystore2/message_macro/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2021, The Android Open Source Project
+// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -13,17 +13,25 @@
// 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_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_open_dice",
- "libdiced_sample_inputs",
+rust_defaults {
+ name: "libmessage_macro_defaults",
+ crate_name: "message_macro",
+ srcs: ["src/lib.rs"],
+}
+
+rust_library {
+ name: "libmessage_macro",
+ defaults: ["libmessage_macro_defaults"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
],
}
diff --git a/keystore2/src/ks_err.rs b/keystore2/message_macro/src/lib.rs
index c9c38c0d..d8cfab0e 100644
--- a/keystore2/src/ks_err.rs
+++ b/keystore2/message_macro/src/lib.rs
@@ -12,20 +12,20 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! A ks_err macro that expands error messages to include the file and line number
+//! A macro that generates a message containing the current source file name
+//! and line number.
+/// Generates a message containing the current source file name and line number.
///
/// # Examples
///
/// ```
-/// use crate::ks_err;
-///
-/// ks_err!("Key is expired.");
+/// source_location_msg!("Key is expired.");
/// Result:
/// "src/lib.rs:7 Key is expired."
/// ```
#[macro_export]
-macro_rules! ks_err {
+macro_rules! source_location_msg {
{ $($arg:tt)+ } => {
format!("{}:{}: {}", file!(), line!(), format_args!($($arg)+))
};
diff --git a/diced/Android.bp b/keystore2/rkpd_client/Android.bp
index 3e15ed71..d6a911fc 100644
--- a/diced/Android.bp
+++ b/keystore2/rkpd_client/Android.bp
@@ -1,4 +1,4 @@
-// Copyright 2021, The Android Open Source Project
+// Copyright 2023, The Android Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -21,48 +21,35 @@ package {
default_applicable_licenses: ["system_security_license"],
}
-rust_library {
- name: "libdiced_utils",
- crate_name: "diced_utils",
- srcs: ["src/utils.rs"],
- rustlibs: [
- "libanyhow",
- ],
-}
-
-rust_test {
- name: "diced_utils_test",
- crate_name: "diced_utils_test",
- srcs: ["src/utils.rs"],
- test_suites: ["general-tests"],
- auto_gen_config: true,
+rust_defaults {
+ name: "librkpd_client_defaults",
+ crate_name: "rkpd_client",
+ srcs: ["src/lib.rs"],
rustlibs: [
+ "android.security.rkp_aidl-rust",
"libanyhow",
+ "libbinder_rs",
+ "liblog_rust",
+ "libmessage_macro",
+ "libthiserror",
+ "libtokio",
],
}
rust_library {
- name: "libdiced_sample_inputs",
- crate_name: "diced_sample_inputs",
- srcs: ["src/sample_inputs.rs"],
- rustlibs: [
- "libanyhow",
- "libciborium",
- "libcoset",
- "libdiced_open_dice",
+ name: "librkpd_client",
+ defaults: ["librkpd_client_defaults"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
],
}
rust_test {
- name: "diced_sample_inputs_test",
- crate_name: "diced_sample_inputs_test",
- srcs: ["src/sample_inputs.rs"],
+ name: "librkpd_client.test",
+ defaults: ["librkpd_client_defaults"],
test_suites: ["general-tests"],
- auto_gen_config: true,
rustlibs: [
- "libanyhow",
- "libciborium",
- "libcoset",
- "libdiced_open_dice",
+ "librand",
],
}
diff --git a/keystore2/src/rkpd_client.rs b/keystore2/rkpd_client/src/lib.rs
index 0ea2d392..d8a5276c 100644
--- a/keystore2/src/rkpd_client.rs
+++ b/keystore2/rkpd_client/src/lib.rs
@@ -14,11 +14,6 @@
//! Helper wrapper around RKPD interface.
-use crate::error::{map_binder_status_code, Error, ResponseCode};
-use crate::globals::get_remotely_provisioned_component_name;
-use crate::ks_err;
-use crate::utils::watchdog as wd;
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::SecurityLevel::SecurityLevel;
use android_security_rkp_aidl::aidl::android::security::rkp::{
IGetKeyCallback::BnGetKeyCallback, IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode,
IGetKeyCallback::IGetKeyCallback, IGetRegistrationCallback::BnGetRegistrationCallback,
@@ -28,8 +23,9 @@ use android_security_rkp_aidl::aidl::android::security::rkp::{
IStoreUpgradedKeyCallback::IStoreUpgradedKeyCallback,
RemotelyProvisionedKey::RemotelyProvisionedKey,
};
-use android_security_rkp_aidl::binder::{BinderFeatures, Interface, Strong};
use anyhow::{Context, Result};
+use binder::{BinderFeatures, Interface, StatusCode, Strong};
+use message_macro::source_location_msg;
use std::sync::Mutex;
use std::time::Duration;
use tokio::sync::oneshot;
@@ -44,6 +40,44 @@ fn tokio_rt() -> tokio::runtime::Runtime {
tokio::runtime::Builder::new_current_thread().enable_all().build().unwrap()
}
+/// Errors occurred during the interaction with RKPD.
+#[derive(Debug, Clone, Copy, thiserror::Error, PartialEq, Eq)]
+pub enum Error {
+ /// An RKPD request gets cancelled.
+ #[error("An RKPD request gets cancelled")]
+ RequestCancelled,
+
+ /// Failed to get registration.
+ #[error("Failed to get registration")]
+ GetRegistrationFailed,
+
+ /// Failed to get key.
+ #[error("Failed to get key: {0:?}")]
+ GetKeyFailed(GetKeyErrorCode),
+
+ /// Failed to store upgraded key.
+ #[error("Failed to store upgraded key")]
+ StoreUpgradedKeyFailed,
+
+ /// Retryable timeout when waiting for a callback.
+ #[error("Retryable timeout when waiting for a callback")]
+ RetryableTimeout,
+
+ /// Timeout when waiting for a callback.
+ #[error("Timeout when waiting for a callback")]
+ Timeout,
+
+ /// Wraps a Binder status code.
+ #[error("Binder transaction error {0:?}")]
+ BinderTransaction(StatusCode),
+}
+
+impl From<StatusCode> for Error {
+ fn from(s: StatusCode) -> Self {
+ Self::BinderTransaction(s)
+ }
+}
+
/// Thread-safe channel for sending a value once and only once. If a value has
/// already been send, subsequent calls to send will noop.
struct SafeSender<T> {
@@ -84,52 +118,43 @@ impl Interface for GetRegistrationCallback {}
impl IGetRegistrationCallback for GetRegistrationCallback {
fn onSuccess(&self, registration: &Strong<dyn IRegistration>) -> binder::Result<()> {
- let _wp = wd::watch_millis("IGetRegistrationCallback::onSuccess", 500);
self.registration_tx.send(Ok(registration.clone()));
Ok(())
}
fn onCancel(&self) -> binder::Result<()> {
- let _wp = wd::watch_millis("IGetRegistrationCallback::onCancel", 500);
log::warn!("IGetRegistrationCallback cancelled");
self.registration_tx.send(
- Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
- .context(ks_err!("GetRegistrationCallback cancelled.")),
+ Err(Error::RequestCancelled)
+ .context(source_location_msg!("GetRegistrationCallback cancelled.")),
);
Ok(())
}
fn onError(&self, description: &str) -> binder::Result<()> {
- let _wp = wd::watch_millis("IGetRegistrationCallback::onError", 500);
log::error!("IGetRegistrationCallback failed: '{description}'");
self.registration_tx.send(
- Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
- .context(ks_err!("GetRegistrationCallback failed: {:?}", description)),
+ Err(Error::GetRegistrationFailed)
+ .context(source_location_msg!("GetRegistrationCallback failed: {:?}", description)),
);
Ok(())
}
}
/// Make a new connection to a IRegistration service.
-async fn get_rkpd_registration(
- security_level: &SecurityLevel,
-) -> Result<binder::Strong<dyn IRegistration>> {
+async fn get_rkpd_registration(rpc_name: &str) -> Result<binder::Strong<dyn IRegistration>> {
let remote_provisioning: Strong<dyn IRemoteProvisioning> =
- map_binder_status_code(binder::get_interface("remote_provisioning"))
- .context(ks_err!("Trying to connect to IRemoteProvisioning service."))?;
-
- let rpc_name = get_remotely_provisioned_component_name(security_level)
- .context(ks_err!("Trying to get IRPC name."))?;
+ binder::get_interface("remote_provisioning")
+ .map_err(Error::from)
+ .context(source_location_msg!("Trying to connect to IRemoteProvisioning service."))?;
let (tx, rx) = oneshot::channel();
let cb = GetRegistrationCallback::new_native_binder(tx);
remote_provisioning
- .getRegistration(&rpc_name, &cb)
- .context(ks_err!("Trying to get registration."))?;
+ .getRegistration(rpc_name, &cb)
+ .context(source_location_msg!("Trying to get registration."))?;
match timeout(RKPD_TIMEOUT, rx).await {
- Err(e) => {
- Err(Error::Rc(ResponseCode::SYSTEM_ERROR)).context(ks_err!("Waiting for RKPD: {:?}", e))
- }
+ Err(e) => Err(Error::Timeout).context(source_location_msg!("Waiting for RKPD: {:?}", e)),
Ok(v) => v.unwrap(),
}
}
@@ -151,7 +176,6 @@ impl Interface for GetKeyCallback {}
impl IGetKeyCallback for GetKeyCallback {
fn onSuccess(&self, key: &RemotelyProvisionedKey) -> binder::Result<()> {
- let _wp = wd::watch_millis("IGetKeyCallback::onSuccess", 500);
self.key_tx.send(Ok(RemotelyProvisionedKey {
keyBlob: key.keyBlob.clone(),
encodedCertChain: key.encodedCertChain.clone(),
@@ -159,32 +183,15 @@ impl IGetKeyCallback for GetKeyCallback {
Ok(())
}
fn onCancel(&self) -> binder::Result<()> {
- let _wp = wd::watch_millis("IGetKeyCallback::onCancel", 500);
log::warn!("IGetKeyCallback cancelled");
self.key_tx.send(
- Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
- .context(ks_err!("GetKeyCallback cancelled.")),
+ Err(Error::RequestCancelled).context(source_location_msg!("GetKeyCallback cancelled.")),
);
Ok(())
}
fn onError(&self, error: GetKeyErrorCode, description: &str) -> binder::Result<()> {
- let _wp = wd::watch_millis("IGetKeyCallback::onError", 500);
log::error!("IGetKeyCallback failed: {description}");
- let rc = match error {
- GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
- GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
- GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
- ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
- }
- GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
- ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
- }
- _ => {
- log::error!("Unexpected error from rkpd: {error:?}");
- ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
- }
- };
- self.key_tx.send(Err(Error::Rc(rc)).context(ks_err!(
+ self.key_tx.send(Err(Error::GetKeyFailed(error)).context(source_location_msg!(
"GetKeyCallback failed: {:?} {:?}",
error,
description
@@ -202,7 +209,7 @@ async fn get_rkpd_attestation_key_from_registration_async(
registration
.getKey(caller_uid.try_into().unwrap(), &cb)
- .context(ks_err!("Trying to get key."))?;
+ .context(source_location_msg!("Trying to get key."))?;
match timeout(RKPD_TIMEOUT, rx).await {
Err(e) => {
@@ -210,20 +217,20 @@ async fn get_rkpd_attestation_key_from_registration_async(
if let Err(e) = registration.cancelGetKey(&cb) {
log::error!("IRegistration::cancelGetKey failed: {:?}", e);
}
- Err(Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
- .context(ks_err!("Waiting for RKPD key timed out: {:?}", e))
+ Err(Error::RetryableTimeout)
+ .context(source_location_msg!("Waiting for RKPD key timed out: {:?}", e))
}
Ok(v) => v.unwrap(),
}
}
async fn get_rkpd_attestation_key_async(
- security_level: &SecurityLevel,
+ rpc_name: &str,
caller_uid: u32,
) -> Result<RemotelyProvisionedKey> {
- let registration = get_rkpd_registration(security_level)
+ let registration = get_rkpd_registration(rpc_name)
.await
- .context(ks_err!("Trying to get to IRegistration service."))?;
+ .context(source_location_msg!("Trying to get to IRegistration service."))?;
get_rkpd_attestation_key_from_registration_async(&registration, caller_uid).await
}
@@ -244,17 +251,15 @@ impl Interface for StoreUpgradedKeyCallback {}
impl IStoreUpgradedKeyCallback for StoreUpgradedKeyCallback {
fn onSuccess(&self) -> binder::Result<()> {
- let _wp = wd::watch_millis("IGetRegistrationCallback::onSuccess", 500);
self.completer.send(Ok(()));
Ok(())
}
fn onError(&self, error: &str) -> binder::Result<()> {
- let _wp = wd::watch_millis("IGetRegistrationCallback::onError", 500);
- log::error!("IGetRegistrationCallback failed: {error}");
+ log::error!("IStoreUpgradedKeyCallback failed: {error}");
self.completer.send(
- Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
- .context(ks_err!("Failed to store upgraded key: {:?}", error)),
+ Err(Error::StoreUpgradedKeyFailed)
+ .context(source_location_msg!("Failed to store upgraded key: {:?}", error)),
);
Ok(())
}
@@ -270,61 +275,50 @@ async fn store_rkpd_attestation_key_with_registration_async(
registration
.storeUpgradedKeyAsync(key_blob, upgraded_blob, &cb)
- .context(ks_err!("Failed to store upgraded blob with RKPD."))?;
+ .context(source_location_msg!("Failed to store upgraded blob with RKPD."))?;
match timeout(RKPD_TIMEOUT, rx).await {
- Err(e) => Err(Error::Rc(ResponseCode::SYSTEM_ERROR))
- .context(ks_err!("Waiting for RKPD to complete storing key: {:?}", e)),
+ Err(e) => Err(Error::Timeout)
+ .context(source_location_msg!("Waiting for RKPD to complete storing key: {:?}", e)),
Ok(v) => v.unwrap(),
}
}
async fn store_rkpd_attestation_key_async(
- security_level: &SecurityLevel,
+ rpc_name: &str,
key_blob: &[u8],
upgraded_blob: &[u8],
) -> Result<()> {
- let registration = get_rkpd_registration(security_level)
+ let registration = get_rkpd_registration(rpc_name)
.await
- .context(ks_err!("Trying to get to IRegistration service."))?;
+ .context(source_location_msg!("Trying to get to IRegistration service."))?;
store_rkpd_attestation_key_with_registration_async(&registration, key_blob, upgraded_blob).await
}
/// Get attestation key from RKPD.
-pub fn get_rkpd_attestation_key(
- security_level: &SecurityLevel,
- caller_uid: u32,
-) -> Result<RemotelyProvisionedKey> {
- let _wp = wd::watch_millis("Calling get_rkpd_attestation_key()", 500);
- tokio_rt().block_on(get_rkpd_attestation_key_async(security_level, caller_uid))
+pub fn get_rkpd_attestation_key(rpc_name: &str, caller_uid: u32) -> Result<RemotelyProvisionedKey> {
+ tokio_rt().block_on(get_rkpd_attestation_key_async(rpc_name, caller_uid))
}
/// Store attestation key in RKPD.
pub fn store_rkpd_attestation_key(
- security_level: &SecurityLevel,
+ rpc_name: &str,
key_blob: &[u8],
upgraded_blob: &[u8],
) -> Result<()> {
- let _wp = wd::watch_millis("Calling store_rkpd_attestation_key()", 500);
- tokio_rt().block_on(store_rkpd_attestation_key_async(security_level, key_blob, upgraded_blob))
+ tokio_rt().block_on(store_rkpd_attestation_key_async(rpc_name, key_blob, upgraded_blob))
}
#[cfg(test)]
mod tests {
use super::*;
- use crate::error::map_km_error;
- use crate::globals::get_keymint_device;
- use crate::utils::upgrade_keyblob_if_required_with;
- use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, AttestationKey::AttestationKey, KeyParameter::KeyParameter,
- KeyParameterValue::KeyParameterValue, Tag::Tag,
- };
use android_security_rkp_aidl::aidl::android::security::rkp::IRegistration::BnRegistration;
- use keystore2_crypto::parse_subject_from_certificate;
- use std::collections::HashMap;
use std::sync::atomic::{AtomicU32, Ordering};
use std::sync::{Arc, Mutex};
+ const DEFAULT_RPC_SERVICE_NAME: &str =
+ "android.hardware.security.keymint.IRemotelyProvisionedComponent/default";
+
struct MockRegistrationValues {
key: RemotelyProvisionedKey,
latency: Option<Duration>,
@@ -442,10 +436,7 @@ mod tests {
assert!(cb.onCancel().is_ok());
let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
- );
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RequestCancelled);
}
#[test]
@@ -455,10 +446,7 @@ mod tests {
assert!(cb.onError("error").is_ok());
let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
- );
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::GetRegistrationFailed);
}
#[test]
@@ -480,29 +468,11 @@ mod tests {
assert!(cb.onCancel().is_ok());
let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
- );
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RequestCancelled);
}
#[test]
fn test_get_key_cb_error() {
- let error_mapping = HashMap::from([
- (GetKeyErrorCode::ERROR_UNKNOWN, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
- (GetKeyErrorCode::ERROR_PERMANENT, ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR),
- (
- GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY,
- ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
- ),
- (
- GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH,
- ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
- ),
- ]);
-
- // Loop over the generated list of enum values to better ensure this test stays in
- // sync with the AIDL.
for get_key_error in GetKeyErrorCode::enum_values() {
let (tx, rx) = oneshot::channel();
let cb = GetKeyCallback::new_native_binder(tx);
@@ -511,7 +481,7 @@ mod tests {
let result = tokio_rt().block_on(rx).unwrap();
assert_eq!(
result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(error_mapping[&get_key_error]),
+ Error::GetKeyFailed(get_key_error),
);
}
}
@@ -532,10 +502,7 @@ mod tests {
assert!(cb.onError("oh no! it failed").is_ok());
let result = tokio_rt().block_on(rx).unwrap();
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::SYSTEM_ERROR)
- );
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::StoreUpgradedKeyFailed);
}
#[test]
@@ -559,10 +526,7 @@ mod tests {
let result =
tokio_rt().block_on(get_rkpd_attestation_key_from_registration_async(&registration, 0));
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
- );
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::RetryableTimeout);
}
#[test]
@@ -587,17 +551,14 @@ mod tests {
&[],
&[],
));
- assert_eq!(
- result.unwrap_err().downcast::<Error>().unwrap(),
- Error::Rc(ResponseCode::SYSTEM_ERROR)
- );
+ assert_eq!(result.unwrap_err().downcast::<Error>().unwrap(), Error::Timeout);
}
#[test]
fn test_get_rkpd_attestation_key() {
binder::ProcessState::start_thread_pool();
let key_id = get_next_key_id();
- let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+ let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
assert!(!key.keyBlob.is_empty());
assert!(!key.encodedCertChain.is_empty());
}
@@ -605,12 +566,11 @@ mod tests {
#[test]
fn test_get_rkpd_attestation_key_same_caller() {
binder::ProcessState::start_thread_pool();
- let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
let key_id = get_next_key_id();
// Multiple calls should return the same key.
- let first_key = get_rkpd_attestation_key(&sec_level, key_id).unwrap();
- let second_key = get_rkpd_attestation_key(&sec_level, key_id).unwrap();
+ let first_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
+ let second_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
assert_eq!(first_key.keyBlob, second_key.keyBlob);
assert_eq!(first_key.encodedCertChain, second_key.encodedCertChain);
@@ -619,13 +579,12 @@ mod tests {
#[test]
fn test_get_rkpd_attestation_key_different_caller() {
binder::ProcessState::start_thread_pool();
- let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
let first_key_id = get_next_key_id();
let second_key_id = get_next_key_id();
// Different callers should be getting different keys.
- let first_key = get_rkpd_attestation_key(&sec_level, first_key_id).unwrap();
- let second_key = get_rkpd_attestation_key(&sec_level, second_key_id).unwrap();
+ let first_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, first_key_id).unwrap();
+ let second_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, second_key_id).unwrap();
assert_ne!(first_key.keyBlob, second_key.keyBlob);
assert_ne!(first_key.encodedCertChain, second_key.encodedCertChain);
@@ -639,81 +598,24 @@ mod tests {
// test case.
fn test_store_rkpd_attestation_key() {
binder::ProcessState::start_thread_pool();
- let sec_level = SecurityLevel::TRUSTED_ENVIRONMENT;
let key_id = get_next_key_id();
- let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+ let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
let new_blob: [u8; 8] = rand::random();
- assert!(store_rkpd_attestation_key(&sec_level, &key.keyBlob, &new_blob).is_ok());
+ assert!(
+ store_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, &key.keyBlob, &new_blob).is_ok()
+ );
- let new_key =
- get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id).unwrap();
+ let new_key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
// Restore original key so that we don't leave RKPD with invalid blobs.
- assert!(store_rkpd_attestation_key(&sec_level, &new_blob, &key.keyBlob).is_ok());
+ assert!(
+ store_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, &new_blob, &key.keyBlob).is_ok()
+ );
assert_eq!(new_key.keyBlob, new_blob);
}
#[test]
- // This is a helper for a manual test. We want to check that after a system upgrade RKPD
- // attestation keys can also be upgraded and stored again with RKPD. The steps are:
- // 1. Run this test and check in stdout that no key upgrade happened.
- // 2. Perform a system upgrade.
- // 3. Run this test and check in stdout that key upgrade did happen.
- //
- // Note that this test must be run with that same UID every time. Running as root, i.e. UID 0,
- // should do the trick. Also, use "--nocapture" flag to get stdout.
- fn test_rkpd_attestation_key_upgrade() {
- binder::ProcessState::start_thread_pool();
- let security_level = SecurityLevel::TRUSTED_ENVIRONMENT;
- let (keymint, _, _) = get_keymint_device(&security_level).unwrap();
- let key_id = get_next_key_id();
- let mut key_upgraded = false;
-
- let key = get_rkpd_attestation_key(&security_level, key_id).unwrap();
- assert!(!key.keyBlob.is_empty());
- assert!(!key.encodedCertChain.is_empty());
-
- upgrade_keyblob_if_required_with(
- &*keymint,
- &key.keyBlob,
- /*upgrade_params=*/ &[],
- /*km_op=*/
- |blob| {
- let params = vec![
- KeyParameter {
- tag: Tag::ALGORITHM,
- value: KeyParameterValue::Algorithm(Algorithm::AES),
- },
- KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) },
- ];
- let attestation_key = AttestationKey {
- keyBlob: blob.to_vec(),
- attestKeyParams: vec![],
- issuerSubjectName: parse_subject_from_certificate(&key.encodedCertChain)
- .unwrap(),
- };
-
- map_km_error(keymint.generateKey(&params, Some(&attestation_key)))
- },
- /*new_blob_handler=*/
- |new_blob| {
- // This handler is only executed if a key upgrade was performed.
- key_upgraded = true;
- store_rkpd_attestation_key(&security_level, &key.keyBlob, new_blob).unwrap();
- Ok(())
- },
- )
- .unwrap();
-
- if key_upgraded {
- println!("RKPD key was upgraded and stored with RKPD.");
- } else {
- println!("RKPD key was NOT upgraded.");
- }
- }
-
- #[test]
fn test_stress_get_rkpd_attestation_key() {
binder::ProcessState::start_thread_pool();
let key_id = get_next_key_id();
@@ -724,8 +626,7 @@ mod tests {
for _ in 0..NTHREADS {
threads.push(std::thread::spawn(move || {
for _ in 0..NCALLS {
- let key = get_rkpd_attestation_key(&SecurityLevel::TRUSTED_ENVIRONMENT, key_id)
- .unwrap();
+ let key = get_rkpd_attestation_key(DEFAULT_RPC_SERVICE_NAME, key_id).unwrap();
assert!(!key.keyBlob.is_empty());
assert!(!key.encodedCertChain.is_empty());
}
diff --git a/keystore2/selinux/src/concurrency_test.rs b/keystore2/selinux/src/concurrency_test.rs
index a5d2df2c..fa97f3aa 100644
--- a/keystore2/selinux/src/concurrency_test.rs
+++ b/keystore2/selinux/src/concurrency_test.rs
@@ -69,7 +69,7 @@ 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),
+ .with_max_level(log::LevelFilter::Debug),
);
let cpus = num_cpus::get();
diff --git a/keystore2/selinux/src/lib.rs b/keystore2/selinux/src/lib.rs
index e5c3091b..695e0291 100644
--- a/keystore2/selinux/src/lib.rs
+++ b/keystore2/selinux/src/lib.rs
@@ -20,6 +20,9 @@
//! * selabel_lookup for the keystore2_key backend.
//! And it provides an owning wrapper around context strings `Context`.
+// TODO(b/290018030): Remove this and add proper safety comments.
+#![allow(clippy::undocumented_unsafe_blocks)]
+
use anyhow::Context as AnyhowContext;
use anyhow::{anyhow, Result};
use lazy_static::lazy_static;
@@ -160,8 +163,9 @@ pub struct KeystoreKeyBackend {
handle: *mut selinux::selabel_handle,
}
-// KeystoreKeyBackend is Sync because selabel_lookup is thread safe.
+// SAFETY: KeystoreKeyBackend is Sync because selabel_lookup is thread safe.
unsafe impl Sync for KeystoreKeyBackend {}
+// SAFETY: KeystoreKeyBackend is Send because selabel_lookup is thread safe.
unsafe impl Send for KeystoreKeyBackend {}
impl KeystoreKeyBackend {
@@ -716,7 +720,7 @@ mod tests {
android_logger::init_once(
android_logger::Config::default()
.with_tag("keystore_selinux_tests")
- .with_min_level(log::Level::Debug),
+ .with_max_level(log::LevelFilter::Debug),
);
let scontext = Context::new("u:r:shell:s0")?;
let backend = KeystoreKeyBackend::new()?;
diff --git a/keystore2/src/apc.rs b/keystore2/src/apc.rs
index 5d2083da..fbf94649 100644
--- a/keystore2/src/apc.rs
+++ b/keystore2/src/apc.rs
@@ -244,7 +244,7 @@ impl ApcManager {
// If cancelled by the user or if aborted by the client.
(ResponseCode::CANCELLED, _, _) | (ResponseCode::ABORTED, true, _) => {
// Penalize.
- let mut rate_info = state.rate_limiting.entry(uid).or_default();
+ let rate_info = state.rate_limiting.entry(uid).or_default();
rate_info.counter += 1;
rate_info.timestamp = start;
}
diff --git a/keystore2/src/attestation_key_utils.rs b/keystore2/src/attestation_key_utils.rs
index 8c4cdea7..184b3cbd 100644
--- a/keystore2/src/attestation_key_utils.rs
+++ b/keystore2/src/attestation_key_utils.rs
@@ -30,17 +30,11 @@ use android_system_keystore2::aidl::android::system::keystore2::{
};
use anyhow::{Context, Result};
use keystore2_crypto::parse_subject_from_certificate;
-use rustutils::system_properties;
/// KeyMint takes two different kinds of attestation keys. Remote provisioned keys
/// and those that have been generated by the user. Unfortunately, they need to be
/// handled quite differently, thus the different representations.
pub enum AttestationKeyInfo {
- RemoteProvisioned {
- key_id_guard: KeyIdGuard,
- attestation_key: AttestationKey,
- attestation_certs: Certificate,
- },
RkpdProvisioned {
attestation_key: AttestationKey,
attestation_certs: Certificate,
@@ -53,12 +47,6 @@ pub enum AttestationKeyInfo {
},
}
-fn use_rkpd() -> bool {
- let property = "remote_provisioning.enable_rkpd";
- let default_value = true;
- system_properties::read_bool(property, default_value).unwrap_or(default_value)
-}
-
/// This function loads and, optionally, assigns the caller's remote provisioned
/// attestation key if a challenge is present. Alternatively, if `attest_key_descriptor` is given,
/// it loads the user generated attestation key from the database.
@@ -75,34 +63,14 @@ pub fn get_attest_key_info(
params.iter().any(|kp| kp.tag == Tag::DEVICE_UNIQUE_ATTESTATION);
match attest_key_descriptor {
// Do not select an RKP key if DEVICE_UNIQUE_ATTESTATION is present.
- None if challenge_present && !is_device_unique_attestation => {
- if use_rkpd() {
- rem_prov_state
- .get_rkpd_attestation_key_and_certs(key, caller_uid, params)
- .context(ks_err!("Trying to get attestation key from RKPD."))
- .map(|result| {
- result.map(|(attestation_key, attestation_certs)| {
- AttestationKeyInfo::RkpdProvisioned {
- attestation_key,
- attestation_certs,
- }
- })
- })
- } else {
- rem_prov_state
- .get_remotely_provisioned_attestation_key_and_certs(key, caller_uid, params, db)
- .context(ks_err!("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,
- }
- })
- })
- }
- }
+ None if challenge_present && !is_device_unique_attestation => rem_prov_state
+ .get_rkpd_attestation_key_and_certs(key, caller_uid, params)
+ .context(ks_err!("Trying to get attestation key from RKPD."))
+ .map(|result| {
+ result.map(|(attestation_key, attestation_certs)| {
+ AttestationKeyInfo::RkpdProvisioned { attestation_key, attestation_certs }
+ })
+ }),
None => Ok(None),
Some(attest_key) => get_user_generated_attestation_key(attest_key, caller_uid, db)
.context(ks_err!("Trying to load attest key"))
diff --git a/keystore2/src/audit_log.rs b/keystore2/src/audit_log.rs
index 07509d36..8d9735e2 100644
--- a/keystore2/src/audit_log.rs
+++ b/keystore2/src/audit_log.rs
@@ -20,7 +20,7 @@ use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor,
};
use libc::uid_t;
-use log_event_list::{LogContext, LogIdSecurity};
+use structured_log::{structured_log, LOG_ID_SECURITY};
const TAG_KEY_GENERATED: u32 = 210024;
const TAG_KEY_IMPORTED: u32 = 210025;
@@ -58,29 +58,19 @@ pub fn log_key_deleted(key: &KeyDescriptor, calling_app: uid_t, success: bool) {
/// Logs key integrity violation to NIAP audit log.
pub fn log_key_integrity_violation(key: &KeyDescriptor) {
- with_log_context(TAG_KEY_INTEGRITY_VIOLATION, |ctx| {
- let owner = key_owner(key.domain, key.nspace, key.nspace as i32);
- ctx.append_str(key.alias.as_ref().map_or("none", String::as_str)).append_i32(owner)
- })
+ let owner = key_owner(key.domain, key.nspace, key.nspace as i32);
+ let alias = String::from(key.alias.as_ref().map_or("none", String::as_str));
+ LOGS_HANDLER.queue_lo(move |_| {
+ let _result =
+ structured_log!(log_id: LOG_ID_SECURITY, TAG_KEY_INTEGRITY_VIOLATION, alias, owner);
+ });
}
fn log_key_event(tag: u32, key: &KeyDescriptor, calling_app: uid_t, success: bool) {
- with_log_context(tag, |ctx| {
- let owner = key_owner(key.domain, key.nspace, calling_app as i32);
- ctx.append_i32(i32::from(success))
- .append_str(key.alias.as_ref().map_or("none", String::as_str))
- .append_i32(owner)
- })
-}
-
-fn with_log_context<F>(tag: u32, f: F)
-where
- F: Fn(LogContext) -> LogContext,
-{
- if let Some(ctx) = LogContext::new(LogIdSecurity, tag) {
- let event = f(ctx);
- LOGS_HANDLER.queue_lo(move |_| {
- event.write();
- });
- }
+ let owner = key_owner(key.domain, key.nspace, calling_app as i32);
+ let alias = String::from(key.alias.as_ref().map_or("none", String::as_str));
+ LOGS_HANDLER.queue_lo(move |_| {
+ let _result =
+ structured_log!(log_id: LOG_ID_SECURITY, tag, i32::from(success), alias, owner);
+ });
}
diff --git a/keystore2/src/authorization.rs b/keystore2/src/authorization.rs
index 19539201..243abf13 100644
--- a/keystore2/src/authorization.rs
+++ b/keystore2/src/authorization.rs
@@ -14,28 +14,29 @@
//! This module implements IKeystoreAuthorization AIDL interface.
-use crate::ks_err;
-use crate::error::Error as KeystoreError;
use crate::error::anyhow_error_to_cstring;
-use crate::globals::{ENFORCEMENTS, SUPER_KEY, DB, LEGACY_IMPORTER};
+use crate::error::Error as KeystoreError;
+use crate::globals::{DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY};
+use crate::ks_err;
use crate::permission::KeystorePerm;
-use crate::super_key::UserState;
use crate::utils::{check_keystore_permission, watchdog as wd};
+use aconfig_android_hardware_biometrics_rust;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
};
-use android_security_authorization::binder::{BinderFeatures, ExceptionCode, Interface, Result as BinderResult,
- Strong, Status as BinderStatus};
use android_security_authorization::aidl::android::security::authorization::{
- IKeystoreAuthorization::BnKeystoreAuthorization, IKeystoreAuthorization::IKeystoreAuthorization,
- LockScreenEvent::LockScreenEvent, AuthorizationTokens::AuthorizationTokens,
- ResponseCode::ResponseCode,
+ AuthorizationTokens::AuthorizationTokens, IKeystoreAuthorization::BnKeystoreAuthorization,
+ IKeystoreAuthorization::IKeystoreAuthorization, ResponseCode::ResponseCode,
};
-use android_system_keystore2::aidl::android::system::keystore2::{
- ResponseCode::ResponseCode as KsResponseCode};
+use android_security_authorization::binder::{
+ BinderFeatures, ExceptionCode, Interface, Result as BinderResult, Status as BinderStatus,
+ Strong,
+};
+use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode as KsResponseCode;
use anyhow::{Context, Result};
use keystore2_crypto::Password;
use keystore2_selinux as selinux;
+use std::ffi::CString;
/// This is the Authorization error type, it wraps binder exceptions and the
/// Authorization ResponseCode
@@ -127,92 +128,94 @@ impl AuthorizationManager {
fn add_auth_token(&self, auth_token: &HardwareAuthToken) -> Result<()> {
// Check keystore permission.
- check_keystore_permission(KeystorePerm::AddAuth).context(ks_err!())?;
+ check_keystore_permission(KeystorePerm::AddAuth)
+ .context(ks_err!("caller missing AddAuth permissions"))?;
+
+ log::info!(
+ "add_auth_token(challenge={}, userId={}, authId={}, authType={:#x}, timestamp={}ms)",
+ auth_token.challenge,
+ auth_token.userId,
+ auth_token.authenticatorId,
+ auth_token.authenticatorType.0,
+ auth_token.timestamp.milliSeconds,
+ );
ENFORCEMENTS.add_auth_token(auth_token.clone());
Ok(())
}
- fn on_lock_screen_event(
- &self,
- lock_screen_event: LockScreenEvent,
- user_id: i32,
- password: Option<Password>,
- unlocking_sids: Option<&[i64]>,
- ) -> Result<()> {
+ fn on_device_unlocked(&self, user_id: i32, password: Option<Password>) -> Result<()> {
log::info!(
- "on_lock_screen_event({:?}, user_id={:?}, password.is_some()={}, unlocking_sids={:?})",
- lock_screen_event,
+ "on_device_unlocked(user_id={}, password.is_some()={})",
user_id,
password.is_some(),
- unlocking_sids
);
- match (lock_screen_event, password) {
- (LockScreenEvent::UNLOCK, Some(password)) => {
- // This corresponds to the unlock() method in legacy keystore API.
- // check permission
- check_keystore_permission(KeystorePerm::Unlock)
- .context(ks_err!("Unlock with password."))?;
- ENFORCEMENTS.set_device_locked(user_id, false);
+ check_keystore_permission(KeystorePerm::Unlock)
+ .context(ks_err!("caller missing Unlock permissions"))?;
+ ENFORCEMENTS.set_device_locked(user_id, false);
- let mut skm = SUPER_KEY.write().unwrap();
+ let mut skm = SUPER_KEY.write().unwrap();
+ if let Some(password) = password {
+ DB.with(|db| {
+ skm.unlock_user(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32, &password)
+ })
+ .context(ks_err!("Unlock with password."))
+ } else {
+ DB.with(|db| skm.try_unlock_user_with_biometric(&mut db.borrow_mut(), user_id as u32))
+ .context(ks_err!("try_unlock_user_with_biometric failed user_id={user_id}"))
+ }
+ }
- DB.with(|db| {
- skm.unlock_screen_lock_bound_key(
- &mut db.borrow_mut(),
- user_id as u32,
- &password,
- )
- })
- .context(ks_err!("unlock_screen_lock_bound_key failed"))?;
+ fn on_device_locked(
+ &self,
+ user_id: i32,
+ unlocking_sids: &[i64],
+ mut weak_unlock_enabled: bool,
+ ) -> Result<()> {
+ log::info!(
+ "on_device_locked(user_id={}, unlocking_sids={:?}, weak_unlock_enabled={})",
+ user_id,
+ unlocking_sids,
+ weak_unlock_enabled
+ );
+ if !android_security_flags::fix_unlocked_device_required_keys_v2() {
+ weak_unlock_enabled = false;
+ }
+ check_keystore_permission(KeystorePerm::Lock)
+ .context(ks_err!("caller missing Lock permission"))?;
+ ENFORCEMENTS.set_device_locked(user_id, true);
+ let mut skm = SUPER_KEY.write().unwrap();
+ DB.with(|db| {
+ skm.lock_unlocked_device_required_keys(
+ &mut db.borrow_mut(),
+ user_id as u32,
+ unlocking_sids,
+ weak_unlock_enabled,
+ );
+ });
+ Ok(())
+ }
- // Unlock super key.
- if let UserState::Uninitialized = DB
- .with(|db| {
- skm.unlock_and_get_user_state(
- &mut db.borrow_mut(),
- &LEGACY_IMPORTER,
- user_id as u32,
- &password,
- )
- })
- .context(ks_err!("Unlock with password."))?
- {
- log::info!(
- "In on_lock_screen_event. Trying to unlock when LSKF is uninitialized."
- );
- }
+ fn on_weak_unlock_methods_expired(&self, user_id: i32) -> Result<()> {
+ log::info!("on_weak_unlock_methods_expired(user_id={})", user_id);
+ if !android_security_flags::fix_unlocked_device_required_keys_v2() {
+ return Ok(());
+ }
+ check_keystore_permission(KeystorePerm::Lock)
+ .context(ks_err!("caller missing Lock permission"))?;
+ SUPER_KEY.write().unwrap().wipe_plaintext_unlocked_device_required_keys(user_id as u32);
+ Ok(())
+ }
- Ok(())
- }
- (LockScreenEvent::UNLOCK, None) => {
- check_keystore_permission(KeystorePerm::Unlock).context(ks_err!("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)
- })
- .context(ks_err!("try_unlock_user_with_biometric failed"))?;
- Ok(())
- }
- (LockScreenEvent::LOCK, None) => {
- check_keystore_permission(KeystorePerm::Lock).context(ks_err!("Lock"))?;
- ENFORCEMENTS.set_device_locked(user_id, true);
- let mut skm = SUPER_KEY.write().unwrap();
- DB.with(|db| {
- skm.lock_screen_lock_bound_key(
- &mut db.borrow_mut(),
- user_id as u32,
- unlocking_sids.unwrap_or(&[]),
- );
- });
- Ok(())
- }
- _ => {
- // Any other combination is not supported.
- Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(ks_err!("Unknown event."))
- }
+ fn on_non_lskf_unlock_methods_expired(&self, user_id: i32) -> Result<()> {
+ log::info!("on_non_lskf_unlock_methods_expired(user_id={})", user_id);
+ if !android_security_flags::fix_unlocked_device_required_keys_v2() {
+ return Ok(());
}
+ check_keystore_permission(KeystorePerm::Lock)
+ .context(ks_err!("caller missing Lock permission"))?;
+ SUPER_KEY.write().unwrap().wipe_all_unlocked_device_required_keys(user_id as u32);
+ Ok(())
}
fn get_auth_tokens_for_credstore(
@@ -223,7 +226,8 @@ 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).context(ks_err!("GetAuthToken"))?;
+ check_keystore_permission(KeystorePerm::GetAuthToken)
+ .context(ks_err!("caller missing GetAuthToken permission"))?;
// If the challenge is zero, return error
if challenge == 0 {
@@ -235,6 +239,32 @@ impl AuthorizationManager {
ENFORCEMENTS.get_auth_tokens(challenge, secure_user_id, auth_token_max_age_millis)?;
Ok(AuthorizationTokens { authToken: auth_token, timestampToken: ts_token })
}
+
+ fn get_last_auth_time(
+ &self,
+ secure_user_id: i64,
+ auth_types: &[HardwareAuthenticatorType],
+ ) -> Result<i64> {
+ // Check keystore permission.
+ check_keystore_permission(KeystorePerm::GetLastAuthTime)
+ .context(ks_err!("caller missing GetLastAuthTime permission"))?;
+
+ let mut max_time: i64 = -1;
+ for auth_type in auth_types.iter() {
+ if let Some(time) = ENFORCEMENTS.get_last_auth_time(secure_user_id, *auth_type) {
+ if time.milliseconds() > max_time {
+ max_time = time.milliseconds();
+ }
+ }
+ }
+
+ if max_time >= 0 {
+ Ok(max_time)
+ } else {
+ Err(Error::Rc(ResponseCode::NO_AUTH_TOKEN_FOUND))
+ .context(ks_err!("No auth token found"))
+ }
+ }
}
impl Interface for AuthorizationManager {}
@@ -245,26 +275,29 @@ impl IKeystoreAuthorization for AuthorizationManager {
map_or_log_err(self.add_auth_token(auth_token), Ok)
}
- fn onLockScreenEvent(
+ fn onDeviceUnlocked(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreAuthorization::onDeviceUnlocked", 500);
+ map_or_log_err(self.on_device_unlocked(user_id, password.map(|pw| pw.into())), Ok)
+ }
+
+ fn onDeviceLocked(
&self,
- lock_screen_event: LockScreenEvent,
user_id: i32,
- password: Option<&[u8]>,
- unlocking_sids: Option<&[i64]>,
+ unlocking_sids: &[i64],
+ weak_unlock_enabled: bool,
) -> BinderResult<()> {
- let _wp =
- wd::watch_millis_with("IKeystoreAuthorization::onLockScreenEvent", 500, move || {
- format!("lock event: {}", lock_screen_event.0)
- });
- map_or_log_err(
- self.on_lock_screen_event(
- lock_screen_event,
- user_id,
- password.map(|pw| pw.into()),
- unlocking_sids,
- ),
- Ok,
- )
+ let _wp = wd::watch_millis("IKeystoreAuthorization::onDeviceLocked", 500);
+ map_or_log_err(self.on_device_locked(user_id, unlocking_sids, weak_unlock_enabled), Ok)
+ }
+
+ fn onWeakUnlockMethodsExpired(&self, user_id: i32) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreAuthorization::onWeakUnlockMethodsExpired", 500);
+ map_or_log_err(self.on_weak_unlock_methods_expired(user_id), Ok)
+ }
+
+ fn onNonLskfUnlockMethodsExpired(&self, user_id: i32) -> BinderResult<()> {
+ let _wp = wd::watch_millis("IKeystoreAuthorization::onNonLskfUnlockMethodsExpired", 500);
+ map_or_log_err(self.on_non_lskf_unlock_methods_expired(user_id), Ok)
}
fn getAuthTokensForCredStore(
@@ -283,4 +316,19 @@ impl IKeystoreAuthorization for AuthorizationManager {
Ok,
)
}
+
+ fn getLastAuthTime(
+ &self,
+ secure_user_id: i64,
+ auth_types: &[HardwareAuthenticatorType],
+ ) -> binder::Result<i64> {
+ if aconfig_android_hardware_biometrics_rust::last_authentication_time() {
+ map_or_log_err(self.get_last_auth_time(secure_user_id, auth_types), Ok)
+ } else {
+ Err(BinderStatus::new_service_specific_error(
+ ResponseCode::PERMISSION_DENIED.0,
+ Some(CString::new("Feature is not enabled.").unwrap().as_c_str()),
+ ))
+ }
+ }
}
diff --git a/keystore2/src/crypto/Android.bp b/keystore2/src/crypto/Android.bp
index 1ac64674..f8da06ff 100644
--- a/keystore2/src/crypto/Android.bp
+++ b/keystore2/src/crypto/Android.bp
@@ -32,8 +32,8 @@ rust_library {
"libthiserror",
],
shared_libs: [
- "libkeystore2_crypto",
"libcrypto",
+ "libkeystore2_crypto",
],
vendor_available: true,
apex_available: [
@@ -45,8 +45,8 @@ rust_library {
cc_library {
name: "libkeystore2_crypto",
srcs: [
- "crypto.cpp",
"certificate_utils.cpp",
+ "crypto.cpp",
],
export_include_dirs: ["include"],
shared_libs: [
@@ -69,29 +69,28 @@ rust_bindgen {
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",
- "--allowlist-function", "CreateKeyId",
- "--allowlist-function", "generateKeyFromPassword",
- "--allowlist-function", "HKDFExtract",
- "--allowlist-function", "HKDFExpand",
- "--allowlist-function", "ECDHComputeKey",
- "--allowlist-function", "ECKEYGenerateKey",
- "--allowlist-function", "ECKEYMarshalPrivateKey",
- "--allowlist-function", "ECKEYParsePrivateKey",
- "--allowlist-function", "EC_KEY_get0_public_key",
- "--allowlist-function", "ECPOINTPoint2Oct",
- "--allowlist-function", "ECPOINTOct2Point",
- "--allowlist-function", "EC_KEY_free",
- "--allowlist-function", "EC_POINT_free",
- "--allowlist-function", "extractSubjectFromCertificate",
- "--allowlist-type", "EC_KEY",
- "--allowlist-type", "EC_POINT",
- "--allowlist-var", "EC_MAX_BYTES",
- "--allowlist-var", "EVP_MAX_MD_SIZE",
+ "--allowlist-function=AES_gcm_decrypt",
+ "--allowlist-function=AES_gcm_encrypt",
+ "--allowlist-function=CreateKeyId",
+ "--allowlist-function=ECDHComputeKey",
+ "--allowlist-function=ECKEYGenerateKey",
+ "--allowlist-function=ECKEYMarshalPrivateKey",
+ "--allowlist-function=ECKEYParsePrivateKey",
+ "--allowlist-function=ECPOINTOct2Point",
+ "--allowlist-function=ECPOINTPoint2Oct",
+ "--allowlist-function=EC_KEY_free",
+ "--allowlist-function=EC_KEY_get0_public_key",
+ "--allowlist-function=EC_POINT_free",
+ "--allowlist-function=HKDFExpand",
+ "--allowlist-function=HKDFExtract",
+ "--allowlist-function=PBKDF2",
+ "--allowlist-function=extractSubjectFromCertificate",
+ "--allowlist-function=hmacSha256",
+ "--allowlist-function=randomBytes",
+ "--allowlist-type=EC_KEY",
+ "--allowlist-type=EC_POINT",
+ "--allowlist-var=EC_MAX_BYTES",
+ "--allowlist-var=EVP_MAX_MD_SIZE",
],
cflags: ["-DBORINGSSL_NO_CXX"],
apex_available: [
diff --git a/keystore2/src/crypto/crypto.cpp b/keystore2/src/crypto/crypto.cpp
index 7feeaff6..56d8de6c 100644
--- a/keystore2/src/crypto/crypto.cpp
+++ b/keystore2/src/crypto/crypto.cpp
@@ -141,7 +141,8 @@ bool AES_gcm_decrypt(const uint8_t* in, uint8_t* out, size_t len, const uint8_t*
EVP_DecryptUpdate(ctx.get(), out_pos, &out_len, in, len);
out_pos += out_len;
if (!EVP_DecryptFinal_ex(ctx.get(), out_pos, &out_len)) {
- ALOGE("Failed to decrypt blob; ciphertext or tag is likely corrupted");
+ // No error log here; this is expected when trying two different keys to see which one
+ // works. The callers handle the error appropriately.
return false;
}
out_pos += out_len;
@@ -191,8 +192,7 @@ static constexpr size_t SALT_SIZE = 16;
// Copied from system/security/keystore/user_state.cpp.
-void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw, size_t pw_len,
- const uint8_t* salt) {
+void PBKDF2(uint8_t* key, size_t key_len, const char* pw, size_t pw_len, const uint8_t* salt) {
const EVP_MD* digest = EVP_sha256();
// SHA1 was used prior to increasing the key size
diff --git a/keystore2/src/crypto/crypto.hpp b/keystore2/src/crypto/crypto.hpp
index 4a161e6c..f67f6407 100644
--- a/keystore2/src/crypto/crypto.hpp
+++ b/keystore2/src/crypto/crypto.hpp
@@ -37,8 +37,7 @@ extern "C" {
bool CreateKeyId(const uint8_t* key_blob, size_t len, km_id_t* out_id);
// The salt parameter must be non-nullptr and point to 16 bytes of data.
- void generateKeyFromPassword(uint8_t* key, size_t key_len, const char* pw,
- size_t pw_len, const uint8_t* salt);
+ void PBKDF2(uint8_t* key, size_t key_len, const char* pw, size_t pw_len, const uint8_t* salt);
#include "openssl/digest.h"
#include "openssl/ec_key.h"
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 08b7589e..09b84ec8 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -19,10 +19,10 @@ mod error;
pub mod zvec;
pub use error::Error;
use keystore2_crypto_bindgen::{
- extractSubjectFromCertificate, generateKeyFromPassword, hmacSha256, 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,
+ extractSubjectFromCertificate, hmacSha256, 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, PBKDF2,
};
use std::convert::TryFrom;
use std::convert::TryInto;
@@ -49,8 +49,8 @@ pub const LEGACY_IV_LENGTH: usize = 16;
/// Generate an AES256 key, essentially 32 random bytes from the underlying
/// boringssl library discretely stuffed into a ZVec.
pub fn generate_aes256_key() -> Result<ZVec, Error> {
- // Safety: key has the same length as the requested number of random bytes.
let mut key = ZVec::new(AES_256_KEY_LENGTH)?;
+ // Safety: key has the same length as the requested number of random bytes.
if unsafe { randomBytes(key.as_mut_ptr(), AES_256_KEY_LENGTH) } {
Ok(key)
} else {
@@ -65,8 +65,8 @@ pub fn generate_salt() -> Result<Vec<u8>, Error> {
/// Generate random data of the given size.
pub fn generate_random_data(size: usize) -> Result<Vec<u8>, Error> {
- // Safety: data has the same length as the requested number of random bytes.
let mut data = vec![0; size];
+ // Safety: data has the same length as the requested number of random bytes.
if unsafe { randomBytes(data.as_mut_ptr(), size) } {
Ok(data)
} else {
@@ -172,7 +172,7 @@ pub fn aes_gcm_encrypt(plaintext: &[u8], key: &[u8]) -> Result<(Vec<u8>, Vec<u8>
}
}
-/// Represents a "password" that can be used to key the PBKDF2 algorithm.
+/// A high-entropy synthetic password from which an AES key may be derived.
pub enum Password<'a> {
/// Borrow an existing byte array
Ref(&'a [u8]),
@@ -194,23 +194,28 @@ impl<'a> Password<'a> {
}
}
- /// Generate a key from the given password and salt.
- /// The salt must be exactly 16 bytes long.
- /// Two key sizes are accepted: 16 and 32 bytes.
- pub fn derive_key(&self, salt: &[u8], key_length: usize) -> Result<ZVec, Error> {
+ /// Derives a key from the given password and salt, using PBKDF2 with 8192 iterations.
+ ///
+ /// The salt length must be 16 bytes, and the output key length must be 16 or 32 bytes.
+ ///
+ /// This function exists only for backwards compatibility reasons. Keystore now receives only
+ /// high-entropy synthetic passwords, which do not require key stretching.
+ pub fn derive_key_pbkdf2(&self, salt: &[u8], out_len: usize) -> Result<ZVec, Error> {
if salt.len() != SALT_LENGTH {
return Err(Error::InvalidSaltLength);
}
- match key_length {
+ match out_len {
AES_128_KEY_LENGTH | AES_256_KEY_LENGTH => {}
_ => return Err(Error::InvalidKeyLength),
}
let pw = self.get_key();
- let mut result = ZVec::new(key_length)?;
+ let mut result = ZVec::new(out_len)?;
+ // Safety: We checked that the salt is exactly 16 bytes long. The other pointers are valid,
+ // and have matching lengths.
unsafe {
- generateKeyFromPassword(
+ PBKDF2(
result.as_mut_ptr(),
result.len(),
pw.as_ptr() as *const std::os::raw::c_char,
@@ -222,6 +227,13 @@ impl<'a> Password<'a> {
Ok(result)
}
+ /// Derives a key from the given high-entropy synthetic password and salt, using HKDF.
+ pub fn derive_key_hkdf(&self, salt: &[u8], out_len: usize) -> Result<ZVec, Error> {
+ let prk = hkdf_extract(self.get_key(), salt)?;
+ let info = [];
+ hkdf_expand(out_len, &prk, &info)
+ }
+
/// Try to make another Password object with the same data.
pub fn try_clone(&self) -> Result<Password<'static>, Error> {
Ok(Password::Owned(ZVec::try_from(self.get_key())?))
@@ -324,10 +336,10 @@ impl Drop for OwnedECPoint {
/// Calls the boringssl ECDH_compute_key function.
pub fn ecdh_compute_key(pub_key: &EC_POINT, priv_key: &ECKey) -> Result<ZVec, Error> {
let mut buf = ZVec::new(EC_MAX_BYTES)?;
+ let result =
// Safety: Our ECDHComputeKey wrapper passes EC_MAX_BYES to ECDH_compute_key, which
// writes at most that many bytes to the output.
// The two keys are valid objects.
- let result =
unsafe { ECDHComputeKey(buf.as_mut_ptr() as *mut std::ffi::c_void, pub_key, priv_key.0) };
if result == -1 {
return Err(Error::ECDHComputeKeyFailed);
@@ -469,9 +481,7 @@ pub fn parse_subject_from_certificate(cert_buf: &[u8]) -> Result<Vec<u8>, Error>
mod tests {
use super::*;
- use keystore2_crypto_bindgen::{
- generateKeyFromPassword, AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId,
- };
+ use keystore2_crypto_bindgen::{AES_gcm_decrypt, AES_gcm_encrypt, CreateKeyId, PBKDF2};
#[test]
fn test_wrapper_roundtrip() {
@@ -487,9 +497,11 @@ mod tests {
let input = vec![0; 16];
let mut out = vec![0; 16];
let mut out2 = vec![0; 16];
- let key = vec![0; 16];
- let iv = vec![0; 12];
+ let key = [0; 16];
+ let iv = [0; 12];
let mut tag = vec![0; 16];
+ // SAFETY: The various pointers are obtained from references so they are valid, and
+ // `AES_gcm_encrypt` and `AES_gcm_decrypt` don't do anything with them after they return.
unsafe {
let res = AES_gcm_encrypt(
input.as_ptr(),
@@ -519,22 +531,27 @@ mod tests {
#[test]
fn test_create_key_id() {
- let blob = vec![0; 16];
+ let blob = [0; 16];
let mut out: u64 = 0;
+ // SAFETY: The pointers are obtained from references so they are valid, the length matches
+ // the length of the array, and `CreateKeyId` doesn't access them after it returns.
unsafe {
- let res = CreateKeyId(blob.as_ptr(), 16, &mut out);
+ let res = CreateKeyId(blob.as_ptr(), blob.len(), &mut out);
assert!(res);
assert_ne!(out, 0);
}
}
#[test]
- fn test_generate_key_from_password() {
+ fn test_pbkdf2() {
let mut key = vec![0; 16];
- let pw = vec![0; 16];
- let salt = vec![0; 16];
+ let pw = [0; 16];
+ let salt = [0; 16];
+ // SAFETY: The pointers are obtained from references so they are valid, the salt is the
+ // expected length, the other lengths match the lengths of the arrays, and `PBKDF2` doesn't
+ // access them after it returns.
unsafe {
- generateKeyFromPassword(key.as_mut_ptr(), 16, pw.as_ptr(), 16, salt.as_ptr());
+ PBKDF2(key.as_mut_ptr(), key.len(), pw.as_ptr(), pw.len(), salt.as_ptr());
}
assert_ne!(key, vec![0; 16]);
}
diff --git a/keystore2/src/crypto/tests/certificate_utils_test.cpp b/keystore2/src/crypto/tests/certificate_utils_test.cpp
index bd949282..a8517987 100644
--- a/keystore2/src/crypto/tests/certificate_utils_test.cpp
+++ b/keystore2/src/crypto/tests/certificate_utils_test.cpp
@@ -313,7 +313,15 @@ TEST_P(CertificateUtilsWithRsa, CertSigningWithCallbackRsa) {
const uint8_t* p = encCert.data();
X509_Ptr decoded_cert(d2i_X509(nullptr, &p, (long)encCert.size()));
EVP_PKEY_Ptr decoded_pkey(X509_get_pubkey(decoded_cert.get()));
- ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+ if ((padding == Padding::PSS) && (digest == Digest::SHA1 || digest == Digest::SHA224)) {
+ // BoringSSL after https://boringssl-review.googlesource.com/c/boringssl/+/53865
+ // does not support these PSS combinations, so skip certificate verification for them
+ // and just check _something_ was returned.
+ EXPECT_NE(decoded_cert.get(), nullptr);
+ EXPECT_NE(decoded_pkey.get(), nullptr);
+ } else {
+ ASSERT_TRUE(X509_verify(decoded_cert.get(), decoded_pkey.get()));
+ }
}
TEST(TimeStringTests, toTimeStringTest) {
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index 5a173c30..00cbb1c8 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -20,6 +20,7 @@ use std::convert::TryFrom;
use std::fmt;
use std::ops::{Deref, DerefMut};
use std::ptr::write_volatile;
+use std::ptr::NonNull;
/// A semi fixed size u8 vector that is zeroed when dropped. It can shrink in
/// size but cannot grow larger than the original size (and if it shrinks it
@@ -45,7 +46,8 @@ impl ZVec {
let v: Vec<u8> = vec![0; size];
let b = v.into_boxed_slice();
if size > 0 {
- unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
+ // SAFETY: The address range is part of our address space.
+ unsafe { mlock(NonNull::from(&b).cast(), b.len()) }?;
}
Ok(Self { elems: b, len: size })
}
@@ -71,11 +73,14 @@ impl ZVec {
impl Drop for ZVec {
fn drop(&mut self) {
for i in 0..self.elems.len() {
- unsafe { write_volatile(self.elems.as_mut_ptr().add(i), 0) };
+ // SAFETY: The pointer is valid and properly aligned because it came from a reference.
+ unsafe { write_volatile(&mut self.elems[i], 0) };
}
if !self.elems.is_empty() {
if let Err(e) =
- unsafe { munlock(self.elems.as_ptr() as *const std::ffi::c_void, self.elems.len()) }
+ // SAFETY: The address range is part of our address space, and was previously locked
+ // by `mlock` in `ZVec::new` or the `TryFrom<Vec<u8>>` implementation.
+ unsafe { munlock(NonNull::from(&self.elems).cast(), self.elems.len()) }
{
log::error!("In ZVec::drop: `munlock` failed: {:?}.", e);
}
@@ -130,7 +135,8 @@ impl TryFrom<Vec<u8>> for ZVec {
v.resize(v.capacity(), 0);
let b = v.into_boxed_slice();
if !b.is_empty() {
- unsafe { mlock(b.as_ptr() as *const std::ffi::c_void, b.len()) }?;
+ // SAFETY: The address range is part of our address space.
+ unsafe { mlock(NonNull::from(&b).cast(), b.len()) }?;
}
Ok(Self { elems: b, len })
}
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index c9c28f6d..0cc982a8 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -46,37 +46,30 @@ 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::key_parameter::{KeyParameter, KeyParameterValue, Tag};
use crate::ks_err;
-use crate::metrics_store::log_rkp_error_stats;
use crate::permission::KeyPermSet;
use crate::utils::{get_current_time_in_milliseconds, watchdog as wd, AID_USER_OFFSET};
use crate::{
error::{Error as KsError, ErrorCode, ResponseCode},
super_key::SuperKeyType,
};
-use anyhow::{anyhow, Context, Result};
-use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
-use utils as db_utils;
-use utils::SqlField;
-
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- HardwareAuthToken::HardwareAuthToken,
- HardwareAuthenticatorType::HardwareAuthenticatorType, SecurityLevel::SecurityLevel,
+ HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
+ SecurityLevel::SecurityLevel,
+};
+use android_security_metrics::aidl::android::security::metrics::{
+ Storage::Storage as MetricsStorage, StorageStats::StorageStats,
};
use android_system_keystore2::aidl::android::system::keystore2::{
Domain::Domain, KeyDescriptor::KeyDescriptor,
};
-use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
- AttestationPoolStatus::AttestationPoolStatus,
-};
-use android_security_metrics::aidl::android::security::metrics::{
- StorageStats::StorageStats,
- Storage::Storage as MetricsStorage,
- RkpError::RkpError as MetricsRkpError,
-};
+use anyhow::{anyhow, Context, Result};
+use keystore2_flags;
+use std::{convert::TryFrom, convert::TryInto, ops::Deref, time::SystemTimeError};
+use utils as db_utils;
+use utils::SqlField;
use keystore2_crypto::ZVec;
use lazy_static::lazy_static;
@@ -89,7 +82,7 @@ use rusqlite::{
types::FromSqlResult,
types::ToSqlOutput,
types::{FromSqlError, Value, ValueRef},
- Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior, NO_PARAMS,
+ Connection, OptionalExtension, ToSql, Transaction, TransactionBehavior,
};
use std::{
@@ -256,8 +249,6 @@ pub enum KeyType {
/// This is a super key type. These keys are created by keystore itself and used to encrypt
/// other key blobs to provide LSKF binding.
Super,
- /// This is an attestation key. These keys are created by the remote provisioning mechanism.
- Attestation,
}
impl ToSql for KeyType {
@@ -265,7 +256,6 @@ impl ToSql for KeyType {
Ok(ToSqlOutput::Owned(Value::Integer(match self {
KeyType::Client => 0,
KeyType::Super => 1,
- KeyType::Attestation => 2,
})))
}
}
@@ -275,7 +265,6 @@ impl FromSql for KeyType {
match i64::column_result(value)? {
0 => Ok(KeyType::Client),
1 => Ok(KeyType::Super),
- 2 => Ok(KeyType::Attestation),
v => Err(FromSqlError::OutOfRange(v)),
}
}
@@ -325,8 +314,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 = 12 * 60 * 60 * 1000;
-
/// Indicates how the sensitive part of this key blob is encrypted.
#[derive(Debug, Eq, PartialEq, Ord, PartialOrd)]
pub enum EncryptedBy {
@@ -774,22 +761,22 @@ pub struct KeystoreDB {
}
/// Database representation of the monotonic time retrieved from the system call clock_gettime with
-/// CLOCK_MONOTONIC_RAW. Stores monotonic time as i64 in milliseconds.
+/// CLOCK_BOOTTIME. Stores monotonic time as i64 in milliseconds.
#[derive(Debug, Copy, Clone, Default, Eq, PartialEq, Ord, PartialOrd)]
-pub struct MonotonicRawTime(i64);
+pub struct BootTime(i64);
-impl MonotonicRawTime {
- /// Constructs a new MonotonicRawTime
+impl BootTime {
+ /// Constructs a new BootTime
pub fn now() -> Self {
Self(get_current_time_in_milliseconds())
}
- /// Returns the value of MonotonicRawTime in milliseconds as i64
+ /// Returns the value of BootTime in milliseconds as i64
pub fn milliseconds(&self) -> i64 {
self.0
}
- /// Returns the integer value of MonotonicRawTime as i64
+ /// Returns the integer value of BootTime as i64
pub fn seconds(&self) -> i64 {
self.0 / 1000
}
@@ -800,13 +787,13 @@ impl MonotonicRawTime {
}
}
-impl ToSql for MonotonicRawTime {
+impl ToSql for BootTime {
fn to_sql(&self) -> rusqlite::Result<ToSqlOutput> {
Ok(ToSqlOutput::Owned(Value::Integer(self.0)))
}
}
-impl FromSql for MonotonicRawTime {
+impl FromSql for BootTime {
fn column_result(value: ValueRef) -> FromSqlResult<Self> {
Ok(Self(i64::column_result(value)?))
}
@@ -818,11 +805,11 @@ impl FromSql for MonotonicRawTime {
pub struct AuthTokenEntry {
auth_token: HardwareAuthToken,
// Time received in milliseconds
- time_received: MonotonicRawTime,
+ time_received: BootTime,
}
impl AuthTokenEntry {
- fn new(auth_token: HardwareAuthToken, time_received: MonotonicRawTime) -> Self {
+ fn new(auth_token: HardwareAuthToken, time_received: BootTime) -> Self {
AuthTokenEntry { auth_token, time_received }
}
@@ -845,7 +832,7 @@ impl AuthTokenEntry {
}
/// Returns the time that this auth token was received.
- pub fn time_received(&self) -> MonotonicRawTime {
+ pub fn time_received(&self) -> BootTime {
self.time_received
}
@@ -855,11 +842,6 @@ impl AuthTokenEntry {
}
}
-/// Shared in-memory databases get destroyed as soon as the last connection to them gets closed.
-/// This object does not allow access to the database connection. But it keeps a database
-/// connection alive in order to keep the in memory per boot database alive.
-pub struct PerBootDbKeepAlive(Connection);
-
impl KeystoreDB {
const UNASSIGNED_KEY_ID: i64 = -1i64;
const CURRENT_DB_VERSION: u32 = 1;
@@ -918,21 +900,21 @@ impl KeystoreDB {
alias BLOB,
state INTEGER,
km_uuid BLOB);",
- NO_PARAMS,
+ [],
)
.context("Failed to initialize \"keyentry\" table.")?;
tx.execute(
"CREATE INDEX IF NOT EXISTS persistent.keyentry_id_index
ON keyentry(id);",
- NO_PARAMS,
+ [],
)
.context("Failed to create index keyentry_id_index.")?;
tx.execute(
"CREATE INDEX IF NOT EXISTS persistent.keyentry_domain_namespace_index
ON keyentry(domain, namespace, alias);",
- NO_PARAMS,
+ [],
)
.context("Failed to create index keyentry_domain_namespace_index.")?;
@@ -942,14 +924,14 @@ impl KeystoreDB {
subcomponent_type INTEGER,
keyentryid INTEGER,
blob BLOB);",
- NO_PARAMS,
+ [],
)
.context("Failed to initialize \"blobentry\" table.")?;
tx.execute(
"CREATE INDEX IF NOT EXISTS persistent.blobentry_keyentryid_index
ON blobentry(keyentryid);",
- NO_PARAMS,
+ [],
)
.context("Failed to create index blobentry_keyentryid_index.")?;
@@ -960,14 +942,14 @@ impl KeystoreDB {
tag INTEGER,
data ANY,
UNIQUE (blobentryid, tag));",
- NO_PARAMS,
+ [],
)
.context("Failed to initialize \"blobmetadata\" table.")?;
tx.execute(
"CREATE INDEX IF NOT EXISTS persistent.blobmetadata_blobentryid_index
ON blobmetadata(blobentryid);",
- NO_PARAMS,
+ [],
)
.context("Failed to create index blobmetadata_blobentryid_index.")?;
@@ -977,14 +959,14 @@ impl KeystoreDB {
tag INTEGER,
data ANY,
security_level INTEGER);",
- NO_PARAMS,
+ [],
)
.context("Failed to initialize \"keyparameter\" table.")?;
tx.execute(
"CREATE INDEX IF NOT EXISTS persistent.keyparameter_keyentryid_index
ON keyparameter(keyentryid);",
- NO_PARAMS,
+ [],
)
.context("Failed to create index keyparameter_keyentryid_index.")?;
@@ -994,14 +976,14 @@ impl KeystoreDB {
tag INTEGER,
data ANY,
UNIQUE (keyentryid, tag));",
- NO_PARAMS,
+ [],
)
.context("Failed to initialize \"keymetadata\" table.")?;
tx.execute(
"CREATE INDEX IF NOT EXISTS persistent.keymetadata_keyentryid_index
ON keymetadata(keyentryid);",
- NO_PARAMS,
+ [],
)
.context("Failed to create index keymetadata_keyentryid_index.")?;
@@ -1011,7 +993,7 @@ impl KeystoreDB {
grantee INTEGER,
keyentryid INTEGER,
access_vector INTEGER);",
- NO_PARAMS,
+ [],
)
.context("Failed to initialize \"grant\" table.")?;
@@ -1027,6 +1009,14 @@ impl KeystoreDB {
let mut persistent_path_str = "file:".to_owned();
persistent_path_str.push_str(&persistent_path.to_string_lossy());
+ // Connect to database in specific mode
+ let persistent_path_mode = if keystore2_flags::wal_db_journalmode_v3() {
+ "?journal_mode=WAL".to_owned()
+ } else {
+ "?journal_mode=DELETE".to_owned()
+ };
+ persistent_path_str.push_str(&persistent_path_mode);
+
Ok(persistent_path_str)
}
@@ -1172,9 +1162,9 @@ impl KeystoreDB {
"DELETE FROM persistent.blobmetadata WHERE blobentryid = ?;",
params![blob_id],
)
- .context("Trying to delete blob metadata.")?;
+ .context(ks_err!("Trying to delete blob metadata: {:?}", blob_id))?;
tx.execute("DELETE FROM persistent.blobentry WHERE id = ?;", params![blob_id])
- .context("Trying to blob.")?;
+ .context(ks_err!("Trying to delete blob: {:?}", blob_id))?;
}
Self::cleanup_unreferenced(tx).context("Trying to cleanup unreferenced.")?;
@@ -1472,7 +1462,7 @@ impl KeystoreDB {
F: Fn(&Transaction) -> Result<(bool, T)>,
{
loop {
- match self
+ let result = self
.conn
.transaction_with_behavior(behavior)
.context(ks_err!())
@@ -1480,7 +1470,8 @@ impl KeystoreDB {
.and_then(|(result, tx)| {
tx.commit().context(ks_err!("Failed to commit transaction."))?;
Ok(result)
- }) {
+ });
+ match result {
Ok(result) => break Ok(result),
Err(e) => {
if Self::is_locked_error(&e) {
@@ -1565,48 +1556,6 @@ impl KeystoreDB {
))
}
- /// Creates a new attestation key entry and allocates a new randomized id for the new key.
- /// The key id gets associated with a domain and namespace later but not with an alias. The
- /// alias will be used to denote if a key has been signed as each key can only be bound to one
- /// domain and namespace pairing so there is no need to use them as a value for indexing into
- /// a key.
- pub fn create_attestation_key_entry(
- &mut self,
- maced_public_key: &[u8],
- raw_public_key: &[u8],
- private_key: &[u8],
- km_uuid: &Uuid,
- ) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::create_attestation_key_entry", 500);
-
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let key_id = KEY_ID_LOCK.get(
- Self::insert_with_retry(|id| {
- tx.execute(
- "INSERT into persistent.keyentry
- (id, key_type, domain, namespace, alias, state, km_uuid)
- VALUES(?, ?, NULL, NULL, NULL, ?, ?);",
- params![id, KeyType::Attestation, KeyLifeCycle::Live, km_uuid],
- )
- })
- .context(ks_err!())?,
- );
- Self::set_blob_internal(
- tx,
- key_id.0,
- SubComponentType::KEY_BLOB,
- Some(private_key),
- None,
- )?;
- 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)?;
- Ok(()).no_gc()
- })
- .context(ks_err!())
- }
-
/// Set a new blob and associates it with the given key id. Each blob
/// has a sub component type.
/// Each key can have one of each sub component type associated. If more
@@ -1666,7 +1615,7 @@ impl KeystoreDB {
.context(ks_err!("Failed to insert blob."))?;
if let Some(blob_metadata) = blob_metadata {
let blob_id = tx
- .query_row("SELECT MAX(id) FROM persistent.blobentry;", NO_PARAMS, |row| {
+ .query_row("SELECT MAX(id) FROM persistent.blobentry;", [], |row| {
row.get(0)
})
.context(ks_err!("Failed to get new blob id."))?;
@@ -1734,442 +1683,6 @@ impl KeystoreDB {
.context(ks_err!())
}
- /// Stores a signed certificate chain signed by a remote provisioning server, keyed
- /// on the public key.
- pub fn store_signed_attestation_certificate_chain(
- &mut self,
- raw_public_key: &[u8],
- batch_cert: &[u8],
- cert_chain: &[u8],
- expiration_date: i64,
- km_uuid: &Uuid,
- ) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::store_signed_attestation_certificate_chain", 500);
-
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let mut stmt = tx
- .prepare(
- "SELECT keyentryid
- FROM persistent.keymetadata
- WHERE tag = ? AND data = ? AND keyentryid IN
- (SELECT id
- FROM persistent.keyentry
- WHERE
- alias IS NULL AND
- domain IS NULL AND
- namespace IS NULL AND
- key_type = ? AND
- km_uuid = ?);",
- )
- .context("Failed to store attestation certificate chain.")?;
- let mut rows = stmt
- .query(params![
- KeyMetaData::AttestationRawPubKey,
- raw_public_key,
- KeyType::Attestation,
- km_uuid
- ])
- .context("Failed to fetch keyid")?;
- let key_id = db_utils::with_rows_extract_one(&mut rows, |row| {
- row.map_or_else(|| Err(KsError::Rc(ResponseCode::KEY_NOT_FOUND)), Ok)?
- .get(0)
- .context("Failed to unpack id.")
- })
- .context("Failed to get key_id.")?;
- let num_updated = tx
- .execute(
- "UPDATE persistent.keyentry
- SET alias = ?
- WHERE id = ?;",
- params!["signed", key_id],
- )
- .context("Failed to update alias.")?;
- if num_updated != 1 {
- return Err(KsError::sys()).context("Alias not updated for the key.");
- }
- let mut metadata = KeyMetaData::new();
- metadata.add(KeyMetaEntry::AttestationExpirationDate(DateTime::from_millis_epoch(
- expiration_date,
- )));
- metadata.store_in_db(key_id, tx).context("Failed to insert key metadata.")?;
- Self::set_blob_internal(
- 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)
- .context("Failed to insert cert")?;
- Ok(()).no_gc()
- })
- .context(ks_err!())
- }
-
- /// Assigns the next unassigned attestation key to a domain/namespace combo that does not
- /// currently have a key assigned to it.
- pub fn assign_attestation_key(
- &mut self,
- domain: Domain,
- namespace: i64,
- km_uuid: &Uuid,
- ) -> Result<()> {
- let _wp = wd::watch_millis("KeystoreDB::assign_attestation_key", 500);
-
- match domain {
- Domain::APP | Domain::SELINUX => {}
- _ => {
- return Err(KsError::sys())
- .context(ks_err!("Domain {:?} must be either App or SELinux.", domain));
- }
- }
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let result = tx
- .execute(
- "UPDATE persistent.keyentry
- SET domain=?1, namespace=?2
- WHERE
- id =
- (SELECT MIN(id)
- FROM persistent.keyentry
- WHERE ALIAS IS NOT NULL
- AND domain IS NULL
- AND key_type IS ?3
- AND state IS ?4
- AND km_uuid IS ?5)
- AND
- (SELECT COUNT(*)
- FROM persistent.keyentry
- WHERE domain=?1
- AND namespace=?2
- AND key_type IS ?3
- AND state IS ?4
- AND km_uuid IS ?5) = 0;",
- params![
- domain.0 as u32,
- namespace,
- KeyType::Attestation,
- KeyLifeCycle::Live,
- km_uuid,
- ],
- )
- .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);
- return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR))
- .context("Out of keys.");
- } else if result > 1 {
- return Err(KsError::sys())
- .context(format!("Expected to update 1 entry, instead updated {}", result));
- }
- Ok(()).no_gc()
- })
- .context(ks_err!())
- }
-
- /// Retrieves num_keys number of attestation keys that have not yet been signed by a remote
- /// provisioning server, or the maximum number available if there are not num_keys number of
- /// entries in the table.
- pub fn fetch_unsigned_attestation_keys(
- &mut self,
- num_keys: i32,
- km_uuid: &Uuid,
- ) -> Result<Vec<Vec<u8>>> {
- let _wp = wd::watch_millis("KeystoreDB::fetch_unsigned_attestation_keys", 500);
-
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let mut stmt = tx
- .prepare(
- "SELECT data
- FROM persistent.keymetadata
- WHERE tag = ? AND keyentryid IN
- (SELECT id
- FROM persistent.keyentry
- WHERE
- alias IS NULL AND
- domain IS NULL AND
- namespace IS NULL AND
- key_type = ? AND
- km_uuid = ?
- LIMIT ?);",
- )
- .context("Failed to prepare statement")?;
- let rows = stmt
- .query_map(
- params![
- KeyMetaData::AttestationMacedPublicKey,
- KeyType::Attestation,
- km_uuid,
- num_keys
- ],
- |row| row.get(0),
- )?
- .collect::<rusqlite::Result<Vec<Vec<u8>>>>()
- .context("Failed to execute statement")?;
- Ok(rows).no_gc()
- })
- .context(ks_err!())
- }
-
- /// Removes any keys that have expired as of the current time. Returns the number of keys
- /// marked unreferenced that are bound to be garbage collected.
- pub fn delete_expired_attestation_keys(&mut self) -> Result<i32> {
- let _wp = wd::watch_millis("KeystoreDB::delete_expired_attestation_keys", 500);
-
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let mut stmt = tx
- .prepare(
- "SELECT keyentryid, data
- FROM persistent.keymetadata
- WHERE tag = ? AND keyentryid IN
- (SELECT id
- FROM persistent.keyentry
- WHERE key_type = ?);",
- )
- .context("Failed to prepare query")?;
- let key_ids_to_check = stmt
- .query_map(
- params![KeyMetaData::AttestationExpirationDate, KeyType::Attestation],
- |row| Ok((row.get(0)?, row.get(1)?)),
- )?
- .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,
- );
- 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)? {
- num_deleted += 1;
- }
- }
- Ok(num_deleted).do_gc(num_deleted != 0)
- })
- .context(ks_err!())
- }
-
- /// Deletes all remotely provisioned attestation keys in the system, regardless of the state
- /// they are in. This is useful primarily as a testing mechanism.
- pub fn delete_all_attestation_keys(&mut self) -> Result<i64> {
- let _wp = wd::watch_millis("KeystoreDB::delete_all_attestation_keys", 500);
-
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let mut stmt = tx
- .prepare(
- "SELECT id FROM persistent.keyentry
- WHERE key_type IS ?;",
- )
- .context("Failed to prepare statement")?;
- let keys_to_delete = stmt
- .query_map(params![KeyType::Attestation], |row| row.get(0))?
- .collect::<rusqlite::Result<Vec<i64>>>()
- .context("Failed to execute statement")?;
- let num_deleted = keys_to_delete
- .iter()
- .map(|id| Self::mark_unreferenced(tx, *id))
- .collect::<Result<Vec<bool>>>()
- .context("Failed to execute mark_unreferenced on a keyid")?
- .into_iter()
- .filter(|result| *result)
- .count() as i64;
- Ok(num_deleted).do_gc(num_deleted != 0)
- })
- .context(ks_err!())
- }
-
- /// Counts the number of keys that will expire by the provided epoch date and the number of
- /// keys not currently assigned to a domain.
- pub fn get_attestation_pool_status(
- &mut self,
- date: i64,
- km_uuid: &Uuid,
- ) -> Result<AttestationPoolStatus> {
- let _wp = wd::watch_millis("KeystoreDB::get_attestation_pool_status", 500);
-
- self.with_transaction(TransactionBehavior::Immediate, |tx| {
- let mut stmt = tx.prepare(
- "SELECT data
- FROM persistent.keymetadata
- WHERE tag = ? AND keyentryid IN
- (SELECT id
- FROM persistent.keyentry
- WHERE alias IS NOT NULL
- AND key_type = ?
- AND km_uuid = ?
- AND state = ?);",
- )?;
- let times = stmt
- .query_map(
- params![
- KeyMetaData::AttestationExpirationDate,
- KeyType::Attestation,
- km_uuid,
- KeyLifeCycle::Live
- ],
- |row| row.get(0),
- )?
- .collect::<rusqlite::Result<Vec<DateTime>>>()
- .context("Failed to execute metadata statement")?;
- let expiring =
- times.iter().filter(|time| time < &&DateTime::from_millis_epoch(date)).count()
- as i32;
- stmt = tx.prepare(
- "SELECT alias, domain
- FROM persistent.keyentry
- WHERE key_type = ? AND km_uuid = ? AND state = ?;",
- )?;
- let rows = stmt
- .query_map(params![KeyType::Attestation, km_uuid, KeyLifeCycle::Live], |row| {
- Ok((row.get(0)?, row.get(1)?))
- })?
- .collect::<rusqlite::Result<Vec<(Option<String>, Option<u32>)>>>()
- .context("Failed to execute keyentry statement")?;
- let mut unassigned = 0i32;
- let mut attested = 0i32;
- let total = rows.len() as i32;
- for (alias, domain) in rows {
- match (alias, domain) {
- (Some(_alias), None) => {
- attested += 1;
- unassigned += 1;
- }
- (Some(_alias), Some(_domain)) => {
- attested += 1;
- }
- _ => {}
- }
- }
- Ok(AttestationPoolStatus { expiring, unassigned, attested, total }).no_gc()
- })
- .context(ks_err!())
- }
-
- 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.
- pub fn retrieve_attestation_key_and_cert_chain(
- &mut self,
- domain: Domain,
- namespace: i64,
- km_uuid: &Uuid,
- ) -> Result<Option<(KeyIdGuard, CertificateChain)>> {
- let _wp = wd::watch_millis("KeystoreDB::retrieve_attestation_key_and_cert_chain", 500);
-
- match domain {
- Domain::APP | Domain::SELINUX => {}
- _ => {
- return Err(KsError::sys())
- .context(format!("Domain {:?} must be either App or SELinux.", domain));
- }
- }
-
- self.delete_expired_attestation_keys()
- .context(ks_err!("Failed to prune expired attestation keys",))?;
- let tx = self
- .conn
- .unchecked_transaction()
- .context(ks_err!("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(ks_err!("Failed to commit keyid query"))?;
- let key_id_guard = KEY_ID_LOCK.get(key_id);
- let tx = self
- .conn
- .unchecked_transaction()
- .context(ks_err!("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;
- }
- _ => Err(KsError::sys()).context("Unknown or incorrect subcomponent type.")?,
- }
- }
- Ok(Some((
- key_id_guard,
- CertificateChain {
- private_key: ZVec::try_from(km_blob)?,
- batch_cert: batch_cert_blob,
- cert_chain: cert_chain_blob,
- },
- )))
- }
-
/// Updates the alias column of the given key id `newid` with the given alias,
/// and atomically, removes the alias, domain, and namespace from another row
/// with the same alias-domain-namespace tuple if such row exits.
@@ -2879,33 +2392,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()
@@ -3030,6 +2543,70 @@ impl KeystoreDB {
.context(ks_err!())
}
+ /// Deletes all auth-bound keys, i.e. keys that require user authentication, for the given user.
+ /// This runs when the user's lock screen is being changed to Swipe or None.
+ ///
+ /// This intentionally does *not* delete keys that require that the device be unlocked, unless
+ /// such keys also require user authentication. Keystore's concept of user authentication is
+ /// fairly strong, and it requires that keys that require authentication be deleted as soon as
+ /// authentication is no longer possible. In contrast, keys that just require that the device
+ /// be unlocked should remain usable when the lock screen is set to Swipe or None, as the device
+ /// is always considered "unlocked" in that case.
+ pub fn unbind_auth_bound_keys_for_user(&mut self, user_id: u32) -> Result<()> {
+ let _wp = wd::watch_millis("KeystoreDB::unbind_auth_bound_keys_for_user", 500);
+
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ let mut stmt = tx
+ .prepare(&format!(
+ "SELECT id from persistent.keyentry
+ WHERE key_type = ?
+ AND domain = ?
+ AND cast ( (namespace/{aid_user_offset}) as int) = ?
+ AND state = ?;",
+ aid_user_offset = AID_USER_OFFSET
+ ))
+ .context(concat!(
+ "In unbind_auth_bound_keys_for_user. ",
+ "Failed to prepare the query to find the keys created by apps."
+ ))?;
+
+ let mut rows = stmt
+ .query(params![KeyType::Client, Domain::APP.0 as u32, user_id, KeyLifeCycle::Live,])
+ .context(ks_err!("Failed to query the keys created by apps."))?;
+
+ let mut key_ids: Vec<i64> = Vec::new();
+ db_utils::with_rows_extract_all(&mut rows, |row| {
+ key_ids
+ .push(row.get(0).context("Failed to read key id of a key created by an app.")?);
+ Ok(())
+ })
+ .context(ks_err!())?;
+
+ let mut notify_gc = false;
+ let mut num_unbound = 0;
+ for key_id in key_ids {
+ // Load the key parameters and filter out non-auth-bound keys. To identify
+ // auth-bound keys, use the presence of UserSecureID. The absence of NoAuthRequired
+ // could also be used, but UserSecureID is what Keystore treats as authoritative
+ // when actually enforcing the key parameters (it might not matter, though).
+ let params = Self::load_key_parameters(key_id, tx)
+ .context("Failed to load key parameters.")?;
+ let is_auth_bound_key = params.iter().any(|kp| {
+ matches!(kp.key_parameter_value(), KeyParameterValue::UserSecureID(_))
+ });
+ if is_auth_bound_key {
+ notify_gc = Self::mark_unreferenced(tx, key_id)
+ .context("In unbind_auth_bound_keys_for_user.")?
+ || notify_gc;
+ num_unbound += 1;
+ }
+ }
+ log::info!("Deleting {num_unbound} auth-bound keys for user {user_id}");
+ Ok(()).do_gc(notify_gc)
+ })
+ .context(ks_err!())
+ }
+
fn load_key_components(
tx: &Transaction,
load_bits: KeyEntryLoadBits,
@@ -3058,32 +2635,50 @@ impl KeystoreDB {
})
}
- /// Returns a list of KeyDescriptors in the selected domain/namespace.
+ /// Returns a list of KeyDescriptors in the selected domain/namespace whose
+ /// aliases are greater than the specified 'start_past_alias'. If no value
+ /// is provided, returns all KeyDescriptors.
/// The key descriptors will have the domain, nspace, and alias field set.
+ /// The returned list will be sorted by alias.
/// Domain must be APP or SELINUX, the caller must make sure of that.
- pub fn list(
+ pub fn list_past_alias(
&mut self,
domain: Domain,
namespace: i64,
key_type: KeyType,
+ start_past_alias: Option<&str>,
) -> Result<Vec<KeyDescriptor>> {
- let _wp = wd::watch_millis("KeystoreDB::list", 500);
+ let _wp = wd::watch_millis("KeystoreDB::list_past_alias", 500);
- self.with_transaction(TransactionBehavior::Deferred, |tx| {
- let mut stmt = tx
- .prepare(
- "SELECT alias FROM persistent.keyentry
+ let query = format!(
+ "SELECT DISTINCT alias FROM persistent.keyentry
WHERE domain = ?
AND namespace = ?
AND alias IS NOT NULL
AND state = ?
- AND key_type = ?;",
- )
- .context(ks_err!("Failed to prepare."))?;
+ AND key_type = ?
+ {}
+ ORDER BY alias ASC;",
+ if start_past_alias.is_some() { " AND alias > ?" } else { "" }
+ );
- let mut rows = stmt
- .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live, key_type])
- .context(ks_err!("Failed to query."))?;
+ self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ let mut stmt = tx.prepare(&query).context(ks_err!("Failed to prepare."))?;
+
+ let mut rows = match start_past_alias {
+ Some(past_alias) => stmt
+ .query(params![
+ domain.0 as u32,
+ namespace,
+ KeyLifeCycle::Live,
+ key_type,
+ past_alias
+ ])
+ .context(ks_err!("Failed to query."))?,
+ None => stmt
+ .query(params![domain.0 as u32, namespace, KeyLifeCycle::Live, key_type,])
+ .context(ks_err!("Failed to query."))?,
+ };
let mut descriptors: Vec<KeyDescriptor> = Vec::new();
db_utils::with_rows_extract_all(&mut rows, |row| {
@@ -3100,6 +2695,33 @@ impl KeystoreDB {
})
}
+ /// Returns a number of KeyDescriptors in the selected domain/namespace.
+ /// Domain must be APP or SELINUX, the caller must make sure of that.
+ pub fn count_keys(
+ &mut self,
+ domain: Domain,
+ namespace: i64,
+ key_type: KeyType,
+ ) -> Result<usize> {
+ let _wp = wd::watch_millis("KeystoreDB::countKeys", 500);
+
+ let num_keys = self.with_transaction(TransactionBehavior::Deferred, |tx| {
+ tx.query_row(
+ "SELECT COUNT(alias) FROM persistent.keyentry
+ WHERE domain = ?
+ AND namespace = ?
+ AND alias IS NOT NULL
+ AND state = ?
+ AND key_type = ?;",
+ params![domain.0 as u32, namespace, KeyLifeCycle::Live, key_type],
+ |row| row.get(0),
+ )
+ .context(ks_err!("Failed to count number of keys."))
+ .no_gc()
+ })?;
+ Ok(num_keys)
+ }
+
/// Adds a grant to the grant table.
/// Like `load_key_entry` this function loads the access tuple before
/// it uses the callback for a permission check. Upon success,
@@ -3232,33 +2854,16 @@ impl KeystoreDB {
/// Insert or replace the auth token based on (user_id, auth_id, auth_type)
pub fn insert_auth_token(&mut self, auth_token: &HardwareAuthToken) {
- self.perboot.insert_auth_token_entry(AuthTokenEntry::new(
- auth_token.clone(),
- MonotonicRawTime::now(),
- ))
+ self.perboot
+ .insert_auth_token_entry(AuthTokenEntry::new(auth_token.clone(), BootTime::now()))
}
/// Find the newest auth token matching the given predicate.
- pub fn find_auth_token_entry<F>(&self, p: F) -> Option<(AuthTokenEntry, MonotonicRawTime)>
+ pub fn find_auth_token_entry<F>(&self, p: F) -> Option<AuthTokenEntry>
where
F: Fn(&AuthTokenEntry) -> bool,
{
- self.perboot.find_auth_token_entry(p).map(|entry| (entry, self.get_last_off_body()))
- }
-
- /// Insert last_off_body into the metadata table at the initialization of auth token table
- pub fn insert_last_off_body(&self, last_off_body: MonotonicRawTime) {
- self.perboot.set_last_off_body(last_off_body)
- }
-
- /// Update last_off_body when on_device_off_body is called
- pub fn update_last_off_body(&self, last_off_body: MonotonicRawTime) {
- self.perboot.set_last_off_body(last_off_body)
- }
-
- /// Get last_off_body time when finding auth tokens
- fn get_last_off_body(&self) -> MonotonicRawTime {
- self.perboot.get_last_off_body()
+ self.perboot.find_auth_token_entry(p)
}
/// Load descriptor of a key by key id
@@ -3284,6 +2889,75 @@ impl KeystoreDB {
})
.context(ks_err!())
}
+
+ /// Returns a list of app UIDs that have keys authenticated by the given secure_user_id
+ /// (for the given user_id).
+ /// This is helpful for finding out which apps will have their keys invalidated when
+ /// the user changes biometrics enrollment or removes their LSKF.
+ pub fn get_app_uids_affected_by_sid(
+ &mut self,
+ user_id: i32,
+ secure_user_id: i64,
+ ) -> Result<Vec<i64>> {
+ let _wp = wd::watch_millis("KeystoreDB::get_app_uids_affected_by_sid", 500);
+
+ let key_ids_and_app_uids = self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ let mut stmt = tx
+ .prepare(&format!(
+ "SELECT id, namespace from persistent.keyentry
+ WHERE key_type = ?
+ AND domain = ?
+ AND cast ( (namespace/{AID_USER_OFFSET}) as int) = ?
+ AND state = ?;",
+ ))
+ .context(concat!(
+ "In get_app_uids_affected_by_sid, ",
+ "failed to prepare the query to find the keys created by apps."
+ ))?;
+
+ let mut rows = stmt
+ .query(params![KeyType::Client, Domain::APP.0 as u32, user_id, KeyLifeCycle::Live,])
+ .context(ks_err!("Failed to query the keys created by apps."))?;
+
+ let mut key_ids_and_app_uids: HashMap<i64, i64> = Default::default();
+ db_utils::with_rows_extract_all(&mut rows, |row| {
+ key_ids_and_app_uids.insert(
+ row.get(0).context("Failed to read key id of a key created by an app.")?,
+ row.get(1).context("Failed to read the app uid")?,
+ );
+ Ok(())
+ })?;
+ Ok(key_ids_and_app_uids).no_gc()
+ })?;
+ let mut app_uids_affected_by_sid: HashSet<i64> = Default::default();
+ for (key_id, app_uid) in key_ids_and_app_uids {
+ // Read the key parameters for each key in its own transaction. It is OK to ignore
+ // an error to get the properties of a particular key since it might have been deleted
+ // under our feet after the previous transaction concluded. If the key was deleted
+ // then it is no longer applicable if it was auth-bound or not.
+ if let Ok(is_key_bound_to_sid) =
+ self.with_transaction(TransactionBehavior::Immediate, |tx| {
+ let params = Self::load_key_parameters(key_id, tx)
+ .context("Failed to load key parameters.")?;
+ // Check if the key is bound to this secure user ID.
+ let is_key_bound_to_sid = params.iter().any(|kp| {
+ matches!(
+ kp.key_parameter_value(),
+ KeyParameterValue::UserSecureID(sid) if *sid == secure_user_id
+ )
+ });
+ Ok(is_key_bound_to_sid).no_gc()
+ })
+ {
+ if is_key_bound_to_sid {
+ app_uids_affected_by_sid.insert(app_uid);
+ }
+ }
+ }
+
+ let app_uids_vec: Vec<i64> = app_uids_affected_by_sid.into_iter().collect();
+ Ok(app_uids_vec)
+ }
}
#[cfg(test)]
@@ -3296,7 +2970,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, USER_AFTER_FIRST_UNLOCK_SUPER_KEY, SuperEncryptionAlgorithm, SuperKeyType};
use keystore2_test_utils::TempDir;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken,
@@ -3305,13 +2979,12 @@ pub mod tests {
use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
Timestamp::Timestamp,
};
- use rusqlite::NO_PARAMS;
use rusqlite::TransactionBehavior;
use std::cell::RefCell;
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;
@@ -3328,18 +3001,6 @@ pub mod tests {
Ok(db)
}
- fn new_test_db_with_gc<F>(path: &Path, cb: F) -> Result<KeystoreDB>
- where
- F: Fn(&Uuid, &[u8]) -> Result<()> + Send + 'static,
- {
- let super_key: Arc<RwLock<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));
-
- KeystoreDB::new(path, Some(Arc::new(gc)))
- }
-
fn rebind_alias(
db: &mut KeystoreDB,
newid: &KeyIdGuard,
@@ -3356,7 +3017,7 @@ pub mod tests {
#[test]
fn datetime() -> Result<()> {
let conn = Connection::open_in_memory()?;
- conn.execute("CREATE TABLE test (ts DATETIME);", NO_PARAMS)?;
+ conn.execute("CREATE TABLE test (ts DATETIME);", [])?;
let now = SystemTime::now();
let duration = Duration::from_secs(1000);
let then = now.checked_sub(duration).unwrap();
@@ -3366,7 +3027,7 @@ pub mod tests {
params![DateTime::try_from(now)?, DateTime::try_from(then)?, DateTime::try_from(soon)?],
)?;
let mut stmt = conn.prepare("SELECT ts FROM test ORDER BY ts ASC;")?;
- let mut rows = stmt.query(NO_PARAMS)?;
+ let mut rows = stmt.query([])?;
assert_eq!(DateTime::try_from(then)?, rows.next()?.unwrap().get(0)?);
assert_eq!(DateTime::try_from(now)?, rows.next()?.unwrap().get(0)?);
assert_eq!(DateTime::try_from(soon)?, rows.next()?.unwrap().get(0)?);
@@ -3517,251 +3178,6 @@ pub mod tests {
}
#[test]
- fn test_add_unsigned_key() -> Result<()> {
- let mut db = new_test_db()?;
- let public_key: Vec<u8> = vec![0x01, 0x02, 0x03];
- let private_key: Vec<u8> = vec![0x04, 0x05, 0x06];
- let raw_public_key: Vec<u8> = vec![0x07, 0x08, 0x09];
- db.create_attestation_key_entry(
- &public_key,
- &raw_public_key,
- &private_key,
- &KEYSTORE_UUID,
- )?;
- let keys = db.fetch_unsigned_attestation_keys(5, &KEYSTORE_UUID)?;
- assert_eq!(keys.len(), 1);
- assert_eq!(keys[0], public_key);
- Ok(())
- }
-
- #[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 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!(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);
- Ok(())
- }
-
- #[test]
- fn test_get_attestation_pool_status() -> Result<()> {
- let mut db = new_test_db()?;
- let namespace: i64 = 30;
- load_attestation_key_pool(
- &mut db, 10, /* expiration */
- namespace, 0x01, /* base_byte */
- )?;
- load_attestation_key_pool(&mut db, 20 /* expiration */, namespace + 1, 0x02)?;
- load_attestation_key_pool(&mut db, 40 /* expiration */, namespace + 2, 0x03)?;
- let mut status = db.get_attestation_pool_status(9 /* expiration */, &KEYSTORE_UUID)?;
- assert_eq!(status.expiring, 0);
- assert_eq!(status.attested, 3);
- assert_eq!(status.unassigned, 0);
- assert_eq!(status.total, 3);
- assert_eq!(
- db.get_attestation_pool_status(15 /* expiration */, &KEYSTORE_UUID)?.expiring,
- 1
- );
- assert_eq!(
- db.get_attestation_pool_status(25 /* expiration */, &KEYSTORE_UUID)?.expiring,
- 2
- );
- assert_eq!(
- db.get_attestation_pool_status(60 /* expiration */, &KEYSTORE_UUID)?.expiring,
- 3
- );
- let public_key: Vec<u8> = vec![0x01, 0x02, 0x03];
- let private_key: Vec<u8> = vec![0x04, 0x05, 0x06];
- let raw_public_key: Vec<u8> = vec![0x07, 0x08, 0x09];
- let cert_chain: Vec<u8> = vec![0x0a, 0x0b, 0x0c];
- let batch_cert: Vec<u8> = vec![0x0d, 0x0e, 0x0f];
- db.create_attestation_key_entry(
- &public_key,
- &raw_public_key,
- &private_key,
- &KEYSTORE_UUID,
- )?;
- status = db.get_attestation_pool_status(0 /* expiration */, &KEYSTORE_UUID)?;
- assert_eq!(status.attested, 3);
- assert_eq!(status.unassigned, 0);
- assert_eq!(status.total, 4);
- db.store_signed_attestation_certificate_chain(
- &raw_public_key,
- &batch_cert,
- &cert_chain,
- 20,
- &KEYSTORE_UUID,
- )?;
- status = db.get_attestation_pool_status(0 /* expiration */, &KEYSTORE_UUID)?;
- assert_eq!(status.attested, 4);
- assert_eq!(status.unassigned, 1);
- assert_eq!(status.total, 4);
- Ok(())
- }
-
- #[test]
- fn test_remove_expired_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 namespace: i64 = 30;
- let namespace_del1: i64 = 45;
- let namespace_del2: i64 = 60;
- let entry_values = load_attestation_key_pool(
- &mut db,
- expiration_date,
- namespace,
- 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)?;
-
- 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);
-
- assert_eq!(db.delete_expired_attestation_keys()?, 2);
-
- 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();
- 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());
-
- cert_chain = db.retrieve_attestation_key_and_cert_chain(
- Domain::APP,
- namespace_del1,
- &KEYSTORE_UUID,
- )?;
- assert!(cert_chain.is_none());
- cert_chain = db.retrieve_attestation_key_and_cert_chain(
- Domain::APP,
- namespace_del2,
- &KEYSTORE_UUID,
- )?;
- assert!(cert_chain.is_none());
-
- // 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 3 blob entries left, because we deleted two of the attestation
- // key entries with three blobs each.
- assert_eq!(blob_entry_row_count, 3);
-
- 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()?;
- load_attestation_key_pool(&mut db, 45 /* expiration */, 1 /* namespace */, 0x02)?;
- load_attestation_key_pool(&mut db, 80 /* expiration */, 2 /* namespace */, 0x03)?;
- db.create_key_entry(&Domain::APP, &42, KeyType::Client, &KEYSTORE_UUID)?;
- let result = db.delete_all_attestation_keys()?;
-
- // Give the garbage collector half a second to catch up.
- std::thread::sleep(Duration::from_millis(500));
-
- // Attestation keys should be deleted, and the regular key should remain.
- assert_eq!(result, 2);
-
- Ok(())
- }
-
- #[test]
fn test_rebind_alias() -> Result<()> {
fn extractor(
ke: &KeyEntryRow,
@@ -3962,15 +3378,9 @@ pub mod tests {
let mut stmt = db
.conn
.prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
- let mut rows =
- stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>(NO_PARAMS, |row| {
- Ok((
- row.get(0)?,
- row.get(1)?,
- row.get(2)?,
- KeyPermSet::from(row.get::<_, i32>(3)?),
- ))
- })?;
+ let mut rows = stmt.query_map::<(i64, u32, i64, KeyPermSet), _, _>([], |row| {
+ Ok((row.get(0)?, row.get(1)?, row.get(2)?, KeyPermSet::from(row.get::<_, i32>(3)?)))
+ })?;
let r = rows.next().unwrap().unwrap();
assert_eq!(r, (next_random, GRANTEE_UID, 1, PVEC1));
@@ -4014,7 +3424,7 @@ pub mod tests {
ORDER BY subcomponent_type ASC;",
)?;
let mut rows = stmt
- .query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>(NO_PARAMS, |row| {
+ .query_map::<((SubComponentType, i64, Vec<u8>), i64), _, _>([], |row| {
Ok(((row.get(0)?, row.get(1)?, row.get(2)?), row.get(3)?))
})?;
let (r, id) = rows.next().unwrap().unwrap();
@@ -5047,7 +4457,7 @@ pub mod tests {
})
.collect();
list_o_descriptors.sort();
- let mut list_result = db.list(*domain, *namespace, KeyType::Client)?;
+ let mut list_result = db.list_past_alias(*domain, *namespace, KeyType::Client, None)?;
list_result.sort();
assert_eq!(list_o_descriptors, list_result);
@@ -5077,7 +4487,10 @@ pub mod tests {
loaded_entries.sort_unstable();
assert_eq!(list_o_ids, loaded_entries);
}
- assert_eq!(Vec::<KeyDescriptor>::new(), db.list(Domain::SELINUX, 101, KeyType::Client)?);
+ assert_eq!(
+ Vec::<KeyDescriptor>::new(),
+ db.list_past_alias(Domain::SELINUX, 101, KeyType::Client, None)?
+ );
Ok(())
}
@@ -5112,7 +4525,7 @@ pub mod tests {
fn get_keyentry(db: &KeystoreDB) -> Result<Vec<KeyEntryRow>> {
db.conn
.prepare("SELECT * FROM persistent.keyentry;")?
- .query_map(NO_PARAMS, |row| {
+ .query_map([], |row| {
Ok(KeyEntryRow {
id: row.get(0)?,
key_type: row.get(1)?,
@@ -5127,39 +4540,17 @@ pub mod tests {
.collect::<Result<Vec<_>>>()
}
- struct RemoteProvValues {
- cert_chain: Vec<u8>,
- priv_key: Vec<u8>,
- batch_cert: Vec<u8>,
- }
-
- fn load_attestation_key_pool(
- db: &mut KeystoreDB,
- expiration_date: i64,
- namespace: i64,
- base_byte: u8,
- ) -> Result<RemoteProvValues> {
- let public_key: Vec<u8> = vec![base_byte, 0x02 * base_byte];
- let cert_chain: Vec<u8> = vec![0x03 * base_byte, 0x04 * base_byte];
- let priv_key: Vec<u8> = vec![0x05 * base_byte, 0x06 * base_byte];
- let raw_public_key: Vec<u8> = vec![0x0b * base_byte, 0x0c * base_byte];
- let batch_cert: Vec<u8> = vec![base_byte * 0x0d, base_byte * 0x0e];
- db.create_attestation_key_entry(&public_key, &raw_public_key, &priv_key, &KEYSTORE_UUID)?;
- db.store_signed_attestation_certificate_chain(
- &raw_public_key,
- &batch_cert,
- &cert_chain,
- expiration_date,
- &KEYSTORE_UUID,
- )?;
- db.assign_attestation_key(Domain::APP, namespace, &KEYSTORE_UUID)?;
- Ok(RemoteProvValues { cert_chain, priv_key, batch_cert })
+ fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+ make_test_params_with_sids(max_usage_count, &[42])
}
// Note: The parameters and SecurityLevel associations are nonsensical. This
// collection is only used to check if the parameters are preserved as expected by the
// database.
- fn make_test_params(max_usage_count: Option<i32>) -> Vec<KeyParameter> {
+ fn make_test_params_with_sids(
+ max_usage_count: Option<i32>,
+ user_secure_ids: &[i64],
+ ) -> Vec<KeyParameter> {
let mut params = vec![
KeyParameter::new(KeyParameterValue::Invalid, SecurityLevel::TRUSTED_ENVIRONMENT),
KeyParameter::new(
@@ -5258,7 +4649,6 @@ pub mod tests {
SecurityLevel::TRUSTED_ENVIRONMENT,
),
KeyParameter::new(KeyParameterValue::UserID(1), SecurityLevel::STRONGBOX),
- KeyParameter::new(KeyParameterValue::UserSecureID(42), SecurityLevel::STRONGBOX),
KeyParameter::new(
KeyParameterValue::NoAuthRequired,
SecurityLevel::TRUSTED_ENVIRONMENT,
@@ -5386,15 +4776,33 @@ pub mod tests {
SecurityLevel::SOFTWARE,
));
}
+
+ for sid in user_secure_ids.iter() {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UserSecureID(*sid),
+ SecurityLevel::STRONGBOX,
+ ));
+ }
params
}
- fn make_test_key_entry(
+ pub fn make_test_key_entry(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ alias: &str,
+ max_usage_count: Option<i32>,
+ ) -> Result<KeyIdGuard> {
+ make_test_key_entry_with_sids(db, domain, namespace, alias, max_usage_count, &[42])
+ }
+
+ pub fn make_test_key_entry_with_sids(
db: &mut KeystoreDB,
domain: Domain,
namespace: i64,
alias: &str,
max_usage_count: Option<i32>,
+ sids: &[i64],
) -> Result<KeyIdGuard> {
let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
let mut blob_metadata = BlobMetaData::new();
@@ -5413,7 +4821,7 @@ pub mod tests {
db.set_blob(&key_id, SubComponentType::CERT, Some(TEST_CERT_BLOB), None)?;
db.set_blob(&key_id, SubComponentType::CERT_CHAIN, Some(TEST_CERT_CHAIN_BLOB), None)?;
- let params = make_test_params(max_usage_count);
+ let params = make_test_params_with_sids(max_usage_count, sids);
db.insert_keyparameter(&key_id, &params)?;
let mut metadata = KeyMetaData::new();
@@ -5448,7 +4856,7 @@ pub mod tests {
}
}
- fn make_bootlevel_key_entry(
+ pub fn make_bootlevel_key_entry(
db: &mut KeystoreDB,
domain: Domain,
namespace: i64,
@@ -5483,6 +4891,53 @@ pub mod tests {
Ok(key_id)
}
+ // Creates an app key that is marked as being superencrypted by the given
+ // super key ID and that has the given authentication and unlocked device
+ // parameters. This does not actually superencrypt the key blob.
+ fn make_superencrypted_key_entry(
+ db: &mut KeystoreDB,
+ namespace: i64,
+ alias: &str,
+ requires_authentication: bool,
+ requires_unlocked_device: bool,
+ super_key_id: i64,
+ ) -> Result<KeyIdGuard> {
+ let domain = Domain::APP;
+ let key_id = db.create_key_entry(&domain, &namespace, KeyType::Client, &KEYSTORE_UUID)?;
+
+ let mut blob_metadata = BlobMetaData::new();
+ blob_metadata.add(BlobMetaEntry::KmUuid(KEYSTORE_UUID));
+ blob_metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::KeyId(super_key_id)));
+ db.set_blob(
+ &key_id,
+ SubComponentType::KEY_BLOB,
+ Some(TEST_KEY_BLOB),
+ Some(&blob_metadata),
+ )?;
+
+ let mut params = vec![];
+ if requires_unlocked_device {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UnlockedDeviceRequired,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ));
+ }
+ if requires_authentication {
+ params.push(KeyParameter::new(
+ KeyParameterValue::UserSecureID(42),
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ ));
+ }
+ db.insert_keyparameter(&key_id, &params)?;
+
+ let mut metadata = KeyMetaData::new();
+ metadata.add(KeyMetaEntry::CreationDate(DateTime::from_millis_epoch(123456789)));
+ db.insert_key_metadata(&key_id, &metadata)?;
+
+ rebind_alias(db, &key_id, alias, domain, namespace)?;
+ Ok(key_id)
+ }
+
fn make_bootlevel_test_key_entry_test_vector(key_id: i64, logical_only: bool) -> KeyEntry {
let mut params = make_test_params(None);
params.push(KeyParameter::new(KeyParameterValue::MaxBootLevel(3), SecurityLevel::KEYSTORE));
@@ -5513,7 +4968,7 @@ pub mod tests {
"SELECT id, key_type, domain, namespace, alias, state, km_uuid FROM persistent.keyentry;",
)?;
let rows = stmt.query_map::<(i64, KeyType, i32, i64, String, KeyLifeCycle, Uuid), _, _>(
- NO_PARAMS,
+ [],
|row| {
Ok((
row.get(0)?,
@@ -5542,7 +4997,7 @@ pub mod tests {
let mut stmt = db
.conn
.prepare("SELECT id, grantee, keyentryid, access_vector FROM persistent.grant;")?;
- let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>(NO_PARAMS, |row| {
+ let rows = stmt.query_map::<(i64, i64, i64, i64), _, _>([], |row| {
Ok((row.get(0)?, row.get(1)?, row.get(2)?, row.get(3)?))
})?;
@@ -5558,7 +5013,7 @@ pub mod tests {
// This allows us to test repeated elements.
thread_local! {
- static RANDOM_COUNTER: RefCell<i64> = RefCell::new(0);
+ static RANDOM_COUNTER: RefCell<i64> = const { RefCell::new(0) };
}
fn reset_random() {
@@ -5576,23 +5031,6 @@ pub mod tests {
}
#[test]
- fn test_last_off_body() -> Result<()> {
- let mut db = new_test_db()?;
- db.insert_last_off_body(MonotonicRawTime::now());
- let tx = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
- tx.commit()?;
- let last_off_body_1 = db.get_last_off_body();
- let one_second = Duration::from_secs(1);
- thread::sleep(one_second);
- db.update_last_off_body(MonotonicRawTime::now());
- let tx2 = db.conn.transaction_with_behavior(TransactionBehavior::Immediate)?;
- tx2.commit()?;
- let last_off_body_2 = db.get_last_off_body();
- assert!(last_off_body_1 < last_off_body_2);
- Ok(())
- }
-
- #[test]
fn test_unbind_keys_for_user() -> Result<()> {
let mut db = new_test_db()?;
db.unbind_keys_for_user(1, false)?;
@@ -5601,11 +5039,11 @@ pub mod tests {
make_test_key_entry(&mut db, Domain::APP, 110000, TEST_ALIAS, None)?;
db.unbind_keys_for_user(2, false)?;
- assert_eq!(1, db.list(Domain::APP, 110000, KeyType::Client)?.len());
- assert_eq!(0, db.list(Domain::APP, 210000, KeyType::Client)?.len());
+ assert_eq!(1, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
+ assert_eq!(0, db.list_past_alias(Domain::APP, 210000, KeyType::Client, None)?.len());
db.unbind_keys_for_user(1, true)?;
- assert_eq!(0, db.list(Domain::APP, 110000, KeyType::Client)?.len());
+ assert_eq!(0, db.list_past_alias(Domain::APP, 110000, KeyType::Client, None)?.len());
Ok(())
}
@@ -5621,11 +5059,13 @@ pub mod tests {
let key_name_enc = SuperKeyType {
alias: "test_super_key_1",
algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ name: "test_super_key_1",
};
let key_name_nonenc = SuperKeyType {
alias: "test_super_key_2",
algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ name: "test_super_key_2",
};
// Install two super keys.
@@ -5684,6 +5124,71 @@ pub mod tests {
Ok(())
}
+ fn app_key_exists(db: &mut KeystoreDB, nspace: i64, alias: &str) -> Result<bool> {
+ db.key_exists(Domain::APP, nspace, alias, KeyType::Client)
+ }
+
+ // Tests the unbind_auth_bound_keys_for_user() function.
+ #[test]
+ fn test_unbind_auth_bound_keys_for_user() -> Result<()> {
+ let mut db = new_test_db()?;
+ let user_id = 1;
+ let nspace: i64 = (user_id * AID_USER_OFFSET).into();
+ let other_user_id = 2;
+ let other_user_nspace: i64 = (other_user_id * AID_USER_OFFSET).into();
+ let super_key_type = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
+
+ // Create a superencryption key.
+ let super_key = keystore2_crypto::generate_aes256_key()?;
+ let pw: keystore2_crypto::Password = (&b"xyzabc"[..]).into();
+ let (encrypted_super_key, blob_metadata) =
+ SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
+ db.store_super_key(
+ user_id,
+ super_key_type,
+ &encrypted_super_key,
+ &blob_metadata,
+ &KeyMetaData::new(),
+ )?;
+ let super_key_id = db.load_super_key(super_key_type, user_id)?.unwrap().0 .0;
+
+ // Store 4 superencrypted app keys, one for each possible combination of
+ // (authentication required, unlocked device required).
+ make_superencrypted_key_entry(&mut db, nspace, "noauth_noud", false, false, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "noauth_ud", false, true, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "auth_noud", true, false, super_key_id)?;
+ make_superencrypted_key_entry(&mut db, nspace, "auth_ud", true, true, super_key_id)?;
+ assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
+ assert!(app_key_exists(&mut db, nspace, "auth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "auth_ud")?);
+
+ // Also store a key for a different user that requires authentication.
+ make_superencrypted_key_entry(
+ &mut db,
+ other_user_nspace,
+ "auth_ud",
+ true,
+ true,
+ super_key_id,
+ )?;
+
+ db.unbind_auth_bound_keys_for_user(user_id)?;
+
+ // Verify that only the user's app keys that require authentication were
+ // deleted. Keys that require an unlocked device but not authentication
+ // should *not* have been deleted, nor should the super key have been
+ // deleted, nor should other users' keys have been deleted.
+ assert!(db.load_super_key(super_key_type, user_id)?.is_some());
+ assert!(app_key_exists(&mut db, nspace, "noauth_noud")?);
+ assert!(app_key_exists(&mut db, nspace, "noauth_ud")?);
+ assert!(!app_key_exists(&mut db, nspace, "auth_noud")?);
+ assert!(!app_key_exists(&mut db, nspace, "auth_ud")?);
+ assert!(app_key_exists(&mut db, other_user_nspace, "auth_ud")?);
+
+ Ok(())
+ }
+
#[test]
fn test_store_super_key() -> Result<()> {
let mut db = new_test_db()?;
@@ -5697,18 +5202,23 @@ pub mod tests {
SuperKeyManager::encrypt_with_password(&super_key, &pw)?;
db.store_super_key(
1,
- &USER_SUPER_KEY,
+ &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
&encrypted_super_key,
&metadata,
&KeyMetaData::new(),
)?;
// Check if super key exists.
- assert!(db.key_exists(Domain::APP, 1, USER_SUPER_KEY.alias, KeyType::Super)?);
+ assert!(db.key_exists(
+ Domain::APP,
+ 1,
+ USER_AFTER_FIRST_UNLOCK_SUPER_KEY.alias,
+ KeyType::Super
+ )?);
- let (_, key_entry) = db.load_super_key(&USER_SUPER_KEY, 1)?.unwrap();
+ let (_, key_entry) = db.load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, 1)?.unwrap();
let loaded_super_key = SuperKeyManager::extract_super_key_from_key_entry(
- USER_SUPER_KEY.algorithm,
+ USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
key_entry,
&pw,
None,
@@ -5775,7 +5285,6 @@ pub mod tests {
for storage in increased_storage_types {
// Verify the expected storage increased.
let new = db.get_storage_stat(storage).unwrap();
- let storage = storage;
let old = &baseline[&storage.0];
assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
assert!(
@@ -5945,7 +5454,7 @@ pub mod tests {
// All three entries are in the database
assert_eq!(db.perboot.auth_tokens_len(), 3);
// It selected the most recent timestamp
- assert_eq!(db.find_auth_token_entry(|_| true).unwrap().0.auth_token.mac, b"mac2".to_vec());
+ assert_eq!(db.find_auth_token_entry(|_| true).unwrap().auth_token.mac, b"mac2".to_vec());
Ok(())
}
@@ -5964,4 +5473,111 @@ pub mod tests {
assert_eq!(db.load_key_descriptor(key_id + 1)?, None);
Ok(())
}
+
+ #[test]
+ fn test_get_list_app_uids_for_sid() -> Result<()> {
+ let uid: i32 = 1;
+ let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+ let first_sid = 667;
+ let second_sid = 669;
+ let first_app_id: i64 = 123 + uid_offset;
+ let second_app_id: i64 = 456 + uid_offset;
+ let third_app_id: i64 = 789 + uid_offset;
+ let unrelated_app_id: i64 = 1011 + uid_offset;
+ let mut db = new_test_db()?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ first_app_id,
+ TEST_ALIAS,
+ None,
+ &[first_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ "alias2",
+ None,
+ &[first_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ TEST_ALIAS,
+ None,
+ &[second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ third_app_id,
+ "alias3",
+ None,
+ &[second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ unrelated_app_id,
+ TEST_ALIAS,
+ None,
+ &[],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+
+ let mut first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+ first_sid_apps.sort();
+ assert_eq!(first_sid_apps, vec![first_app_id, second_app_id]);
+ let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+ second_sid_apps.sort();
+ assert_eq!(second_sid_apps, vec![second_app_id, third_app_id]);
+ Ok(())
+ }
+
+ #[test]
+ fn test_get_list_app_uids_with_multiple_sids() -> Result<()> {
+ let uid: i32 = 1;
+ let uid_offset: i64 = (uid as i64) * (AID_USER_OFFSET as i64);
+ let first_sid = 667;
+ let second_sid = 669;
+ let third_sid = 772;
+ let first_app_id: i64 = 123 + uid_offset;
+ let second_app_id: i64 = 456 + uid_offset;
+ let mut db = new_test_db()?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ first_app_id,
+ TEST_ALIAS,
+ None,
+ &[first_sid, second_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+ make_test_key_entry_with_sids(
+ &mut db,
+ Domain::APP,
+ second_app_id,
+ "alias2",
+ None,
+ &[second_sid, third_sid],
+ )
+ .context("test_get_list_app_uids_for_sid")?;
+
+ let first_sid_apps = db.get_app_uids_affected_by_sid(uid, first_sid)?;
+ assert_eq!(first_sid_apps, vec![first_app_id]);
+
+ let mut second_sid_apps = db.get_app_uids_affected_by_sid(uid, second_sid)?;
+ second_sid_apps.sort();
+ assert_eq!(second_sid_apps, vec![first_app_id, second_app_id]);
+
+ let third_sid_apps = db.get_app_uids_affected_by_sid(uid, third_sid)?;
+ assert_eq!(third_sid_apps, vec![second_app_id]);
+ Ok(())
+ }
}
diff --git a/keystore2/src/database/perboot.rs b/keystore2/src/database/perboot.rs
index 7ff35fa4..4727015f 100644
--- a/keystore2/src/database/perboot.rs
+++ b/keystore2/src/database/perboot.rs
@@ -13,15 +13,14 @@
// limitations under the License.
//! This module implements a per-boot, shared, in-memory storage of auth tokens
-//! and last-time-on-body for the main Keystore 2.0 database module.
+//! for the main Keystore 2.0 database module.
-use super::{AuthTokenEntry, MonotonicRawTime};
+use super::AuthTokenEntry;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
HardwareAuthToken::HardwareAuthToken, HardwareAuthenticatorType::HardwareAuthenticatorType,
};
use lazy_static::lazy_static;
use std::collections::HashSet;
-use std::sync::atomic::{AtomicI64, Ordering};
use std::sync::Arc;
use std::sync::RwLock;
@@ -62,17 +61,13 @@ impl PartialEq<AuthTokenEntryWrap> for AuthTokenEntryWrap {
impl Eq for AuthTokenEntryWrap {}
-/// Per-boot state structure. Currently only used to track auth tokens and
-/// last-off-body.
+/// Per-boot state structure. Currently only used to track auth tokens.
#[derive(Default)]
pub struct PerbootDB {
// We can use a .unwrap() discipline on this lock, because only panicking
// while holding a .write() lock will poison it. The only write usage is
// an insert call which inserts a pre-constructed pair.
auth_tokens: RwLock<HashSet<AuthTokenEntryWrap>>,
- // Ordering::Relaxed is appropriate for accessing this atomic, since it
- // does not currently need to be synchronized with anything else.
- last_off_body: AtomicI64,
}
lazy_static! {
@@ -102,14 +97,6 @@ impl PerbootDB {
matches.sort_by_key(|x| x.0.time_received);
matches.last().map(|x| x.0.clone())
}
- /// Get the last time the device was off the user's body
- pub fn get_last_off_body(&self) -> MonotonicRawTime {
- MonotonicRawTime(self.last_off_body.load(Ordering::Relaxed))
- }
- /// Set the last time the device was off the user's body
- pub fn set_last_off_body(&self, last_off_body: MonotonicRawTime) {
- self.last_off_body.store(last_off_body.0, Ordering::Relaxed)
- }
/// Return how many auth tokens are currently tracked.
pub fn auth_tokens_len(&self) -> usize {
self.auth_tokens.read().unwrap().len()
diff --git a/keystore2/src/database/versioning.rs b/keystore2/src/database/versioning.rs
index e3a95c8e..2c816f44 100644
--- a/keystore2/src/database/versioning.rs
+++ b/keystore2/src/database/versioning.rs
@@ -13,21 +13,19 @@
// limitations under the License.
use anyhow::{anyhow, Context, Result};
-use rusqlite::{params, OptionalExtension, Transaction, NO_PARAMS};
+use rusqlite::{params, OptionalExtension, Transaction};
pub fn create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u32> {
tx.execute(
"CREATE TABLE IF NOT EXISTS persistent.version (
id INTEGER PRIMARY KEY,
version INTEGER);",
- NO_PARAMS,
+ [],
)
.context("In create_or_get_version: Failed to create version table.")?;
let version = tx
- .query_row("SELECT version FROM persistent.version WHERE id = 0;", NO_PARAMS, |row| {
- row.get(0)
- })
+ .query_row("SELECT version FROM persistent.version WHERE id = 0;", [], |row| row.get(0))
.optional()
.context("In create_or_get_version: Failed to read version.")?;
@@ -44,7 +42,7 @@ pub fn create_or_get_version(tx: &Transaction, current_version: u32) -> Result<u
.query_row(
"SELECT name FROM persistent.sqlite_master
WHERE type = 'table' AND name = 'keyentry';",
- NO_PARAMS,
+ [],
|_| Ok(()),
)
.optional()
@@ -94,12 +92,12 @@ where
#[cfg(test)]
mod test {
use super::*;
- use rusqlite::{Connection, TransactionBehavior, NO_PARAMS};
+ use rusqlite::{Connection, TransactionBehavior};
#[test]
fn upgrade_database_test() {
let mut conn = Connection::open_in_memory().unwrap();
- conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+ conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", []).unwrap();
let upgraders: Vec<_> = (0..30_u32)
.map(move |i| {
@@ -125,19 +123,19 @@ mod test {
alias BLOB,
state INTEGER,
km_uuid BLOB);",
- NO_PARAMS,
+ [],
)
.unwrap();
}
for from in 1..29 {
for to in from..30 {
- conn.execute("DROP TABLE IF EXISTS persistent.version;", NO_PARAMS).unwrap();
- conn.execute("DROP TABLE IF EXISTS persistent.test;", NO_PARAMS).unwrap();
+ conn.execute("DROP TABLE IF EXISTS persistent.version;", []).unwrap();
+ conn.execute("DROP TABLE IF EXISTS persistent.test;", []).unwrap();
conn.execute(
"CREATE TABLE IF NOT EXISTS persistent.test (
id INTEGER PRIMARY KEY,
test_field INTEGER);",
- NO_PARAMS,
+ [],
)
.unwrap();
@@ -163,7 +161,7 @@ mod test {
to - from,
conn.query_row(
"SELECT COUNT(test_field) FROM persistent.test;",
- NO_PARAMS,
+ [],
|row| row.get(0)
)
.unwrap()
@@ -188,7 +186,7 @@ mod test {
#[test]
fn create_or_get_version_new_database() {
let mut conn = Connection::open_in_memory().unwrap();
- conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+ conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", []).unwrap();
{
let tx = conn.transaction_with_behavior(TransactionBehavior::Immediate).unwrap();
let version = create_or_get_version(&tx, 3).unwrap();
@@ -202,7 +200,7 @@ mod test {
conn.query_row(
"SELECT name FROM persistent.sqlite_master
WHERE type = 'table' AND name = 'version';",
- NO_PARAMS,
+ [],
|row| row.get(0),
)
);
@@ -210,18 +208,14 @@ mod test {
// There is exactly one row in the version table.
assert_eq!(
Ok(1),
- conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
- .get(0))
+ conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
);
// The version must be set to 3
assert_eq!(
Ok(3),
- conn.query_row(
- "SELECT version from persistent.version WHERE id = 0;",
- NO_PARAMS,
- |row| row.get(0)
- )
+ conn.query_row("SELECT version from persistent.version WHERE id = 0;", [], |row| row
+ .get(0))
);
// Will subsequent calls to create_or_get_version still return the same version even
@@ -236,8 +230,7 @@ mod test {
// There is still exactly one row in the version table.
assert_eq!(
Ok(1),
- conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
- .get(0))
+ conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
);
// Bump the version.
@@ -258,25 +251,21 @@ mod test {
// There is still exactly one row in the version table.
assert_eq!(
Ok(1),
- conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
- .get(0))
+ conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
);
// The version must be set to 5
assert_eq!(
Ok(5),
- conn.query_row(
- "SELECT version from persistent.version WHERE id = 0;",
- NO_PARAMS,
- |row| row.get(0)
- )
+ conn.query_row("SELECT version from persistent.version WHERE id = 0;", [], |row| row
+ .get(0))
);
}
#[test]
fn create_or_get_version_legacy_database() {
let mut conn = Connection::open_in_memory().unwrap();
- conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", NO_PARAMS).unwrap();
+ conn.execute("ATTACH DATABASE 'file::memory:' as persistent;", []).unwrap();
// A legacy (version 0) database is detected if the keyentry table exists but no
// version table.
conn.execute(
@@ -288,7 +277,7 @@ mod test {
alias BLOB,
state INTEGER,
km_uuid BLOB);",
- NO_PARAMS,
+ [],
)
.unwrap();
@@ -306,7 +295,7 @@ mod test {
conn.query_row(
"SELECT name FROM persistent.sqlite_master
WHERE type = 'table' AND name = 'version';",
- NO_PARAMS,
+ [],
|row| row.get(0),
)
);
@@ -314,18 +303,14 @@ mod test {
// There is exactly one row in the version table.
assert_eq!(
Ok(1),
- conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
- .get(0))
+ conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
);
// The version must be set to 0
assert_eq!(
Ok(0),
- conn.query_row(
- "SELECT version from persistent.version WHERE id = 0;",
- NO_PARAMS,
- |row| row.get(0)
- )
+ conn.query_row("SELECT version from persistent.version WHERE id = 0;", [], |row| row
+ .get(0))
);
// Will subsequent calls to create_or_get_version still return the same version even
@@ -340,8 +325,7 @@ mod test {
// There is still exactly one row in the version table.
assert_eq!(
Ok(1),
- conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
- .get(0))
+ conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
);
// Bump the version.
@@ -362,18 +346,14 @@ mod test {
// There is still exactly one row in the version table.
assert_eq!(
Ok(1),
- conn.query_row("SELECT COUNT(id) from persistent.version;", NO_PARAMS, |row| row
- .get(0))
+ conn.query_row("SELECT COUNT(id) from persistent.version;", [], |row| row.get(0))
);
// The version must be set to 5
assert_eq!(
Ok(5),
- conn.query_row(
- "SELECT version from persistent.version WHERE id = 0;",
- NO_PARAMS,
- |row| row.get(0)
- )
+ conn.query_row("SELECT version from persistent.version WHERE id = 0;", [], |row| row
+ .get(0))
);
}
}
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 8d5e9855..95dd026d 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -20,7 +20,7 @@ use crate::globals::{get_timestamp_service, ASYNC_TASK, DB, ENFORCEMENTS};
use crate::key_parameter::{KeyParameter, KeyParameterValue};
use crate::{authorization::Error as AuthzError, super_key::SuperEncryptionType};
use crate::{
- database::{AuthTokenEntry, MonotonicRawTime},
+ database::{AuthTokenEntry, BootTime},
globals::SUPER_KEY,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
@@ -51,9 +51,9 @@ enum AuthRequestState {
/// An outstanding per operation authorization request.
OpAuth,
/// An outstanding request for per operation authorization and secure timestamp.
- TimeStampedOpAuth(Receiver<Result<TimeStampToken, Error>>),
+ TimeStampedOpAuth(Mutex<Receiver<Result<TimeStampToken, Error>>>),
/// An outstanding request for a timestamp token.
- TimeStamp(Receiver<Result<TimeStampToken, Error>>),
+ TimeStamp(Mutex<Receiver<Result<TimeStampToken, Error>>>),
}
#[derive(Debug)]
@@ -64,8 +64,6 @@ struct AuthRequest {
hat: Mutex<Option<HardwareAuthToken>>,
}
-unsafe impl Sync for AuthRequest {}
-
impl AuthRequest {
fn op_auth() -> Arc<Self> {
Arc::new(Self { state: AuthRequestState::OpAuth, hat: Mutex::new(None) })
@@ -73,7 +71,7 @@ impl AuthRequest {
fn timestamped_op_auth(receiver: Receiver<Result<TimeStampToken, Error>>) -> Arc<Self> {
Arc::new(Self {
- state: AuthRequestState::TimeStampedOpAuth(receiver),
+ state: AuthRequestState::TimeStampedOpAuth(Mutex::new(receiver)),
hat: Mutex::new(None),
})
}
@@ -82,7 +80,10 @@ impl AuthRequest {
hat: HardwareAuthToken,
receiver: Receiver<Result<TimeStampToken, Error>>,
) -> Arc<Self> {
- Arc::new(Self { state: AuthRequestState::TimeStamp(receiver), hat: Mutex::new(Some(hat)) })
+ Arc::new(Self {
+ state: AuthRequestState::TimeStamp(Mutex::new(receiver)),
+ hat: Mutex::new(Some(hat)),
+ })
}
fn add_auth_token(&self, hat: HardwareAuthToken) {
@@ -100,7 +101,11 @@ impl AuthRequest {
let tst = match &self.state {
AuthRequestState::TimeStampedOpAuth(recv) | AuthRequestState::TimeStamp(recv) => {
- let result = recv.recv().context("In get_auth_tokens: Sender disconnected.")?;
+ let result = recv
+ .lock()
+ .unwrap()
+ .recv()
+ .context("In get_auth_tokens: Sender disconnected.")?;
Some(result.context(ks_err!(
"Worker responded with error \
from generating timestamp token.",
@@ -471,7 +476,6 @@ impl Enforcements {
let mut user_id: i32 = -1;
let mut user_secure_ids = Vec::<i64>::new();
let mut key_time_out: Option<i64> = None;
- let mut allow_while_on_body = false;
let mut unlocked_device_required = false;
let mut key_usage_limited: Option<i64> = None;
let mut confirmation_token_receiver: Option<Arc<Mutex<Option<Receiver<Vec<u8>>>>>> = None;
@@ -528,9 +532,6 @@ impl Enforcements {
KeyParameterValue::UnlockedDeviceRequired => {
unlocked_device_required = true;
}
- KeyParameterValue::AllowWhileOnBody => {
- allow_while_on_body = true;
- }
KeyParameterValue::UsageCountLimit(_) => {
// We don't examine the limit here because this is enforced on finish.
// Instead, we store the key_id so that finish can look up the key
@@ -598,6 +599,41 @@ impl Enforcements {
}
}
+ if android_security_flags::fix_unlocked_device_required_keys_v2() {
+ let (hat, state) = if user_secure_ids.is_empty() {
+ (None, DeferredAuthState::NoAuthRequired)
+ } else if let Some(key_time_out) = key_time_out {
+ let hat = Self::find_auth_token(|hat: &AuthTokenEntry| match user_auth_type {
+ Some(auth_type) => hat.satisfies(&user_secure_ids, auth_type),
+ None => false, // not reachable due to earlier check
+ })
+ .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ .context(ks_err!("No suitable auth token found."))?;
+ let now = BootTime::now();
+ let token_age = now
+ .checked_sub(&hat.time_received())
+ .ok_or_else(Error::sys)
+ .context(ks_err!(
+ "Overflow while computing Auth token validity. \
+ Validity cannot be established."
+ ))?;
+
+ if token_age.seconds() > key_time_out {
+ return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ .context(ks_err!("matching auth token is expired."));
+ }
+ let state = if requires_timestamp {
+ DeferredAuthState::TimeStampRequired(hat.auth_token().clone())
+ } else {
+ DeferredAuthState::NoAuthRequired
+ };
+ (Some(hat.take_auth_token()), state)
+ } else {
+ (None, DeferredAuthState::OpAuthRequired)
+ };
+ return Ok((hat, AuthInfo { state, key_usage_limited, confirmation_token_receiver }));
+ }
+
if !unlocked_device_required && no_auth_required {
return Ok((
None,
@@ -617,8 +653,8 @@ impl Enforcements {
let need_auth_token = timeout_bound || unlocked_device_required;
- let hat_and_last_off_body = if need_auth_token {
- let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| {
+ let hat = if need_auth_token {
+ let hat = Self::find_auth_token(|hat: &AuthTokenEntry| {
if let (Some(auth_type), true) = (user_auth_type, timeout_bound) {
hat.satisfies(&user_secure_ids, auth_type)
} else {
@@ -626,8 +662,7 @@ impl Enforcements {
}
});
Some(
- hat_and_last_off_body
- .ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
+ hat.ok_or(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
.context(ks_err!("No suitable auth token found."))?,
)
} else {
@@ -635,9 +670,9 @@ impl Enforcements {
};
// Now check the validity of the auth token if the key is timeout bound.
- let hat = match (hat_and_last_off_body, key_time_out) {
- (Some((hat, last_off_body)), Some(key_time_out)) => {
- let now = MonotonicRawTime::now();
+ let hat = match (hat, key_time_out) {
+ (Some(hat), Some(key_time_out)) => {
+ let now = BootTime::now();
let token_age = now
.checked_sub(&hat.time_received())
.ok_or_else(Error::sys)
@@ -646,15 +681,13 @@ impl Enforcements {
Validity cannot be established."
))?;
- let on_body_extended = allow_while_on_body && last_off_body < hat.time_received();
-
- if token_age.seconds() > key_time_out && !on_body_extended {
+ if token_age.seconds() > key_time_out {
return Err(Error::Km(Ec::KEY_USER_NOT_AUTHENTICATED))
.context(ks_err!("matching auth token is expired."));
}
Some(hat)
}
- (Some((hat, _)), None) => Some(hat),
+ (Some(hat), None) => Some(hat),
// If timeout_bound is true, above code must have retrieved a HAT or returned with
// KEY_USER_NOT_AUTHENTICATED. This arm should not be reachable.
(None, Some(_)) => panic!("Logical error."),
@@ -685,7 +718,7 @@ impl Enforcements {
})
}
- fn find_auth_token<F>(p: F) -> Option<(AuthTokenEntry, MonotonicRawTime)>
+ fn find_auth_token<F>(p: F) -> Option<AuthTokenEntry>
where
F: Fn(&AuthTokenEntry) -> bool,
{
@@ -769,10 +802,10 @@ impl Enforcements {
Candidate { priority: 3, enc_type: SuperEncryptionType::BootLevel(*level) }
}
KeyParameterValue::UnlockedDeviceRequired if *domain == Domain::APP => {
- Candidate { priority: 2, enc_type: SuperEncryptionType::ScreenLockBound }
+ Candidate { priority: 2, enc_type: SuperEncryptionType::UnlockedDeviceRequired }
}
KeyParameterValue::UserSecureID(_) if *domain == Domain::APP => {
- Candidate { priority: 1, enc_type: SuperEncryptionType::LskfBound }
+ Candidate { priority: 1, enc_type: SuperEncryptionType::AfterFirstUnlock }
}
_ => Candidate { priority: 0, enc_type: SuperEncryptionType::None },
};
@@ -805,12 +838,12 @@ impl Enforcements {
(challenge == hat.challenge()) && hat.satisfies(&sids, auth_type)
});
- let auth_token = if let Some((auth_token_entry, _)) = result {
+ let auth_token = if let Some(auth_token_entry) = result {
auth_token_entry.take_auth_token()
} else {
// Filter the matching auth tokens by age.
if auth_token_max_age_millis != 0 {
- let now_in_millis = MonotonicRawTime::now();
+ let now_in_millis = BootTime::now();
let result = Self::find_auth_token(|auth_token_entry: &AuthTokenEntry| {
let token_valid = now_in_millis
.checked_sub(&auth_token_entry.time_received())
@@ -820,7 +853,7 @@ impl Enforcements {
token_valid && auth_token_entry.satisfies(&sids, auth_type)
});
- if let Some((auth_token_entry, _)) = result {
+ if let Some(auth_token_entry) = result {
auth_token_entry.take_auth_token()
} else {
return Err(AuthzError::Rc(AuthzResponseCode::NO_AUTH_TOKEN_FOUND))
@@ -840,6 +873,20 @@ impl Enforcements {
get_timestamp_token(challenge).context(ks_err!("Error in getting timestamp token."))?;
Ok((auth_token, tst))
}
+
+ /// Finds the most recent received time for an auth token that matches the given secure user id and authenticator
+ pub fn get_last_auth_time(
+ &self,
+ secure_user_id: i64,
+ auth_type: HardwareAuthenticatorType,
+ ) -> Option<BootTime> {
+ let sids: Vec<i64> = vec![secure_user_id];
+
+ let result =
+ Self::find_auth_token(|entry: &AuthTokenEntry| entry.satisfies(&sids, auth_type));
+
+ result.map(|auth_token_entry| auth_token_entry.time_received())
+ }
}
// TODO: Add tests to enforcement module (b/175578618).
diff --git a/keystore2/src/entropy.rs b/keystore2/src/entropy.rs
index de381875..1dcdc86f 100644
--- a/keystore2/src/entropy.rs
+++ b/keystore2/src/entropy.rs
@@ -29,7 +29,7 @@ struct FeederInfo {
/// Register the entropy feeder as an idle callback.
pub fn register_feeder() {
crate::globals::ASYNC_TASK.add_idle(|shelf| {
- let mut info = shelf.get_mut::<FeederInfo>();
+ let info = shelf.get_mut::<FeederInfo>();
let now = Instant::now();
let feed_needed = match info.last_feed {
None => true,
diff --git a/keystore2/src/error.rs b/keystore2/src/error.rs
index 3ca3942a..f0d0d27b 100644
--- a/keystore2/src/error.rs
+++ b/keystore2/src/error.rs
@@ -13,29 +13,28 @@
// limitations under the License.
//! Keystore error provides convenience methods and types for Keystore error handling.
-//! Clients of Keystore expect one of two error codes, i.e., a Keystore ResponseCode as
-//! defined by the Keystore AIDL interface, or a Keymint ErrorCode as defined by
-//! the Keymint HAL specification.
-//! This crate provides `Error` which can wrap both. It is to be used
-//! internally by Keystore to diagnose error conditions that need to be reported to
-//! the client. To report the error condition to the client the Keystore AIDL
-//! interface defines a wire type `Result` which is distinctly different from Rust's
-//! `enum Result<T,E>`.
//!
-//! This crate provides the convenience method `map_or_log_err` to convert `anyhow::Error`
-//! into this wire type. In addition to handling the conversion of `Error`
-//! to the `Result` wire type it handles any other error by mapping it to
-//! `ResponseCode::SYSTEM_ERROR` and logs any error condition.
+//! Here are some important types and helper functions:
//!
-//! Keystore functions should use `anyhow::Result` to return error conditions, and
-//! context should be added every time an error is forwarded.
+//! `Error` type encapsulate Keystore, Keymint, and Binder errors. It is used internally by
+//! Keystore to diagnose error conditions that need to be reported to the client.
+//!
+//! `SerializedError` is used send error codes on the wire.
+//!
+//! `map_or_log_err` is a convenience method used to convert `anyhow::Error` into `SerializedError`
+//! wire type.
+//!
+//! Keystore functions should use `anyhow::Result` to return error conditions, and context should
+//! be added every time an error is forwarded.
pub use android_hardware_security_keymint::aidl::android::hardware::security::keymint::ErrorCode::ErrorCode;
+use android_security_rkp_aidl::aidl::android::security::rkp::IGetKeyCallback::ErrorCode::ErrorCode as GetKeyErrorCode;
pub use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
use android_system_keystore2::binder::{
ExceptionCode, Result as BinderResult, Status as BinderStatus, StatusCode,
};
use keystore2_selinux as selinux;
+use rkpd_client::Error as RkpdError;
use std::cmp::PartialEq;
use std::ffi::CString;
@@ -55,10 +54,6 @@ pub enum Error {
/// Wraps a Binder status code.
#[error("Binder transaction error {0:?}")]
BinderTransaction(StatusCode),
- /// Wraps a Remote Provisioning ErrorCode as defined by the IRemotelyProvisionedComponent
- /// AIDL interface spec.
- #[error("Error::Rp({0:?})")]
- Rp(ErrorCode),
}
impl Error {
@@ -73,6 +68,49 @@ impl Error {
}
}
+impl From<RkpdError> for Error {
+ fn from(e: RkpdError) -> Self {
+ match e {
+ RkpdError::RequestCancelled | RkpdError::GetRegistrationFailed => {
+ Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR)
+ }
+ RkpdError::GetKeyFailed(e) => {
+ let response_code = match e {
+ GetKeyErrorCode::ERROR_UNKNOWN => ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+ GetKeyErrorCode::ERROR_PERMANENT => ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+ GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY => {
+ ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY
+ }
+ GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH => {
+ ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE
+ }
+ _ => {
+ log::error!("Unexpected get key error from rkpd: {e:?}");
+ ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR
+ }
+ };
+ Error::Rc(response_code)
+ }
+ RkpdError::RetryableTimeout => Error::Rc(ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ RkpdError::StoreUpgradedKeyFailed | RkpdError::Timeout => {
+ Error::Rc(ResponseCode::SYSTEM_ERROR)
+ }
+ RkpdError::BinderTransaction(s) => Error::BinderTransaction(s),
+ }
+ }
+}
+
+/// Maps an `rkpd_client::Error` that is wrapped with an `anyhow::Error` to a keystore2 `Error`.
+pub fn wrapped_rkpd_error_to_ks_error(e: &anyhow::Error) -> Error {
+ match e.downcast_ref::<RkpdError>() {
+ Some(e) => Error::from(*e),
+ None => {
+ log::error!("Failed to downcast the anyhow::Error to rkpd_client::Error: {e:?}");
+ Error::Rc(ResponseCode::SYSTEM_ERROR)
+ }
+ }
+}
+
/// Helper function to map the binder status we get from calls into KeyMint
/// to a Keystore Error. We don't create an anyhow error here to make
/// it easier to evaluate KeyMint errors, which we must do in some cases, e.g.,
@@ -103,16 +141,6 @@ pub fn map_km_error<T>(r: BinderResult<T>) -> Result<T, Error> {
})
}
-/// Helper function to map the binder status we get from calls into a RemotelyProvisionedComponent
-/// to a Keystore Error. We don't create an anyhow error here to make
-/// it easier to evaluate service specific errors.
-pub fn map_rem_prov_error<T>(r: BinderResult<T>) -> Result<T, Error> {
- r.map_err(|s| match s.exception_code() {
- ExceptionCode::SERVICE_SPECIFIC => Error::Rp(ErrorCode(s.service_specific_error())),
- e_code => Error::Binder(e_code, 0),
- })
-}
-
/// This function is similar to map_km_error only that we don't expect
/// any KeyMint error codes, we simply preserve the exception code and optional
/// service specific exception.
@@ -140,14 +168,6 @@ pub fn map_binder_status_code<T>(r: Result<T, StatusCode>) -> Result<T, Error> {
///
/// All error conditions get logged by this function, except for KEY_NOT_FOUND error.
///
-/// All `Error::Rc(x)` and `Error::Km(x)` variants get mapped onto a service specific error
-/// code of x. This is possible because KeyMint `ErrorCode` errors are always negative and
-/// `ResponseCode` codes are always positive.
-/// `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).
@@ -214,9 +234,9 @@ where
result.map_or_else(
|e| {
let e = map_err(e);
- let rc = get_error_code(&e);
+ let rc = anyhow_error_to_serialized_error(&e);
Err(BinderStatus::new_service_specific_error(
- rc,
+ rc.0,
anyhow_error_to_cstring(&e).as_deref(),
))
},
@@ -224,22 +244,42 @@ where
)
}
-/// Returns the error code given a reference to the error
-pub fn get_error_code(e: &anyhow::Error) -> i32 {
+/// This type is used to send error codes on the wire.
+///
+/// Errors are squashed into one number space using following rules:
+/// - All Keystore and Keymint errors codes are identity mapped. It's possible because by
+/// convention Keystore `ResponseCode` errors are positive, and Keymint `ErrorCode` errors are
+/// negative.
+/// - `selinux::Error::PermissionDenied` is mapped to `ResponseCode::PERMISSION_DENIED`.
+/// - All other error conditions, e.g. Binder errors, are mapped to `ResponseCode::SYSTEM_ERROR`.
+///
+/// The type should be used to forward all error codes to clients of Keystore AIDL interface and to
+/// metrics events.
+#[derive(Debug, Copy, Clone, Eq, PartialEq, Ord, PartialOrd)]
+pub struct SerializedError(pub i32);
+
+/// Returns a SerializedError given a reference to Error.
+pub fn error_to_serialized_error(e: &Error) -> SerializedError {
+ match e {
+ Error::Rc(rcode) => SerializedError(rcode.0),
+ Error::Km(ec) => SerializedError(ec.0),
+ // Binder errors are reported as system error.
+ Error::Binder(_, _) | Error::BinderTransaction(_) => {
+ SerializedError(ResponseCode::SYSTEM_ERROR.0)
+ }
+ }
+}
+
+/// Returns a SerializedError given a reference to anyhow::Error.
+pub fn anyhow_error_to_serialized_error(e: &anyhow::Error) -> SerializedError {
let root_cause = e.root_cause();
match root_cause.downcast_ref::<Error>() {
- Some(Error::Rc(rcode)) => rcode.0,
- Some(Error::Km(ec)) => ec.0,
- Some(Error::Rp(_)) => ResponseCode::SYSTEM_ERROR.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
- }
+ Some(e) => error_to_serialized_error(e),
None => match root_cause.downcast_ref::<selinux::Error>() {
- Some(selinux::Error::PermissionDenied) => ResponseCode::PERMISSION_DENIED.0,
- _ => ResponseCode::SYSTEM_ERROR.0,
+ Some(selinux::Error::PermissionDenied) => {
+ SerializedError(ResponseCode::PERMISSION_DENIED.0)
+ }
+ _ => SerializedError(ResponseCode::SYSTEM_ERROR.0),
},
}
}
@@ -312,7 +352,7 @@ pub mod tests {
android_logger::init_once(
android_logger::Config::default()
.with_tag("keystore_error_tests")
- .with_min_level(log::Level::Debug),
+ .with_max_level(log::LevelFilter::Debug),
);
// All Error::Rc(x) get mapped on a service specific error
// code of x.
@@ -414,4 +454,35 @@ pub mod tests {
expected_error_string
);
}
+
+ #[test]
+ fn rkpd_error_is_in_sync_with_response_code() {
+ let error_mapping = [
+ (RkpdError::RequestCancelled, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (RkpdError::GetRegistrationFailed, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_UNKNOWN),
+ ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PERMANENT),
+ ResponseCode::OUT_OF_KEYS_PERMANENT_ERROR,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_PENDING_INTERNET_CONNECTIVITY),
+ ResponseCode::OUT_OF_KEYS_PENDING_INTERNET_CONNECTIVITY,
+ ),
+ (
+ RkpdError::GetKeyFailed(GetKeyErrorCode::ERROR_REQUIRES_SECURITY_PATCH),
+ ResponseCode::OUT_OF_KEYS_REQUIRES_SYSTEM_UPGRADE,
+ ),
+ (RkpdError::StoreUpgradedKeyFailed, ResponseCode::SYSTEM_ERROR),
+ (RkpdError::RetryableTimeout, ResponseCode::OUT_OF_KEYS_TRANSIENT_ERROR),
+ (RkpdError::Timeout, ResponseCode::SYSTEM_ERROR),
+ ];
+ for (rkpd_error, expected_response_code) in error_mapping {
+ let e: Error = rkpd_error.into();
+ assert_eq!(e, Error::Rc(expected_response_code));
+ }
+ }
} // mod tests
diff --git a/keystore2/src/fuzzers/Android.bp b/keystore2/src/fuzzers/Android.bp
index 9a2d98d1..7ddbfc09 100644
--- a/keystore2/src/fuzzers/Android.bp
+++ b/keystore2/src/fuzzers/Android.bp
@@ -20,13 +20,13 @@ rust_fuzz {
name: "keystore2_unsafe_fuzzer",
srcs: ["keystore2_unsafe_fuzzer.rs"],
rustlibs: [
+ "libarbitrary",
"libkeystore2",
- "libkeystore2_crypto_rust",
- "libkeystore2_vintf_rust",
"libkeystore2_aaid-rust",
"libkeystore2_apc_compat-rust",
+ "libkeystore2_crypto_rust",
+ "libkeystore2_hal_names_rust",
"libkeystore2_selinux",
- "libarbitrary",
],
fuzz_config: {
fuzz_on_haiku_device: true,
@@ -38,19 +38,18 @@ rust_fuzz {
},
}
-
rust_fuzz {
name: "authorization_service_fuzzer",
srcs: ["aidl-fuzzers/authorization_service_fuzzer.rs"],
rustlibs: [
+ "libbinder_random_parcel_rs",
+ "libbinder_rs",
"libkeystore2",
- "libkeystore2_crypto_rust",
- "libkeystore2_vintf_rust",
"libkeystore2_aaid-rust",
"libkeystore2_apc_compat-rust",
+ "libkeystore2_crypto_rust",
+ "libkeystore2_hal_names_rust",
"libkeystore2_selinux",
- "libbinder_rs",
- "libbinder_random_parcel_rs",
],
fuzz_config: {
fuzz_on_haiku_device: true,
@@ -58,7 +57,7 @@ rust_fuzz {
cc: [
"android-media-fuzzing-reports@google.com",
"smoreland@google.com",
- "waghpawan@google.com"
+ "waghpawan@google.com",
],
// Adds bugs to hotlist "AIDL fuzzers bugs" on buganizer
hotlists: ["4637097"],
diff --git a/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs b/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs
index c1b2098f..9f83b400 100644
--- a/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs
+++ b/keystore2/src/fuzzers/aidl-fuzzers/authorization_service_fuzzer.rs
@@ -16,11 +16,10 @@
#![allow(missing_docs)]
#![no_main]
-#[macro_use]
-extern crate libfuzzer_sys;
use binder_random_parcel_rs::fuzz_service;
use keystore2::authorization::AuthorizationManager;
+use libfuzzer_sys::fuzz_target;
fuzz_target!(|data: &[u8]| {
let authorization_service = AuthorizationManager::new_native_binder().unwrap_or_else(|e| {
diff --git a/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs b/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs
index 1a385e7e..fb4c9ad7 100644
--- a/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs
+++ b/keystore2/src/fuzzers/keystore2_unsafe_fuzzer.rs
@@ -14,10 +14,8 @@
//! Fuzzes unsafe APIs of libkeystore2 module
-#![feature(slice_internals)]
#![no_main]
-use core::slice::memchr;
use keystore2::{legacy_blob::LegacyBlobLoader, utils::ui_opts_2_compat};
use keystore2_aaid::get_aaid;
use keystore2_apc_compat::ApcHal;
@@ -27,8 +25,8 @@ use keystore2_crypto::{
ec_point_point_to_oct, ecdh_compute_key, generate_random_data, hkdf_expand, hkdf_extract,
hmac_sha256, parse_subject_from_certificate, Password, ZVec,
};
+use keystore2_hal_names::get_hidl_instances;
use keystore2_selinux::{check_access, getpidcon, setcon, Backend, Context, KeystoreKeyBackend};
-use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
use libfuzzer_sys::{arbitrary::Arbitrary, fuzz_target};
use std::{ffi::CString, sync::Arc};
@@ -37,7 +35,7 @@ const MAX_SIZE_MODIFIER: usize = 1024;
/// CString does not contain any internal 0 bytes
fn get_valid_cstring_data(data: &[u8]) -> &[u8] {
- match memchr::memchr(0, data) {
+ match data.iter().position(|&b| b == 0) {
Some(idx) => &data[0..idx],
None => data,
}
@@ -95,11 +93,6 @@ enum FuzzCommand<'a> {
minor_version: usize,
hidl_interface_name: &'a str,
},
- GetAidlInstances {
- aidl_package: &'a str,
- version: usize,
- aidl_interface_name: &'a str,
- },
GetAaid {
aaid_uid: u32,
},
@@ -151,7 +144,8 @@ fuzz_target!(|commands: Vec<FuzzCommand>| {
let _res = aes_gcm_encrypt(plaintext, key_aes_encrypt);
}
FuzzCommand::Password { pw, salt, key_length } => {
- let _res = Password::from(pw).derive_key(salt, key_length % MAX_SIZE_MODIFIER);
+ let _res =
+ Password::from(pw).derive_key_pbkdf2(salt, key_length % MAX_SIZE_MODIFIER);
}
FuzzCommand::HkdfExtract { hkdf_secret, hkdf_salt } => {
let _res = hkdf_extract(hkdf_secret, hkdf_salt);
@@ -191,9 +185,6 @@ fuzz_target!(|commands: Vec<FuzzCommand>| {
} => {
get_hidl_instances(hidl_package, major_version, minor_version, hidl_interface_name);
}
- FuzzCommand::GetAidlInstances { aidl_package, version, aidl_interface_name } => {
- get_aidl_instances(aidl_package, version, aidl_interface_name);
- }
FuzzCommand::GetAaid { aaid_uid } => {
let _res = get_aaid(aaid_uid);
}
diff --git a/keystore2/src/globals.rs b/keystore2/src/globals.rs
index ed595784..7ac1038d 100644
--- a/keystore2/src/globals.rs
+++ b/keystore2/src/globals.rs
@@ -16,33 +16,36 @@
//! database connections and connections to services that Keystore needs
//! to talk to.
-use crate::ks_err;
+use crate::async_task::AsyncTask;
use crate::gc::Gc;
+use crate::km_compat::{BacklevelKeyMintWrapper, KeyMintV1};
+use crate::ks_err;
use crate::legacy_blob::LegacyBlobLoader;
use crate::legacy_importer::LegacyImporter;
use crate::super_key::SuperKeyManager;
use crate::utils::watchdog as wd;
-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, KeyMintHardwareInfo::KeyMintHardwareInfo,
- SecurityLevel::SecurityLevel,
+ IKeyMintDevice::BpKeyMintDevice, IKeyMintDevice::IKeyMintDevice,
+ KeyMintHardwareInfo::KeyMintHardwareInfo, SecurityLevel::SecurityLevel,
+};
+use android_hardware_security_keymint::binder::{StatusCode, Strong};
+use android_hardware_security_rkp::aidl::android::hardware::security::keymint::{
+ IRemotelyProvisionedComponent::BpRemotelyProvisionedComponent,
+ IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
};
-use android_hardware_security_rkp::aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent::IRemotelyProvisionedComponent;
use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::{
- ISecureClock::ISecureClock,
+ ISecureClock::BpSecureClock, ISecureClock::ISecureClock,
};
-use android_hardware_security_keymint::binder::{StatusCode, Strong};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
use anyhow::{Context, Result};
+use binder::get_declared_instances;
use binder::FromIBinder;
-use keystore2_vintf::get_aidl_instances;
use lazy_static::lazy_static;
use std::sync::{Arc, Mutex, RwLock};
use std::{cell::RefCell, sync::Once};
@@ -65,7 +68,6 @@ pub fn create_thread_local_db() -> KeystoreDB {
DB_INIT.call_once(|| {
log::info!("Touching Keystore 2.0 database for this first time since boot.");
- db.insert_last_off_body(MonotonicRawTime::now());
log::info!("Calling cleanup leftovers.");
let n = db.cleanup_leftovers().expect("Failed to cleanup database on startup.");
if n != 0 {
@@ -134,26 +136,6 @@ impl<T: FromIBinder + ?Sized> Default for DevicesMap<T> {
}
}
-struct RemotelyProvisionedDevicesMap<T: FromIBinder + ?Sized> {
- devices_by_sec_level: HashMap<SecurityLevel, Strong<T>>,
-}
-
-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>> {
- self.devices_by_sec_level.get(sec_level).map(|dev| (*dev).clone())
- }
-
- fn insert(&mut self, sec_level: SecurityLevel, dev: Strong<T>) {
- self.devices_by_sec_level.insert(sec_level, dev);
- }
-}
-
lazy_static! {
/// The path where keystore stores all its keys.
pub static ref DB_PATH: RwLock<PathBuf> = RwLock::new(
@@ -164,10 +146,6 @@ lazy_static! {
static ref KEY_MINT_DEVICES: Mutex<DevicesMap<dyn IKeyMintDevice>> = Default::default();
/// Timestamp service.
static ref TIME_STAMP_DEVICE: Mutex<Option<Strong<dyn ISecureClock>>> = Default::default();
- /// RemotelyProvisionedComponent HAL devices.
- static ref REMOTELY_PROVISIONED_COMPONENT_DEVICES:
- Mutex<RemotelyProvisionedDevicesMap<dyn IRemotelyProvisionedComponent>> =
- 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();
@@ -198,43 +176,37 @@ 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)>> {
- let keymint_instances =
- get_aidl_instances("android.hardware.security.keymint", version as usize, "IKeyMintDevice");
+/// gotten by binder service from the device and determining what services
+/// are available.
+fn keymint_service_name(security_level: &SecurityLevel) -> Result<Option<String>> {
+ let keymint_descriptor: &str = <BpKeyMintDevice as IKeyMintDevice>::get_descriptor();
+ let keymint_instances = get_declared_instances(keymint_descriptor).unwrap();
let service_name = match *security_level {
SecurityLevel::TRUSTED_ENVIRONMENT => {
if keymint_instances.iter().any(|instance| *instance == "default") {
- Some(format!("{}/default", KEYMINT_SERVICE_NAME))
+ Some(format!("{}/default", keymint_descriptor))
} else {
None
}
}
SecurityLevel::STRONGBOX => {
if keymint_instances.iter().any(|instance| *instance == "strongbox") {
- Some(format!("{}/strongbox", KEYMINT_SERVICE_NAME))
+ Some(format!("{}/strongbox", keymint_descriptor))
} else {
None
}
}
_ => {
return Err(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE)).context(ks_err!(
- "Trying to find keymint V{} for security level: {:?}",
- version,
+ "Trying to find keymint for security level: {:?}",
security_level
));
}
};
- Ok(service_name.map(|service_name| (version, service_name)))
+ Ok(service_name)
}
/// Make a new connection to a KeyMint device of the given security level.
@@ -243,28 +215,22 @@ fn keymint_service_name_by_version(
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(ks_err!())?;
+ // Show the keymint interface that is registered in the binder
+ // service and use the security level to get the service name.
+ let service_name = keymint_service_name(security_level)
+ .context(ks_err!("Get service name from binder service"))?;
- let (keymint, hal_version) = if let Some((version, service_name)) = service_name {
+ let (keymint, hal_version) = if let Some(service_name) = service_name {
let km: Strong<dyn IKeyMintDevice> =
map_binder_status_code(binder::get_interface(&service_name))
.context(ks_err!("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
+ // - V3 is 300
// etc.
- (km, Some(version * 100))
+ let km_version = km.getInterfaceVersion()?;
+ (km, Some(km_version * 100))
} else {
// This is a no-op if it was called before.
keystore2_km_compat::add_keymint_device_service();
@@ -280,7 +246,11 @@ fn connect_keymint(
}
e => e,
})
- .context(ks_err!("Trying to get Legacy wrapper."))?,
+ .context(ks_err!(
+ "Trying to get Legacy wrapper. Attempt to get keystore \
+ compat service for security level {:?}",
+ *security_level
+ ))?,
None,
)
};
@@ -288,8 +258,17 @@ 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(300) => {
+ // Current KeyMint version: use as-is as v3 Keymint is current version
+ log::info!(
+ "KeyMint device is current version ({:?}) for security level: {:?}",
+ hal_version,
+ security_level
+ );
+ keymint
+ }
Some(200) => {
- // Current KeyMint version: use as-is.
+ // Previous KeyMint version: use as-is as we don't have any software emulation of v3-specific KeyMint features.
log::info!(
"KeyMint device is current version ({:?}) for security level: {:?}",
hal_version,
@@ -359,7 +338,8 @@ pub fn get_keymint_device(
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(ks_err!())?;
+ let (dev, hw_info) =
+ connect_keymint(security_level).context(ks_err!("Cannot connect to Keymint"))?;
devices_map.insert(*security_level, dev, hw_info);
// Unwrap must succeed because we just inserted it.
Ok(devices_map.dev_by_sec_level(security_level).unwrap())
@@ -386,19 +366,17 @@ pub fn get_keymint_devices() -> Vec<Strong<dyn IKeyMintDevice>> {
KEY_MINT_DEVICES.lock().unwrap().devices()
}
-static TIME_STAMP_SERVICE_NAME: &str = "android.hardware.security.secureclock.ISecureClock";
-
/// 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>> {
- let secureclock_instances =
- get_aidl_instances("android.hardware.security.secureclock", 1, "ISecureClock");
+ let secure_clock_descriptor: &str = <BpSecureClock as ISecureClock>::get_descriptor();
+ let secureclock_instances = get_declared_instances(secure_clock_descriptor).unwrap();
let secure_clock_available =
secureclock_instances.iter().any(|instance| *instance == "default");
- let default_time_stamp_service_name = format!("{}/default", TIME_STAMP_SERVICE_NAME);
+ let default_time_stamp_service_name = format!("{}/default", secure_clock_descriptor);
let secureclock = if secure_clock_available {
map_binder_status_code(binder::get_interface(&default_time_stamp_service_name))
@@ -419,7 +397,7 @@ fn connect_secureclock() -> Result<Strong<dyn ISecureClock>> {
}
e => e,
})
- .context(ks_err!("Trying to get Legacy wrapper."))
+ .context(ks_err!("Failed attempt to get legacy secure clock."))
}?;
Ok(secureclock)
@@ -438,25 +416,23 @@ pub fn get_timestamp_service() -> Result<Strong<dyn ISecureClock>> {
}
}
-static REMOTE_PROVISIONING_HAL_SERVICE_NAME: &str =
- "android.hardware.security.keymint.IRemotelyProvisionedComponent";
-
/// Get the service name of a remotely provisioned component corresponding to given security level.
pub fn get_remotely_provisioned_component_name(security_level: &SecurityLevel) -> Result<String> {
- let remotely_prov_instances =
- get_aidl_instances("android.hardware.security.keymint", 1, "IRemotelyProvisionedComponent");
+ let remote_prov_descriptor: &str =
+ <BpRemotelyProvisionedComponent as IRemotelyProvisionedComponent>::get_descriptor();
+ let remotely_prov_instances = get_declared_instances(remote_prov_descriptor).unwrap();
match *security_level {
SecurityLevel::TRUSTED_ENVIRONMENT => {
if remotely_prov_instances.iter().any(|instance| *instance == "default") {
- Some(format!("{}/default", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
+ Some(format!("{}/default", remote_prov_descriptor))
} else {
None
}
}
SecurityLevel::STRONGBOX => {
if remotely_prov_instances.iter().any(|instance| *instance == "strongbox") {
- Some(format!("{}/strongbox", REMOTE_PROVISIONING_HAL_SERVICE_NAME))
+ Some(format!("{}/strongbox", remote_prov_descriptor))
} else {
None
}
@@ -464,31 +440,5 @@ pub fn get_remotely_provisioned_component_name(security_level: &SecurityLevel) -
_ => None,
}
.ok_or(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE))
- .context(ks_err!())
-}
-
-fn connect_remotely_provisioned_component(
- security_level: &SecurityLevel,
-) -> Result<Strong<dyn IRemotelyProvisionedComponent>> {
- let service_name = get_remotely_provisioned_component_name(security_level)?;
- let rem_prov_hal: Strong<dyn IRemotelyProvisionedComponent> =
- map_binder_status_code(binder::get_interface(&service_name))
- .context(ks_err!("Trying to connect to RemotelyProvisionedComponent service."))?;
- Ok(rem_prov_hal)
-}
-
-/// 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>> {
- let mut devices_map = REMOTELY_PROVISIONED_COMPONENT_DEVICES.lock().unwrap();
- if let Some(dev) = devices_map.dev_by_sec_level(security_level) {
- Ok(dev)
- } else {
- let dev = connect_remotely_provisioned_component(security_level).context(ks_err!())?;
- devices_map.insert(*security_level, dev);
- // Unwrap must succeed because we just inserted it.
- Ok(devices_map.dev_by_sec_level(security_level).unwrap())
- }
+ .context(ks_err!("Failed to get rpc for sec level {:?}", *security_level))
}
diff --git a/keystore2/src/vintf/Android.bp b/keystore2/src/hal_instance_names/Android.bp
index 34719aaa..2f1d5c3d 100644
--- a/keystore2/src/vintf/Android.bp
+++ b/keystore2/src/hal_instance_names/Android.bp
@@ -22,41 +22,41 @@ package {
}
rust_library {
- name: "libkeystore2_vintf_rust",
- crate_name: "keystore2_vintf",
+ name: "libkeystore2_hal_names_rust",
+ crate_name: "keystore2_hal_names",
srcs: ["lib.rs"],
rustlibs: [
"libcxx",
],
shared_libs: [
- "libvintf",
+ "libhidlbase",
],
static_libs: [
- "libkeystore2_vintf_cpp",
+ "libkeystore2_hal_names_cpp",
],
}
cc_library_static {
- name: "libkeystore2_vintf_cpp",
- srcs: ["vintf.cpp"],
+ name: "libkeystore2_hal_names_cpp",
+ srcs: ["hal_names.cpp"],
generated_headers: ["cxx-bridge-header"],
- generated_sources: ["vintf_bridge_code"],
+ generated_sources: ["hal_names_bridge_code"],
shared_libs: [
- "libvintf",
+ "libhidlbase",
],
}
genrule {
- name: "vintf_bridge_code",
+ name: "hal_names_bridge_code",
tools: ["cxxbridge"],
cmd: "$(location cxxbridge) $(in) >> $(out)",
srcs: ["lib.rs"],
- out: ["vintf_cxx_generated.cc"],
+ out: ["hal_names_cxx_generated.cc"],
}
rust_test {
- name: "keystore2_vintf_test",
- crate_name: "keystore2_vintf_test",
+ name: "keystore2_hal_names_test",
+ crate_name: "keystore2_hal_names_test",
srcs: ["lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
@@ -64,10 +64,10 @@ rust_test {
"libcxx",
],
static_libs: [
- "libkeystore2_vintf_cpp",
+ "libkeystore2_hal_names_cpp",
],
shared_libs: [
"libc++",
- "libvintf",
+ "libhidlbase",
],
}
diff --git a/keystore2/src/hal_instance_names/hal_names.cpp b/keystore2/src/hal_instance_names/hal_names.cpp
new file mode 100644
index 00000000..316c26c2
--- /dev/null
+++ b/keystore2/src/hal_instance_names/hal_names.cpp
@@ -0,0 +1,35 @@
+/*
+ * 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 <hidl/ServiceManagement.h>
+
+#include "rust/cxx.h"
+
+rust::Vec<rust::String> convert(const std::vector<std::string>& names) {
+ rust::Vec<rust::String> result;
+ std::copy(names.begin(), names.end(), std::back_inserter(result));
+ return result;
+}
+
+rust::Vec<rust::String> get_hidl_instances(rust::Str package, size_t major_version,
+ size_t minor_version, rust::Str interfaceName) {
+ std::string version = std::to_string(major_version) + "." + std::to_string(minor_version);
+ std::string factoryName = static_cast<std::string>(package) + "@" + version +
+ "::" + static_cast<std::string>(interfaceName);
+
+ const auto halNames = android::hardware::getAllHalInstanceNames(factoryName);
+ return convert(halNames);
+}
diff --git a/keystore2/src/vintf/vintf.hpp b/keystore2/src/hal_instance_names/hal_names.hpp
index c4a7ef60..ef1e7882 100644
--- a/keystore2/src/vintf/vintf.hpp
+++ b/keystore2/src/hal_instance_names/hal_names.hpp
@@ -20,5 +20,3 @@
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);
diff --git a/keystore2/src/vintf/lib.rs b/keystore2/src/hal_instance_names/lib.rs
index 08384bd4..36a9c4f9 100644
--- a/keystore2/src/vintf/lib.rs
+++ b/keystore2/src/hal_instance_names/lib.rs
@@ -17,7 +17,7 @@
#[cxx::bridge]
mod ffi {
unsafe extern "C++" {
- include!("vintf.hpp");
+ include!("hal_names.hpp");
/// 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.
@@ -27,10 +27,6 @@ mod ffi {
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>;
}
}
diff --git a/keystore2/src/id_rotation.rs b/keystore2/src/id_rotation.rs
index 460caa77..59040476 100644
--- a/keystore2/src/id_rotation.rs
+++ b/keystore2/src/id_rotation.rs
@@ -26,7 +26,7 @@ use anyhow::{Context, Result};
use std::fs;
use std::io::ErrorKind;
use std::path::{Path, PathBuf};
-use std::time::Duration;
+use std::time::{Duration, SystemTime};
const ID_ROTATION_PERIOD: Duration = Duration::from_secs(30 * 24 * 60 * 60); // Thirty days.
static TIMESTAMP_FILE_NAME: &str = "timestamp";
@@ -36,6 +36,8 @@ static TIMESTAMP_FILE_NAME: &str = "timestamp";
/// and passed down to the users of the feature which can then query the timestamp on demand.
#[derive(Debug, Clone)]
pub struct IdRotationState {
+ /// We consider the time of last factory reset to be the point in time when this timestamp file
+ /// is created.
timestamp_path: PathBuf,
}
@@ -47,25 +49,41 @@ impl IdRotationState {
Self { timestamp_path }
}
- /// Reads the metadata of or creates the timestamp file. It returns true if the timestamp
- /// file is younger than `ID_ROTATION_PERIOD`, i.e., 30 days.
- pub fn had_factory_reset_since_id_rotation(&self) -> Result<bool> {
+ /// Returns true iff a factory reset has occurred since the last ID rotation.
+ pub fn had_factory_reset_since_id_rotation(
+ &self,
+ creation_datetime: &SystemTime,
+ ) -> Result<bool> {
match fs::metadata(&self.timestamp_path) {
Ok(metadata) => {
- let duration_since_factory_reset = metadata
- .modified()
- .context("File creation time not supported.")?
- .elapsed()
- .context("Failed to compute time elapsed since factory reset.")?;
- Ok(duration_since_factory_reset < ID_ROTATION_PERIOD)
+ // For Tag::UNIQUE_ID, temporal counter value is defined as Tag::CREATION_DATETIME
+ // divided by 2592000000, dropping any remainder. Temporal counter value is
+ // effectively the index of the ID rotation period that we are currently in, with
+ // each ID rotation period being 30 days.
+ let temporal_counter_value = creation_datetime
+ .duration_since(SystemTime::UNIX_EPOCH)
+ .context(ks_err!("Failed to get epoch time"))?
+ .as_millis()
+ / ID_ROTATION_PERIOD.as_millis();
+
+ // Calculate the beginning of the current ID rotation period, which is also the
+ // last time ID was rotated.
+ let id_rotation_time: SystemTime = SystemTime::UNIX_EPOCH
+ .checked_add(ID_ROTATION_PERIOD * temporal_counter_value.try_into()?)
+ .context(ks_err!("Failed to get ID rotation time."))?;
+
+ let factory_reset_time =
+ metadata.modified().context(ks_err!("File creation time not supported."))?;
+
+ Ok(id_rotation_time <= factory_reset_time)
}
Err(e) => match e.kind() {
ErrorKind::NotFound => {
fs::File::create(&self.timestamp_path)
- .context("Failed to create timestamp file.")?;
+ .context(ks_err!("Failed to create timestamp file."))?;
Ok(true)
}
- _ => Err(e).context("Failed to open timestamp file."),
+ _ => Err(e).context(ks_err!("Failed to open timestamp file.")),
},
}
.context(ks_err!())
@@ -78,47 +96,75 @@ mod test {
use keystore2_test_utils::TempDir;
use nix::sys::stat::utimes;
use nix::sys::time::{TimeVal, TimeValLike};
- use std::convert::TryInto;
- use std::time::UNIX_EPOCH;
+ use std::thread::sleep;
- #[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.");
+ static TEMP_DIR_NAME: &str = "test_had_factory_reset_since_id_rotation_";
+
+ fn set_up() -> (TempDir, PathBuf, IdRotationState) {
+ let temp_dir = TempDir::new(TEMP_DIR_NAME).expect("Failed to create temp dir.");
+ let mut timestamp_file_path = temp_dir.path().to_owned();
+ timestamp_file_path.push(TIMESTAMP_FILE_NAME);
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);
+ (temp_dir, timestamp_file_path, id_rotation_state)
+ }
+
+ #[test]
+ fn test_timestamp_creation() {
+ let (_temp_dir, timestamp_file_path, id_rotation_state) = set_up();
+ let creation_datetime = SystemTime::now();
// The timestamp file should not exist.
- assert!(!temp_file_path.exists());
+ assert!(!timestamp_file_path.exists());
- // This should return true.
- assert!(id_rotation_state.had_factory_reset_since_id_rotation()?);
+ // Trigger timestamp file creation one second later.
+ sleep(Duration::new(1, 0));
+ assert!(id_rotation_state.had_factory_reset_since_id_rotation(&creation_datetime).unwrap());
// Now the timestamp file should exist.
- assert!(temp_file_path.exists());
-
- // We should still return true because the timestamp file is young.
- assert!(id_rotation_state.had_factory_reset_since_id_rotation()?);
+ assert!(timestamp_file_path.exists());
- // Now let's age the timestamp file by backdating the modification time.
- let metadata = fs::metadata(&temp_file_path)?;
- let mtime = metadata.modified()?;
- let mtime = mtime.duration_since(UNIX_EPOCH)?;
- let mtime =
- mtime.checked_sub(ID_ROTATION_PERIOD).expect("Failed to subtract id rotation period");
- let mtime = TimeVal::seconds(mtime.as_secs().try_into().unwrap());
-
- let atime = metadata.accessed()?;
- let atime = atime.duration_since(UNIX_EPOCH)?;
- let atime = TimeVal::seconds(atime.as_secs().try_into().unwrap());
-
- utimes(&temp_file_path, &atime, &mtime)?;
-
- // Now that the file has aged we should see false.
- assert!(!id_rotation_state.had_factory_reset_since_id_rotation()?);
+ let metadata = fs::metadata(&timestamp_file_path).unwrap();
+ assert!(metadata.modified().unwrap() > creation_datetime);
+ }
- Ok(())
+ #[test]
+ fn test_existing_timestamp() {
+ let (_temp_dir, timestamp_file_path, id_rotation_state) = set_up();
+
+ // Let's start with at a known point in time, so that it's easier to control which ID
+ // rotation period we're in.
+ let mut creation_datetime = SystemTime::UNIX_EPOCH;
+
+ // Create timestamp file and backdate it back to Unix epoch.
+ fs::File::create(&timestamp_file_path).unwrap();
+ let mtime = TimeVal::seconds(0);
+ let atime = TimeVal::seconds(0);
+ utimes(&timestamp_file_path, &atime, &mtime).unwrap();
+
+ // Timestamp file was backdated to the very beginning of the current ID rotation period.
+ // So, this should return true.
+ assert!(id_rotation_state.had_factory_reset_since_id_rotation(&creation_datetime).unwrap());
+
+ // Move time forward, but stay in the same ID rotation period.
+ creation_datetime += Duration::from_millis(1);
+
+ // We should still return true because we're in the same ID rotation period.
+ assert!(id_rotation_state.had_factory_reset_since_id_rotation(&creation_datetime).unwrap());
+
+ // Move time to the next ID rotation period.
+ creation_datetime += ID_ROTATION_PERIOD;
+
+ // Now we should see false.
+ assert!(!id_rotation_state
+ .had_factory_reset_since_id_rotation(&creation_datetime)
+ .unwrap());
+
+ // Move timestamp to the future. This shouldn't ever happen, but even in this edge case ID
+ // must be rotated.
+ let mtime = TimeVal::seconds((ID_ROTATION_PERIOD.as_secs() * 10).try_into().unwrap());
+ let atime = TimeVal::seconds((ID_ROTATION_PERIOD.as_secs() * 10).try_into().unwrap());
+ utimes(&timestamp_file_path, &atime, &mtime).unwrap();
+ assert!(id_rotation_state.had_factory_reset_since_id_rotation(&creation_datetime).unwrap());
}
}
diff --git a/keystore2/src/key_parameter.rs b/keystore2/src/key_parameter.rs
index b3dcf45e..bd452073 100644
--- a/keystore2/src/key_parameter.rs
+++ b/keystore2/src/key_parameter.rs
@@ -837,6 +837,11 @@ pub enum KeyParameterValue {
#[serde(serialize_with = "serialize_primitive")]
#[key_param(tag = DIGEST, field = Digest)]
Digest(Digest),
+ /// Digest algorithms that can be used for MGF in RSA-OAEP.
+ #[serde(deserialize_with = "deserialize_primitive")]
+ #[serde(serialize_with = "serialize_primitive")]
+ #[key_param(tag = RSA_OAEP_MGF_DIGEST, field = Digest)]
+ RsaOaepMgfDigest(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")]
@@ -907,7 +912,8 @@ pub enum KeyParameterValue {
/// The time in seconds for which the key is authorized for use, after user authentication
#[key_param(tag = AUTH_TIMEOUT, field = Integer)]
AuthTimeout(i32),
- /// The key may be used after authentication timeout if device is still on-body
+ /// The key's authentication timeout, if it has one, is automatically expired when the device is
+ /// removed from the user's body. No longer implemented; this tag is no longer enforced.
#[key_param(tag = ALLOW_WHILE_ON_BODY, field = BoolValue)]
AllowWhileOnBody,
/// The key must be unusable except when the user has provided proof of physical presence
@@ -1098,6 +1104,7 @@ mod generated_key_parameter_tests {
Tag::BLOCK_MODE => return KmKeyParameterValue::BlockMode(Default::default()),
Tag::PADDING => return KmKeyParameterValue::PaddingMode(Default::default()),
Tag::DIGEST => return KmKeyParameterValue::Digest(Default::default()),
+ Tag::RSA_OAEP_MGF_DIGEST => return KmKeyParameterValue::Digest(Default::default()),
Tag::EC_CURVE => return KmKeyParameterValue::EcCurve(Default::default()),
Tag::ORIGIN => return KmKeyParameterValue::Origin(Default::default()),
Tag::PURPOSE => return KmKeyParameterValue::KeyPurpose(Default::default()),
@@ -1210,7 +1217,7 @@ mod storage_tests {
use crate::key_parameter::*;
use anyhow::Result;
use rusqlite::types::ToSql;
- use rusqlite::{params, Connection, NO_PARAMS};
+ use rusqlite::{params, Connection};
/// Test initializing a KeyParameter (with key parameter value corresponding to an enum of i32)
/// from a database table row.
@@ -1417,7 +1424,7 @@ mod storage_tests {
tag INTEGER,
data ANY,
security_level INTEGER);",
- NO_PARAMS,
+ [],
)
.context("Failed to initialize \"keyparameter\" table.")?;
Ok(db)
@@ -1453,7 +1460,7 @@ mod storage_tests {
fn query_from_keyparameter(db: &Connection) -> Result<KeyParameter> {
let mut stmt =
db.prepare("SELECT tag, data, security_level FROM persistent.keyparameter")?;
- let mut rows = stmt.query(NO_PARAMS)?;
+ let mut rows = stmt.query([])?;
let row = rows.next()?.unwrap();
KeyParameter::new_from_sql(
Tag(row.get(0)?),
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index 8c5bf0f6..178b36c7 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -19,9 +19,6 @@ use keystore2::globals::ENFORCEMENTS;
use keystore2::maintenance::Maintenance;
use keystore2::metrics::Metrics;
use keystore2::metrics_store;
-use keystore2::remote_provisioning::{
- RemoteProvisioningService, RemotelyProvisionedKeyPoolService,
-};
use keystore2::service::KeystoreService;
use keystore2::{apc::ApcManager, shared_secret_negotiation};
use keystore2::{authorization::AuthorizationManager, id_rotation::IdRotationState};
@@ -34,9 +31,6 @@ static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/defau
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";
@@ -46,8 +40,8 @@ fn main() {
android_logger::init_once(
android_logger::Config::default()
.with_tag("keystore2")
- .with_min_level(log::Level::Debug)
- .with_log_id(android_logger::LogId::System)
+ .with_max_level(log::LevelFilter::Debug)
+ .with_log_buffer(android_logger::LogId::System)
.format(|buf, record| {
writeln!(
buf,
@@ -74,6 +68,8 @@ fn main() {
fn sqlite_log_handler(err: c_int, message: &str) {
log::error!("[SQLITE3] {}: {}", err, message);
}
+ // SAFETY: There are no other threads yet, `sqlite_log_handler` is threadsafe, and it doesn't
+ // invoke any SQLite calls.
unsafe { sqlite_trace::config_log(Some(sqlite_log_handler)) }
.expect("Error setting sqlite log callback.");
@@ -146,43 +142,6 @@ fn main() {
panic!("Failed to register service {} because of {:?}.", METRICS_SERVICE_NAME, e);
});
- // Devices with KS2 and KM 1.0 may not have any IRemotelyProvisionedComponent HALs at all. Do
- // not panic if new_native_binder returns failure because it could not find the TEE HAL.
- match RemoteProvisioningService::new_native_binder() {
- Ok(remote_provisioning_service) => {
- binder::add_service(
- REMOTE_PROVISIONING_SERVICE_NAME,
- remote_provisioning_service.as_binder(),
- )
- .unwrap_or_else(|e| {
- panic!(
- "Failed to register service {} because of {:?}.",
- REMOTE_PROVISIONING_SERVICE_NAME, e
- );
- });
- }
- Err(e) => log::info!("Not publishing {}: {:?}", REMOTE_PROVISIONING_SERVICE_NAME, e),
- }
-
- // 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
index 035edd90..03c9d027 100644
--- a/keystore2/src/km_compat.rs
+++ b/keystore2/src/km_compat.rs
@@ -32,6 +32,16 @@ use android_security_compat::aidl::android::security::compat::IKeystoreCompatSer
use anyhow::Context;
use keystore2_crypto::{hmac_sha256, HMAC_SHA256_LEN};
+/// Magic prefix used by the km_compat C++ code to mark a key that is owned by an
+/// underlying Keymaster hardware device that has been wrapped by km_compat. (The
+/// final zero byte indicates that the blob is not software emulated.)
+pub const KEYMASTER_BLOB_HW_PREFIX: &[u8] = b"pKMblob\x00";
+
+/// Magic prefix used by the km_compat C++ code to mark a key that is owned by an
+/// software emulation device that has been wrapped by km_compat. (The final one
+/// byte indicates that the blob is software emulated.)
+pub const KEYMASTER_BLOB_SW_PREFIX: &[u8] = b"pKMblob\x01";
+
/// Key data associated with key generation/import.
#[derive(Debug, PartialEq, Eq)]
pub enum KeyImportData<'a> {
@@ -89,7 +99,7 @@ fn wrap_keyblob(keyblob: &[u8]) -> anyhow::Result<Vec<u8>> {
/// 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 {
+pub fn unwrap_keyblob(keyblob: &[u8]) -> KeyBlob {
if !keyblob.starts_with(KEYBLOB_PREFIX) {
return KeyBlob::Raw(keyblob);
}
@@ -159,7 +169,7 @@ where
}
}
-impl<T> binder::Interface for BacklevelKeyMintWrapper<T> where T: EmulationDetector {}
+impl<T> binder::Interface for BacklevelKeyMintWrapper<T> where T: EmulationDetector + 'static {}
impl<T> IKeyMintDevice for BacklevelKeyMintWrapper<T>
where
@@ -195,6 +205,15 @@ where
let _ = self.soft.earlyBootEnded();
self.real.earlyBootEnded()
}
+ 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)
+ }
// For methods that emit keyblobs, check whether the underlying real device
// supports the relevant parameters, and forward to the appropriate device.
@@ -299,15 +318,6 @@ where
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)
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
index 806f3dcf..36e18f0f 100644
--- a/keystore2/src/km_compat/Android.bp
+++ b/keystore2/src/km_compat/Android.bp
@@ -33,7 +33,7 @@ rust_library {
],
shared_libs: [
"libkm_compat_service",
- ]
+ ],
}
rust_test {
@@ -91,9 +91,9 @@ cc_library {
"android.security.compat-ndk",
"libbinder_ndk",
"libcrypto",
- "libkm_compat",
"libkeymaster4_1support",
"libkeystore2_crypto",
+ "libkm_compat",
],
}
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index e27cd1c7..e9ff1fff 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -144,6 +144,11 @@ const size_t kKeyBlobPrefixSize = 8;
//
const uint8_t kKeyBlobMagic[7] = {'p', 'K', 'M', 'b', 'l', 'o', 'b'};
+// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set
+// to 9999-12-31T23:59:59Z.
+//
+const uint64_t kUndefinedNotAfter = 253402300799000;
+
// Prefixes a keyblob returned by e.g. generateKey() with information on whether it
// originated from the real underlying KeyMaster HAL or from soft-KeyMint.
//
@@ -260,6 +265,16 @@ extractNewAndKeystoreEnforceableParams(const std::vector<KMV1::KeyParameter>& pa
return result;
}
+std::vector<KMV1::KeyParameter>
+extractCombinedParams(const std::vector<KMV1::KeyCharacteristics>& characteristics) {
+ std::vector<KMV1::KeyParameter> result;
+ for (auto characteristic : characteristics) {
+ std::copy(characteristic.authorizations.begin(), characteristic.authorizations.end(),
+ std::back_inserter(result));
+ }
+ return result;
+}
+
ScopedAStatus convertErrorCode(KMV1::ErrorCode result) {
if (result == KMV1::ErrorCode::OK) {
return ScopedAStatus::ok();
@@ -587,6 +602,15 @@ KeyMintDevice::importWrappedKey(const std::vector<uint8_t>& in_inWrappedKeyData,
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
return convertErrorCode(KMV1::ErrorCode::UNKNOWN_ERROR);
}
+ if (errorCode == KMV1::ErrorCode::OK) {
+ auto params = extractCombinedParams(out_creationResult->keyCharacteristics);
+ auto cert = getCertificate(params, out_creationResult->keyBlob, true /* isWrappedKey */);
+ // importWrappedKey used to not generate a certificate. Ignore the error to preserve
+ // backwards compatibility with clients that can't successfully generate a certificate.
+ if (std::holds_alternative<std::vector<Certificate>>(cert)) {
+ out_creationResult->certificateChain = std::get<std::vector<Certificate>>(cert);
+ }
+ }
return convertErrorCode(errorCode);
}
@@ -1055,7 +1079,7 @@ getMaximum(const std::vector<KeyParameter>& keyParams, T tag,
static std::variant<keystore::X509_Ptr, KMV1::ErrorCode>
makeCert(::android::sp<Keymaster> mDevice, const std::vector<KeyParameter>& keyParams,
- const std::vector<uint8_t>& keyBlob) {
+ const std::vector<uint8_t>& keyBlob, bool isWrappedKey) {
// Start generating the certificate.
// Get public key for makeCert.
KMV1::ErrorCode errorCode;
@@ -1097,15 +1121,21 @@ makeCert(::android::sp<Keymaster> mDevice, const std::vector<KeyParameter>& keyP
serial = *blob;
}
+ // There is no way to specify CERTIFICATE_NOT_BEFORE and CERTIFICATE_NOT_AFTER for wrapped keys.
+ // So we provide default values.
int64_t activation;
- if (auto date = getParam(keyParams, KMV1::TAG_CERTIFICATE_NOT_BEFORE)) {
+ if (isWrappedKey) {
+ activation = 0;
+ } else if (auto date = getParam(keyParams, KMV1::TAG_CERTIFICATE_NOT_BEFORE)) {
activation = static_cast<int64_t>(*date);
} else {
return KMV1::ErrorCode::MISSING_NOT_BEFORE;
}
int64_t expiration;
- if (auto date = getParam(keyParams, KMV1::TAG_CERTIFICATE_NOT_AFTER)) {
+ if (isWrappedKey) {
+ expiration = kUndefinedNotAfter;
+ } else if (auto date = getParam(keyParams, KMV1::TAG_CERTIFICATE_NOT_AFTER)) {
expiration = static_cast<int64_t>(*date);
} else {
return KMV1::ErrorCode::MISSING_NOT_AFTER;
@@ -1235,7 +1265,7 @@ KeyMintDevice::signCertificate(const std::vector<KeyParameter>& keyParams,
std::variant<std::vector<Certificate>, KMV1::ErrorCode>
KeyMintDevice::getCertificate(const std::vector<KeyParameter>& keyParams,
- const std::vector<uint8_t>& prefixedKeyBlob) {
+ const std::vector<uint8_t>& prefixedKeyBlob, bool isWrappedKey) {
const std::vector<uint8_t>& keyBlob = prefixedKeyBlobRemovePrefix(prefixedKeyBlob);
// There are no certificates for symmetric keys.
@@ -1278,7 +1308,7 @@ KeyMintDevice::getCertificate(const std::vector<KeyParameter>& keyParams,
}
// makeCert
- auto certOrError = makeCert(mDevice, keyParams, keyBlob);
+ auto certOrError = makeCert(mDevice, keyParams, keyBlob, isWrappedKey);
if (std::holds_alternative<KMV1::ErrorCode>(certOrError)) {
return std::get<KMV1::ErrorCode>(certOrError);
}
@@ -1420,7 +1450,12 @@ KeymasterDevices enumerateKeymasterDevices(IServiceManager* serviceManager) {
KeymasterDevices initializeKeymasters() {
auto serviceManager = IServiceManager::getService();
- CHECK(serviceManager.get()) << "Failed to get ServiceManager";
+ if (!serviceManager.get()) {
+ // New devices no longer have HIDL support, so failing to get hwservicemanager is
+ // expected behavior.
+ LOG(INFO) << "Skipping keymaster compat, this system is AIDL only.";
+ return KeymasterDevices();
+ }
auto result = enumerateKeymasterDevices<Keymaster4>(serviceManager.get());
auto softKeymaster = result[SecurityLevel::SOFTWARE];
if ((!result[SecurityLevel::TRUSTED_ENVIRONMENT]) && (!result[SecurityLevel::STRONGBOX])) {
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 6654c4a6..c4bcdaa9 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -150,7 +150,8 @@ class KeyMintDevice : public aidl::android::hardware::security::keymint::BnKeyMi
// 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>
- getCertificate(const std::vector<KeyParameter>& keyParams, const std::vector<uint8_t>& keyBlob);
+ getCertificate(const std::vector<KeyParameter>& keyParams, const std::vector<uint8_t>& keyBlob,
+ bool isWrappedKey = false);
void setNumFreeSlots(uint8_t numFreeSlots);
diff --git a/keystore2/src/km_compat/lib.rs b/keystore2/src/km_compat/lib.rs
index 2632ec49..e61a13a7 100644
--- a/keystore2/src/km_compat/lib.rs
+++ b/keystore2/src/km_compat/lib.rs
@@ -21,6 +21,7 @@ extern "C" {
#[allow(missing_docs)] // TODO remove this
pub fn add_keymint_device_service() -> i32 {
+ // SAFETY: This is always safe to call.
unsafe { addKeyMintDeviceService() }
}
diff --git a/keystore2/src/legacy_blob.rs b/keystore2/src/legacy_blob.rs
index 2ffcc711..2bb7f27b 100644
--- a/keystore2/src/legacy_blob.rs
+++ b/keystore2/src/legacy_blob.rs
@@ -1313,7 +1313,7 @@ impl LegacyBlobLoader {
Blob { flags, value: BlobValue::PwEncrypted { iv, tag, data, salt, key_size } } => {
if (flags & flags::ENCRYPTED) != 0 {
let key = pw
- .derive_key(&salt, key_size)
+ .derive_key_pbkdf2(&salt, key_size)
.context(ks_err!("Failed to derive key from password."))?;
let blob = aes_gcm_decrypt(&data, &iv, &tag, &key)
.context(ks_err!("while trying to decrypt legacy super key blob."))?;
@@ -1953,7 +1953,7 @@ mod test {
std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key(SUPERKEY_SALT, 32).unwrap());
+ let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
let super_key =
Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
@@ -2040,7 +2040,7 @@ mod test {
std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key(SUPERKEY_SALT, 32).unwrap());
+ let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
let super_key =
Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
@@ -2128,7 +2128,7 @@ mod test {
std::fs::create_dir(&*temp_dir.build().push("user_0")).unwrap();
let pw: Password = PASSWORD.into();
- let pw_key = TestKey(pw.derive_key(SUPERKEY_SALT, 32).unwrap());
+ let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
let super_key =
Arc::new(TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap()));
diff --git a/keystore2/src/legacy_importer.rs b/keystore2/src/legacy_importer.rs
index 9eb702dc..7dcb98d6 100644
--- a/keystore2/src/legacy_importer.rs
+++ b/keystore2/src/legacy_importer.rs
@@ -22,7 +22,7 @@ use crate::error::{map_km_error, Error};
use crate::key_parameter::{KeyParameter, KeyParameterValue};
use crate::ks_err;
use crate::legacy_blob::{self, Blob, BlobValue, LegacyKeyCharacteristics};
-use crate::super_key::USER_SUPER_KEY;
+use crate::super_key::USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
use crate::utils::{
key_characteristics_to_internal, uid_to_android_user, upgrade_keyblob_if_required_with,
watchdog as wd, AesGcm,
@@ -102,6 +102,11 @@ impl LegacyImporter {
}
}
+ #[cfg(test)]
+ pub fn set_empty(&mut self) {
+ self.state = AtomicU8::new(Self::STATE_EMPTY);
+ }
+
/// The legacy importer 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
@@ -445,7 +450,7 @@ impl LegacyImporterState {
match self
.db
- .load_super_key(&USER_SUPER_KEY, user_id)
+ .load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, user_id)
.context(ks_err!("Failed to load super key"))?
{
Some((_, entry)) => Ok(entry.id()),
@@ -724,7 +729,7 @@ impl LegacyImporterState {
self.db
.store_super_key(
user_id,
- &USER_SUPER_KEY,
+ &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
&blob,
&blob_metadata,
&KeyMetaData::new(),
@@ -767,7 +772,7 @@ impl LegacyImporterState {
let super_key_id = self
.db
- .load_super_key(&USER_SUPER_KEY, user_id)
+ .load_super_key(&USER_AFTER_FIRST_UNLOCK_SUPER_KEY, user_id)
.context(ks_err!("Failed to load super key"))?
.map(|(_, entry)| entry.id());
@@ -909,11 +914,12 @@ 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)
+ let (km_dev, info) = crate::globals::get_keymint_dev_by_uuid(uuid)
.with_context(|| ks_err!("Trying to get km device for id {:?}", uuid))?;
let (characteristics, upgraded_blob) = upgrade_keyblob_if_required_with(
&*km_dev,
+ info.versionNumber,
blob,
&[],
|blob| {
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 97948899..c0eecd89 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -28,7 +28,6 @@ pub mod globals;
pub mod id_rotation;
/// Internal Representation of Key Parameter and convenience functions.
pub mod key_parameter;
-pub mod ks_err;
pub mod legacy_blob;
pub mod legacy_importer;
pub mod maintenance;
@@ -38,7 +37,6 @@ pub mod operation;
pub mod permission;
pub mod raw_device;
pub mod remote_provisioning;
-pub mod rkpd_client;
pub mod security_level;
pub mod service;
pub mod shared_secret_negotiation;
@@ -49,6 +47,7 @@ mod audit_log;
mod gc;
mod km_compat;
mod super_key;
+mod sw_keyblob;
+mod watchdog_helper;
-#[cfg(feature = "watchdog")]
-mod watchdog;
+use message_macro::source_location_msg as ks_err;
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 5efb798d..8780e9e9 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -14,7 +14,7 @@
//! This module implements IKeystoreMaintenance AIDL interface.
-use crate::database::{KeyEntryLoadBits, KeyType, MonotonicRawTime};
+use crate::database::{KeyEntryLoadBits, KeyType};
use crate::error::map_km_error;
use crate::error::map_or_log_err;
use crate::error::Error;
@@ -24,14 +24,14 @@ use crate::ks_err;
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,
+ check_get_app_uids_affected_by_sid_permissions, 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 android_security_maintenance::aidl::android::security::maintenance::{
- IKeystoreMaintenance::{BnKeystoreMaintenance, IKeystoreMaintenance},
- UserState::UserState as AidlUserState,
+use android_security_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::{
+ BnKeystoreMaintenance, IKeystoreMaintenance,
};
use android_security_maintenance::binder::{
BinderFeatures, Interface, Result as BinderResult, Strong, ThreadState,
@@ -78,31 +78,29 @@ impl Maintenance {
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)
+ skm.unlock_unlocked_device_required_keys(&mut db.borrow_mut(), user_id as u32, pw)
})
- .context(ks_err!("unlock_screen_lock_bound_key failed"))?;
+ .context(ks_err!("unlock_unlocked_device_required_keys failed"))?;
}
- match DB
- .with(|db| {
- skm.reset_or_init_user_and_get_user_state(
- &mut db.borrow_mut(),
- &LEGACY_IMPORTER,
- user_id as u32,
- password.as_ref(),
- )
- })
- .context(ks_err!())?
+ if let UserState::BeforeFirstUnlock = DB
+ .with(|db| skm.get_user_state(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32))
+ .context(ks_err!("Could not get user state while changing password!"))?
{
- UserState::LskfLocked => {
- // Error - password can not be changed when the device is locked
- Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."))
+ // Error - password can not be changed when the device is locked
+ return Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."));
+ }
+
+ DB.with(|db| match password {
+ Some(pass) => {
+ skm.init_user(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32, &pass)
}
- _ => {
- // LskfLocked is the only error case for password change
- Ok(())
+ None => {
+ // User transitioned to swipe.
+ skm.reset_user(&mut db.borrow_mut(), &LEGACY_IMPORTER, user_id as u32)
}
- }
+ })
+ .context(ks_err!("Failed to change user password!"))
}
fn add_or_remove_user(&self, user_id: i32) -> Result<()> {
@@ -111,11 +109,10 @@ impl Maintenance {
check_keystore_permission(KeystorePerm::ChangeUser).context(ks_err!())?;
DB.with(|db| {
- SUPER_KEY.write().unwrap().reset_user(
+ SUPER_KEY.write().unwrap().remove_user(
&mut db.borrow_mut(),
&LEGACY_IMPORTER,
user_id as u32,
- false,
)
})
.context(ks_err!("Trying to delete keys from db."))?;
@@ -124,6 +121,41 @@ impl Maintenance {
.context(ks_err!("While invoking the delete listener."))
}
+ fn init_user_super_keys(
+ &self,
+ user_id: i32,
+ password: Password,
+ allow_existing: bool,
+ ) -> Result<()> {
+ // Permission check. Must return on error. Do not touch the '?'.
+ check_keystore_permission(KeystorePerm::ChangeUser).context(ks_err!())?;
+
+ let mut skm = SUPER_KEY.write().unwrap();
+ DB.with(|db| {
+ skm.initialize_user(
+ &mut db.borrow_mut(),
+ &LEGACY_IMPORTER,
+ user_id as u32,
+ &password,
+ allow_existing,
+ )
+ })
+ .context(ks_err!("Failed to initialize user super keys"))
+ }
+
+ // Deletes all auth-bound keys when the user's LSKF is removed.
+ fn on_user_lskf_removed(user_id: i32) -> Result<()> {
+ // Permission check. Must return on error. Do not touch the '?'.
+ check_keystore_permission(KeystorePerm::ChangePassword).context(ks_err!())?;
+
+ LEGACY_IMPORTER
+ .bulk_delete_user(user_id as u32, true)
+ .context(ks_err!("Failed to delete legacy keys."))?;
+
+ DB.with(|db| db.borrow_mut().unbind_auth_bound_keys_for_user(user_id as u32))
+ .context(ks_err!("Failed to delete auth-bound keys."))
+ }
+
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.")?;
@@ -138,27 +170,6 @@ impl Maintenance {
.context(ks_err!("While invoking the delete listener."))
}
- 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.")?;
- let state = DB
- .with(|db| {
- SUPER_KEY.read().unwrap().get_user_state(
- &mut db.borrow_mut(),
- &LEGACY_IMPORTER,
- user_id as u32,
- )
- })
- .context(ks_err!("Trying to get UserState."))?;
-
- match state {
- UserState::Uninitialized => Ok(AidlUserState::UNINITIALIZED),
- UserState::LskfUnlocked(_) => Ok(AidlUserState::LSKF_UNLOCKED),
- UserState::LskfLocked => Ok(AidlUserState::LSKF_LOCKED),
- }
- }
-
fn call_with_watchdog<F>(sec_level: SecurityLevel, name: &'static str, op: &F) -> Result<()>
where
F: Fn(Strong<dyn IKeyMintDevice>) -> binder::Result<()>,
@@ -181,7 +192,7 @@ impl Maintenance {
(SecurityLevel::TRUSTED_ENVIRONMENT, "TRUSTED_ENVIRONMENT"),
(SecurityLevel::STRONGBOX, "STRONGBOX"),
];
- sec_levels.iter().fold(Ok(()), move |result, (sec_level, sec_level_string)| {
+ sec_levels.iter().try_fold((), |_result, (sec_level, sec_level_string)| {
let curr_result = Maintenance::call_with_watchdog(*sec_level, name, &op);
match curr_result {
Ok(()) => log::info!(
@@ -196,7 +207,7 @@ impl Maintenance {
e
),
}
- result.and(curr_result)
+ curr_result
})
}
@@ -213,14 +224,6 @@ impl Maintenance {
Maintenance::call_on_all_security_levels("earlyBootEnded", |dev| dev.earlyBootEnded())
}
- fn on_device_off_body() -> Result<()> {
- // Security critical permission check. This statement must return on fail.
- check_keystore_permission(KeystorePerm::ReportOffBody).context(ks_err!())?;
-
- 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();
@@ -242,7 +245,7 @@ impl Maintenance {
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 super_key = SUPER_KEY.read().unwrap().get_after_first_unlock_key_by_user_id(user_id);
DB.with(|db| {
let (key_id_guard, _) = LEGACY_IMPORTER
@@ -276,57 +279,97 @@ impl Maintenance {
Maintenance::call_on_all_security_levels("deleteAllKeys", |dev| dev.deleteAllKeys())
}
+
+ fn get_app_uids_affected_by_sid(
+ user_id: i32,
+ secure_user_id: i64,
+ ) -> Result<std::vec::Vec<i64>> {
+ // This method is intended to be called by Settings and discloses a list of apps
+ // associated with a user, so it requires the "android.permission.MANAGE_USERS"
+ // permission (to avoid leaking list of apps to unauthorized callers).
+ check_get_app_uids_affected_by_sid_permissions().context(ks_err!())?;
+ DB.with(|db| db.borrow_mut().get_app_uids_affected_by_sid(user_id, secure_user_id))
+ .context(ks_err!("Failed to get app UIDs affected by SID"))
+ }
}
impl Interface for Maintenance {}
impl IKeystoreMaintenance for Maintenance {
fn onUserPasswordChanged(&self, user_id: i32, password: Option<&[u8]>) -> BinderResult<()> {
+ log::info!(
+ "onUserPasswordChanged(user={}, password.is_some()={})",
+ user_id,
+ password.is_some()
+ );
let _wp = wd::watch_millis("IKeystoreMaintenance::onUserPasswordChanged", 500);
map_or_log_err(Self::on_user_password_changed(user_id, password.map(|pw| pw.into())), Ok)
}
fn onUserAdded(&self, user_id: i32) -> BinderResult<()> {
+ log::info!("onUserAdded(user={user_id})");
let _wp = wd::watch_millis("IKeystoreMaintenance::onUserAdded", 500);
map_or_log_err(self.add_or_remove_user(user_id), Ok)
}
+ fn initUserSuperKeys(
+ &self,
+ user_id: i32,
+ password: &[u8],
+ allow_existing: bool,
+ ) -> BinderResult<()> {
+ log::info!("initUserSuperKeys(user={user_id}, allow_existing={allow_existing})");
+ let _wp = wd::watch_millis("IKeystoreMaintenance::initUserSuperKeys", 500);
+ map_or_log_err(self.init_user_super_keys(user_id, password.into(), allow_existing), Ok)
+ }
+
fn onUserRemoved(&self, user_id: i32) -> BinderResult<()> {
+ log::info!("onUserRemoved(user={user_id})");
let _wp = wd::watch_millis("IKeystoreMaintenance::onUserRemoved", 500);
map_or_log_err(self.add_or_remove_user(user_id), Ok)
}
+ fn onUserLskfRemoved(&self, user_id: i32) -> BinderResult<()> {
+ log::info!("onUserLskfRemoved(user={user_id})");
+ let _wp = wd::watch_millis("IKeystoreMaintenance::onUserLskfRemoved", 500);
+ map_or_log_err(Self::on_user_lskf_removed(user_id), Ok)
+ }
+
fn clearNamespace(&self, domain: Domain, nspace: i64) -> BinderResult<()> {
+ log::info!("clearNamespace({domain:?}, nspace={nspace})");
let _wp = wd::watch_millis("IKeystoreMaintenance::clearNamespace", 500);
map_or_log_err(self.clear_namespace(domain, nspace), Ok)
}
- fn getState(&self, user_id: i32) -> BinderResult<AidlUserState> {
- let _wp = wd::watch_millis("IKeystoreMaintenance::getState", 500);
- map_or_log_err(Self::get_state(user_id), Ok)
- }
-
fn earlyBootEnded(&self) -> BinderResult<()> {
+ log::info!("earlyBootEnded()");
let _wp = wd::watch_millis("IKeystoreMaintenance::earlyBootEnded", 500);
map_or_log_err(Self::early_boot_ended(), Ok)
}
- fn onDeviceOffBody(&self) -> BinderResult<()> {
- let _wp = wd::watch_millis("IKeystoreMaintenance::onDeviceOffBody", 500);
- map_or_log_err(Self::on_device_off_body(), Ok)
- }
-
fn migrateKeyNamespace(
&self,
source: &KeyDescriptor,
destination: &KeyDescriptor,
) -> BinderResult<()> {
+ log::info!("migrateKeyNamespace(src={source:?}, dest={destination:?})");
let _wp = wd::watch_millis("IKeystoreMaintenance::migrateKeyNamespace", 500);
map_or_log_err(Self::migrate_key_namespace(source, destination), Ok)
}
fn deleteAllKeys(&self) -> BinderResult<()> {
+ log::warn!("deleteAllKeys()");
let _wp = wd::watch_millis("IKeystoreMaintenance::deleteAllKeys", 500);
map_or_log_err(Self::delete_all_keys(), Ok)
}
+
+ fn getAppUidsAffectedBySid(
+ &self,
+ user_id: i32,
+ secure_user_id: i64,
+ ) -> BinderResult<std::vec::Vec<i64>> {
+ log::info!("getAppUidsAffectedBySid(secure_user_id={secure_user_id:?})");
+ let _wp = wd::watch_millis("IKeystoreMaintenance::getAppUidsAffectedBySid", 500);
+ map_or_log_err(Self::get_app_uids_affected_by_sid(user_id, secure_user_id), Ok)
+ }
}
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
index 6043612c..5a76d04e 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -17,12 +17,11 @@
//! 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::anyhow_error_to_serialized_error;
use crate::globals::DB;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::ks_err;
use crate::operation::Outcome;
-use crate::remote_provisioning::get_pool_status;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
@@ -42,16 +41,12 @@ use android_security_metrics::aidl::android::security::metrics::{
KeystoreAtom::KeystoreAtom, KeystoreAtomPayload::KeystoreAtomPayload,
Outcome::Outcome as MetricsOutcome, Purpose::Purpose as MetricsPurpose,
RkpError::RkpError as MetricsRkpError, RkpErrorStats::RkpErrorStats,
- RkpPoolStats::RkpPoolStats, SecurityLevel::SecurityLevel as MetricsSecurityLevel,
- Storage::Storage as MetricsStorage,
+ SecurityLevel::SecurityLevel as MetricsSecurityLevel, Storage::Storage as MetricsStorage,
};
-use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
-use anyhow::{Context, Result};
+use anyhow::{anyhow, Context, Result};
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};
// Note: Crash events are recorded at keystore restarts, based on the assumption that keystore only
// gets restarted after a crash, during a boot cycle.
@@ -95,19 +90,17 @@ impl MetricsStore {
return pull_storage_stats();
}
- // Process and return RKP pool stats.
- if AtomID::RKP_POOL_STATS == atom_id {
- return pull_attestation_pool_stats();
- }
-
// Process keystore crash stats.
if AtomID::CRASH_STATS == atom_id {
- return Ok(vec![KeystoreAtom {
- payload: KeystoreAtomPayload::CrashStats(CrashStats {
- count_of_crash_events: read_keystore_crash_count()?,
- }),
- ..Default::default()
- }]);
+ return match read_keystore_crash_count()? {
+ Some(count) => Ok(vec![KeystoreAtom {
+ payload: KeystoreAtomPayload::CrashStats(CrashStats {
+ count_of_crash_events: count,
+ }),
+ ..Default::default()
+ }]),
+ None => Err(anyhow!("Crash count property is not set")),
+ };
}
// It is safe to call unwrap here since the lock can not be poisoned based on its usage
@@ -126,15 +119,14 @@ impl MetricsStore {
// It is ok to unwrap here since the mutex cannot be poisoned according to the way it is
// used in this module. And the lock is not acquired by this thread before.
let mut metrics_store_guard = self.metrics_store.lock().unwrap();
- let atom_count_map = metrics_store_guard.entry(atom_id).or_insert_with(HashMap::new);
+ let atom_count_map = metrics_store_guard.entry(atom_id).or_default();
if atom_count_map.len() < MetricsStore::SINGLE_ATOM_STORE_MAX_SIZE {
let atom_count = atom_count_map.entry(atom).or_insert(0);
*atom_count += 1;
} else {
// Insert an overflow atom
- let overflow_atom_count_map = metrics_store_guard
- .entry(AtomID::KEYSTORE2_ATOM_WITH_OVERFLOW)
- .or_insert_with(HashMap::new);
+ let overflow_atom_count_map =
+ metrics_store_guard.entry(AtomID::KEYSTORE2_ATOM_WITH_OVERFLOW).or_default();
if overflow_atom_count_map.len() < MetricsStore::SINGLE_ATOM_STORE_MAX_SIZE {
let overflow_atom = Keystore2AtomWithOverflow { atom_id };
@@ -209,7 +201,7 @@ fn process_key_creation_event_stats<U>(
};
if let Err(ref e) = result {
- key_creation_with_general_info.error_code = get_error_code(e);
+ key_creation_with_general_info.error_code = anyhow_error_to_serialized_error(e).0;
}
key_creation_with_auth_info.security_level = process_security_level(sec_level);
@@ -560,45 +552,6 @@ fn pull_storage_stats() -> Result<Vec<KeystoreAtom>> {
Ok(atom_vec)
}
-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(ks_err!("Failed to compute expired by system time."))?
- .duration_since(UNIX_EPOCH)
- .context(ks_err!("Failed to compute expired by duration."))?
- .as_millis() as i64;
-
- let result = get_pool_status(expired_by, *sec_level);
-
- if let Ok(pool_status) = result {
- let rkp_pool_stats = RkpPoolStats {
- security_level: process_security_level(*sec_level),
- expiring: pool_status.expiring,
- unassigned: pool_status.unassigned,
- attested: pool_status.attested,
- total: pool_status.total,
- };
- atoms.push(KeystoreAtom {
- payload: KeystoreAtomPayload::RkpPoolStats(rkp_pool_stats),
- ..Default::default()
- });
- } else {
- log::error!(
- concat!(
- "In pull_attestation_pool_stats: Failed to retrieve pool status",
- " for security level: {:?}"
- ),
- sec_level
- );
- }
- }
- Ok(atoms)
-}
-
/// 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 {
@@ -612,27 +565,21 @@ pub fn log_rkp_error_stats(rkp_error: MetricsRkpError, sec_level: &SecurityLevel
/// If the property is absent, it sets the property with value 0. If the property is present, it
/// increments the value. This helps tracking keystore crashes internally.
pub fn update_keystore_crash_sysprop() {
- let crash_count = read_keystore_crash_count();
- let new_count = match crash_count {
- Ok(count) => count + 1,
+ let new_count = match read_keystore_crash_count() {
+ Ok(Some(count)) => count + 1,
+ // If the property is absent, then this is the first start up during the boot.
+ // Proceed to write the system property with value 0.
+ Ok(None) => 0,
Err(error) => {
- // If the property is absent, this is the first start up during the boot.
- // Proceed to write the system property with value 0. Otherwise, log and return.
- if !matches!(
- error.root_cause().downcast_ref::<PropertyWatcherError>(),
- Some(PropertyWatcherError::SystemPropertyAbsent)
- ) {
- log::warn!(
- concat!(
- "In update_keystore_crash_sysprop: ",
- "Failed to read the existing system property due to: {:?}.",
- "Therefore, keystore crashes will not be logged."
- ),
- error
- );
- return;
- }
- 0
+ log::warn!(
+ concat!(
+ "In update_keystore_crash_sysprop: ",
+ "Failed to read the existing system property due to: {:?}.",
+ "Therefore, keystore crashes will not be logged."
+ ),
+ error
+ );
+ return;
}
};
@@ -650,12 +597,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(ks_err!("Failed read property."))?
- .context(ks_err!("Property not set."))?
- .parse::<i32>()
- .map_err(std::convert::Into::into)
+pub fn read_keystore_crash_count() -> Result<Option<i32>> {
+ match rustutils::system_properties::read("keystore.crash_count") {
+ Ok(Some(count)) => count.parse::<i32>().map(Some).map_err(std::convert::Into::into),
+ Ok(None) => Ok(None),
+ Err(e) => Err(e).context(ks_err!("Failed to read crash count 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 2034a8a0..11eaf17a 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -126,7 +126,10 @@
//! Either way, we have to revaluate the pruning scores.
use crate::enforcements::AuthInfo;
-use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
+use crate::error::{
+ error_to_serialized_error, map_err_with, map_km_error, map_or_log_err, Error, ErrorCode,
+ ResponseCode, SerializedError,
+};
use crate::ks_err;
use crate::metrics_store::log_key_operation_event_stats;
use crate::utils::watchdog as wd;
@@ -162,7 +165,7 @@ pub enum Outcome {
/// Operation is pruned.
Pruned,
/// Operation is failed with the error code.
- ErrorCode(ErrorCode),
+ ErrorCode(SerializedError),
}
/// Operation bundles all of the operation related resources and tracks the operation's
@@ -287,7 +290,7 @@ impl Operation {
// 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()) {
- log::error!("In prune: KeyMint::abort failed with {:?}.", e);
+ log::warn!("In prune: KeyMint::abort failed with {:?}.", e);
}
Ok(())
@@ -305,8 +308,7 @@ impl Operation {
err: Result<T, Error>,
) -> Result<T, Error> {
match &err {
- Err(Error::Km(e)) => *locked_outcome = Outcome::ErrorCode(*e),
- Err(_) => *locked_outcome = Outcome::ErrorCode(ErrorCode::UNKNOWN_ERROR),
+ Err(e) => *locked_outcome = Outcome::ErrorCode(error_to_serialized_error(e)),
Ok(_) => (),
}
err
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index d9bdf791..982bc821 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -109,9 +109,6 @@ implement_class!(
/// Checked when an app is uninstalled or wiped.
#[selinux(name = clear_ns)]
ClearNs,
- /// Checked when the user state is queried from Keystore 2.0.
- #[selinux(name = get_state)]
- GetState,
/// 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)]
@@ -140,10 +137,7 @@ implement_class!(
/// Checked when earlyBootEnded() is called.
#[selinux(name = early_boot_ended)]
EarlyBootEnded,
- /// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
- #[selinux(name = report_off_body)]
- ReportOffBody,
- /// Checked when IkeystoreMetrics::pullMetrics is called.
+ /// Checked when IKeystoreMetrics::pullMetrics is called.
#[selinux(name = pull_metrics)]
PullMetrics,
/// Checked when IKeystoreMaintenance::deleteAllKeys is called.
@@ -152,6 +146,9 @@ implement_class!(
/// Checked on calls to IRemotelyProvisionedKeyPool::getAttestationKey
#[selinux(name = get_attestation_key)]
GetAttestationKey,
+ /// Checked on IKeystoreAuthorization::getLastAuthTime() is called.
+ #[selinux(name = get_last_auth_time)]
+ GetLastAuthTime,
}
);
@@ -500,7 +497,6 @@ mod tests {
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());
@@ -510,7 +506,6 @@ mod tests {
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));
diff --git a/keystore2/src/raw_device.rs b/keystore2/src/raw_device.rs
index fa9872a7..44d805c3 100644
--- a/keystore2/src/raw_device.rs
+++ b/keystore2/src/raw_device.rs
@@ -263,35 +263,31 @@ impl KeyMintDevice {
where
F: Fn(&[u8]) -> Result<T, Error>,
{
- match f(&key_blob) {
- Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => {
- let upgraded_blob = map_km_error({
- let _wp = wd::watch_millis(
- "In KeyMintDevice::upgrade_keyblob_if_required_with: calling upgradeKey.",
- 500,
- );
- self.km_dev.upgradeKey(&key_blob, &[])
- })
- .context(ks_err!("Upgrade failed"))?;
-
+ let (f_result, upgraded_blob) = crate::utils::upgrade_keyblob_if_required_with(
+ &*self.km_dev,
+ self.version(),
+ &key_blob,
+ &[],
+ f,
+ |upgraded_blob| {
let mut new_blob_metadata = BlobMetaData::new();
new_blob_metadata.add(BlobMetaEntry::KmUuid(self.km_uuid));
db.set_blob(
key_id_guard,
SubComponentType::KEY_BLOB,
- Some(&upgraded_blob),
+ Some(upgraded_blob),
Some(&new_blob_metadata),
)
.context(ks_err!("Failed to insert upgraded blob into the database"))?;
-
- Ok((
- f(&upgraded_blob).context(ks_err!("Closure failed after upgrade"))?,
- KeyBlob::NonSensitive(upgraded_blob),
- ))
- }
- result => Ok((result.context(ks_err!("Closure failed"))?, key_blob)),
- }
+ Ok(())
+ },
+ )?;
+ let returned_blob = match upgraded_blob {
+ None => key_blob,
+ Some(upgraded_blob) => KeyBlob::NonSensitive(upgraded_blob),
+ };
+ Ok((f_result, returned_blob))
}
/// Use the created key in an operation that can be done with
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index 1a833391..0ef8c953 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -19,42 +19,24 @@
//! certificate chains signed by some root authority and stored in a keystore SQLite
//! DB.
-use std::collections::HashMap;
-
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, AttestationKey::AttestationKey, Certificate::Certificate,
KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue, SecurityLevel::SecurityLevel,
Tag::Tag,
};
-use android_hardware_security_rkp::aidl::android::hardware::security::keymint::{
- DeviceInfo::DeviceInfo, IRemotelyProvisionedComponent::IRemotelyProvisionedComponent,
- MacedPublicKey::MacedPublicKey, ProtectedData::ProtectedData,
-};
-use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
- AttestationPoolStatus::AttestationPoolStatus, IRemoteProvisioning::BnRemoteProvisioning,
- IRemoteProvisioning::IRemoteProvisioning,
- IRemotelyProvisionedKeyPool::BnRemotelyProvisionedKeyPool,
- IRemotelyProvisionedKeyPool::IRemotelyProvisionedKeyPool, ImplInfo::ImplInfo,
- RemotelyProvisionedKey::RemotelyProvisionedKey,
-};
-use android_security_remoteprovisioning::binder::{BinderFeatures, Strong};
+use android_security_rkp_aidl::aidl::android::security::rkp::RemotelyProvisionedKey::RemotelyProvisionedKey;
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::error::{self, map_or_log_err, map_rem_prov_error, Error};
-use crate::globals::{get_keymint_device, get_remotely_provisioned_component, DB};
+use crate::database::Uuid;
+use crate::error::wrapped_rkpd_error_to_ks_error;
+use crate::globals::get_remotely_provisioned_component_name;
use crate::ks_err;
use crate::metrics_store::log_rkp_error_stats;
-use crate::permission::KeystorePerm;
-use crate::rkpd_client::get_rkpd_attestation_key;
-use crate::utils::{check_keystore_permission, watchdog as wd};
+use crate::watchdog_helper::watchdog as wd;
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,
@@ -63,18 +45,12 @@ use android_security_metrics::aidl::android::security::metrics::RkpError::RkpErr
pub struct RemProvState {
security_level: SecurityLevel,
km_uuid: Uuid,
- 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) }
+ Self { security_level, km_uuid }
}
/// Returns the uuid for the KM instance attached to this RemProvState struct.
@@ -95,30 +71,6 @@ impl RemProvState {
.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()
- {
- self.is_hal_present.store(false, Ordering::Relaxed);
- return Ok(false);
- }
- // To check if remote provisioning is enabled on a system that supports both remote
- // provisioning and factory provisioned keys, we only need to check if there are any
- // keys at all generated to indicate if the app has gotten the signal to begin filling
- // the key pool from the server.
- let pool_status = db
- .get_attestation_pool_status(0 /* date */, &self.km_uuid)
- .context("In check_rem_prov_enabled: failed to get attestation pool status.")?;
- Ok(pool_status.total != 0)
- }
-
fn is_asymmetric_key(&self, params: &[KeyParameter]) -> bool {
params.iter().any(|kp| {
matches!(
@@ -134,58 +86,6 @@ impl RemProvState {
})
}
- /// Checks to see (1) if the key in question should be attested to based on the algorithm and
- /// (2) if remote provisioning is present and enabled on the system. If these conditions are
- /// met, it makes an attempt to fetch the attestation key assigned to the `caller_uid`.
- ///
- /// It returns the ResponseCode `OUT_OF_KEYS_TRANSIENT_ERROR` if there is not one key currently
- /// assigned to the `caller_uid` and there are none available to assign.
- pub fn get_remotely_provisioned_attestation_key_and_certs(
- &self,
- key: &KeyDescriptor,
- caller_uid: u32,
- params: &[KeyParameter],
- db: &mut KeystoreDB,
- ) -> Result<Option<(KeyIdGuard, 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
- // factory provisioned key instead. Alternatively, it's not an asymmetric key
- // and therefore will not be attested.
- Ok(None)
- } else {
- match get_rem_prov_attest_key(key.domain, caller_uid, db, &self.km_uuid) {
- Err(e) => {
- if self.is_rkp_only() {
- log::error!("Error occurred: {:?}", e);
- return Err(e);
- }
- log::warn!("Error occurred: {:?}", e);
- log_rkp_error_stats(
- MetricsRkpError::FALL_BACK_DURING_HYBRID,
- &self.security_level,
- );
- Ok(None)
- }
- Ok(v) => match v {
- Some((guard, cert_chain)) => Ok(Some((
- guard,
- AttestationKey {
- keyBlob: cert_chain.private_key.to_vec(),
- attestKeyParams: vec![],
- issuerSubjectName: parse_subject_from_certificate(
- &cert_chain.batch_cert,
- )
- .context(ks_err!("Failed to parse subject."))?,
- },
- Certificate { encodedCertificate: cert_chain.cert_chain },
- ))),
- None => Ok(None),
- },
- }
- }
- }
-
/// Fetches attestation key and corresponding certificates from RKPD.
pub fn get_rkpd_attestation_key_and_certs(
&self,
@@ -200,7 +100,7 @@ impl RemProvState {
Err(e) => {
if self.is_rkp_only() {
log::error!("Error occurred: {:?}", e);
- return Err(e);
+ return Err(wrapped_rkpd_error_to_ks_error(&e)).context(format!("{e:?}"));
}
log::warn!("Error occurred: {:?}", e);
log_rkp_error_stats(
@@ -226,895 +126,16 @@ impl RemProvState {
}
}
-/// Implementation of the IRemoteProvisioning service.
-#[derive(Default)]
-pub struct RemoteProvisioningService {
- device_by_sec_level: HashMap<SecurityLevel, Strong<dyn IRemotelyProvisionedComponent>>,
- curve_by_sec_level: HashMap<SecurityLevel, i32>,
-}
-
-impl RemoteProvisioningService {
- fn get_dev_by_sec_level(
- &self,
- sec_level: &SecurityLevel,
- ) -> Result<&dyn IRemotelyProvisionedComponent> {
- if let Some(dev) = self.device_by_sec_level.get(sec_level) {
- Ok(dev.as_ref())
- } else {
- Err(error::Error::sys()).context(ks_err!(
- "Remote instance for requested security level \
- not found.",
- ))
- }
- }
-
- /// Creates a new instance of the remote provisioning service
- pub fn new_native_binder() -> Result<Strong<dyn IRemoteProvisioning>> {
- let mut result: Self = Default::default();
- let dev = get_remotely_provisioned_component(&SecurityLevel::TRUSTED_ENVIRONMENT)
- .context(ks_err!("Failed to get TEE Remote Provisioner instance."))?;
- result.curve_by_sec_level.insert(
- SecurityLevel::TRUSTED_ENVIRONMENT,
- dev.getHardwareInfo()
- .context(ks_err!("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) {
- result.curve_by_sec_level.insert(
- SecurityLevel::STRONGBOX,
- dev.getHardwareInfo()
- .context(ks_err!("Failed to get hardware info for StrongBox."))?
- .supportedEekCurve,
- );
- result.device_by_sec_level.insert(SecurityLevel::STRONGBOX, dev);
- }
- 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(ks_err!("COSE_Mac0 returned from IRPC cannot be parsed"))?;
- if cose_mac0.len() != COSE_MAC0_LEN {
- return Err(error::Error::sys()).context(ks_err!(
- "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(ks_err!("COSE_Mac0 payload is malformed."))?)
- }
- _ => {
- Err(error::Error::sys()).context(ks_err!("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,
- /// along with a server provided `eek` and `challenge`. The endpoint encryption key will
- /// be used to encrypt the sensitive contents being transmitted to the server, and the
- /// challenge will ensure freshness. A `test_mode` flag will instruct the remote provisioning
- /// HAL if it is okay to accept EEKs that aren't signed by something that chains back to the
- /// baked in root of trust in the underlying IRemotelyProvisionedComponent instance.
- #[allow(clippy::too_many_arguments)]
- pub fn generate_csr(
- &self,
- test_mode: bool,
- num_csr: i32,
- eek: &[u8],
- challenge: &[u8],
- sec_level: SecurityLevel,
- protected_data: &mut ProtectedData,
- device_info: &mut DeviceInfo,
- ) -> Result<Vec<u8>> {
- let dev = self.get_dev_by_sec_level(&sec_level)?;
- let (_, _, uuid) = get_keymint_device(&sec_level)?;
- let keys_to_sign = DB.with::<_, Result<Vec<MacedPublicKey>>>(|db| {
- let mut db = db.borrow_mut();
- Ok(db
- .fetch_unsigned_attestation_keys(num_csr, &uuid)?
- .iter()
- .map(|key| MacedPublicKey { macedKey: key.to_vec() })
- .collect())
- })?;
- let mac = map_rem_prov_error(dev.generateCertificateRequest(
- test_mode,
- &keys_to_sign,
- eek,
- challenge,
- device_info,
- protected_data,
- ))
- .context(ks_err!("Failed to generate csr"))?;
- let mut mac_and_keys: Vec<Value> = vec![Value::from(mac)];
- for maced_public_key in keys_to_sign {
- mac_and_keys.push(
- Self::extract_payload_from_cose_mac(&maced_public_key.macedKey)
- .context(ks_err!("Failed to get the payload from the COSE_Mac0"))?,
- )
- }
- let cbor_array: Value = Value::Array(mac_and_keys);
- serde_cbor::to_vec(&cbor_array)
- .context(ks_err!("Failed to serialize the mac and keys array"))
- }
-
- /// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
- /// `public_key` is used to index into the SQL database in order to insert the `certs` blob
- /// which represents a PEM encoded X.509 certificate chain. The `expiration_date` is provided
- /// as a convenience from the caller to avoid having to parse the certificates semantically
- /// 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(ks_err!("COSE_Mac0 returned from IRPC cannot be parsed"))?;
- if cose_mac0.len() != COSE_MAC0_LEN {
- return Err(error::Error::sys()).context(ks_err!(
- "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(ks_err!("COSE_Key is malformed."))?
- }
- _ => {
- Err(error::Error::sys()).context(ks_err!("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(ks_err!("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(ks_err!(
- "COSE_Key X-coordinate is not the right length. \
- Expected: 32; Actual: {}",
- x_coord.len()
- ));
- }
- _ => {
- return Err(error::Error::sys())
- .context(ks_err!("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(ks_err!(
- "COSE_Key Y-coordinate is not the right length. \
- Expected: 32; Actual: {}",
- y_coord.len()
- ));
- }
- _ => {
- return Err(error::Error::sys())
- .context(ks_err!("COSE_Key Y-coordinate is not a bstr"));
- }
- }
- Ok(raw_key)
- }
-
- /// 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<()> {
- let (_, _, uuid) = get_keymint_device(&sec_level)?;
- let dev = self
- .get_dev_by_sec_level(&sec_level)
- .context(ks_err!("Failed to get device for security 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(ks_err!("Failed to generated ECDSA keypair."))?;
- let raw_key = Self::parse_cose_mac0_for_coords(&maced_key.macedKey)
- .context(ks_err!("Failed to parse raw key"))?;
- db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
- .context(ks_err!("Failed to insert attestation key entry"))
- }
-
- /// Checks the security level of each available IRemotelyProvisionedComponent hal and returns
- /// all levels in an array to the caller.
- pub fn get_implementation_info(&self) -> Result<Vec<ImplInfo>> {
- Ok(self
- .curve_by_sec_level
- .iter()
- .map(|(sec_level, curve)| ImplInfo { secLevel: *sec_level, supportedCurve: *curve })
- .collect())
- }
-
- /// Deletes all attestation keys generated by the IRemotelyProvisionedComponent from the device,
- /// regardless of what state of the attestation key lifecycle they were in.
- pub fn delete_all_keys(&self) -> Result<i64> {
- DB.with::<_, Result<i64>>(|db| {
- let mut db = db.borrow_mut();
- db.delete_all_attestation_keys()
- })
- }
-}
-
-/// Populates the AttestationPoolStatus parcelable with information about how many
-/// certs will be expiring by the date provided in `expired_by` along with how many
-/// keys have not yet been assigned.
-pub fn get_pool_status(expired_by: i64, sec_level: SecurityLevel) -> Result<AttestationPoolStatus> {
- let (_, _, uuid) = get_keymint_device(&sec_level)?;
- DB.with::<_, Result<AttestationPoolStatus>>(|db| {
- let mut db = db.borrow_mut();
- // delete_expired_attestation_keys is always safe to call, and will remove anything
- // older than the date at the time of calling. No work should be done on the
- // attestation keys unless the pool status is checked first, so this call should be
- // enough to routinely clean out expired keys.
- db.delete_expired_attestation_keys()?;
- db.get_attestation_pool_status(expired_by, &uuid)
- })
-}
-
-/// 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_TRANSIENT_ERROR`
-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(ks_err!(
- "Failed to get a key after \
- attempting to assign one.",
- ))?
- .map_or_else(
- || {
- Err(Error::sys()).context(ks_err!(
- "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,
+fn get_rkpd_attestation_key(
+ security_level: &SecurityLevel,
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(ks_err!("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(ks_err!("Failed to assign a key"))?;
- Ok(None)
- }
- }
-}
-
-impl binder::Interface for RemoteProvisioningService {}
-
-// Implementation of IRemoteProvisioning. See AIDL spec at
-// :aidl/android/security/remoteprovisioning/IRemoteProvisioning.aidl
-impl IRemoteProvisioning for RemoteProvisioningService {
- fn getPoolStatus(
- &self,
- expired_by: i64,
- sec_level: SecurityLevel,
- ) -> binder::Result<AttestationPoolStatus> {
- let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
- map_or_log_err(get_pool_status(expired_by, sec_level), Ok)
- }
-
- fn generateCsr(
- &self,
- test_mode: bool,
- num_csr: i32,
- eek: &[u8],
- challenge: &[u8],
- sec_level: SecurityLevel,
- protected_data: &mut ProtectedData,
- device_info: &mut DeviceInfo,
- ) -> binder::Result<Vec<u8>> {
- let _wp = wd::watch_millis("IRemoteProvisioning::generateCsr", 500);
- map_or_log_err(
- self.generate_csr(
- test_mode,
- num_csr,
- eek,
- challenge,
- sec_level,
- protected_data,
- device_info,
- ),
- Ok,
- )
- }
-
- fn provisionCertChain(
- &self,
- public_key: &[u8],
- batch_cert: &[u8],
- certs: &[u8],
- expiration_date: i64,
- sec_level: SecurityLevel,
- ) -> binder::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,
- )
- })
- }
-
- fn generateKeyPair(&self, is_test_mode: bool, sec_level: SecurityLevel) -> binder::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,
- )
- })
- }
-
- fn getImplementationInfo(&self) -> binder::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> {
- 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_TRANSIENT_ERROR` 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(ks_err!())?;
- 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_TRANSIENT_ERROR))
- .context(ks_err!("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(ks_err!("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_rkp::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())
- }
-
- fn generateCertificateRequestV2(
- &self,
- _keys_to_sign: &[MacedPublicKey],
- _challenge: &[u8],
- ) -> 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_TRANSIENT_ERROR)
- );
- }
-
- #[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_TRANSIENT_ERROR)
- );
- }
-
- #[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()
- );
- }
+) -> Result<RemotelyProvisionedKey> {
+ // Depending on the Android release, RKP may not have been mandatory for the
+ // TEE or StrongBox KM instances. In such cases, lookup failure for the IRPC
+ // HAL service is WAI and should not cause a failure. The error should be caught
+ // by the calling function and allow for natural fallback to the factory key.
+ let rpc_name = get_remotely_provisioned_component_name(security_level)
+ .context(ks_err!("Trying to get IRPC name."))?;
+ let _wd = wd::watch_millis("Calling get_rkpd_attestation_key()", 500);
+ rkpd_client::get_rkpd_attestation_key(&rpc_name, caller_uid)
}
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index cc1f816c..5f9745f0 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -19,19 +19,22 @@ 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::error::{self, map_km_error, map_or_log_err, Error, ErrorCode};
-use crate::globals::{DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY};
+use crate::error::{
+ self, map_km_error, map_or_log_err, wrapped_rkpd_error_to_ks_error, Error, ErrorCode,
+};
+use crate::globals::{
+ get_remotely_provisioned_component_name, DB, ENFORCEMENTS, LEGACY_IMPORTER, SUPER_KEY,
+};
use crate::key_parameter::KeyParameter as KsKeyParam;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::ks_err;
use crate::metrics_store::log_key_creation_event_stats;
use crate::remote_provisioning::RemProvState;
-use crate::rkpd_client::store_rkpd_attestation_key;
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,
+ key_characteristics_to_internal, uid_to_android_user, watchdog as wd, UNDEFINED_NOT_AFTER,
};
use crate::{
database::{
@@ -60,6 +63,7 @@ use android_system_keystore2::aidl::android::system::keystore2::{
KeyMetadata::KeyMetadata, KeyParameters::KeyParameters, ResponseCode::ResponseCode,
};
use anyhow::{anyhow, Context, Result};
+use rkpd_client::store_rkpd_attestation_key;
use std::convert::TryInto;
use std::time::SystemTime;
@@ -77,10 +81,6 @@ pub struct KeystoreSecurityLevel {
// Blob of 32 zeroes used as empty masking key.
static ZERO_BLOB_32: &[u8] = &[0; 32];
-// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime
-// 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
-const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64;
-
impl KeystoreSecurityLevel {
/// Creates a new security level instance wrapped in a
/// BnKeystoreSecurityLevel proxy object. It also enables
@@ -247,7 +247,7 @@ impl KeystoreSecurityLevel {
let super_key = SUPER_KEY
.read()
.unwrap()
- .get_per_boot_key_by_user_id(uid_to_android_user(caller_uid));
+ .get_after_first_unlock_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, || {
@@ -317,7 +317,6 @@ impl KeystoreSecurityLevel {
let (begin_result, upgraded_blob) = self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
key_id_guard,
&km_blob,
blob_metadata.km_uuid().copied(),
@@ -405,8 +404,7 @@ impl KeystoreSecurityLevel {
) -> Result<Vec<KeyParameter>> {
let mut result = params.to_vec();
- // Unconditionally add the CREATION_DATETIME tag and prevent callers from
- // specifying it.
+ // Prevent callers from specifying the CREATION_DATETIME tag.
if params.iter().any(|kp| kp.tag == Tag::CREATION_DATETIME) {
return Err(Error::Rc(ResponseCode::INVALID_ARGUMENT)).context(ks_err!(
"KeystoreSecurityLevel::add_required_parameters: \
@@ -414,12 +412,16 @@ impl KeystoreSecurityLevel {
));
}
+ // Use this variable to refer to notion of "now". This eliminates discrepancies from
+ // quering the clock multiple times.
+ let creation_datetime = SystemTime::now();
+
// 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()
+ creation_datetime
.duration_since(SystemTime::UNIX_EPOCH)
.context(ks_err!(
"KeystoreSecurityLevel::add_required_parameters: \
@@ -462,7 +464,7 @@ impl KeystoreSecurityLevel {
}
if self
.id_rotation_state
- .had_factory_reset_since_id_rotation()
+ .had_factory_reset_since_id_rotation(&creation_datetime)
.context(ks_err!("Call to had_factory_reset_since_id_rotation failed."))?
{
result.push(KeyParameter {
@@ -558,7 +560,6 @@ impl KeystoreSecurityLevel {
issuer_subject,
}) => self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
Some(key_id_guard),
&KeyBlob::Ref(&blob),
blob_metadata.km_uuid().copied(),
@@ -583,40 +584,6 @@ impl KeystoreSecurityLevel {
)
.context(ks_err!("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())
- })
- },
- )
- .context(ks_err!("While generating Key with remote provisioned attestation key."))
- .map(|(mut result, _)| {
- result.certificateChain.push(attestation_certs);
- result
- }),
Some(AttestationKeyInfo::RkpdProvisioned { attestation_key, attestation_certs }) => {
self.upgrade_rkpd_keyblob_if_required_with(&attestation_key.keyBlob, &[], |blob| {
map_km_error({
@@ -766,7 +733,7 @@ impl KeystoreSecurityLevel {
// Import_wrapped_key requires the rebind permission for the new key.
check_key_permission(KeyPerm::Rebind, &key, &None).context(ks_err!())?;
- let super_key = SUPER_KEY.read().unwrap().get_per_boot_key_by_user_id(user_id);
+ let super_key = SUPER_KEY.read().unwrap().get_after_first_unlock_key_by_user_id(user_id);
let (wrapping_key_id_guard, mut wrapping_key_entry) = DB
.with(|db| {
@@ -817,7 +784,6 @@ impl KeystoreSecurityLevel {
let (creation_result, _) = self
.upgrade_keyblob_if_required_with(
- &*self.keymint,
Some(wrapping_key_id_guard),
&wrapping_key_blob,
wrapping_blob_metadata.km_uuid().copied(),
@@ -873,7 +839,6 @@ impl KeystoreSecurityLevel {
fn upgrade_keyblob_if_required_with<T, F>(
&self,
- km_dev: &dyn IKeyMintDevice,
mut key_id_guard: Option<KeyIdGuard>,
key_blob: &KeyBlob,
km_uuid: Option<Uuid>,
@@ -884,7 +849,8 @@ impl KeystoreSecurityLevel {
F: Fn(&[u8]) -> Result<T, Error>,
{
let (v, upgraded_blob) = crate::utils::upgrade_keyblob_if_required_with(
- km_dev,
+ &*self.keymint,
+ self.hw_info.versionNumber,
key_blob,
params,
f,
@@ -922,14 +888,21 @@ impl KeystoreSecurityLevel {
where
F: Fn(&[u8]) -> Result<T, Error>,
{
+ let rpc_name = get_remotely_provisioned_component_name(&self.security_level)
+ .context(ks_err!("Trying to get IRPC name."))?;
crate::utils::upgrade_keyblob_if_required_with(
&*self.keymint,
+ self.hw_info.versionNumber,
key_blob,
params,
f,
|upgraded_blob| {
- store_rkpd_attestation_key(&self.security_level, key_blob, upgraded_blob)
- .context(ks_err!("Failed store_rkpd_attestation_key()."))
+ let _wp = wd::watch_millis("Calling store_rkpd_attestation_key()", 500);
+ if let Err(e) = store_rkpd_attestation_key(&rpc_name, key_blob, upgraded_blob) {
+ Err(wrapped_rkpd_error_to_ks_error(&e)).context(format!("{e:?}"))
+ } else {
+ Ok(())
+ }
},
)
.context(ks_err!())
@@ -954,7 +927,7 @@ impl KeystoreSecurityLevel {
.context(ks_err!("Check permission"))?;
let km_dev = &self.keymint;
- match {
+ let res = {
let _wp = self.watch_millis(
concat!(
"In IKeystoreSecurityLevel::convert_storage_key_to_ephemeral: ",
@@ -963,7 +936,8 @@ impl KeystoreSecurityLevel {
500,
);
map_km_error(km_dev.convertStorageKeyToEphemeral(key_blob))
- } {
+ };
+ match res {
Ok(result) => {
Ok(EphemeralStorageKeyResponse { ephemeralKey: result, upgradedBlob: None })
}
@@ -1090,3 +1064,83 @@ impl IKeystoreSecurityLevel for KeystoreSecurityLevel {
map_or_log_err(result, Ok)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::error::map_km_error;
+ use crate::globals::get_keymint_device;
+ use crate::utils::upgrade_keyblob_if_required_with;
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, AttestationKey::AttestationKey, KeyParameter::KeyParameter,
+ KeyParameterValue::KeyParameterValue, Tag::Tag,
+ };
+ use keystore2_crypto::parse_subject_from_certificate;
+ use rkpd_client::get_rkpd_attestation_key;
+
+ #[test]
+ // This is a helper for a manual test. We want to check that after a system upgrade RKPD
+ // attestation keys can also be upgraded and stored again with RKPD. The steps are:
+ // 1. Run this test and check in stdout that no key upgrade happened.
+ // 2. Perform a system upgrade.
+ // 3. Run this test and check in stdout that key upgrade did happen.
+ //
+ // Note that this test must be run with that same UID every time. Running as root, i.e. UID 0,
+ // should do the trick. Also, use "--nocapture" flag to get stdout.
+ fn test_rkpd_attestation_key_upgrade() {
+ binder::ProcessState::start_thread_pool();
+ let security_level = SecurityLevel::TRUSTED_ENVIRONMENT;
+ let (keymint, info, _) = get_keymint_device(&security_level).unwrap();
+ let key_id = 0;
+ let mut key_upgraded = false;
+
+ let rpc_name = get_remotely_provisioned_component_name(&security_level).unwrap();
+ let key = get_rkpd_attestation_key(&rpc_name, key_id).unwrap();
+ assert!(!key.keyBlob.is_empty());
+ assert!(!key.encodedCertChain.is_empty());
+
+ upgrade_keyblob_if_required_with(
+ &*keymint,
+ info.versionNumber,
+ &key.keyBlob,
+ /*upgrade_params=*/ &[],
+ /*km_op=*/
+ |blob| {
+ let params = vec![
+ KeyParameter {
+ tag: Tag::ALGORITHM,
+ value: KeyParameterValue::Algorithm(Algorithm::AES),
+ },
+ KeyParameter {
+ tag: Tag::ATTESTATION_CHALLENGE,
+ value: KeyParameterValue::Blob(vec![0; 16]),
+ },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) },
+ ];
+ let attestation_key = AttestationKey {
+ keyBlob: blob.to_vec(),
+ attestKeyParams: vec![],
+ issuerSubjectName: parse_subject_from_certificate(&key.encodedCertChain)
+ .unwrap(),
+ };
+
+ map_km_error(keymint.generateKey(&params, Some(&attestation_key)))
+ },
+ /*new_blob_handler=*/
+ |new_blob| {
+ // This handler is only executed if a key upgrade was performed.
+ key_upgraded = true;
+ let _wp = wd::watch_millis("Calling store_rkpd_attestation_key()", 500);
+ store_rkpd_attestation_key(&rpc_name, &key.keyBlob, new_blob).unwrap();
+ Ok(())
+ },
+ )
+ .unwrap();
+
+ if key_upgraded {
+ println!("RKPD key was upgraded and stored with RKPD.");
+ } else {
+ println!("RKPD key was NOT upgraded.");
+ }
+ }
+}
diff --git a/keystore2/src/service.rs b/keystore2/src/service.rs
index 10402286..1459254e 100644
--- a/keystore2/src/service.rs
+++ b/keystore2/src/service.rs
@@ -22,7 +22,7 @@ use crate::ks_err;
use crate::permission::{KeyPerm, KeystorePerm};
use crate::security_level::KeystoreSecurityLevel;
use crate::utils::{
- check_grant_permission, check_key_permission, check_keystore_permission,
+ check_grant_permission, check_key_permission, check_keystore_permission, count_key_entries,
key_parameters_to_authorizations, list_key_entries, uid_to_android_user, watchdog as wd,
};
use crate::{
@@ -126,8 +126,10 @@ 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 super_key = SUPER_KEY
+ .read()
+ .unwrap()
+ .get_after_first_unlock_key_by_user_id(uid_to_android_user(caller_uid));
let (key_id_guard, mut key_entry) = DB
.with(|db| {
@@ -181,8 +183,10 @@ 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));
+ let super_key = SUPER_KEY
+ .read()
+ .unwrap()
+ .get_after_first_unlock_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, || {
@@ -251,7 +255,11 @@ impl KeystoreService {
.context(ks_err!())
}
- fn list_entries(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+ fn get_key_descriptor_for_lookup(
+ &self,
+ domain: Domain,
+ namespace: i64,
+ ) -> Result<KeyDescriptor> {
let mut k = match domain {
Domain::APP => KeyDescriptor {
domain,
@@ -284,14 +292,37 @@ impl KeystoreService {
return Err(e).context(ks_err!("While checking key permission."))?;
}
}
+ Ok(k)
+ }
+
+ fn list_entries(&self, domain: Domain, namespace: i64) -> Result<Vec<KeyDescriptor>> {
+ let k = self.get_key_descriptor_for_lookup(domain, namespace)?;
+
+ DB.with(|db| list_key_entries(&mut db.borrow_mut(), k.domain, k.nspace, None))
+ }
- DB.with(|db| list_key_entries(&mut db.borrow_mut(), k.domain, k.nspace))
+ fn count_num_entries(&self, domain: Domain, namespace: i64) -> Result<i32> {
+ let k = self.get_key_descriptor_for_lookup(domain, namespace)?;
+
+ DB.with(|db| count_key_entries(&mut db.borrow_mut(), k.domain, k.nspace))
+ }
+
+ fn list_entries_batched(
+ &self,
+ domain: Domain,
+ namespace: i64,
+ start_past_alias: Option<&str>,
+ ) -> Result<Vec<KeyDescriptor>> {
+ let k = self.get_key_descriptor_for_lookup(domain, namespace)?;
+ DB.with(|db| list_key_entries(&mut db.borrow_mut(), k.domain, k.nspace, start_past_alias))
}
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));
+ let super_key = SUPER_KEY
+ .read()
+ .unwrap()
+ .get_after_first_unlock_key_by_user_id(uid_to_android_user(caller_uid));
DB.with(|db| {
LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
@@ -312,8 +343,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));
+ let super_key = SUPER_KEY
+ .read()
+ .unwrap()
+ .get_after_first_unlock_key_by_user_id(uid_to_android_user(caller_uid));
DB.with(|db| {
LEGACY_IMPORTER.with_try_import(key, caller_uid, super_key, || {
@@ -389,4 +422,18 @@ impl IKeystoreService for KeystoreService {
let _wp = wd::watch_millis("IKeystoreService::ungrant", 500);
map_or_log_err(self.ungrant(key, grantee_uid), Ok)
}
+ fn listEntriesBatched(
+ &self,
+ domain: Domain,
+ namespace: i64,
+ start_past_alias: Option<&str>,
+ ) -> binder::Result<Vec<KeyDescriptor>> {
+ let _wp = wd::watch_millis("IKeystoreService::listEntriesBatched", 500);
+ map_or_log_err(self.list_entries_batched(domain, namespace, start_past_alias), Ok)
+ }
+
+ fn getNumberOfEntries(&self, domain: Domain, namespace: i64) -> binder::Result<i32> {
+ let _wp = wd::watch_millis("IKeystoreService::getNumberOfEntries", 500);
+ map_or_log_err(self.count_num_entries(domain, namespace), Ok)
+ }
}
diff --git a/keystore2/src/shared_secret_negotiation.rs b/keystore2/src/shared_secret_negotiation.rs
index 42d38d29..ff0ddf8a 100644
--- a/keystore2/src/shared_secret_negotiation.rs
+++ b/keystore2/src/shared_secret_negotiation.rs
@@ -19,19 +19,21 @@ 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,
+ ISharedSecret::BpSharedSecret, ISharedSecret::ISharedSecret,
+ SharedSecretParameters::SharedSecretParameters,
};
use android_security_compat::aidl::android::security::compat::IKeystoreCompatService::IKeystoreCompatService;
use anyhow::Result;
-use keystore2_vintf::{get_aidl_instances, get_hidl_instances};
+use binder::get_declared_instances;
+use keystore2_hal_names::get_hidl_instances;
use std::fmt::{self, Display, Formatter};
use std::time::Duration;
/// This function initiates the shared secret negotiation. It starts a thread and then returns
-/// immediately. The thread consults the vintf manifest to enumerate expected negotiation
-/// participants. It then attempts to connect to all of these participants. If any connection
-/// fails the thread will retry once per second to connect to the failed instance(s) until all of
-/// the instances are connected. It then performs the negotiation.
+/// immediately. The thread gets hal names from the android ServiceManager. It then attempts
+/// to connect to all of these participants. If any connection fails the thread will retry once
+/// per second to connect to the failed instance(s) until all of the instances are connected.
+/// It then performs the negotiation.
///
/// During the first phase of the negotiation it will again try every second until
/// all instances have responded successfully to account for instances that register early but
@@ -62,11 +64,9 @@ enum SharedSecretParticipant {
impl Display for SharedSecretParticipant {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
match self {
- Self::Aidl(instance) => write!(
- f,
- "{}.{}/{}",
- SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance
- ),
+ Self::Aidl(instance) => {
+ write!(f, "{}/{}", <BpSharedSecret as ISharedSecret>::get_descriptor(), instance)
+ }
Self::Hidl { is_strongbox, version: (ma, mi) } => write!(
f,
"{}@V{}.{}::{}/{}",
@@ -109,8 +109,6 @@ fn filter_map_legacy_km_instances(
static KEYMASTER_PACKAGE_NAME: &str = "android.hardware.keymaster";
static KEYMASTER_INTERFACE_NAME: &str = "IKeymasterDevice";
-static SHARED_SECRET_PACKAGE_NAME: &str = "android.hardware.security.sharedsecret";
-static SHARED_SECRET_INTERFACE_NAME: &str = "ISharedSecret";
static COMPAT_PACKAGE_NAME: &str = "android.security.compat";
/// Lists participants.
@@ -121,11 +119,11 @@ fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
let mut legacy_strongbox_found: bool = false;
Ok([(4, 1), (4, 0)]
.iter()
- .map(|(ma, mi)| {
+ .flat_map(|(ma, mi)| {
get_hidl_instances(KEYMASTER_PACKAGE_NAME, *ma, *mi, KEYMASTER_INTERFACE_NAME)
- .into_iter()
+ .iter()
.filter_map(|name| {
- filter_map_legacy_km_instances(name, (*ma, *mi)).and_then(|sp| {
+ 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;
@@ -140,10 +138,9 @@ fn list_participants() -> Result<Vec<SharedSecretParticipant>> {
})
.collect::<Vec<SharedSecretParticipant>>()
})
- .into_iter()
- .flatten()
.chain({
- get_aidl_instances(SHARED_SECRET_PACKAGE_NAME, 1, SHARED_SECRET_INTERFACE_NAME)
+ get_declared_instances(<BpSharedSecret as ISharedSecret>::get_descriptor())
+ .unwrap()
.into_iter()
.map(SharedSecretParticipant::Aidl)
.collect::<Vec<_>>()
@@ -164,8 +161,9 @@ fn connect_participants(
match e {
SharedSecretParticipant::Aidl(instance_name) => {
let service_name = format!(
- "{}.{}/{}",
- SHARED_SECRET_PACKAGE_NAME, SHARED_SECRET_INTERFACE_NAME, instance_name
+ "{}/{}",
+ <BpSharedSecret as ISharedSecret>::get_descriptor(),
+ instance_name
);
match map_binder_status_code(binder::get_interface(&service_name)) {
Err(e) => {
diff --git a/keystore2/src/super_key.rs b/keystore2/src/super_key.rs
index f0002131..11ab734f 100644
--- a/keystore2/src/super_key.rs
+++ b/keystore2/src/super_key.rs
@@ -26,7 +26,6 @@ use crate::{
error::ResponseCode,
key_parameter::{KeyParameter, KeyParameterValue},
ks_err,
- legacy_blob::LegacyBlobLoader,
legacy_importer::LegacyImporter,
raw_device::KeyMintDevice,
utils::{watchdog as wd, AesGcm, AID_KEYSTORE},
@@ -79,25 +78,35 @@ pub struct SuperKeyType<'a> {
pub alias: &'a str,
/// Encryption algorithm
pub algorithm: SuperEncryptionAlgorithm,
+ /// What to call this key in log messages. Not used for anything else.
+ pub name: &'a str,
}
-/// Key used for LskfLocked keys; the corresponding superencryption key is loaded in memory
-/// when the user first unlocks, and remains in memory until the device reboots.
-pub const USER_SUPER_KEY: SuperKeyType =
- SuperKeyType { alias: "USER_SUPER_KEY", algorithm: SuperEncryptionAlgorithm::Aes256Gcm };
-/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
-/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
-/// Symmetric.
-pub const USER_SCREEN_LOCK_BOUND_KEY: SuperKeyType = SuperKeyType {
+/// The user's AfterFirstUnlock super key. This super key is loaded into memory when the user first
+/// unlocks the device, and it remains in memory until the device reboots. This is used to encrypt
+/// keys that require user authentication but not an unlocked device.
+pub const USER_AFTER_FIRST_UNLOCK_SUPER_KEY: SuperKeyType = SuperKeyType {
+ alias: "USER_SUPER_KEY",
+ algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ name: "AfterFirstUnlock super key",
+};
+
+/// The user's UnlockedDeviceRequired symmetric super key. This super key is loaded into memory each
+/// time the user unlocks the device, and it is cleared from memory each time the user locks the
+/// device. This is used to encrypt keys that use the UnlockedDeviceRequired key parameter.
+pub const USER_UNLOCKED_DEVICE_REQUIRED_SYMMETRIC_SUPER_KEY: SuperKeyType = SuperKeyType {
alias: "USER_SCREEN_LOCK_BOUND_KEY",
algorithm: SuperEncryptionAlgorithm::Aes256Gcm,
+ name: "UnlockedDeviceRequired symmetric super key",
};
-/// Key used for ScreenLockBound keys; the corresponding superencryption key is loaded in memory
-/// each time the user enters their LSKF, and cleared from memory each time the device is locked.
-/// Asymmetric, so keys can be encrypted when the device is locked.
-pub const USER_SCREEN_LOCK_BOUND_P521_KEY: SuperKeyType = SuperKeyType {
+
+/// The user's UnlockedDeviceRequired asymmetric super key. This is used to allow, while the device
+/// is locked, the creation of keys that use the UnlockedDeviceRequired key parameter. The private
+/// part of this key is loaded and cleared when the symmetric key is loaded and cleared.
+pub const USER_UNLOCKED_DEVICE_REQUIRED_P521_SUPER_KEY: SuperKeyType = SuperKeyType {
alias: "USER_SCREEN_LOCK_BOUND_P521_KEY",
algorithm: SuperEncryptionAlgorithm::EcdhP521,
+ name: "UnlockedDeviceRequired asymmetric super key",
};
/// Superencryption to apply to a new key.
@@ -105,10 +114,10 @@ pub const USER_SCREEN_LOCK_BOUND_P521_KEY: SuperKeyType = SuperKeyType {
pub enum SuperEncryptionType {
/// Do not superencrypt this key.
None,
- /// Superencrypt with a key that remains in memory from first unlock to reboot.
- LskfBound,
- /// Superencrypt with a key cleared from memory when the device is locked.
- ScreenLockBound,
+ /// Superencrypt with the AfterFirstUnlock super key.
+ AfterFirstUnlock,
+ /// Superencrypt with an UnlockedDeviceRequired super key.
+ UnlockedDeviceRequired,
/// Superencrypt with a key based on the desired boot level
BootLevel(i32),
}
@@ -225,33 +234,33 @@ impl LockedKey {
}
}
-/// Keys for unlocking UNLOCKED_DEVICE_REQUIRED keys, as LockedKeys, complete with
-/// a database descriptor for the encrypting key and the sids for the auth tokens
-/// that can be used to decrypt it.
+/// A user's UnlockedDeviceRequired super keys, encrypted with a biometric-bound key, and
+/// information about that biometric-bound key.
struct BiometricUnlock {
- /// List of auth token SIDs that can be used to unlock these keys.
+ /// List of auth token SIDs that are accepted by the encrypting biometric-bound key.
sids: Vec<i64>,
- /// Database descriptor of key to use to unlock.
+ /// Key descriptor of the encrypting biometric-bound key.
key_desc: KeyDescriptor,
- /// Locked versions of the matching UserSuperKeys fields
- screen_lock_bound: LockedKey,
- screen_lock_bound_private: LockedKey,
+ /// The UnlockedDeviceRequired super keys, encrypted with a biometric-bound key.
+ symmetric: LockedKey,
+ private: LockedKey,
}
#[derive(Default)]
struct UserSuperKeys {
- /// The per boot key is used for LSKF binding of authentication bound keys. There is one
- /// key per android user. The key is stored on flash encrypted with a key derived from a
- /// secret, that is itself derived from the user's lock screen knowledge factor (LSKF).
- /// When the user unlocks the device for the first time, this key is unlocked, i.e., decrypted,
- /// and stays memory resident until the device reboots.
- per_boot: Option<Arc<SuperKey>>,
- /// The screen lock key works like the per boot key with the distinction that it is cleared
- /// from memory when the screen lock is engaged.
- screen_lock_bound: Option<Arc<SuperKey>>,
- /// When the device is locked, screen-lock-bound keys can still be encrypted, using
- /// ECDH public-key encryption. This field holds the decryption private key.
- screen_lock_bound_private: Option<Arc<SuperKey>>,
+ /// The AfterFirstUnlock super key is used for synthetic password binding of authentication
+ /// bound keys. There is one key per android user. The key is stored on flash encrypted with a
+ /// key derived from a secret, that is itself derived from the user's synthetic password. (In
+ /// most cases, the user's synthetic password can, in turn, only be decrypted using the user's
+ /// Lock Screen Knowledge Factor or LSKF.) When the user unlocks the device for the first time,
+ /// this key is unlocked, i.e., decrypted, and stays memory resident until the device reboots.
+ after_first_unlock: Option<Arc<SuperKey>>,
+ /// The UnlockedDeviceRequired symmetric super key works like the AfterFirstUnlock super key
+ /// with the distinction that it is cleared from memory when the device is locked.
+ unlocked_device_required_symmetric: Option<Arc<SuperKey>>,
+ /// When the device is locked, keys that use the UnlockedDeviceRequired key parameter can still
+ /// be created, using ECDH public-key encryption. This field holds the decryption private key.
+ unlocked_device_required_private: Option<Arc<SuperKey>>,
/// Versions of the above two keys, locked behind a biometric.
biometric_unlock: Option<BiometricUnlock>,
}
@@ -336,7 +345,7 @@ impl SuperKeyManager {
break;
}
}
- w.wait().context(ks_err!("property wait failed"))?;
+ w.wait(None).context(ks_err!("property wait failed"))?;
}
Ok(())
}
@@ -352,7 +361,7 @@ impl SuperKeyManager {
self.data.user_keys.remove(&user);
}
- fn install_per_boot_key_for_user(
+ fn install_after_first_unlock_key_for_user(
&mut self,
user: UserId,
super_key: Arc<SuperKey>,
@@ -360,7 +369,7 @@ impl SuperKeyManager {
self.data
.add_key_to_key_index(&super_key)
.context(ks_err!("add_key_to_key_index failed"))?;
- self.data.user_keys.entry(user).or_default().per_boot = Some(super_key);
+ self.data.user_keys.entry(user).or_default().after_first_unlock = Some(super_key);
Ok(())
}
@@ -388,61 +397,21 @@ impl SuperKeyManager {
})
}
- pub fn get_per_boot_key_by_user_id(
+ /// Returns the AfterFirstUnlock superencryption key for the given user ID, or None if the user
+ /// has not yet unlocked the device since boot.
+ pub fn get_after_first_unlock_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)
+ self.get_after_first_unlock_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())
- }
-
- /// This function unlocks the super keys for a given user.
- /// This means the key is loaded from the database, decrypted and placed in the
- /// 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,
- db: &mut KeystoreDB,
- user: UserId,
- pw: &Password,
- legacy_blob_loader: &LegacyBlobLoader,
- ) -> Result<()> {
- let (_, entry) = db
- .get_or_create_key_with(
- Domain::APP,
- user as u64 as i64,
- USER_SUPER_KEY.alias,
- crate::database::KEYSTORE_UUID,
- || {
- // For backward compatibility we need to check if there is a super key present.
- let super_key = legacy_blob_loader
- .load_super_key(user, pw)
- .context(ks_err!("Failed to load legacy key blob."))?;
- let super_key = match super_key {
- None => {
- // No legacy file was found. So we generate a new key.
- generate_aes256_key()
- .context(ks_err!("Failed to generate AES 256 key."))?
- }
- Some(key) => key,
- };
- // Regardless of whether we loaded an old AES128 key or generated a new AES256
- // key as the super key, we derive a AES256 key from the password and re-encrypt
- // the super key before we insert it in the database. The length of the key is
- // preserved by the encryption so we don't need any extra flags to inform us
- // which algorithm to use it with.
- Self::encrypt_with_password(&super_key, pw).context("In create_new_key.")
- },
- )
- .context(ks_err!("Failed to get key id."))?;
-
- self.populate_cache_from_super_key_blob(user, USER_SUPER_KEY.algorithm, entry, pw)
- .context(ks_err!())?;
- Ok(())
+ fn get_after_first_unlock_key_by_user_id_internal(
+ &self,
+ user_id: UserId,
+ ) -> Option<Arc<SuperKey>> {
+ self.data.user_keys.get(&user_id).and_then(|e| e.after_first_unlock.as_ref().cloned())
}
/// Check if a given key is super-encrypted, from its metadata. If so, unwrap the key using
@@ -506,7 +475,7 @@ impl SuperKeyManager {
}
}
- /// Checks if user has setup LSKF, even when super key cache is empty for the user.
+ /// Checks if the user's AfterFirstUnlock super key exists in the database (or legacy database).
/// 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(
@@ -516,7 +485,12 @@ impl SuperKeyManager {
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_AFTER_FIRST_UNLOCK_SUPER_KEY.alias,
+ KeyType::Super,
+ )
.context(ks_err!())?;
if key_in_db {
@@ -526,83 +500,6 @@ impl SuperKeyManager {
}
}
- /// Checks if user has already setup LSKF (i.e. a super key is persisted in the database or the
- /// 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,
- db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- 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))
- .context(ks_err!("Failed to load super key"))?;
-
- match result {
- Some((_, entry)) => {
- let super_key = self
- .populate_cache_from_super_key_blob(user_id, alias.algorithm, entry, pw)
- .context(ks_err!())?;
- Ok(UserState::LskfUnlocked(super_key))
- }
- None => Ok(UserState::Uninitialized),
- }
- }
-
- /// Checks if user has already setup LSKF (i.e. a super key is persisted in the database or the
- /// legacy database). If so, return LskfLocked state.
- /// If the password is provided, generate a new super key, encrypt with the password,
- /// store in the database and populate the super key cache for the new user
- /// and return LskfUnlocked state.
- /// If the password is not provided, return Uninitialized state.
- pub fn check_and_initialize_super_key(
- &mut self,
- db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- 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(ks_err!("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.
- let super_key =
- generate_aes256_key().context(ks_err!("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.
- let (encrypted_super_key, blob_metadata) =
- Self::encrypt_with_password(&super_key, pw).context(ks_err!())?;
-
- let key_entry = db
- .store_super_key(
- user_id,
- &USER_SUPER_KEY,
- &encrypted_super_key,
- &blob_metadata,
- &KeyMetaData::new(),
- )
- .context(ks_err!("Failed to store super key."))?;
-
- let super_key = self
- .populate_cache_from_super_key_blob(
- user_id,
- USER_SUPER_KEY.algorithm,
- key_entry,
- pw,
- )
- .context(ks_err!())?;
- Ok(UserState::LskfUnlocked(super_key))
- } else {
- Ok(UserState::Uninitialized)
- }
- }
-
// 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,
@@ -613,7 +510,8 @@ impl SuperKeyManager {
) -> Result<Arc<SuperKey>> {
let super_key = Self::extract_super_key_from_key_entry(algorithm, entry, pw, None)
.context(ks_err!("Failed to extract super key from key entry"))?;
- self.install_per_boot_key_for_user(user_id, super_key.clone())?;
+ self.install_after_first_unlock_key_for_user(user_id, super_key.clone())
+ .context(ks_err!("Failed to install AfterFirstUnlock super key for user!"))?;
Ok(super_key)
}
@@ -634,11 +532,17 @@ impl SuperKeyManager {
(Some(&EncryptedBy::Password), Some(salt), Some(iv), Some(tag)) => {
// Note that password encryption is AES no matter the value of algorithm.
let key = pw
- .derive_key(salt, AES_256_KEY_LENGTH)
- .context(ks_err!("Failed to generate key from password."))?;
-
- aes_gcm_decrypt(blob, iv, tag, &key)
- .context(ks_err!("Failed to decrypt key blob."))?
+ .derive_key_hkdf(salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive key from password."))?;
+
+ aes_gcm_decrypt(blob, iv, tag, &key).or_else(|_e| {
+ // Handle old key stored before the switch to HKDF.
+ let key = pw
+ .derive_key_pbkdf2(salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive key from password (PBKDF2)."))?;
+ aes_gcm_decrypt(blob, iv, tag, &key)
+ .context(ks_err!("Failed to decrypt key blob."))
+ })?
}
(enc_by, salt, iv, tag) => {
return Err(Error::Rc(ResponseCode::VALUE_CORRUPTED)).context(ks_err!(
@@ -665,14 +569,20 @@ impl SuperKeyManager {
}
/// Encrypts the super key from a key derived from the password, before storing in the database.
+ /// This does not stretch the password; i.e., it assumes that the password is a high-entropy
+ /// synthetic password, not a low-entropy user provided password.
pub fn encrypt_with_password(
super_key: &[u8],
pw: &Password,
) -> Result<(Vec<u8>, BlobMetaData)> {
let salt = generate_salt().context("In encrypt_with_password: Failed to generate salt.")?;
- let derived_key = pw
- .derive_key(&salt, AES_256_KEY_LENGTH)
- .context(ks_err!("Failed to derive password."))?;
+ let derived_key = if android_security_flags::fix_unlocked_device_required_keys_v2() {
+ pw.derive_key_hkdf(&salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive key from password."))?
+ } else {
+ pw.derive_key_pbkdf2(&salt, AES_256_KEY_LENGTH)
+ .context(ks_err!("Failed to derive password."))?
+ };
let mut metadata = BlobMetaData::new();
metadata.add(BlobMetaEntry::EncryptedBy(EncryptedBy::Password));
metadata.add(BlobMetaEntry::Salt(salt));
@@ -683,33 +593,6 @@ impl SuperKeyManager {
Ok((encrypted_key, metadata))
}
- // Encrypt the given key blob with the user's super key, if the super key exists and the device
- // is unlocked. If the super key exists and the device is locked, or LSKF is not setup,
- // return error. Note that it is out of the scope of this function to check if super encryption
- // is required. Such check should be performed before calling this function.
- fn super_encrypt_on_key_init(
- &self,
- db: &mut KeystoreDB,
- legacy_importer: &LegacyImporter,
- user_id: UserId,
- key_blob: &[u8],
- ) -> Result<(Vec<u8>, BlobMetaData)> {
- match self
- .get_user_state(db, legacy_importer, user_id)
- .context(ks_err!("Failed to get user state."))?
- {
- UserState::LskfUnlocked(super_key) => {
- Self::encrypt_with_aes_super_key(key_blob, &super_key)
- .context(ks_err!("Failed to encrypt the key."))
- }
- UserState::LskfLocked => {
- Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."))
- }
- UserState::Uninitialized => Err(Error::Rc(ResponseCode::UNINITIALIZED))
- .context(ks_err!("LSKF is not setup for the user.")),
- }
- }
-
// 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.
@@ -729,6 +612,52 @@ impl SuperKeyManager {
Ok((encrypted_key, metadata))
}
+ // Encrypts a given key_blob using a hybrid approach, which can either use the symmetric super
+ // key or the public super key depending on which is available.
+ //
+ // If the symmetric_key is available, the key_blob is encrypted using symmetric encryption with
+ // the provided symmetric super key. Otherwise, the function loads the public super key from
+ // the KeystoreDB and encrypts the key_blob using ECDH encryption and marks the keyblob to be
+ // re-encrypted with the symmetric super key on the first use.
+ //
+ // This hybrid scheme allows keys that use the UnlockedDeviceRequired key parameter to be
+ // created while the device is locked.
+ fn encrypt_with_hybrid_super_key(
+ key_blob: &[u8],
+ symmetric_key: Option<&SuperKey>,
+ public_key_type: &SuperKeyType,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ ) -> Result<(Vec<u8>, BlobMetaData)> {
+ if let Some(super_key) = symmetric_key {
+ Self::encrypt_with_aes_super_key(key_blob, super_key).context(ks_err!(
+ "Failed to encrypt with UnlockedDeviceRequired symmetric super key."
+ ))
+ } else {
+ // Symmetric key is not available, use public key encryption
+ let loaded = db
+ .load_super_key(public_key_type, user_id)
+ .context(ks_err!("load_super_key failed."))?;
+ let (key_id_guard, key_entry) =
+ loaded.ok_or_else(Error::sys).context(ks_err!("User ECDH super key missing."))?;
+ let public_key = key_entry
+ .metadata()
+ .sec1_public_key()
+ .ok_or_else(Error::sys)
+ .context(ks_err!("sec1_public_key missing."))?;
+ let mut metadata = BlobMetaData::new();
+ let (ephem_key, salt, iv, encrypted_key, aead_tag) =
+ ECDHPrivateKey::encrypt_message(public_key, key_blob)
+ .context(ks_err!("ECDHPrivateKey::encrypt_message failed."))?;
+ metadata.add(BlobMetaEntry::PublicKey(ephem_key));
+ metadata.add(BlobMetaEntry::Salt(salt));
+ metadata.add(BlobMetaEntry::Iv(iv));
+ metadata.add(BlobMetaEntry::AeadTag(aead_tag));
+ SuperKeyIdentifier::DatabaseId(key_id_guard.id()).add_to_metadata(&mut metadata);
+ Ok((encrypted_key, metadata))
+ }
+ }
+
/// Check if super encryption is required and if so, super-encrypt the key to be stored in
/// the database.
#[allow(clippy::too_many_arguments)]
@@ -744,40 +673,42 @@ impl SuperKeyManager {
) -> Result<(Vec<u8>, BlobMetaData)> {
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)
- .context(ks_err!("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(ks_err!("Failed to encrypt with ScreenLockBound key."))
- } else {
- // Symmetric key is not available, use public key encryption
- let loaded = db
- .load_super_key(&USER_SCREEN_LOCK_BOUND_P521_KEY, user_id)
- .context(ks_err!("load_super_key failed."))?;
- let (key_id_guard, key_entry) =
- loaded.ok_or_else(Error::sys).context(ks_err!("User ECDH key missing."))?;
- let public_key = key_entry
- .metadata()
- .sec1_public_key()
- .ok_or_else(Error::sys)
- .context(ks_err!("sec1_public_key missing."))?;
- let mut metadata = BlobMetaData::new();
- let (ephem_key, salt, iv, encrypted_key, aead_tag) =
- ECDHPrivateKey::encrypt_message(public_key, key_blob)
- .context(ks_err!("ECDHPrivateKey::encrypt_message failed."))?;
- metadata.add(BlobMetaEntry::PublicKey(ephem_key));
- metadata.add(BlobMetaEntry::Salt(salt));
- metadata.add(BlobMetaEntry::Iv(iv));
- metadata.add(BlobMetaEntry::AeadTag(aead_tag));
- SuperKeyIdentifier::DatabaseId(key_id_guard.id())
- .add_to_metadata(&mut metadata);
- Ok((encrypted_key, metadata))
+ SuperEncryptionType::AfterFirstUnlock => {
+ // Encrypt the given key blob with the user's AfterFirstUnlock super key. If the
+ // user has not unlocked the device since boot or the super keys were never
+ // initialized for the user for some reason, an error is returned.
+ match self
+ .get_user_state(db, legacy_importer, user_id)
+ .context(ks_err!("Failed to get user state for user {user_id}"))?
+ {
+ UserState::AfterFirstUnlock(super_key) => {
+ Self::encrypt_with_aes_super_key(key_blob, &super_key).context(ks_err!(
+ "Failed to encrypt with AfterFirstUnlock super key for user {user_id}"
+ ))
+ }
+ UserState::BeforeFirstUnlock => {
+ Err(Error::Rc(ResponseCode::LOCKED)).context(ks_err!("Device is locked."))
+ }
+ UserState::Uninitialized => Err(Error::Rc(ResponseCode::UNINITIALIZED))
+ .context(ks_err!("User {user_id} does not have super keys")),
}
}
+ SuperEncryptionType::UnlockedDeviceRequired => {
+ let symmetric_key = self
+ .data
+ .user_keys
+ .get(&user_id)
+ .and_then(|e| e.unlocked_device_required_symmetric.as_ref())
+ .map(|arc| arc.as_ref());
+ Self::encrypt_with_hybrid_super_key(
+ key_blob,
+ symmetric_key,
+ &USER_UNLOCKED_DEVICE_REQUIRED_P521_SUPER_KEY,
+ db,
+ user_id,
+ )
+ .context(ks_err!("Failed to encrypt with UnlockedDeviceRequired hybrid scheme."))
+ }
SuperEncryptionType::BootLevel(level) => {
let key_id = SuperKeyIdentifier::BootLevel(level);
let super_key = self
@@ -809,6 +740,47 @@ impl SuperKeyManager {
}
}
+ fn create_super_key(
+ &mut self,
+ db: &mut KeystoreDB,
+ user_id: UserId,
+ key_type: &SuperKeyType,
+ password: &Password,
+ reencrypt_with: Option<Arc<SuperKey>>,
+ ) -> Result<Arc<SuperKey>> {
+ log::info!("Creating {} for user {}", key_type.name, user_id);
+ let (super_key, public_key) = match key_type.algorithm {
+ SuperEncryptionAlgorithm::Aes256Gcm => {
+ (generate_aes256_key().context(ks_err!("Failed to generate AES-256 key."))?, None)
+ }
+ SuperEncryptionAlgorithm::EcdhP521 => {
+ let key =
+ ECDHPrivateKey::generate().context(ks_err!("Failed to generate ECDH key"))?;
+ (
+ key.private_key().context(ks_err!("private_key failed"))?,
+ Some(key.public_key().context(ks_err!("public_key failed"))?),
+ )
+ }
+ };
+ // Derive an AES-256 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(ks_err!())?;
+ let mut key_metadata = KeyMetaData::new();
+ if let Some(pk) = public_key {
+ key_metadata.add(KeyMetaEntry::Sec1PublicKey(pk));
+ }
+ let key_entry = db
+ .store_super_key(user_id, key_type, &encrypted_super_key, &blob_metadata, &key_metadata)
+ .context(ks_err!("Failed to store super key."))?;
+ Ok(Arc::new(SuperKey {
+ algorithm: key_type.algorithm,
+ key: super_key,
+ id: SuperKeyIdentifier::DatabaseId(key_entry.id()),
+ reencrypt_with,
+ }))
+ }
+
/// Fetch a superencryption key from the database, or create it if it doesn't already exist.
/// 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.
@@ -829,83 +801,59 @@ impl SuperKeyManager {
reencrypt_with,
)?)
} else {
- let (super_key, public_key) = match key_type.algorithm {
- SuperEncryptionAlgorithm::Aes256Gcm => (
- generate_aes256_key().context(ks_err!("Failed to generate AES 256 key."))?,
- None,
- ),
- SuperEncryptionAlgorithm::EcdhP521 => {
- let key = ECDHPrivateKey::generate()
- .context(ks_err!("Failed to generate ECDH key"))?;
- (
- key.private_key().context(ks_err!("private_key failed"))?,
- Some(key.public_key().context(ks_err!("public_key failed"))?),
- )
- }
- };
- // 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(ks_err!())?;
- let mut key_metadata = KeyMetaData::new();
- if let Some(pk) = public_key {
- key_metadata.add(KeyMetaEntry::Sec1PublicKey(pk));
- }
- let key_entry = db
- .store_super_key(
- user_id,
- key_type,
- &encrypted_super_key,
- &blob_metadata,
- &key_metadata,
- )
- .context(ks_err!("Failed to store super key."))?;
- Ok(Arc::new(SuperKey {
- algorithm: key_type.algorithm,
- key: super_key,
- id: SuperKeyIdentifier::DatabaseId(key_entry.id()),
- reencrypt_with,
- }))
+ self.create_super_key(db, user_id, key_type, password, reencrypt_with)
}
}
- /// Decrypt the screen-lock bound keys for this user using the password and store in memory.
- pub fn unlock_screen_lock_bound_key(
+ /// Decrypt the UnlockedDeviceRequired super keys for this user using the password and store
+ /// them in memory. If these keys don't exist yet, create them.
+ pub fn unlock_unlocked_device_required_keys(
&mut self,
db: &mut KeystoreDB,
user_id: UserId,
password: &Password,
) -> Result<()> {
- let (screen_lock_bound, screen_lock_bound_private) = self
+ let (symmetric, private) = self
.data
.user_keys
.get(&user_id)
- .map(|e| (e.screen_lock_bound.clone(), e.screen_lock_bound_private.clone()))
+ .map(|e| {
+ (
+ e.unlocked_device_required_symmetric.clone(),
+ e.unlocked_device_required_private.clone(),
+ )
+ })
.unwrap_or((None, None));
- if screen_lock_bound.is_some() && screen_lock_bound_private.is_some() {
+ if symmetric.is_some() && 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
+ let aes = if let Some(symmetric) = symmetric {
+ // This is weird. If this point is reached only one of the UnlockedDeviceRequired super
+ // keys was initialized. This should never happen.
+ symmetric
} else {
- self.get_or_create_super_key(db, user_id, &USER_SCREEN_LOCK_BOUND_KEY, password, None)
- .context(ks_err!("Trying to get or create symmetric key."))?
+ self.get_or_create_super_key(
+ db,
+ user_id,
+ &USER_UNLOCKED_DEVICE_REQUIRED_SYMMETRIC_SUPER_KEY,
+ password,
+ None,
+ )
+ .context(ks_err!("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
+ let ecdh = if let Some(private) = private {
+ // This is weird. If this point is reached only one of the UnlockedDeviceRequired super
+ // keys was initialized. This should never happen.
+ private
} else {
self.get_or_create_super_key(
db,
user_id,
- &USER_SCREEN_LOCK_BOUND_P521_KEY,
+ &USER_UNLOCKED_DEVICE_REQUIRED_P521_SUPER_KEY,
password,
Some(aes.clone()),
)
@@ -915,86 +863,119 @@ impl SuperKeyManager {
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);
+ entry.unlocked_device_required_symmetric = Some(aes);
+ entry.unlocked_device_required_private = Some(ecdh);
Ok(())
}
- /// Wipe the screen-lock bound keys for this user from memory.
- pub fn lock_screen_lock_bound_key(
+ /// Protects the user's UnlockedDeviceRequired super keys in a way such that they can only be
+ /// unlocked by the enabled unlock methods.
+ pub fn lock_unlocked_device_required_keys(
&mut self,
db: &mut KeystoreDB,
user_id: UserId,
unlocking_sids: &[i64],
+ weak_unlock_enabled: bool,
) {
- log::info!("Locking screen bound for user {} sids {:?}", user_id, unlocking_sids);
- let mut entry = self.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(),
- entry.screen_lock_bound_private.as_ref().cloned(),
- ) {
- let res = (|| -> Result<()> {
- let key_desc = KeyMintDevice::internal_descriptor(format!(
- "biometric_unlock_key_{}",
- user_id
- ));
- let encrypting_key = generate_aes256_key()?;
- let km_dev: KeyMintDevice =
- KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
- .context(ks_err!("KeyMintDevice::get failed"))?;
- let mut key_params = vec![
- KeyParameterValue::Algorithm(Algorithm::AES),
- KeyParameterValue::KeySize(256),
- KeyParameterValue::BlockMode(BlockMode::GCM),
- KeyParameterValue::PaddingMode(PaddingMode::NONE),
- KeyParameterValue::CallerNonce,
- KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
- KeyParameterValue::MinMacLength(128),
- KeyParameterValue::AuthTimeout(BIOMETRIC_AUTH_TIMEOUT_S),
- KeyParameterValue::HardwareAuthenticatorType(
- HardwareAuthenticatorType::FINGERPRINT,
- ),
- ];
- for sid in unlocking_sids {
- key_params.push(KeyParameterValue::UserSecureID(*sid));
- }
- let key_params: Vec<KmKeyParameter> =
- key_params.into_iter().map(|x| x.into()).collect();
- km_dev.create_and_store_key(
- db,
- &key_desc,
- KeyType::Client, /* TODO Should be Super b/189470584 */
- |dev| {
- let _wp = wd::watch_millis(
- "In lock_screen_lock_bound_key: calling importKey.",
- 500,
- );
- dev.importKey(
- key_params.as_slice(),
- KeyFormat::RAW,
- &encrypting_key,
- None,
- )
- },
- )?;
- entry.biometric_unlock = Some(BiometricUnlock {
- sids: unlocking_sids.into(),
- key_desc,
- screen_lock_bound: LockedKey::new(&encrypting_key, &aes)?,
- screen_lock_bound_private: LockedKey::new(&encrypting_key, &ecdh)?,
- });
- Ok(())
- })();
- // There is no reason to propagate an error here upwards. We must discard
- // entry.screen_lock_bound* in any case.
- if let Err(e) = res {
- log::error!("Error setting up biometric unlock: {:#?}", e);
+ let entry = self.data.user_keys.entry(user_id).or_default();
+ if unlocking_sids.is_empty() {
+ if android_security_flags::fix_unlocked_device_required_keys_v2() {
+ entry.biometric_unlock = None;
+ }
+ } else if let (Some(aes), Some(ecdh)) = (
+ entry.unlocked_device_required_symmetric.as_ref().cloned(),
+ entry.unlocked_device_required_private.as_ref().cloned(),
+ ) {
+ // If class 3 biometric unlock methods are enabled, create a biometric-encrypted copy of
+ // the keys. Do this even if weak unlock methods are enabled too; in that case we'll
+ // also retain a plaintext copy of the keys, but that copy will be wiped later if weak
+ // unlock methods expire. So we need the biometric-encrypted copy too just in case.
+ let res = (|| -> Result<()> {
+ let key_desc =
+ KeyMintDevice::internal_descriptor(format!("biometric_unlock_key_{}", user_id));
+ let encrypting_key = generate_aes256_key()?;
+ let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
+ .context(ks_err!("KeyMintDevice::get failed"))?;
+ let mut key_params = vec![
+ KeyParameterValue::Algorithm(Algorithm::AES),
+ KeyParameterValue::KeySize(256),
+ KeyParameterValue::BlockMode(BlockMode::GCM),
+ KeyParameterValue::PaddingMode(PaddingMode::NONE),
+ KeyParameterValue::CallerNonce,
+ KeyParameterValue::KeyPurpose(KeyPurpose::DECRYPT),
+ KeyParameterValue::MinMacLength(128),
+ KeyParameterValue::AuthTimeout(BIOMETRIC_AUTH_TIMEOUT_S),
+ KeyParameterValue::HardwareAuthenticatorType(
+ HardwareAuthenticatorType::FINGERPRINT,
+ ),
+ ];
+ for sid in unlocking_sids {
+ key_params.push(KeyParameterValue::UserSecureID(*sid));
}
+ let key_params: Vec<KmKeyParameter> =
+ key_params.into_iter().map(|x| x.into()).collect();
+ km_dev.create_and_store_key(
+ db,
+ &key_desc,
+ KeyType::Client, /* TODO Should be Super b/189470584 */
+ |dev| {
+ let _wp = wd::watch_millis(
+ "In lock_unlocked_device_required_keys: calling importKey.",
+ 500,
+ );
+ dev.importKey(key_params.as_slice(), KeyFormat::RAW, &encrypting_key, None)
+ },
+ )?;
+ entry.biometric_unlock = Some(BiometricUnlock {
+ sids: unlocking_sids.into(),
+ key_desc,
+ symmetric: LockedKey::new(&encrypting_key, &aes)?,
+ private: LockedKey::new(&encrypting_key, &ecdh)?,
+ });
+ Ok(())
+ })();
+ if let Err(e) = res {
+ log::error!("Error setting up biometric unlock: {:#?}", e);
+ // The caller can't do anything about the error, and for security reasons we still
+ // wipe the keys (unless a weak unlock method is enabled). So just log the error.
}
}
- entry.screen_lock_bound = None;
- entry.screen_lock_bound_private = None;
+ // Wipe the plaintext copy of the keys, unless a weak unlock method is enabled.
+ if !weak_unlock_enabled {
+ entry.unlocked_device_required_symmetric = None;
+ entry.unlocked_device_required_private = None;
+ }
+ Self::log_status_of_unlocked_device_required_keys(user_id, entry);
+ }
+
+ pub fn wipe_plaintext_unlocked_device_required_keys(&mut self, user_id: UserId) {
+ let entry = self.data.user_keys.entry(user_id).or_default();
+ entry.unlocked_device_required_symmetric = None;
+ entry.unlocked_device_required_private = None;
+ Self::log_status_of_unlocked_device_required_keys(user_id, entry);
+ }
+
+ pub fn wipe_all_unlocked_device_required_keys(&mut self, user_id: UserId) {
+ let entry = self.data.user_keys.entry(user_id).or_default();
+ entry.unlocked_device_required_symmetric = None;
+ entry.unlocked_device_required_private = None;
+ entry.biometric_unlock = None;
+ Self::log_status_of_unlocked_device_required_keys(user_id, entry);
+ }
+
+ fn log_status_of_unlocked_device_required_keys(user_id: UserId, entry: &UserSuperKeys) {
+ let status = match (
+ // Note: the status of the symmetric and private keys should always be in sync.
+ // So we only check one here.
+ entry.unlocked_device_required_symmetric.is_some(),
+ entry.biometric_unlock.is_some(),
+ ) {
+ (false, false) => "fully protected",
+ (false, true) => "biometric-encrypted",
+ (true, false) => "retained in plaintext",
+ (true, true) => "retained in plaintext, with biometric-encrypted copy too",
+ };
+ log::info!("UnlockedDeviceRequired super keys for user {user_id} are {status}.");
}
/// User has unlocked, not using a password. See if any of our stored auth tokens can be used
@@ -1004,7 +985,17 @@ impl SuperKeyManager {
db: &mut KeystoreDB,
user_id: UserId,
) -> Result<()> {
- let mut entry = self.data.user_keys.entry(user_id).or_default();
+ let entry = self.data.user_keys.entry(user_id).or_default();
+ if android_security_flags::fix_unlocked_device_required_keys_v2()
+ && entry.unlocked_device_required_symmetric.is_some()
+ && entry.unlocked_device_required_private.is_some()
+ {
+ // If the keys are already cached in plaintext, then there is no need to decrypt the
+ // biometric-encrypted copy. Both copies can be present here if the user has both
+ // class 3 biometric and weak unlock methods enabled, and the device was unlocked before
+ // the weak unlock methods expired.
+ return Ok(());
+ }
if let Some(biometric) = entry.biometric_unlock.as_ref() {
let (key_id_guard, key_entry) = db
.load_key_entry(
@@ -1017,12 +1008,14 @@ impl SuperKeyManager {
.context(ks_err!("load_key_entry failed"))?;
let km_dev: KeyMintDevice = KeyMintDevice::get(SecurityLevel::TRUSTED_ENVIRONMENT)
.context(ks_err!("KeyMintDevice::get failed"))?;
+ let mut errs = vec![];
for sid in &biometric.sids {
- if let Some((auth_token_entry, _)) = db.find_auth_token_entry(|entry| {
- entry.auth_token().userId == *sid || entry.auth_token().authenticatorId == *sid
+ let sid = *sid;
+ if let Some(auth_token_entry) = db.find_auth_token_entry(|entry| {
+ entry.auth_token().userId == sid || entry.auth_token().authenticatorId == sid
}) {
let res: Result<(Arc<SuperKey>, Arc<SuperKey>)> = (|| {
- let slb = biometric.screen_lock_bound.decrypt(
+ let symmetric = biometric.symmetric.decrypt(
db,
&km_dev,
&key_id_guard,
@@ -1030,31 +1023,38 @@ impl SuperKeyManager {
auth_token_entry.auth_token(),
None,
)?;
- let slbp = biometric.screen_lock_bound_private.decrypt(
+ let private = biometric.private.decrypt(
db,
&km_dev,
&key_id_guard,
&key_entry,
auth_token_entry.auth_token(),
- Some(slb.clone()),
+ Some(symmetric.clone()),
)?;
- Ok((slb, slbp))
+ Ok((symmetric, private))
})();
match res {
- 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)?;
- log::info!("Successfully unlocked with biometric");
+ Ok((symmetric, private)) => {
+ entry.unlocked_device_required_symmetric = Some(symmetric.clone());
+ entry.unlocked_device_required_private = Some(private.clone());
+ self.data.add_key_to_key_index(&symmetric)?;
+ self.data.add_key_to_key_index(&private)?;
+ log::info!("Successfully unlocked user {user_id} with biometric {sid}",);
return Ok(());
}
Err(e) => {
- log::warn!("attempt failed: {:?}", e)
+ // Don't log an error yet, as some other biometric SID might work.
+ errs.push((sid, e));
}
}
}
}
+ if !errs.is_empty() {
+ log::warn!("biometric unlock failed for all SIDs, with errors:");
+ for (sid, err) in errs {
+ log::warn!(" biometric {sid}: {err}");
+ }
+ }
}
Ok(())
}
@@ -1068,8 +1068,8 @@ impl SuperKeyManager {
legacy_importer: &LegacyImporter,
user_id: UserId,
) -> Result<UserState> {
- match self.get_per_boot_key_by_user_id_internal(user_id) {
- Some(super_key) => Ok(UserState::LskfUnlocked(super_key)),
+ match self.get_after_first_unlock_key_by_user_id_internal(user_id) {
+ Some(super_key) => Ok(UserState::AfterFirstUnlock(super_key)),
None => {
// Check if a super key exists in the database or legacy database.
// If so, return locked user state.
@@ -1077,7 +1077,7 @@ impl SuperKeyManager {
.super_key_exists_in_db_for_user(db, legacy_importer, user_id)
.context(ks_err!())?
{
- Ok(UserState::LskfLocked)
+ Ok(UserState::BeforeFirstUnlock)
} else {
Ok(UserState::Uninitialized)
}
@@ -1085,108 +1085,200 @@ 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(
+ /// Deletes all keys and super keys for the given user.
+ /// This is called when a user is deleted.
+ pub fn remove_user(
&mut self,
db: &mut KeystoreDB,
legacy_importer: &LegacyImporter,
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(ks_err!("Trying to delete keys from the db."))?;
- // Lskf is now removed in Keystore.
- Ok(UserState::Uninitialized)
+ ) -> Result<()> {
+ log::info!("remove_user(user={user_id})");
+ // Mark keys created on behalf of the user as unreferenced.
+ legacy_importer
+ .bulk_delete_user(user_id, false)
+ .context(ks_err!("Trying to delete legacy keys."))?;
+ db.unbind_keys_for_user(user_id, false).context(ks_err!("Error in unbinding keys."))?;
+
+ // Delete super key in cache, if exists.
+ self.forget_all_keys_for_user(user_id);
+ Ok(())
+ }
+
+ /// Deletes all authentication bound keys and super keys for the given user. The user must be
+ /// unlocked before this function is called. This function is used to transition a user to
+ /// swipe.
+ pub fn reset_user(
+ &mut self,
+ db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: UserId,
+ ) -> Result<()> {
+ log::info!("reset_user(user={user_id})");
+ match self.get_user_state(db, legacy_importer, user_id)? {
+ UserState::Uninitialized => {
+ Err(Error::sys()).context(ks_err!("Tried to reset an uninitialized user!"))
}
- 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))
+ UserState::BeforeFirstUnlock => {
+ Err(Error::sys()).context(ks_err!("Tried to reset a locked user's password!"))
}
- 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)
+ UserState::AfterFirstUnlock(_) => {
+ // Mark keys created on behalf of the user as unreferenced.
+ legacy_importer
+ .bulk_delete_user(user_id, true)
+ .context(ks_err!("Trying to delete legacy keys."))?;
+ db.unbind_keys_for_user(user_id, true)
+ .context(ks_err!("Error in unbinding keys."))?;
+
+ // Delete super key in cache, if exists.
+ self.forget_all_keys_for_user(user_id);
+ Ok(())
}
}
}
- /// 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(
+ /// If the user hasn't been initialized yet, then this function generates the user's
+ /// AfterFirstUnlock super key and sets the user's state to AfterFirstUnlock. Otherwise this
+ /// function returns an error.
+ pub fn init_user(
&mut self,
db: &mut KeystoreDB,
legacy_importer: &LegacyImporter,
user_id: UserId,
password: &Password,
- ) -> Result<UserState> {
- match self.get_per_boot_key_by_user_id_internal(user_id) {
- Some(super_key) => {
- log::info!("Trying to unlock when already unlocked.");
- Ok(UserState::LskfUnlocked(super_key))
+ ) -> Result<()> {
+ log::info!("init_user(user={user_id})");
+ match self.get_user_state(db, legacy_importer, user_id)? {
+ UserState::AfterFirstUnlock(_) | UserState::BeforeFirstUnlock => {
+ Err(Error::sys()).context(ks_err!("Tried to re-init an initialized user!"))
}
- 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(ks_err!("Failed to unlock super key."))
+ UserState::Uninitialized => {
+ // Generate a new super key.
+ let super_key =
+ generate_aes256_key().context(ks_err!("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.
+ let (encrypted_super_key, blob_metadata) =
+ Self::encrypt_with_password(&super_key, password)
+ .context(ks_err!("Failed to encrypt super key with password!"))?;
+
+ let key_entry = db
+ .store_super_key(
+ user_id,
+ &USER_AFTER_FIRST_UNLOCK_SUPER_KEY,
+ &encrypted_super_key,
+ &blob_metadata,
+ &KeyMetaData::new(),
+ )
+ .context(ks_err!("Failed to store super key."))?;
+
+ self.populate_cache_from_super_key_blob(
+ user_id,
+ USER_AFTER_FIRST_UNLOCK_SUPER_KEY.algorithm,
+ key_entry,
+ password,
+ )
+ .context(ks_err!("Failed to initialize user!"))?;
+ Ok(())
}
}
}
- /// Delete all the keys created on behalf of the user.
- /// If 'keep_non_super_encrypted_keys' is set to true, delete only the super key and super
- /// encrypted keys.
- pub fn reset_user(
+ /// Initializes the given user by creating their super keys, both AfterFirstUnlock and
+ /// UnlockedDeviceRequired. If allow_existing is true, then the user already being initialized
+ /// is not considered an error.
+ pub fn initialize_user(
&mut self,
db: &mut KeystoreDB,
legacy_importer: &LegacyImporter,
user_id: UserId,
- keep_non_super_encrypted_keys: bool,
+ password: &Password,
+ allow_existing: bool,
) -> Result<()> {
- // Mark keys created on behalf of the user as unreferenced.
- legacy_importer
- .bulk_delete_user(user_id, keep_non_super_encrypted_keys)
- .context(ks_err!("Trying to delete legacy keys."))?;
- db.unbind_keys_for_user(user_id, keep_non_super_encrypted_keys)
- .context(ks_err!("Error in unbinding keys."))?;
+ // Create the AfterFirstUnlock super key.
+ if self.super_key_exists_in_db_for_user(db, legacy_importer, user_id)? {
+ log::info!("AfterFirstUnlock super key already exists");
+ if !allow_existing {
+ return Err(Error::sys()).context(ks_err!("Tried to re-init an initialized user!"));
+ }
+ } else {
+ let super_key = self
+ .create_super_key(db, user_id, &USER_AFTER_FIRST_UNLOCK_SUPER_KEY, password, None)
+ .context(ks_err!("Failed to create AfterFirstUnlock super key"))?;
- // Delete super key in cache, if exists.
- self.forget_all_keys_for_user(user_id);
- Ok(())
+ self.install_after_first_unlock_key_for_user(user_id, super_key)
+ .context(ks_err!("Failed to install AfterFirstUnlock super key for user"))?;
+ }
+
+ // Create the UnlockedDeviceRequired super keys.
+ self.unlock_unlocked_device_required_keys(db, user_id, password)
+ .context(ks_err!("Failed to create UnlockedDeviceRequired super keys"))
+ }
+
+ /// Unlocks the given user with the given password.
+ ///
+ /// If the user state is BeforeFirstUnlock:
+ /// - Unlock the user's AfterFirstUnlock super key
+ /// - Unlock the user's UnlockedDeviceRequired super keys
+ ///
+ /// If the user state is AfterFirstUnlock:
+ /// - Unlock the user's UnlockedDeviceRequired super keys only
+ ///
+ pub fn unlock_user(
+ &mut self,
+ db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: UserId,
+ password: &Password,
+ ) -> Result<()> {
+ log::info!("unlock_user(user={user_id})");
+ match self.get_user_state(db, legacy_importer, user_id)? {
+ UserState::AfterFirstUnlock(_) => {
+ self.unlock_unlocked_device_required_keys(db, user_id, password)
+ }
+ UserState::Uninitialized => {
+ Err(Error::sys()).context(ks_err!("Tried to unlock an uninitialized user!"))
+ }
+ UserState::BeforeFirstUnlock => {
+ let alias = &USER_AFTER_FIRST_UNLOCK_SUPER_KEY;
+ let result = legacy_importer
+ .with_try_import_super_key(user_id, password, || {
+ db.load_super_key(alias, user_id)
+ })
+ .context(ks_err!("Failed to load super key"))?;
+
+ match result {
+ Some((_, entry)) => {
+ self.populate_cache_from_super_key_blob(
+ user_id,
+ alias.algorithm,
+ entry,
+ password,
+ )
+ .context(ks_err!("Failed when unlocking user."))?;
+ self.unlock_unlocked_device_required_keys(db, user_id, password)
+ }
+ None => {
+ Err(Error::sys()).context(ks_err!("Locked user does not have a super key!"))
+ }
+ }
+ }
+ }
}
}
/// 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.
+ // The user's super keys exist, and the user has unlocked the device at least once since boot.
+ // Hence, the AfterFirstUnlock super key is available in the cache.
+ AfterFirstUnlock(Arc<SuperKey>),
+ // The user's super keys exist, but the user hasn't unlocked the device at least once since
+ // boot. Hence, the AfterFirstUnlock and UnlockedDeviceRequired super keys are not available in
+ // the cache. However, they exist in the database in encrypted form.
+ BeforeFirstUnlock,
+ // The user's super keys don't exist. I.e., there's no user with the given user ID, or the user
+ // is in the process of being created or destroyed.
Uninitialized,
}
@@ -1233,3 +1325,390 @@ impl<'a> Deref for KeyBlob<'a> {
}
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use crate::database::tests::make_bootlevel_key_entry;
+ use crate::database::tests::make_test_key_entry;
+ use crate::database::tests::new_test_db;
+ use rand::prelude::*;
+ const USER_ID: u32 = 0;
+ const TEST_KEY_ALIAS: &str = "TEST_KEY";
+ const TEST_BOOT_KEY_ALIAS: &str = "TEST_BOOT_KEY";
+
+ pub fn generate_password_blob() -> Password<'static> {
+ let mut rng = rand::thread_rng();
+ let mut password = vec![0u8; 64];
+ rng.fill_bytes(&mut password);
+
+ let mut zvec = ZVec::new(64).expect("Failed to create ZVec");
+ zvec[..].copy_from_slice(&password[..]);
+
+ Password::Owned(zvec)
+ }
+
+ fn setup_test(pw: &Password) -> (Arc<RwLock<SuperKeyManager>>, KeystoreDB, LegacyImporter) {
+ let mut keystore_db = new_test_db().unwrap();
+ let mut legacy_importer = LegacyImporter::new(Arc::new(Default::default()));
+ legacy_importer.set_empty();
+ let skm: Arc<RwLock<SuperKeyManager>> = Default::default();
+ assert!(skm
+ .write()
+ .unwrap()
+ .init_user(&mut keystore_db, &legacy_importer, USER_ID, pw)
+ .is_ok());
+ (skm, keystore_db, legacy_importer)
+ }
+
+ fn assert_unlocked(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+ ) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::AfterFirstUnlock(_) => {}
+ _ => panic!("{}", err_msg),
+ }
+ }
+
+ fn assert_locked(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+ ) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::BeforeFirstUnlock => {}
+ _ => panic!("{}", err_msg),
+ }
+ }
+
+ fn assert_uninitialized(
+ skm: &Arc<RwLock<SuperKeyManager>>,
+ keystore_db: &mut KeystoreDB,
+ legacy_importer: &LegacyImporter,
+ user_id: u32,
+ err_msg: &str,
+ ) {
+ let user_state =
+ skm.write().unwrap().get_user_state(keystore_db, legacy_importer, user_id).unwrap();
+ match user_state {
+ UserState::Uninitialized => {}
+ _ => panic!("{}", err_msg),
+ }
+ }
+
+ #[test]
+ fn test_init_user() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+ }
+
+ #[test]
+ fn test_unlock_user() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
+ .is_ok());
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user did not unlock!",
+ );
+ }
+
+ #[test]
+ fn test_unlock_wrong_password() {
+ let pw: Password = generate_password_blob();
+ let wrong_pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &wrong_pw)
+ .is_err());
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was unlocked with an incorrect password!",
+ );
+ }
+
+ #[test]
+ fn test_unlock_user_idempotent() {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+
+ for _ in 0..5 {
+ assert!(skm
+ .write()
+ .unwrap()
+ .unlock_user(&mut keystore_db, &legacy_importer, USER_ID, &pw)
+ .is_ok());
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user did not unlock!",
+ );
+ }
+ }
+
+ fn test_user_removal(locked: bool) {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ assert!(make_test_key_entry(
+ &mut keystore_db,
+ Domain::APP,
+ USER_ID.into(),
+ TEST_KEY_ALIAS,
+ None
+ )
+ .is_ok());
+ assert!(make_bootlevel_key_entry(
+ &mut keystore_db,
+ Domain::APP,
+ USER_ID.into(),
+ TEST_BOOT_KEY_ALIAS,
+ false
+ )
+ .is_ok());
+
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+
+ if locked {
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+ }
+
+ assert!(skm
+ .write()
+ .unwrap()
+ .remove_user(&mut keystore_db, &legacy_importer, USER_ID)
+ .is_ok());
+ assert_uninitialized(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not removed!",
+ );
+
+ assert!(!skm
+ .write()
+ .unwrap()
+ .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
+ .unwrap());
+
+ assert!(!keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(!keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ }
+
+ fn test_user_reset(locked: bool) {
+ let pw: Password = generate_password_blob();
+ let (skm, mut keystore_db, legacy_importer) = setup_test(&pw);
+ assert_unlocked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not unlocked after initialization!",
+ );
+
+ assert!(make_test_key_entry(
+ &mut keystore_db,
+ Domain::APP,
+ USER_ID.into(),
+ TEST_KEY_ALIAS,
+ None
+ )
+ .is_ok());
+ assert!(make_bootlevel_key_entry(
+ &mut keystore_db,
+ Domain::APP,
+ USER_ID.into(),
+ TEST_BOOT_KEY_ALIAS,
+ false
+ )
+ .is_ok());
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+
+ if locked {
+ skm.write().unwrap().data.user_keys.clear();
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "Clearing the cache did not lock the user!",
+ );
+ assert!(skm
+ .write()
+ .unwrap()
+ .reset_user(&mut keystore_db, &legacy_importer, USER_ID)
+ .is_err());
+ assert_locked(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "User state should not have changed!",
+ );
+
+ // Keys should still exist.
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ } else {
+ assert!(skm
+ .write()
+ .unwrap()
+ .reset_user(&mut keystore_db, &legacy_importer, USER_ID)
+ .is_ok());
+ assert_uninitialized(
+ &skm,
+ &mut keystore_db,
+ &legacy_importer,
+ USER_ID,
+ "The user was not reset!",
+ );
+ assert!(!skm
+ .write()
+ .unwrap()
+ .super_key_exists_in_db_for_user(&mut keystore_db, &legacy_importer, USER_ID)
+ .unwrap());
+
+ // Auth bound key should no longer exist.
+ assert!(!keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ assert!(keystore_db
+ .key_exists(Domain::APP, USER_ID.into(), TEST_BOOT_KEY_ALIAS, KeyType::Client)
+ .unwrap());
+ }
+ }
+
+ #[test]
+ fn test_remove_unlocked_user() {
+ test_user_removal(false);
+ }
+
+ #[test]
+ fn test_remove_locked_user() {
+ test_user_removal(true);
+ }
+
+ #[test]
+ fn test_reset_unlocked_user() {
+ test_user_reset(false);
+ }
+
+ #[test]
+ fn test_reset_locked_user() {
+ test_user_reset(true);
+ }
+}
diff --git a/keystore2/src/sw_keyblob.rs b/keystore2/src/sw_keyblob.rs
new file mode 100644
index 00000000..47ab49fd
--- /dev/null
+++ b/keystore2/src/sw_keyblob.rs
@@ -0,0 +1,1036 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Code for parsing software-backed keyblobs, as emitted by the C++ reference implementation of
+//! KeyMint.
+
+use crate::error::Error;
+use crate::ks_err;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType,
+ KeyFormat::KeyFormat, KeyOrigin::KeyOrigin, KeyParameter::KeyParameter,
+ KeyParameterValue::KeyParameterValue, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ Tag::Tag, TagType::TagType,
+};
+use anyhow::Result;
+use keystore2_crypto::hmac_sha256;
+use std::mem::size_of;
+
+/// Root of trust value.
+const SOFTWARE_ROOT_OF_TRUST: &[u8] = b"SW";
+
+/// Error macro.
+macro_rules! bloberr {
+ { $($arg:tt)+ } => {
+ anyhow::Error::new(Error::Km(ErrorCode::INVALID_KEY_BLOB)).context(ks_err!($($arg)+))
+ };
+}
+
+/// Get the `KeyParameterValue` associated with a tag from a collection of `KeyParameter`s.
+fn get_tag_value(params: &[KeyParameter], tag: Tag) -> Option<&KeyParameterValue> {
+ params.iter().find_map(|kp| if kp.tag == tag { Some(&kp.value) } else { None })
+}
+
+/// Get the [`TagType`] for a [`Tag`].
+fn tag_type(tag: &Tag) -> TagType {
+ TagType((tag.0 as u32 & 0xf0000000) as i32)
+}
+
+/// Extract key material and combined key characteristics from a legacy authenticated keyblob.
+pub fn export_key(
+ data: &[u8],
+ params: &[KeyParameter],
+) -> Result<(KeyFormat, Vec<u8>, Vec<KeyParameter>)> {
+ let hidden = hidden_params(params, &[SOFTWARE_ROOT_OF_TRUST]);
+ let KeyBlob { key_material, hw_enforced, sw_enforced } =
+ KeyBlob::new_from_serialized(data, &hidden)?;
+
+ let mut combined = hw_enforced;
+ combined.extend_from_slice(&sw_enforced);
+
+ let algo_val =
+ get_tag_value(&combined, Tag::ALGORITHM).ok_or_else(|| bloberr!("No algorithm found!"))?;
+
+ let format = match algo_val {
+ KeyParameterValue::Algorithm(Algorithm::AES)
+ | KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES)
+ | KeyParameterValue::Algorithm(Algorithm::HMAC) => KeyFormat::RAW,
+ KeyParameterValue::Algorithm(Algorithm::RSA)
+ | KeyParameterValue::Algorithm(Algorithm::EC) => KeyFormat::PKCS8,
+ _ => return Err(bloberr!("Unexpected algorithm {:?}", algo_val)),
+ };
+
+ let key_material = match (format, algo_val) {
+ (KeyFormat::PKCS8, KeyParameterValue::Algorithm(Algorithm::EC)) => {
+ // Key material format depends on the curve.
+ let curve = get_tag_value(&combined, Tag::EC_CURVE)
+ .ok_or_else(|| bloberr!("Failed to determine curve for EC key!"))?;
+ match curve {
+ KeyParameterValue::EcCurve(EcCurve::CURVE_25519) => key_material,
+ KeyParameterValue::EcCurve(EcCurve::P_224) => {
+ pkcs8_wrap_nist_key(&key_material, EcCurve::P_224)?
+ }
+ KeyParameterValue::EcCurve(EcCurve::P_256) => {
+ pkcs8_wrap_nist_key(&key_material, EcCurve::P_256)?
+ }
+ KeyParameterValue::EcCurve(EcCurve::P_384) => {
+ pkcs8_wrap_nist_key(&key_material, EcCurve::P_384)?
+ }
+ KeyParameterValue::EcCurve(EcCurve::P_521) => {
+ pkcs8_wrap_nist_key(&key_material, EcCurve::P_521)?
+ }
+ _ => {
+ return Err(bloberr!("Unexpected EC curve {curve:?}"));
+ }
+ }
+ }
+ (KeyFormat::RAW, _) => key_material,
+ (format, algo) => {
+ return Err(bloberr!(
+ "Unsupported combination of {format:?} format for {algo:?} algorithm"
+ ));
+ }
+ };
+ Ok((format, key_material, combined))
+}
+
+/// DER-encoded `AlgorithmIdentifier` for a P-224 key.
+const DER_ALGORITHM_ID_P224: &[u8] = &[
+ 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
+ 0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
+ 0x06, 0x05, // OBJECT IDENTIFIER (param)
+ 0x2b, 0x81, 0x04, 0x00, 0x21, // 1.3.132.0.33 (secp224r1) }
+];
+
+/// DER-encoded `AlgorithmIdentifier` for a P-256 key.
+const DER_ALGORITHM_ID_P256: &[u8] = &[
+ 0x30, 0x13, // SEQUENCE (AlgorithmIdentifier) {
+ 0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
+ 0x06, 0x08, // OBJECT IDENTIFIER (param)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x03, 0x01, 0x07, // 1.2.840.10045.3.1.7 (secp256r1) }
+];
+
+/// DER-encoded `AlgorithmIdentifier` for a P-384 key.
+const DER_ALGORITHM_ID_P384: &[u8] = &[
+ 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
+ 0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
+ 0x06, 0x05, // OBJECT IDENTIFIER (param)
+ 0x2b, 0x81, 0x04, 0x00, 0x22, // 1.3.132.0.34 (secp384r1) }
+];
+
+/// DER-encoded `AlgorithmIdentifier` for a P-384 key.
+const DER_ALGORITHM_ID_P521: &[u8] = &[
+ 0x30, 0x10, // SEQUENCE (AlgorithmIdentifier) {
+ 0x06, 0x07, // OBJECT IDENTIFIER (algorithm)
+ 0x2a, 0x86, 0x48, 0xce, 0x3d, 0x02, 0x01, // 1.2.840.10045.2.1 (ecPublicKey)
+ 0x06, 0x05, // OBJECT IDENTIFIER (param)
+ 0x2b, 0x81, 0x04, 0x00, 0x23, // 1.3.132.0.35 (secp521r1) }
+];
+
+/// DER-encoded integer value zero.
+const DER_VERSION_0: &[u8] = &[
+ 0x02, // INTEGER
+ 0x01, // len
+ 0x00, // value 0
+];
+
+/// Given a NIST curve EC key in the form of a DER-encoded `ECPrivateKey`
+/// (RFC 5915 s3), wrap it in a DER-encoded PKCS#8 format (RFC 5208 s5).
+fn pkcs8_wrap_nist_key(nist_key: &[u8], curve: EcCurve) -> Result<Vec<u8>> {
+ let der_alg_id = match curve {
+ EcCurve::P_224 => DER_ALGORITHM_ID_P224,
+ EcCurve::P_256 => DER_ALGORITHM_ID_P256,
+ EcCurve::P_384 => DER_ALGORITHM_ID_P384,
+ EcCurve::P_521 => DER_ALGORITHM_ID_P521,
+ _ => return Err(bloberr!("unknown curve {curve:?}")),
+ };
+
+ // Output format is:
+ //
+ // PrivateKeyInfo ::= SEQUENCE {
+ // version INTEGER,
+ // privateKeyAlgorithm AlgorithmIdentifier,
+ // privateKey OCTET STRING,
+ // }
+ //
+ // Start by building the OCTET STRING so we know its length.
+ let mut nist_key_octet_string = Vec::new();
+ nist_key_octet_string.push(0x04); // OCTET STRING
+ add_der_len(&mut nist_key_octet_string, nist_key.len())?;
+ nist_key_octet_string.extend_from_slice(nist_key);
+
+ let mut buf = Vec::new();
+ buf.push(0x30); // SEQUENCE
+ add_der_len(&mut buf, DER_VERSION_0.len() + der_alg_id.len() + nist_key_octet_string.len())?;
+ buf.extend_from_slice(DER_VERSION_0);
+ buf.extend_from_slice(der_alg_id);
+ buf.extend_from_slice(&nist_key_octet_string);
+ Ok(buf)
+}
+
+/// Append a DER-encoded length value to the given buffer.
+fn add_der_len(buf: &mut Vec<u8>, len: usize) -> Result<()> {
+ if len <= 0x7f {
+ buf.push(len as u8)
+ } else if len <= 0xff {
+ buf.push(0x81); // One length octet to come
+ buf.push(len as u8);
+ } else if len <= 0xffff {
+ buf.push(0x82); // Two length octets to come
+ buf.push((len >> 8) as u8);
+ buf.push((len & 0xff) as u8);
+ } else {
+ return Err(bloberr!("Unsupported DER length {len}"));
+ }
+ Ok(())
+}
+
+/// Plaintext key blob, with key characteristics.
+#[derive(PartialEq, Eq)]
+struct KeyBlob {
+ /// Raw key material.
+ key_material: Vec<u8>,
+ /// Hardware-enforced key characteristics.
+ hw_enforced: Vec<KeyParameter>,
+ /// Software-enforced key characteristics.
+ sw_enforced: Vec<KeyParameter>,
+}
+
+impl KeyBlob {
+ /// Key blob version.
+ const KEY_BLOB_VERSION: u8 = 0;
+
+ /// Hard-coded HMAC key used for keyblob authentication.
+ const LEGACY_HMAC_KEY: &'static [u8] = b"IntegrityAssuredBlob0\0";
+
+ /// Size (in bytes) of appended MAC.
+ const MAC_LEN: usize = 8;
+
+ /// Parse a serialized [`KeyBlob`].
+ fn new_from_serialized(mut data: &[u8], hidden: &[KeyParameter]) -> Result<Self> {
+ // Keyblob needs to be at least long enough for:
+ // - version byte,
+ // - 4-byte len for key material
+ // - 4-byte len for hw_enforced params
+ // - 4-byte len for sw_enforced params
+ // - MAC tag.
+ if data.len() < (1 + 3 * size_of::<u32>() + Self::MAC_LEN) {
+ return Err(bloberr!("blob not long enough (len = {})", data.len()));
+ }
+
+ // Check the HMAC in the last 8 bytes before doing anything else.
+ let mac = &data[data.len() - Self::MAC_LEN..];
+ let computed_mac = Self::compute_hmac(&data[..data.len() - Self::MAC_LEN], hidden)?;
+ if mac != computed_mac {
+ return Err(bloberr!("invalid key blob"));
+ }
+
+ let version = consume_u8(&mut data)?;
+ if version != Self::KEY_BLOB_VERSION {
+ return Err(bloberr!("unexpected blob version {}", version));
+ }
+ let key_material = consume_vec(&mut data)?;
+ let hw_enforced = deserialize_params(&mut data)?;
+ let sw_enforced = deserialize_params(&mut data)?;
+
+ // Should just be the (already-checked) MAC left.
+ let rest = &data[Self::MAC_LEN..];
+ if !rest.is_empty() {
+ return Err(bloberr!("extra data (len {})", rest.len()));
+ }
+ Ok(KeyBlob { key_material, hw_enforced, sw_enforced })
+ }
+
+ /// Compute the authentication HMAC for a KeyBlob. This is built as:
+ /// HMAC-SHA256(HK, data || serialize(hidden))
+ /// with HK = b"IntegrityAssuredBlob0\0".
+ fn compute_hmac(data: &[u8], hidden: &[KeyParameter]) -> Result<Vec<u8>> {
+ let hidden_data = serialize_params(hidden)?;
+ let mut combined = data.to_vec();
+ combined.extend_from_slice(&hidden_data);
+ let mut tag = hmac_sha256(Self::LEGACY_HMAC_KEY, &combined)?;
+ tag.truncate(Self::MAC_LEN);
+ Ok(tag)
+ }
+}
+
+/// Build the parameters that are used as the hidden input to HMAC calculations:
+/// - `ApplicationId(data)` if present
+/// - `ApplicationData(data)` if present
+/// - (repeated) `RootOfTrust(rot)` where `rot` is a hardcoded piece of root of trust information.
+fn hidden_params(params: &[KeyParameter], rots: &[&[u8]]) -> Vec<KeyParameter> {
+ let mut results = Vec::new();
+ if let Some(app_id) = get_tag_value(params, Tag::APPLICATION_ID) {
+ results.push(KeyParameter { tag: Tag::APPLICATION_ID, value: app_id.clone() });
+ }
+ if let Some(app_data) = get_tag_value(params, Tag::APPLICATION_DATA) {
+ results.push(KeyParameter { tag: Tag::APPLICATION_DATA, value: app_data.clone() });
+ }
+ for rot in rots {
+ results.push(KeyParameter {
+ tag: Tag::ROOT_OF_TRUST,
+ value: KeyParameterValue::Blob(rot.to_vec()),
+ });
+ }
+ results
+}
+
+/// Retrieve a `u8` from the start of the given slice, if possible.
+fn consume_u8(data: &mut &[u8]) -> Result<u8> {
+ match data.first() {
+ Some(b) => {
+ *data = &(*data)[1..];
+ Ok(*b)
+ }
+ None => Err(bloberr!("failed to find 1 byte")),
+ }
+}
+
+/// Move past a bool value from the start of the given slice, if possible.
+/// Bool values should only be included if `true`, so fail if the value
+/// is anything other than 1.
+fn consume_bool(data: &mut &[u8]) -> Result<bool> {
+ let b = consume_u8(data)?;
+ if b == 0x01 {
+ Ok(true)
+ } else {
+ Err(bloberr!("bool value other than 1 encountered"))
+ }
+}
+
+/// Retrieve a (host-ordered) `u32` from the start of the given slice, if possible.
+fn consume_u32(data: &mut &[u8]) -> Result<u32> {
+ const LEN: usize = size_of::<u32>();
+ if data.len() < LEN {
+ return Err(bloberr!("failed to find {LEN} bytes"));
+ }
+ let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked
+ *data = &(*data)[LEN..];
+ Ok(u32::from_ne_bytes(chunk))
+}
+
+/// Retrieve a (host-ordered) `i32` from the start of the given slice, if possible.
+fn consume_i32(data: &mut &[u8]) -> Result<i32> {
+ const LEN: usize = size_of::<i32>();
+ if data.len() < LEN {
+ return Err(bloberr!("failed to find {LEN} bytes"));
+ }
+ let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked
+ *data = &(*data)[4..];
+ Ok(i32::from_ne_bytes(chunk))
+}
+
+/// Retrieve a (host-ordered) `i64` from the start of the given slice, if possible.
+fn consume_i64(data: &mut &[u8]) -> Result<i64> {
+ const LEN: usize = size_of::<i64>();
+ if data.len() < LEN {
+ return Err(bloberr!("failed to find {LEN} bytes"));
+ }
+ let chunk: [u8; LEN] = data[..LEN].try_into().unwrap(); // safe: just checked
+ *data = &(*data)[LEN..];
+ Ok(i64::from_ne_bytes(chunk))
+}
+
+/// Retrieve a vector of bytes from the start of the given slice, if possible,
+/// with the length of the data expected to appear as a host-ordered `u32` prefix.
+fn consume_vec(data: &mut &[u8]) -> Result<Vec<u8>> {
+ let len = consume_u32(data)? as usize;
+ if len > data.len() {
+ return Err(bloberr!("failed to find {} bytes", len));
+ }
+ let result = data[..len].to_vec();
+ *data = &(*data)[len..];
+ Ok(result)
+}
+
+/// Retrieve the contents of a tag of `TagType::Bytes`. The `data` parameter holds
+/// the as-yet unparsed data, and a length and offset are read from this (and consumed).
+/// This length and offset refer to a location in the combined `blob_data`; however,
+/// the offset is expected to be the next unconsumed chunk of `blob_data`, as indicated
+/// by `next_blob_offset` (which itself is updated as a result of consuming the data).
+fn consume_blob(
+ data: &mut &[u8],
+ next_blob_offset: &mut usize,
+ blob_data: &[u8],
+) -> Result<Vec<u8>> {
+ let data_len = consume_u32(data)? as usize;
+ let data_offset = consume_u32(data)? as usize;
+ // Expect the blob data to come from the next offset in the initial blob chunk.
+ if data_offset != *next_blob_offset {
+ return Err(bloberr!("got blob offset {} instead of {}", data_offset, next_blob_offset));
+ }
+ if (data_offset + data_len) > blob_data.len() {
+ return Err(bloberr!(
+ "blob at offset [{}..{}+{}] goes beyond blob data size {}",
+ data_offset,
+ data_offset,
+ data_len,
+ blob_data.len(),
+ ));
+ }
+
+ let slice = &blob_data[data_offset..data_offset + data_len];
+ *next_blob_offset += data_len;
+ Ok(slice.to_vec())
+}
+
+/// Deserialize a collection of [`KeyParam`]s in legacy serialized format. The provided slice is
+/// modified to contain the unconsumed part of the data.
+fn deserialize_params(data: &mut &[u8]) -> Result<Vec<KeyParameter>> {
+ let blob_data_size = consume_u32(data)? as usize;
+ if blob_data_size > data.len() {
+ return Err(bloberr!(
+ "blob data size {} bigger than data (len={})",
+ blob_data_size,
+ data.len()
+ ));
+ }
+
+ let blob_data = &data[..blob_data_size];
+ let mut next_blob_offset = 0;
+
+ // Move past the blob data.
+ *data = &data[blob_data_size..];
+
+ let param_count = consume_u32(data)? as usize;
+ let param_size = consume_u32(data)? as usize;
+ if param_size > data.len() {
+ return Err(bloberr!(
+ "size mismatch 4+{}+4+4+{} > {}",
+ blob_data_size,
+ param_size,
+ data.len()
+ ));
+ }
+
+ let mut results = Vec::new();
+ for _i in 0..param_count {
+ let tag_num = consume_u32(data)? as i32;
+ let tag = Tag(tag_num);
+ let value = match tag_type(&tag) {
+ TagType::INVALID => return Err(bloberr!("invalid tag {:?} encountered", tag)),
+ TagType::ENUM | TagType::ENUM_REP => {
+ let val = consume_i32(data)?;
+ match tag {
+ Tag::ALGORITHM => KeyParameterValue::Algorithm(Algorithm(val)),
+ Tag::BLOCK_MODE => KeyParameterValue::BlockMode(BlockMode(val)),
+ Tag::PADDING => KeyParameterValue::PaddingMode(PaddingMode(val)),
+ Tag::DIGEST | Tag::RSA_OAEP_MGF_DIGEST => {
+ KeyParameterValue::Digest(Digest(val))
+ }
+ Tag::EC_CURVE => KeyParameterValue::EcCurve(EcCurve(val)),
+ Tag::ORIGIN => KeyParameterValue::Origin(KeyOrigin(val)),
+ Tag::PURPOSE => KeyParameterValue::KeyPurpose(KeyPurpose(val)),
+ Tag::USER_AUTH_TYPE => {
+ KeyParameterValue::HardwareAuthenticatorType(HardwareAuthenticatorType(val))
+ }
+ _ => KeyParameterValue::Integer(val),
+ }
+ }
+ TagType::UINT | TagType::UINT_REP => KeyParameterValue::Integer(consume_i32(data)?),
+ TagType::ULONG | TagType::ULONG_REP => {
+ KeyParameterValue::LongInteger(consume_i64(data)?)
+ }
+ TagType::DATE => KeyParameterValue::DateTime(consume_i64(data)?),
+ TagType::BOOL => KeyParameterValue::BoolValue(consume_bool(data)?),
+ TagType::BIGNUM | TagType::BYTES => {
+ KeyParameterValue::Blob(consume_blob(data, &mut next_blob_offset, blob_data)?)
+ }
+ _ => return Err(bloberr!("unexpected tag type for {:?}", tag)),
+ };
+ results.push(KeyParameter { tag, value });
+ }
+
+ Ok(results)
+}
+
+/// Serialize a collection of [`KeyParameter`]s into a format that is compatible with previous
+/// implementations:
+///
+/// ```text
+/// [0..4] Size B of `TagType::Bytes` data, in host order.
+/// [4..4+B] (*) Concatenated contents of each `TagType::Bytes` tag.
+/// [4+B..4+B+4] Count N of the number of parameters, in host order.
+/// [8+B..8+B+4] Size Z of encoded parameters.
+/// [12+B..12+B+Z] Serialized parameters one after another.
+/// ```
+///
+/// Individual parameters are serialized in the last chunk as:
+///
+/// ```text
+/// [0..4] Tag number, in host order.
+/// Followed by one of the following depending on the tag's `TagType`; all integers in host order:
+/// [4..5] Bool value (`TagType::Bool`)
+/// [4..8] i32 values (`TagType::Uint[Rep]`, `TagType::Enum[Rep]`)
+/// [4..12] i64 values, in host order (`TagType::UlongRep`, `TagType::Date`)
+/// [4..8] + [8..12] Size + offset of data in (*) above (`TagType::Bytes`, `TagType::Bignum`)
+/// ```
+fn serialize_params(params: &[KeyParameter]) -> Result<Vec<u8>> {
+ // First 4 bytes are the length of the combined [`TagType::Bytes`] data; come back to set that
+ // in a moment.
+ let mut result = vec![0; 4];
+
+ // Next append the contents of all of the [`TagType::Bytes`] data.
+ let mut blob_size = 0u32;
+ for param in params {
+ let tag_type = tag_type(&param.tag);
+ if let KeyParameterValue::Blob(v) = &param.value {
+ if tag_type != TagType::BIGNUM && tag_type != TagType::BYTES {
+ return Err(bloberr!("unexpected tag type for tag {:?} with blob", param.tag));
+ }
+ result.extend_from_slice(v);
+ blob_size += v.len() as u32;
+ }
+ }
+ // Go back and fill in the combined blob length in native order at the start.
+ result[..4].clone_from_slice(&blob_size.to_ne_bytes());
+
+ result.extend_from_slice(&(params.len() as u32).to_ne_bytes());
+
+ let params_size_offset = result.len();
+ result.extend_from_slice(&[0u8; 4]); // placeholder for size of elements
+ let first_param_offset = result.len();
+ let mut blob_offset = 0u32;
+ for param in params {
+ result.extend_from_slice(&(param.tag.0 as u32).to_ne_bytes());
+ match &param.value {
+ KeyParameterValue::Invalid(_v) => {
+ return Err(bloberr!("invalid tag found in {:?}", param))
+ }
+
+ // Enum-holding variants.
+ KeyParameterValue::Algorithm(v) => {
+ result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
+ }
+ KeyParameterValue::BlockMode(v) => {
+ result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
+ }
+ KeyParameterValue::PaddingMode(v) => {
+ result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
+ }
+ KeyParameterValue::Digest(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()),
+ KeyParameterValue::EcCurve(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()),
+ KeyParameterValue::Origin(v) => result.extend_from_slice(&(v.0 as u32).to_ne_bytes()),
+ KeyParameterValue::KeyPurpose(v) => {
+ result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
+ }
+ KeyParameterValue::HardwareAuthenticatorType(v) => {
+ result.extend_from_slice(&(v.0 as u32).to_ne_bytes())
+ }
+
+ // Value-holding variants.
+ KeyParameterValue::Integer(v) => result.extend_from_slice(&(*v as u32).to_ne_bytes()),
+ KeyParameterValue::BoolValue(_v) => result.push(0x01u8),
+ KeyParameterValue::LongInteger(v) | KeyParameterValue::DateTime(v) => {
+ result.extend_from_slice(&(*v as u64).to_ne_bytes())
+ }
+ KeyParameterValue::Blob(v) => {
+ let blob_len = v.len() as u32;
+ result.extend_from_slice(&blob_len.to_ne_bytes());
+ result.extend_from_slice(&blob_offset.to_ne_bytes());
+ blob_offset += blob_len;
+ }
+
+ _ => return Err(bloberr!("unknown value found in {:?}", param)),
+ }
+ }
+ let serialized_size = (result.len() - first_param_offset) as u32;
+
+ // Go back and fill in the total serialized size.
+ result[params_size_offset..params_size_offset + 4]
+ .clone_from_slice(&serialized_size.to_ne_bytes());
+ Ok(result)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ KeyOrigin::KeyOrigin, KeyParameter::KeyParameter,
+ KeyParameterValue::KeyParameterValue as KPV, KeyPurpose::KeyPurpose,
+ PaddingMode::PaddingMode, Tag::Tag,
+ };
+
+ macro_rules! expect_err {
+ ($result:expr, $err_msg:expr) => {
+ assert!(
+ $result.is_err(),
+ "Expected error containing '{}', got success {:?}",
+ $err_msg,
+ $result
+ );
+ let err = $result.err();
+ assert!(
+ format!("{:?}", err).contains($err_msg),
+ "Unexpected error {:?}, doesn't contain '{}'",
+ err,
+ $err_msg
+ );
+ };
+ }
+
+ #[test]
+ fn test_consume_u8() {
+ let buffer = [1, 2];
+ let mut data = &buffer[..];
+ assert_eq!(1u8, consume_u8(&mut data).unwrap());
+ assert_eq!(2u8, consume_u8(&mut data).unwrap());
+ let result = consume_u8(&mut data);
+ expect_err!(result, "failed to find 1 byte");
+ }
+
+ #[test]
+ fn test_consume_u32() {
+ // All supported platforms are little-endian.
+ let buffer = [
+ 0x01, 0x02, 0x03, 0x04, // little-endian u32
+ 0x04, 0x03, 0x02, 0x01, // little-endian u32
+ 0x11, 0x12, 0x13,
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(0x04030201u32, consume_u32(&mut data).unwrap());
+ assert_eq!(0x01020304u32, consume_u32(&mut data).unwrap());
+ let result = consume_u32(&mut data);
+ expect_err!(result, "failed to find 4 bytes");
+ }
+
+ #[test]
+ fn test_consume_i64() {
+ // All supported platforms are little-endian.
+ let buffer = [
+ 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, // little-endian i64
+ 0x08, 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, // little-endian i64
+ 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(0x0807060504030201i64, consume_i64(&mut data).unwrap());
+ assert_eq!(0x0102030405060708i64, consume_i64(&mut data).unwrap());
+ let result = consume_i64(&mut data);
+ expect_err!(result, "failed to find 8 bytes");
+ }
+
+ #[test]
+ fn test_consume_vec() {
+ let buffer = [
+ 0x01, 0x00, 0x00, 0x00, 0xaa, //
+ 0x00, 0x00, 0x00, 0x00, //
+ 0x01, 0x00, 0x00, 0x00, 0xbb, //
+ 0x07, 0x00, 0x00, 0x00, 0xbb, // not enough data
+ ];
+ let mut data = &buffer[..];
+ assert_eq!(vec![0xaa], consume_vec(&mut data).unwrap());
+ assert_eq!(Vec::<u8>::new(), consume_vec(&mut data).unwrap());
+ assert_eq!(vec![0xbb], consume_vec(&mut data).unwrap());
+ let result = consume_vec(&mut data);
+ expect_err!(result, "failed to find 7 bytes");
+
+ let buffer = [
+ 0x01, 0x00, 0x00, //
+ ];
+ let mut data = &buffer[..];
+ let result = consume_vec(&mut data);
+ expect_err!(result, "failed to find 4 bytes");
+ }
+
+ #[test]
+ fn test_key_new_from_serialized() {
+ let hidden = hidden_params(&[], &[SOFTWARE_ROOT_OF_TRUST]);
+ // Test data originally generated by instrumenting Cuttlefish C++ KeyMint while running VTS
+ // tests.
+ let tests = [
+ (
+ concat!(
+ "0010000000d43c2f04f948521b81bdbf001310f5920000000000000000000000",
+ "00000000000c0000006400000002000010200000000300003080000000010000",
+ "2000000000010000200100000004000020020000000600002001000000be0200",
+ "1000000000c1020030b0ad0100c20200307b150300bd020060a8bb52407b0100",
+ "00ce02003011643401cf020030000000003b06b13ae6ae6671",
+ ),
+ KeyBlob {
+ key_material: hex::decode("d43c2f04f948521b81bdbf001310f592").unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::AES) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(128) },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KPV::KeyPurpose(KeyPurpose::ENCRYPT),
+ },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KPV::KeyPurpose(KeyPurpose::DECRYPT),
+ },
+ KeyParameter {
+ tag: Tag::BLOCK_MODE,
+ value: KPV::BlockMode(BlockMode::CBC),
+ },
+ KeyParameter {
+ tag: Tag::PADDING,
+ value: KPV::PaddingMode(PaddingMode::NONE),
+ },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871769000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::RAW),
+ ),
+ (
+ concat!(
+ "00df0000003081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6b",
+ "a14e46ab7ca532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654",
+ "e9e79413cd503eae3d9cf68ed24f47a00706052b81040023a181890381860004",
+ "006b840f0db0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d14",
+ "0743bdd028db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b7",
+ "45e30142e90685646661550344113aaf28bdee6cb02d19df1faab4398556a909",
+ "7d6f64b95209601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3",
+ "b877984d000000000000000000000000000000000c0000006400000002000010",
+ "030000000a000010030000000100002002000000010000200300000005000020",
+ "000000000300003009020000be02001000000000c1020030b0ad0100c2020030",
+ "7b150300bd02006018d352407b010000ce02003011643401cf02003000000000",
+ "2f69002e55e9b0a3"
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "3081dc020101044200b6ce876b947e263d61b8e3998d50dc0afb6ba14e46ab7c",
+ "a532fbe2a379b155d0a5bb99265402857b1601fb20be6c244bf654e9e79413cd",
+ "503eae3d9cf68ed24f47a00706052b81040023a181890381860004006b840f0d",
+ "b0b12f074ab916c7773cfa7d42967c9e5b4fae09cf999f7e116d140743bdd028",
+ "db0a3fcc670e721b9f00bc7fb70aa401c7d6de6582fc26962a29b745e30142e9",
+ "0685646661550344113aaf28bdee6cb02d19df1faab4398556a9097d6f64b952",
+ "09601a549389a311231c6cce78354f2cdbc3a904abf70686f5f0c3b877984d",
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::EC) },
+ KeyParameter { tag: Tag::EC_CURVE, value: KPV::EcCurve(EcCurve::P_521) },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KPV::KeyPurpose(KeyPurpose::SIGN),
+ },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KPV::KeyPurpose(KeyPurpose::VERIFY),
+ },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(521) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871775000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::PKCS8),
+ ),
+ (
+ concat!(
+ "0037000000541d4c440223650d5f51753c1abd80c725034485551e874d62327c",
+ "65f6247a057f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc00000000",
+ "0000000000000000000000000c00000064000000020000108000000003000030",
+ "b801000001000020020000000100002003000000050000200400000008000030",
+ "00010000be02001000000000c1020030b0ad0100c20200307b150300bd020060",
+ "00d752407b010000ce02003011643401cf0200300000000036e6986ffc45fbb0",
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "541d4c440223650d5f51753c1abd80c725034485551e874d62327c65f6247a05",
+ "7f1218bd6c8cd7d319103ddb823fc11fb6c2c7268b5acc"
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter {
+ tag: Tag::ALGORITHM,
+ value: KPV::Algorithm(Algorithm::HMAC),
+ },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(440) },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KPV::KeyPurpose(KeyPurpose::SIGN),
+ },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KPV::KeyPurpose(KeyPurpose::VERIFY),
+ },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::SHA_2_256) },
+ KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KPV::Integer(256) },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871776000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ Some(KeyFormat::RAW),
+ ),
+ (
+ concat!(
+ "00a8040000308204a40201000282010100bc47b5c71116766669b91fa747df87",
+ "a1963df83956569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082",
+ "825c7c6e482a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31",
+ "c1ced84878edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c3973",
+ "6838151642eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d",
+ "5337c8bf9245d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0de",
+ "b0957d61dbba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2",
+ "540c84e45c4a99fb338b76bba7722856b5113341c349708937228f167d238ed8",
+ "efb9cc19547dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af",
+ "59fe87421af9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d3703",
+ "8bf9f588ae20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178",
+ "a848e06d558c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88e",
+ "e610260e406c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30a",
+ "e7a31f8262da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73a",
+ "e6f8b29a9144eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b",
+ "4217c8db50db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817",
+ "d9380b190bd382aaffa37785759f285194c11a188bccde0e2e2902818100fb23",
+ "3335770c9f3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a63",
+ "3f7790d1011bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f2",
+ "7a74852d6c7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd78009490",
+ "4c2856d2b944fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f45990281",
+ "8100bfecf2bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a7158458",
+ "18e01181ff06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb",
+ "0078b77fb5b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc84",
+ "20fcbf48d1eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b57",
+ "298902818100ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd",
+ "3765559994a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55",
+ "dad696d3821def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2",
+ "d8480ed07a7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f6332",
+ "38f521ba764902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6",
+ "603c6265f70018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d46",
+ "93fa48e3a9abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b",
+ "2a57e18a2e131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc",
+ "2af4ed75760858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1",
+ "c0a258d882984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4",
+ "340a9fae64a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c5526",
+ "1941b3654533b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a2",
+ "47f56a337e3b9845b4f2b61356000000000000000000000000000000000d0000",
+ "007000000002000010010000000300003000080000c800005001000100000000",
+ "0001000020020000000100002003000000050000200000000006000020010000",
+ "00be02001000000000c1020030b0ad0100c20200307b150300bd020060a8bb52",
+ "407b010000ce02003011643401cf02003000000000544862e9c961e857",
+ ),
+ KeyBlob {
+ key_material: hex::decode(concat!(
+ "308204a40201000282010100bc47b5c71116766669b91fa747df87a1963df839",
+ "56569d4ac232aeba8a246c0ec73bf606374a6d07f30c2162f97082825c7c6e48",
+ "2a2841dfeaec1429d84e52c54a6b2f760dec952c9c44a3c3a80f31c1ced84878",
+ "edd4858059071c4d20d9ab0aae978bd68c1eb448e174a9736c39736838151642",
+ "eda8215107375865a99a57f29467c74c40f37b0221b93ec3f4f22d5337c8bf92",
+ "45d56936196a92b1dea315ecce8785f9fa9b7d159ca207612cc0deb0957d61db",
+ "ba5d9bd38784f4fecbf233b04e686a340528665ecd03db8e8a09b2540c84e45c",
+ "4a99fb338b76bba7722856b5113341c349708937228f167d238ed8efb9cc1954",
+ "7dd620f6a90d95f07e50bfe102030100010282010002f91b69d9af59fe87421a",
+ "f9ba60f15c77f9c1c90effd6634332876f8ee5a116b126f55d37038bf9f588ae",
+ "20c8d951d842e35c9ef35a7822d3ebf72c0b7c3e229b289ae2e178a848e06d55",
+ "8c2e03d26871ee98a35f370d461ff1c4acc39d684de680a25ec88ee610260e40",
+ "6c400bdeb2893b2d0330cb483e662fa5abd24c2b82143e85dfe30ae7a31f8262",
+ "da2903d882b35a34a26b699ff2d812bad4b126a0065ec0e101d73ae6f8b29a91",
+ "44eb83f54940a371fc7416c2c0370df6a41cb5391f17ba33239e1b4217c8db50",
+ "db5c6bf77ccf621354ecc652a4f7196054c254566fd7b3bc0f3817d9380b190b",
+ "d382aaffa37785759f285194c11a188bccde0e2e2902818100fb233335770c9f",
+ "3cbd4b6ede5f12d03c449b1997bce06a8249bc3de99972fd0d0a633f7790d101",
+ "1bf5eedee16fa45a9107a910656ecaee364ce9edb4369843be71f27a74852d6c",
+ "7215a6cc60d9803bcac544922f806d8e5844e0ddd914bd780094904c2856d2b9",
+ "44fade3fb1d67d4a33fb7663a9ab660ab372c2e4868a0f459902818100bfecf2",
+ "bb4012e880fd065a0b088f2d757af2878d3f1305f21ce7a715845818e01181ff",
+ "06b2f406239fc50808ce3dbe7b68ec01174913c0f237feb3c8c7eb0078b77fb5",
+ "b8f214b72f6d3835b1a7ebe8b132feb6cb34ab09ce22b98160fc8420fcbf48d1",
+ "eee49f874e902f049b206a61a095f0405a4935e7c5e49757ab7b572989028181",
+ "00ec0049383e16f3716de5fc5b2677148efe5dceb02483b43399bd3765559994",
+ "a9f3900eed7a7e9e8f3b0eee0e660eca392e3cb736cae612f39e55dad696d382",
+ "1def10d1f8bbca52f5e6d8e7893ffbdcb491aafdc17bebf86f84d2d8480ed07a",
+ "7bf9209d20ef6e79429489d4cb7768281a2f7e32ec1830fd6f633238f521ba76",
+ "4902818100b2c3ce5751580b4e51df3fb175387f5c24b79040a4d6603c6265f7",
+ "0018b441ff3aef7d8e4cd2f480ec0906f1c4c0481304e8861f9d4693fa48e3a9",
+ "abc362859eeb343e1c5507ac94b5439ce7ac04154a2fb886a4819b2a57e18a2e",
+ "131b412ac4a09b004766959cdf357745f003e272aab3de02e2d5bc2af4ed7576",
+ "0858ab181902818061d19c2a8dcacde104b97f7c4fae11216157c1c0a258d882",
+ "984d12383a73dc56fe2ac93512bb321df9706ecdb2f70a44c949c4340a9fae64",
+ "a0646cf51f37c58c08bebde91667b3b2fa7c895f7983d4786c55261941b36545",
+ "33b0598383ebbcffcdf28b6cf13d376e3a70b49b14d8d06e8563a247f56a337e",
+ "3b9845b4f2b61356",
+ ))
+ .unwrap(),
+ hw_enforced: vec![],
+ sw_enforced: vec![
+ KeyParameter { tag: Tag::ALGORITHM, value: KPV::Algorithm(Algorithm::RSA) },
+ KeyParameter { tag: Tag::KEY_SIZE, value: KPV::Integer(2048) },
+ KeyParameter {
+ tag: Tag::RSA_PUBLIC_EXPONENT,
+ value: KPV::LongInteger(65537),
+ },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KPV::KeyPurpose(KeyPurpose::SIGN),
+ },
+ KeyParameter {
+ tag: Tag::PURPOSE,
+ value: KPV::KeyPurpose(KeyPurpose::VERIFY),
+ },
+ KeyParameter { tag: Tag::DIGEST, value: KPV::Digest(Digest::NONE) },
+ KeyParameter {
+ tag: Tag::PADDING,
+ value: KPV::PaddingMode(PaddingMode::NONE),
+ },
+ KeyParameter { tag: Tag::ORIGIN, value: KPV::Origin(KeyOrigin::GENERATED) },
+ KeyParameter { tag: Tag::OS_VERSION, value: KPV::Integer(110000) },
+ KeyParameter { tag: Tag::OS_PATCHLEVEL, value: KPV::Integer(202107) },
+ KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KPV::DateTime(1628871769000),
+ },
+ KeyParameter { tag: Tag::VENDOR_PATCHLEVEL, value: KPV::Integer(20210705) },
+ KeyParameter { tag: Tag::BOOT_PATCHLEVEL, value: KPV::Integer(0) },
+ ],
+ },
+ // No support for RSA keys in export_key().
+ None,
+ ),
+ ];
+
+ for (input, want, want_format) in tests {
+ let input = hex::decode(input).unwrap();
+ let got = KeyBlob::new_from_serialized(&input, &hidden).expect("invalid keyblob!");
+ assert!(got == want);
+
+ if let Some(want_format) = want_format {
+ let (got_format, _key_material, params) =
+ export_key(&input, &[]).expect("invalid keyblob!");
+ assert_eq!(got_format, want_format);
+ // All the test cases are software-only keys.
+ assert_eq!(params, got.sw_enforced);
+ }
+ }
+ }
+
+ #[test]
+ fn test_add_der_len() {
+ let tests = [
+ (0, "00"),
+ (1, "01"),
+ (126, "7e"),
+ (127, "7f"),
+ (128, "8180"),
+ (129, "8181"),
+ (255, "81ff"),
+ (256, "820100"),
+ (257, "820101"),
+ (65535, "82ffff"),
+ ];
+ for (input, want) in tests {
+ let mut got = Vec::new();
+ add_der_len(&mut got, input).unwrap();
+ assert_eq!(hex::encode(got), want, " for input length {input}");
+ }
+ }
+
+ #[test]
+ fn test_pkcs8_wrap_key_p256() {
+ // Key material taken from `ec_256_key` in
+ // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
+ let input = hex::decode(concat!(
+ "3025", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0420", // OCTET STRING (privateKey)
+ "737c2ecd7b8d1940bf2930aa9b4ed3ff",
+ "941eed09366bc03299986481f3a4d859",
+ ))
+ .unwrap();
+ let want = hex::decode(concat!(
+ // RFC 5208 s5
+ "3041", // SEQUENCE (PrivateKeyInfo) {
+ "020100", // INTEGER length 1 value 0 (version)
+ "3013", // SEQUENCE length 0x13 (AlgorithmIdentifier) {
+ "0607", // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
+ "0608", // OBJECT IDENTIFIER length 8 (param)
+ "2a8648ce3d030107", // 1.2.840.10045.3.1.7 (secp256r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0427", // OCTET STRING (privateKey) holding...
+ "3025", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0420", // OCTET STRING length 0x20 (privateKey)
+ "737c2ecd7b8d1940bf2930aa9b4ed3ff",
+ "941eed09366bc03299986481f3a4d859",
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+ ))
+ .unwrap();
+ let got = pkcs8_wrap_nist_key(&input, EcCurve::P_256).unwrap();
+ assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
+ }
+
+ #[test]
+ fn test_pkcs8_wrap_key_p521() {
+ // Key material taken from `ec_521_key` in
+ // hardware/interfaces/security/keymint/aidl/vts/function/KeyMintTest.cpp
+ let input = hex::decode(concat!(
+ "3047", // SEQUENCE length 0xd3 (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0442", // OCTET STRING length 0x42 (privateKey)
+ "0011458c586db5daa92afab03f4fe46a",
+ "a9d9c3ce9a9b7a006a8384bec4c78e8e",
+ "9d18d7d08b5bcfa0e53c75b064ad51c4",
+ "49bae0258d54b94b1e885ded08ed4fb2",
+ "5ce9",
+ // } end SEQUENCE (ECPrivateKey)
+ ))
+ .unwrap();
+ let want = hex::decode(concat!(
+ // RFC 5208 s5
+ "3060", // SEQUENCE (PrivateKeyInfo) {
+ "020100", // INTEGER length 1 value 0 (version)
+ "3010", // SEQUENCE length 0x10 (AlgorithmIdentifier) {
+ "0607", // OBJECT IDENTIFIER length 7 (algorithm)
+ "2a8648ce3d0201", // 1.2.840.10045.2.1 (ecPublicKey)
+ "0605", // OBJECT IDENTIFIER length 5 (param)
+ "2b81040023", // 1.3.132.0.35 (secp521r1)
+ // } end SEQUENCE (AlgorithmIdentifier)
+ "0449", // OCTET STRING (privateKey) holding...
+ "3047", // SEQUENCE (ECPrivateKey)
+ "020101", // INTEGER length 1 value 1 (version)
+ "0442", // OCTET STRING length 0x42 (privateKey)
+ "0011458c586db5daa92afab03f4fe46a",
+ "a9d9c3ce9a9b7a006a8384bec4c78e8e",
+ "9d18d7d08b5bcfa0e53c75b064ad51c4",
+ "49bae0258d54b94b1e885ded08ed4fb2",
+ "5ce9",
+ // } end SEQUENCE (ECPrivateKey)
+ // } end SEQUENCE (PrivateKeyInfo)
+ ))
+ .unwrap();
+ let got = pkcs8_wrap_nist_key(&input, EcCurve::P_521).unwrap();
+ assert_eq!(hex::encode(got), hex::encode(want), " for input {}", hex::encode(input));
+ }
+}
diff --git a/keystore2/src/utils.rs b/keystore2/src/utils.rs
index 7bc548eb..a3fd8824 100644
--- a/keystore2/src/utils.rs
+++ b/keystore2/src/utils.rs
@@ -20,13 +20,16 @@ use crate::key_parameter::KeyParameter;
use crate::ks_err;
use crate::permission;
use crate::permission::{KeyPerm, KeyPermSet, KeystorePerm};
+pub use crate::watchdog_helper::watchdog;
use crate::{
database::{KeyType, KeystoreDB},
globals::LEGACY_IMPORTER,
+ km_compat,
+ raw_device::KeyMintDevice,
};
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
- KeyParameter::KeyParameter as KmKeyParameter, Tag::Tag,
+ Algorithm::Algorithm, IKeyMintDevice::IKeyMintDevice, KeyCharacteristics::KeyCharacteristics,
+ KeyParameter::KeyParameter as KmKeyParameter, KeyParameterValue::KeyParameterValue, Tag::Tag,
};
use android_os_permissions_aidl::aidl::android::os::IPermissionController;
use android_security_apc::aidl::android::security::apc::{
@@ -46,6 +49,10 @@ use keystore2_apc_compat::{
use keystore2_crypto::{aes_gcm_decrypt, aes_gcm_encrypt, ZVec};
use std::iter::IntoIterator;
+/// Per RFC 5280 4.1.2.5, an undefined expiration (not-after) field should be set to GeneralizedTime
+/// 999912312359559, which is 253402300799000 ms from Jan 1, 1970.
+pub const UNDEFINED_NOT_AFTER: i64 = 253402300799000i64;
+
/// 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.
@@ -122,6 +129,15 @@ pub fn check_unique_id_attestation_permissions() -> anyhow::Result<()> {
check_android_permission("android.permission.REQUEST_UNIQUE_ID_ATTESTATION")
}
+/// This function checks whether the calling app has the Android permissions needed to manage
+/// users. Only callers that can manage users are allowed to get a list of apps affected
+/// by a user's SID changing.
+/// 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_get_app_uids_affected_by_sid_permissions() -> anyhow::Result<()> {
+ check_android_permission("android.permission.MANAGE_USERS")
+}
+
fn check_android_permission(permission: &str) -> anyhow::Result<()> {
let permission_controller: Strong<dyn IPermissionController::IPermissionController> =
binder::get_interface("permission")?;
@@ -163,6 +179,148 @@ pub fn key_characteristics_to_internal(
.collect()
}
+/// Import a keyblob that is of the format used by the software C++ KeyMint implementation. After
+/// successful import, invoke both the `new_blob_handler` and `km_op` closures. On success a tuple
+/// of the `km_op`s result and the optional upgraded blob is returned.
+fn import_keyblob_and_perform_op<T, KmOp, NewBlobHandler>(
+ km_dev: &dyn IKeyMintDevice,
+ inner_keyblob: &[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<()>,
+{
+ let (format, key_material, mut chars) =
+ crate::sw_keyblob::export_key(inner_keyblob, upgrade_params)?;
+ log::debug!(
+ "importing {:?} key material (len={}) with original chars={:?}",
+ format,
+ key_material.len(),
+ chars
+ );
+ let asymmetric = chars.iter().any(|kp| {
+ kp.tag == Tag::ALGORITHM
+ && (kp.value == KeyParameterValue::Algorithm(Algorithm::RSA)
+ || (kp.value == KeyParameterValue::Algorithm(Algorithm::EC)))
+ });
+
+ // Combine the characteristics of the previous keyblob with the upgrade parameters (which might
+ // include special things like APPLICATION_ID / APPLICATION_DATA).
+ chars.extend_from_slice(upgrade_params);
+
+ // Now filter out values from the existing keyblob that shouldn't be set on import, either
+ // because they are per-operation parameter or because they are auto-added by KeyMint itself.
+ let mut import_params: Vec<KmKeyParameter> = chars
+ .into_iter()
+ .filter(|kp| {
+ !matches!(
+ kp.tag,
+ Tag::ORIGIN
+ | Tag::ROOT_OF_TRUST
+ | Tag::OS_VERSION
+ | Tag::OS_PATCHLEVEL
+ | Tag::UNIQUE_ID
+ | Tag::ATTESTATION_CHALLENGE
+ | Tag::ATTESTATION_APPLICATION_ID
+ | Tag::ATTESTATION_ID_BRAND
+ | Tag::ATTESTATION_ID_DEVICE
+ | Tag::ATTESTATION_ID_PRODUCT
+ | Tag::ATTESTATION_ID_SERIAL
+ | Tag::ATTESTATION_ID_IMEI
+ | Tag::ATTESTATION_ID_MEID
+ | Tag::ATTESTATION_ID_MANUFACTURER
+ | Tag::ATTESTATION_ID_MODEL
+ | Tag::VENDOR_PATCHLEVEL
+ | Tag::BOOT_PATCHLEVEL
+ | Tag::DEVICE_UNIQUE_ATTESTATION
+ | Tag::ATTESTATION_ID_SECOND_IMEI
+ | Tag::NONCE
+ | Tag::MAC_LENGTH
+ | Tag::CERTIFICATE_SERIAL
+ | Tag::CERTIFICATE_SUBJECT
+ | Tag::CERTIFICATE_NOT_BEFORE
+ | Tag::CERTIFICATE_NOT_AFTER
+ )
+ })
+ .collect();
+
+ // Now that any previous values have been removed, add any additional parameters that needed for
+ // import. In particular, if we are generating/importing an asymmetric key, we need to make sure
+ // that NOT_BEFORE and NOT_AFTER are present.
+ if asymmetric {
+ import_params.push(KmKeyParameter {
+ tag: Tag::CERTIFICATE_NOT_BEFORE,
+ value: KeyParameterValue::DateTime(0),
+ });
+ import_params.push(KmKeyParameter {
+ tag: Tag::CERTIFICATE_NOT_AFTER,
+ value: KeyParameterValue::DateTime(UNDEFINED_NOT_AFTER),
+ });
+ }
+ log::debug!("import parameters={import_params:?}");
+
+ let creation_result = {
+ let _wp = watchdog::watch_millis(
+ "In utils::import_keyblob_and_perform_op: calling importKey.",
+ 500,
+ );
+ map_km_error(km_dev.importKey(&import_params, format, &key_material, None))
+ }
+ .context(ks_err!("Upgrade failed."))?;
+
+ // Note that the importKey operation will produce key characteristics that may be different
+ // than are already stored in Keystore's SQL database. In particular, the KeyMint
+ // implementation will now mark the key as `Origin::IMPORTED` not `Origin::GENERATED`, and
+ // the security level for characteristics will now be `TRUSTED_ENVIRONMENT` not `SOFTWARE`.
+ //
+ // However, the DB metadata still accurately reflects the original origin of the key, and
+ // so we leave the values as-is (and so any `KeyInfo` retrieved in the Java layer will get the
+ // same results before and after import).
+ //
+ // Note that this also applies to the `USAGE_COUNT_LIMIT` parameter -- if the key has already
+ // been used, then the DB version of the parameter will be (and will continue to be) lower
+ // than the original count bound to the keyblob. This means that Keystore's policing of
+ // usage counts will continue where it left off.
+
+ new_blob_handler(&creation_result.keyBlob).context(ks_err!("calling new_blob_handler."))?;
+
+ km_op(&creation_result.keyBlob)
+ .map(|v| (v, Some(creation_result.keyBlob)))
+ .context(ks_err!("Calling km_op after upgrade."))
+}
+
+/// Upgrade a keyblob then invoke both the `new_blob_handler` and the `km_op` closures. On success
+/// a tuple of the `km_op`s result and the optional upgraded blob is returned.
+fn upgrade_keyblob_and_perform_op<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<()>,
+{
+ let upgraded_blob = {
+ let _wp = watchdog::watch_millis(
+ "In utils::upgrade_keyblob_and_perform_op: calling upgradeKey.",
+ 500,
+ );
+ map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
+ }
+ .context(ks_err!("Upgrade failed."))?;
+
+ new_blob_handler(&upgraded_blob).context(ks_err!("calling new_blob_handler."))?;
+
+ km_op(&upgraded_blob)
+ .map(|v| (v, Some(upgraded_blob)))
+ .context(ks_err!("Calling km_op after upgrade."))
+}
+
/// 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
@@ -171,6 +329,7 @@ pub fn key_characteristics_to_internal(
/// optional upgraded blob is returned.
pub fn upgrade_keyblob_if_required_with<T, KmOp, NewBlobHandler>(
km_dev: &dyn IKeyMintDevice,
+ km_dev_version: i32,
key_blob: &[u8],
upgrade_params: &[KmKeyParameter],
km_op: KmOp,
@@ -181,21 +340,91 @@ where
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,
+ Err(Error::Km(ErrorCode::KEY_REQUIRES_UPGRADE)) => upgrade_keyblob_and_perform_op(
+ km_dev,
+ key_blob,
+ upgrade_params,
+ km_op,
+ new_blob_handler,
+ ),
+ Err(Error::Km(ErrorCode::INVALID_KEY_BLOB))
+ if km_dev_version >= KeyMintDevice::KEY_MINT_V1 =>
+ {
+ // A KeyMint (not Keymaster via km_compat) device says that this is an invalid keyblob.
+ //
+ // This may be because the keyblob was created before an Android upgrade, and as part of
+ // the device upgrade the underlying Keymaster/KeyMint implementation has been upgraded.
+ //
+ // If that's the case, there are three possible scenarios:
+ if key_blob.starts_with(km_compat::KEYMASTER_BLOB_HW_PREFIX) {
+ // 1) The keyblob was created in hardware by the km_compat C++ code, using a prior
+ // Keymaster implementation, and wrapped.
+ //
+ // In this case, the keyblob will have the km_compat magic prefix, including the
+ // marker that indicates that this was a hardware-backed key.
+ //
+ // The inner keyblob should still be recognized by the hardware implementation, so
+ // strip the prefix and attempt a key upgrade.
+ log::info!(
+ "found apparent km_compat(Keymaster) HW blob, attempt strip-and-upgrade"
+ );
+ let inner_keyblob = &key_blob[km_compat::KEYMASTER_BLOB_HW_PREFIX.len()..];
+ upgrade_keyblob_and_perform_op(
+ km_dev,
+ inner_keyblob,
+ upgrade_params,
+ km_op,
+ new_blob_handler,
+ )
+ } else if keystore2_flags::import_previously_emulated_keys()
+ && key_blob.starts_with(km_compat::KEYMASTER_BLOB_SW_PREFIX)
+ {
+ // 2) The keyblob was created in software by the km_compat C++ code because a prior
+ // Keymaster implementation did not support ECDH (which was only added in KeyMint).
+ //
+ // In this case, the keyblob with have the km_compat magic prefix, but with the
+ // marker that indicates that this was a software-emulated key.
+ //
+ // The inner keyblob should be in the format produced by the C++ reference
+ // implementation of KeyMint. Extract the key material and import it into the
+ // current KeyMint device.
+ log::info!("found apparent km_compat(Keymaster) SW blob, attempt strip-and-import");
+ let inner_keyblob = &key_blob[km_compat::KEYMASTER_BLOB_SW_PREFIX.len()..];
+ import_keyblob_and_perform_op(
+ km_dev,
+ inner_keyblob,
+ upgrade_params,
+ km_op,
+ new_blob_handler,
+ )
+ } else if let (true, km_compat::KeyBlob::Wrapped(inner_keyblob)) = (
+ keystore2_flags::import_previously_emulated_keys(),
+ km_compat::unwrap_keyblob(key_blob),
+ ) {
+ // 3) The keyblob was created in software by km_compat.rs because a prior KeyMint
+ // implementation did not support a feature present in the current KeyMint spec.
+ // (For example, a curve 25519 key created when the device only supported KeyMint
+ // v1).
+ //
+ // In this case, the keyblob with have the km_compat.rs wrapper around it to
+ // indicate that this was a software-emulated key.
+ //
+ // The inner keyblob should be in the format produced by the C++ reference
+ // implementation of KeyMint. Extract the key material and import it into the
+ // current KeyMint device.
+ log::info!(
+ "found apparent km_compat.rs(KeyMint) SW blob, attempt strip-and-import"
);
- map_km_error(km_dev.upgradeKey(key_blob, upgrade_params))
+ import_keyblob_and_perform_op(
+ km_dev,
+ inner_keyblob,
+ upgrade_params,
+ km_op,
+ new_blob_handler,
+ )
+ } else {
+ Err(Error::Km(ErrorCode::INVALID_KEY_BLOB)).context(ks_err!("Calling km_op"))
}
- .context(ks_err!("Upgrade failed."))?;
-
- new_blob_handler(&upgraded_blob).context(ks_err!("calling new_blob_handler."))?;
-
- km_op(&upgraded_blob)
- .map(|v| (v, Some(upgraded_blob)))
- .context(ks_err!("Calling km_op after upgrade."))
}
r => r.map(|v| (v, None)).context(ks_err!("Calling km_op.")),
}
@@ -215,9 +444,9 @@ pub fn key_parameters_to_authorizations(
/// as an integer.
pub fn get_current_time_in_milliseconds() -> i64 {
let mut current_time = libc::timespec { tv_sec: 0, tv_nsec: 0 };
- // Following unsafe block includes one system call to get monotonic time.
- // Therefore, it is not considered harmful.
- unsafe { libc::clock_gettime(libc::CLOCK_MONOTONIC_RAW, &mut current_time) };
+ // SAFETY: The pointer is valid because it comes from a reference, and clock_gettime doesn't
+ // retain it beyond the call.
+ unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut current_time) };
current_time.tv_sec as i64 * 1000 + (current_time.tv_nsec as i64 / 1_000_000)
}
@@ -258,32 +487,49 @@ 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(ks_err!("Trying to list legacy keys."))?,
- );
- result.append(
- &mut db
- .list(domain, namespace, KeyType::Client)
- .context(ks_err!("Trying to list keystore database."))?,
- );
+/// Merges and filters two lists of key descriptors. The first input list, legacy_descriptors,
+/// is assumed to not be sorted or filtered. As such, all key descriptors in that list whose
+/// alias is less than, or equal to, start_past_alias (if provided) will be removed.
+/// This list will then be merged with the second list, db_descriptors. The db_descriptors list
+/// is assumed to be sorted and filtered so the output list will be sorted prior to returning.
+/// The returned value is a list of KeyDescriptor objects whose alias is greater than
+/// start_past_alias, sorted and de-duplicated.
+fn merge_and_filter_key_entry_lists(
+ legacy_descriptors: &[KeyDescriptor],
+ db_descriptors: &[KeyDescriptor],
+ start_past_alias: Option<&str>,
+) -> Vec<KeyDescriptor> {
+ let mut result: Vec<KeyDescriptor> =
+ match start_past_alias {
+ Some(past_alias) => legacy_descriptors
+ .iter()
+ .filter(|kd| {
+ if let Some(alias) = &kd.alias {
+ alias.as_str() > past_alias
+ } else {
+ false
+ }
+ })
+ .cloned()
+ .collect(),
+ None => legacy_descriptors.to_vec(),
+ };
+
+ result.extend_from_slice(db_descriptors);
result.sort_unstable();
result.dedup();
+ result
+}
+fn estimate_safe_amount_to_return(
+ key_descriptors: &[KeyDescriptor],
+ response_size_limit: usize,
+) -> usize {
let mut items_to_return = 0;
let mut returned_bytes: usize = 0;
- const RESPONSE_SIZE_LIMIT: usize = 358400;
// Estimate the transaction size to avoid returning more items than what
// could fit in a binder transaction.
- for kd in result.iter() {
+ for kd in key_descriptors.iter() {
// 4 bytes for the Domain enum
// 8 bytes for the Namespace long.
returned_bytes += 4 + 8;
@@ -298,11 +544,11 @@ pub fn list_key_entries(
// The binder transaction size limit is 1M. Empirical measurements show
// that the binder overhead is 60% (to be confirmed). So break after
// 350KB and return a partial list.
- if returned_bytes > RESPONSE_SIZE_LIMIT {
+ if returned_bytes > response_size_limit {
log::warn!(
"Key descriptors list ({} items) may exceed binder \
size, returning {} items est {} bytes.",
- result.len(),
+ key_descriptors.len(),
items_to_return,
returned_bytes
);
@@ -310,37 +556,47 @@ pub fn list_key_entries(
}
items_to_return += 1;
}
- Ok(result[..items_to_return].to_vec())
+ items_to_return
}
-/// This module provides helpers for simplified use of the watchdog module.
-#[cfg(feature = "watchdog")]
-pub mod watchdog {
- pub use crate::watchdog::WatchPoint;
- use crate::watchdog::Watchdog;
- use lazy_static::lazy_static;
- use std::sync::Arc;
- use std::time::Duration;
-
- lazy_static! {
- /// A Watchdog thread, that can be used to create watch points.
- static ref WD: Arc<Watchdog> = Watchdog::new(Duration::from_secs(10));
- }
+/// List all key aliases for a given domain + namespace. whose alias is greater
+/// than start_past_alias (if provided).
+pub fn list_key_entries(
+ db: &mut KeystoreDB,
+ domain: Domain,
+ namespace: i64,
+ start_past_alias: Option<&str>,
+) -> Result<Vec<KeyDescriptor>> {
+ let legacy_key_descriptors: Vec<KeyDescriptor> = LEGACY_IMPORTER
+ .list_uid(domain, namespace)
+ .context(ks_err!("Trying to list legacy keys."))?;
+
+ // The results from the database will be sorted and unique
+ let db_key_descriptors: Vec<KeyDescriptor> = db
+ .list_past_alias(domain, namespace, KeyType::Client, start_past_alias)
+ .context(ks_err!("Trying to list keystore database past alias."))?;
+
+ let merged_key_entries = merge_and_filter_key_entry_lists(
+ &legacy_key_descriptors,
+ &db_key_descriptors,
+ start_past_alias,
+ );
- /// Sets a watch point with `id` and a timeout of `millis` milliseconds.
- pub fn watch_millis(id: &'static str, millis: u64) -> Option<WatchPoint> {
- Watchdog::watch(&WD, id, Duration::from_millis(millis))
- }
+ const RESPONSE_SIZE_LIMIT: usize = 358400;
+ let safe_amount_to_return =
+ estimate_safe_amount_to_return(&merged_key_entries, RESPONSE_SIZE_LIMIT);
+ Ok(merged_key_entries[..safe_amount_to_return].to_vec())
+}
- /// Like `watch_millis` but with a callback that is called every time a report
- /// is printed about this watch point.
- pub fn watch_millis_with(
- id: &'static str,
- millis: u64,
- callback: impl Fn() -> String + Send + 'static,
- ) -> Option<WatchPoint> {
- Watchdog::watch_with(&WD, id, Duration::from_millis(millis), callback)
- }
+/// Count all key aliases for a given domain + namespace.
+pub fn count_key_entries(db: &mut KeystoreDB, domain: Domain, namespace: i64) -> Result<i32> {
+ let legacy_keys = LEGACY_IMPORTER
+ .list_uid(domain, namespace)
+ .context(ks_err!("Trying to list legacy keys."))?;
+
+ let num_keys_in_db = db.count_keys(domain, namespace, KeyType::Client)?;
+
+ Ok((legacy_keys.len() + num_keys_in_db) as i32)
}
/// Trait implemented by objects that can be used to decrypt cipher text using AES-GCM.
@@ -372,25 +628,6 @@ impl<T: AesGcmKey> AesGcm for T {
}
}
-/// This module provides empty/noop implementations of the watch dog utility functions.
-#[cfg(not(feature = "watchdog"))]
-pub mod watchdog {
- /// Noop watch point.
- pub struct WatchPoint();
- /// Sets a Noop watch point.
- fn watch_millis(_: &'static str, _: u64) -> Option<WatchPoint> {
- None
- }
-
- pub fn watch_millis_with(
- _: &'static str,
- _: u64,
- _: impl Fn() -> String + Send + 'static,
- ) -> Option<WatchPoint> {
- None
- }
-}
-
#[cfg(test)]
mod tests {
use super::*;
@@ -407,4 +644,84 @@ mod tests {
}
})
}
+
+ fn create_key_descriptors_from_aliases(key_aliases: &[&str]) -> Vec<KeyDescriptor> {
+ key_aliases
+ .iter()
+ .map(|key_alias| KeyDescriptor {
+ domain: Domain::APP,
+ nspace: 0,
+ alias: Some(key_alias.to_string()),
+ blob: None,
+ })
+ .collect::<Vec<KeyDescriptor>>()
+ }
+
+ fn aliases_from_key_descriptors(key_descriptors: &[KeyDescriptor]) -> Vec<String> {
+ key_descriptors
+ .iter()
+ .map(
+ |kd| {
+ if let Some(alias) = &kd.alias {
+ String::from(alias)
+ } else {
+ String::from("")
+ }
+ },
+ )
+ .collect::<Vec<String>>()
+ }
+
+ #[test]
+ fn test_safe_amount_to_return() -> Result<()> {
+ let key_aliases = vec!["key1", "key2", "key3"];
+ let key_descriptors = create_key_descriptors_from_aliases(&key_aliases);
+
+ assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 20), 1);
+ assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 50), 2);
+ assert_eq!(estimate_safe_amount_to_return(&key_descriptors, 100), 3);
+ Ok(())
+ }
+
+ #[test]
+ fn test_merge_and_sort_lists_without_filtering() -> Result<()> {
+ let legacy_key_aliases = vec!["key_c", "key_a", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_a", "key_d"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result =
+ merge_and_filter_key_entry_lists(&legacy_key_descriptors, &db_key_descriptors, None);
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_a", "key_b", "key_c", "key_d"]);
+ Ok(())
+ }
+
+ #[test]
+ fn test_merge_and_sort_lists_with_filtering() -> Result<()> {
+ let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_c", "key_g"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result = merge_and_filter_key_entry_lists(
+ &legacy_key_descriptors,
+ &db_key_descriptors,
+ Some("key_b"),
+ );
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_c", "key_e", "key_f", "key_g"]);
+ Ok(())
+ }
+
+ #[test]
+ fn test_merge_and_sort_lists_with_filtering_and_dups() -> Result<()> {
+ let legacy_key_aliases = vec!["key_f", "key_a", "key_e", "key_b"];
+ let legacy_key_descriptors = create_key_descriptors_from_aliases(&legacy_key_aliases);
+ let db_key_aliases = vec!["key_d", "key_e", "key_g"];
+ let db_key_descriptors = create_key_descriptors_from_aliases(&db_key_aliases);
+ let result = merge_and_filter_key_entry_lists(
+ &legacy_key_descriptors,
+ &db_key_descriptors,
+ Some("key_c"),
+ );
+ assert_eq!(aliases_from_key_descriptors(&result), vec!["key_d", "key_e", "key_f", "key_g"]);
+ Ok(())
+ }
}
diff --git a/keystore2/src/vintf/vintf.cpp b/keystore2/src/vintf/vintf.cpp
deleted file mode 100644
index a550b100..00000000
--- a/keystore2/src/vintf/vintf.cpp
+++ /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.
- */
-
-#include <algorithm>
-#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;
-}
-
-rust::Vec<rust::String> get_hidl_instances(rust::Str package, size_t major_version,
- size_t minor_version, rust::Str 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));
- 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));
- return convert(names);
-}
diff --git a/keystore2/src/watchdog_helper.rs b/keystore2/src/watchdog_helper.rs
new file mode 100644
index 00000000..92a0abc1
--- /dev/null
+++ b/keystore2/src/watchdog_helper.rs
@@ -0,0 +1,64 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//! Helpers for the watchdog module.
+
+/// This module provides helpers for simplified use of the watchdog module.
+#[cfg(feature = "watchdog")]
+pub mod watchdog {
+ use lazy_static::lazy_static;
+ use std::sync::Arc;
+ use std::time::Duration;
+ pub use watchdog_rs::WatchPoint;
+ use watchdog_rs::Watchdog;
+
+ lazy_static! {
+ /// A Watchdog thread, that can be used to create watch points.
+ static ref WD: Arc<Watchdog> = Watchdog::new(Duration::from_secs(10));
+ }
+
+ /// Sets a watch point with `id` and a timeout of `millis` milliseconds.
+ pub fn watch_millis(id: &'static str, millis: u64) -> Option<WatchPoint> {
+ Watchdog::watch(&WD, id, Duration::from_millis(millis))
+ }
+
+ /// Like `watch_millis` but with a callback that is called every time a report
+ /// is printed about this watch point.
+ pub fn watch_millis_with(
+ id: &'static str,
+ millis: u64,
+ callback: impl Fn() -> String + Send + 'static,
+ ) -> Option<WatchPoint> {
+ Watchdog::watch_with(&WD, id, Duration::from_millis(millis), callback)
+ }
+}
+
+/// This module provides empty/noop implementations of the watch dog utility functions.
+#[cfg(not(feature = "watchdog"))]
+pub mod watchdog {
+ /// Noop watch point.
+ pub struct WatchPoint();
+ /// Sets a Noop watch point.
+ fn watch_millis(_: &'static str, _: u64) -> Option<WatchPoint> {
+ None
+ }
+
+ pub fn watch_millis_with(
+ _: &'static str,
+ _: u64,
+ _: impl Fn() -> String + Send + 'static,
+ ) -> Option<WatchPoint> {
+ None
+ }
+}
diff --git a/keystore2/test_utils/Android.bp b/keystore2/test_utils/Android.bp
new file mode 100644
index 00000000..4c7c18a4
--- /dev/null
+++ b/keystore2/test_utils/Android.bp
@@ -0,0 +1,109 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+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_defaults {
+ name: "libkeystore2_test_utils_defaults",
+ defaults: [
+ "keymint_use_latest_hal_aidl_rust",
+ "keystore2_use_latest_aidl_rust",
+ ],
+ rustlibs: [
+ "android.security.authorization-rust",
+ "libanyhow",
+ "libbinder_rs",
+ "libcxx",
+ "libkeystore2_selinux",
+ "liblog_rust",
+ "libnix",
+ "librand",
+ "librustutils",
+ "libserde",
+ "libserde_cbor",
+ "libthiserror",
+ ],
+ static_libs: [
+ "libkeystore-engine",
+ "libkeystore2_ffi_test_utils",
+ ],
+ shared_libs: [
+ "android.system.keystore2-V4-ndk",
+ "libbase",
+ "libcrypto",
+ "libkeymaster_portable",
+ "libkeymint_support",
+ ],
+}
+
+rust_library {
+ name: "libkeystore2_test_utils",
+ crate_name: "keystore2_test_utils",
+ srcs: ["lib.rs"],
+ defaults: ["libkeystore2_test_utils_defaults"],
+}
+
+rust_test {
+ name: "keystore2_test_utils_test",
+ srcs: ["lib.rs"],
+ defaults: ["libkeystore2_test_utils_defaults"],
+ test_suites: ["general-tests"],
+ require_root: true,
+ auto_gen_config: true,
+ compile_multilib: "first",
+}
+
+cc_library_static {
+ name: "libkeystore2_ffi_test_utils",
+ srcs: ["ffi_test_utils.cpp"],
+ defaults: [
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ "keystore2_use_latest_aidl_ndk_shared",
+ ],
+ generated_headers: [
+ "cxx-bridge-header",
+ "libkeystore2_ffi_test_utils_bridge_header",
+ ],
+ generated_sources: ["libkeystore2_ffi_test_utils_bridge_code"],
+ static_libs: ["libkeystore-engine"],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libkeymaster_portable",
+ "libkeymint_support",
+ ],
+}
+
+genrule {
+ name: "libkeystore2_ffi_test_utils_bridge_code",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) >> $(out)",
+ srcs: ["ffi_test_utils.rs"],
+ out: ["libkeystore2_test_utils_cxx_generated.cc"],
+}
+
+genrule {
+ name: "libkeystore2_ffi_test_utils_bridge_header",
+ tools: ["cxxbridge"],
+ cmd: "$(location cxxbridge) $(in) --header >> $(out)",
+ srcs: ["ffi_test_utils.rs"],
+ out: ["ffi_test_utils.rs.h"],
+}
diff --git a/keystore2/test_utils/authorizations.rs b/keystore2/test_utils/authorizations.rs
index 4608bc5f..2cb2aaf6 100644
--- a/keystore2/test_utils/authorizations.rs
+++ b/keystore2/test_utils/authorizations.rs
@@ -161,6 +161,205 @@ impl AuthSetBuilder {
.push(KeyParameter { tag: Tag::MIN_MAC_LENGTH, value: KeyParameterValue::Integer(l) });
self
}
+
+ /// Add Attestation-Device-Brand.
+ pub fn attestation_device_brand(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_BRAND,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Add Attestation-Device-name.
+ pub fn attestation_device_name(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_DEVICE,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Add Attestation-Device-Product-Name.
+ pub fn attestation_device_product_name(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_PRODUCT,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Add Attestation-Device-Serial.
+ pub fn attestation_device_serial(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_SERIAL,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Add Attestation-Device-IMEI.
+ pub fn attestation_device_imei(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_IMEI,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Add Attestation-Device-IMEI.
+ pub fn attestation_device_second_imei(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_SECOND_IMEI,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Add Attestation-Device-MEID.
+ pub fn attestation_device_meid(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_MEID,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Add Attestation-Device-Manufacturer.
+ pub fn attestation_device_manufacturer(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_MANUFACTURER,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Add Attestation-Device-Model.
+ pub fn attestation_device_model(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ATTESTATION_ID_MODEL,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
+
+ /// Set active date-time.
+ pub fn active_date_time(mut self, date: i64) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ACTIVE_DATETIME,
+ value: KeyParameterValue::DateTime(date),
+ });
+ self
+ }
+
+ /// Set origination expire date-time.
+ pub fn origination_expire_date_time(mut self, date: i64) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::ORIGINATION_EXPIRE_DATETIME,
+ value: KeyParameterValue::DateTime(date),
+ });
+ self
+ }
+
+ /// Set usage expire date-time.
+ pub fn usage_expire_date_time(mut self, date: i64) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::USAGE_EXPIRE_DATETIME,
+ value: KeyParameterValue::DateTime(date),
+ });
+ self
+ }
+
+ /// Set boot loader only.
+ pub fn boot_loader_only(mut self) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::BOOTLOADER_ONLY,
+ value: KeyParameterValue::BoolValue(true),
+ });
+ self
+ }
+
+ /// Set early boot only.
+ pub fn early_boot_only(mut self) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::EARLY_BOOT_ONLY,
+ value: KeyParameterValue::BoolValue(true),
+ });
+ self
+ }
+
+ /// Set max uses per boot.
+ pub fn max_uses_per_boot(mut self, max_uses: i32) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::MAX_USES_PER_BOOT,
+ value: KeyParameterValue::Integer(max_uses),
+ });
+ self
+ }
+
+ /// Set max usage count.
+ pub fn usage_count_limit(mut self, usage_count: i32) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::USAGE_COUNT_LIMIT,
+ value: KeyParameterValue::Integer(usage_count),
+ });
+ self
+ }
+
+ /// Set creation date-time.
+ pub fn creation_date_time(mut self, date: i64) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::CREATION_DATETIME,
+ value: KeyParameterValue::DateTime(date),
+ });
+ self
+ }
+
+ /// Set include unique id.
+ pub fn include_unique_id(mut self) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::INCLUDE_UNIQUE_ID,
+ value: KeyParameterValue::BoolValue(true),
+ });
+ self
+ }
+
+ /// Add app-data.
+ pub fn app_data(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter { tag: Tag::APPLICATION_DATA, value: KeyParameterValue::Blob(b) });
+ self
+ }
+
+ /// Add app-id.
+ pub fn app_id(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter { tag: Tag::APPLICATION_ID, value: KeyParameterValue::Blob(b) });
+ self
+ }
+
+ /// Set device-unique-attestation.
+ pub fn device_unique_attestation(mut self) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::DEVICE_UNIQUE_ATTESTATION,
+ value: KeyParameterValue::BoolValue(true),
+ });
+ self
+ }
+
+ /// Add certificate serial number.
+ pub fn cert_serial(mut self, b: Vec<u8>) -> Self {
+ self.0
+ .push(KeyParameter { tag: Tag::CERTIFICATE_SERIAL, value: KeyParameterValue::Blob(b) });
+ self
+ }
+
+ /// Add certificate subject name.
+ pub fn cert_subject_name(mut self, b: Vec<u8>) -> Self {
+ self.0.push(KeyParameter {
+ tag: Tag::CERTIFICATE_SUBJECT,
+ value: KeyParameterValue::Blob(b),
+ });
+ self
+ }
}
impl Deref for AuthSetBuilder {
diff --git a/keystore2/test_utils/ffi_test_utils.cpp b/keystore2/test_utils/ffi_test_utils.cpp
new file mode 100644
index 00000000..ea030692
--- /dev/null
+++ b/keystore2/test_utils/ffi_test_utils.cpp
@@ -0,0 +1,752 @@
+#include "ffi_test_utils.hpp"
+
+#include <iostream>
+#include <vector>
+
+#include <android-base/logging.h>
+#include <keymaster/km_openssl/attestation_record.h>
+#include <keymaster/km_openssl/openssl_err.h>
+#include <keymaster/km_openssl/openssl_utils.h>
+#include <keymint_support/attestation_record.h>
+#include <keymint_support/keymint_utils.h>
+#include <openssl/mem.h>
+
+using keymaster::ASN1_OBJECT_Ptr;
+using keymaster::EVP_PKEY_Ptr;
+using keymaster::X509_Ptr;
+using std::endl;
+using std::string;
+using std::vector;
+
+#define TAG_SEQUENCE 0x30
+#define LENGTH_MASK 0x80
+#define LENGTH_VALUE_MASK 0x7F
+
+/* EVP_PKEY_from_keystore is from system/security/keystore-engine. */
+extern "C" EVP_PKEY* EVP_PKEY_from_keystore(const char* key_id);
+
+typedef std::vector<uint8_t> certificate_t;
+
+/**
+ * ASN.1 structure for `KeyDescription` Schema.
+ * See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
+ * KeyDescription ::= SEQUENCE(
+ * keyFormat INTEGER, # Values from KeyFormat enum.
+ * keyParams AuthorizationList,
+ * )
+ */
+typedef struct key_description {
+ ASN1_INTEGER* key_format;
+ keymaster::KM_AUTH_LIST* key_params;
+} TEST_KEY_DESCRIPTION;
+
+ASN1_SEQUENCE(TEST_KEY_DESCRIPTION) = {
+ ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_format, ASN1_INTEGER),
+ ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_params, keymaster::KM_AUTH_LIST),
+} ASN1_SEQUENCE_END(TEST_KEY_DESCRIPTION);
+DECLARE_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION);
+
+/**
+ * ASN.1 structure for `SecureKeyWrapper` Schema.
+ * See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper` schema.
+ * SecureKeyWrapper ::= SEQUENCE(
+ * version INTEGER, # Contains value 0
+ * encryptedTransportKey OCTET_STRING,
+ * initializationVector OCTET_STRING,
+ * keyDescription KeyDescription,
+ * encryptedKey OCTET_STRING,
+ * tag OCTET_STRING
+ * )
+ */
+typedef struct secure_key_wrapper {
+ ASN1_INTEGER* version;
+ ASN1_OCTET_STRING* encrypted_transport_key;
+ ASN1_OCTET_STRING* initialization_vector;
+ TEST_KEY_DESCRIPTION* key_desc;
+ ASN1_OCTET_STRING* encrypted_key;
+ ASN1_OCTET_STRING* tag;
+} TEST_SECURE_KEY_WRAPPER;
+
+ASN1_SEQUENCE(TEST_SECURE_KEY_WRAPPER) = {
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, version, ASN1_INTEGER),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_transport_key, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, initialization_vector, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, key_desc, TEST_KEY_DESCRIPTION),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_key, ASN1_OCTET_STRING),
+ ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, tag, ASN1_OCTET_STRING),
+} ASN1_SEQUENCE_END(TEST_SECURE_KEY_WRAPPER);
+DECLARE_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER);
+
+IMPLEMENT_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER);
+IMPLEMENT_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION);
+
+struct TEST_KEY_DESCRIPTION_Delete {
+ void operator()(TEST_KEY_DESCRIPTION* p) { TEST_KEY_DESCRIPTION_free(p); }
+};
+struct TEST_SECURE_KEY_WRAPPER_Delete {
+ void operator()(TEST_SECURE_KEY_WRAPPER* p) { TEST_SECURE_KEY_WRAPPER_free(p); }
+};
+
+const std::string keystore2_grant_id_prefix("ks2_keystore-engine_grant_id:");
+
+string bin2hex(const vector<uint8_t>& data) {
+ string retval;
+ char nibble2hex[16] = {'0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
+ retval.reserve(data.size() * 2 + 1);
+ for (uint8_t byte : data) {
+ retval.push_back(nibble2hex[0x0F & (byte >> 4)]);
+ retval.push_back(nibble2hex[0x0F & byte]);
+ }
+ return retval;
+}
+
+string x509NameToStr(X509_NAME* name) {
+ char* s = X509_NAME_oneline(name, nullptr, 0);
+ string retval(s);
+ OPENSSL_free(s);
+ return retval;
+}
+
+X509_Ptr parseCertBlob(const vector<uint8_t>& blob) {
+ const uint8_t* p = blob.data();
+ return X509_Ptr(d2i_X509(nullptr /* allocate new */, &p, blob.size()));
+}
+
+// Extract attestation record from cert. Returned object is still part of cert; don't free it
+// separately.
+ASN1_OCTET_STRING* getAttestationRecord(X509* certificate) {
+ ASN1_OBJECT_Ptr oid(OBJ_txt2obj(aidl::android::hardware::security::keymint::kAttestionRecordOid,
+ 1 /* dotted string format */));
+ if (!oid.get()) return nullptr;
+
+ int location = X509_get_ext_by_OBJ(certificate, oid.get(), -1 /* search from beginning */);
+ if (location == -1) return nullptr;
+
+ X509_EXTENSION* attest_rec_ext = X509_get_ext(certificate, location);
+ if (!attest_rec_ext) return nullptr;
+
+ ASN1_OCTET_STRING* attest_rec = X509_EXTENSION_get_data(attest_rec_ext);
+ return attest_rec;
+}
+
+bool ChainSignaturesAreValid(const vector<certificate_t>& chain, bool strict_issuer_check) {
+ std::stringstream cert_data;
+
+ for (size_t i = 0; i < chain.size(); ++i) {
+ cert_data << bin2hex(chain[i]) << std::endl;
+
+ X509_Ptr key_cert(parseCertBlob(chain[i]));
+ X509_Ptr signing_cert;
+ if (i < chain.size() - 1) {
+ signing_cert = parseCertBlob(chain[i + 1]);
+ } else {
+ signing_cert = parseCertBlob(chain[i]);
+ }
+ if (!key_cert.get() || !signing_cert.get()) {
+ LOG(ERROR) << cert_data.str();
+ return false;
+ }
+
+ EVP_PKEY_Ptr signing_pubkey(X509_get_pubkey(signing_cert.get()));
+ if (!signing_pubkey.get()) {
+ LOG(ERROR) << cert_data.str();
+ return false;
+ }
+
+ if (!X509_verify(key_cert.get(), signing_pubkey.get())) {
+ // Handles the case of device-unique attestation chain which is not expected to be
+ // self-signed - b/191361618
+ // For device-unique attestation chain `strict_issuer_check` is not set, so ignore the
+ // root certificate signature verification result and in all other cases return the
+ // error.
+ bool is_root_cert = (i == chain.size() - 1);
+ if (strict_issuer_check || !is_root_cert) {
+ LOG(ERROR) << "Verification of certificate " << i << " failed "
+ << "OpenSSL error string: " << ERR_error_string(ERR_get_error(), NULL)
+ << '\n'
+ << cert_data.str();
+ return false;
+ }
+ }
+
+ string cert_issuer = x509NameToStr(X509_get_issuer_name(key_cert.get()));
+ string signer_subj = x509NameToStr(X509_get_subject_name(signing_cert.get()));
+ if (cert_issuer != signer_subj && strict_issuer_check) {
+ LOG(ERROR) << "Cert " << i << " has wrong issuer.\n"
+ << " Signer subject is " << signer_subj << " Issuer subject is "
+ << cert_issuer << endl
+ << cert_data.str();
+ }
+ }
+
+ // Dump cert data.
+ LOG(ERROR) << cert_data.str();
+ return true;
+}
+
+/* This function extracts a certificate from the certs_chain_buffer at the given
+ * offset. Each DER encoded certificate starts with TAG_SEQUENCE followed by the
+ * total length of the certificate. The length of the certificate is determined
+ * as per ASN.1 encoding rules for the length octets.
+ *
+ * @param certs_chain_buffer: buffer containing DER encoded X.509 certificates
+ * arranged sequentially.
+ * @data_size: Length of the DER encoded X.509 certificates buffer.
+ * @index: DER encoded X.509 certificates buffer offset.
+ * @cert: Encoded certificate to be extracted from buffer as outcome.
+ * @return: true on success, otherwise false.
+ */
+bool extractCertFromCertChainBuffer(uint8_t* certs_chain_buffer, int certs_chain_buffer_size,
+ int& index, certificate_t& cert) {
+ if (index >= certs_chain_buffer_size) {
+ return false;
+ }
+
+ uint32_t length = 0;
+ std::vector<uint8_t> cert_bytes;
+ if (certs_chain_buffer[index] == TAG_SEQUENCE) {
+ // Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length.
+ if (0 == (certs_chain_buffer[index + 1] & LENGTH_MASK)) {
+ length = (uint32_t)certs_chain_buffer[index];
+ // Add SEQ and Length fields
+ length += 2;
+ } else {
+ // Long form. Two to 127 octets. Bit 8 of first octet has value "1" and
+ // bits 7-1 give the number of additional length octets. Second and following
+ // octets give the actual length.
+ int additionalBytes = certs_chain_buffer[index + 1] & LENGTH_VALUE_MASK;
+ if (additionalBytes == 0x01) {
+ length = certs_chain_buffer[index + 2];
+ // Add SEQ and Length fields
+ length += 3;
+ } else if (additionalBytes == 0x02) {
+ length = (certs_chain_buffer[index + 2] << 8 | certs_chain_buffer[index + 3]);
+ // Add SEQ and Length fields
+ length += 4;
+ } else if (additionalBytes == 0x04) {
+ length = certs_chain_buffer[index + 2] << 24;
+ length |= certs_chain_buffer[index + 3] << 16;
+ length |= certs_chain_buffer[index + 4] << 8;
+ length |= certs_chain_buffer[index + 5];
+ // Add SEQ and Length fields
+ length += 6;
+ } else {
+ // Length is larger than uint32_t max limit.
+ return false;
+ }
+ }
+ cert_bytes.insert(cert_bytes.end(), (certs_chain_buffer + index),
+ (certs_chain_buffer + index + length));
+ index += length;
+
+ for (int i = 0; i < cert_bytes.size(); i++) {
+ cert = std::move(cert_bytes);
+ }
+ } else {
+ // SEQUENCE TAG MISSING.
+ return false;
+ }
+
+ return true;
+}
+
+bool getCertificateChain(rust::Vec<rust::u8>& chainBuffer, std::vector<certificate_t>& certChain) {
+ uint8_t* data = chainBuffer.data();
+ int index = 0;
+ int data_size = chainBuffer.size();
+
+ while (index < data_size) {
+ certificate_t cert;
+ if (!extractCertFromCertChainBuffer(data, data_size, index, cert)) {
+ return false;
+ }
+ certChain.push_back(std::move(cert));
+ }
+ return true;
+}
+
+bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check) {
+ std::vector<certificate_t> cert_chain = std::vector<certificate_t>();
+ if (cert_len <= 0) {
+ return false;
+ }
+ if (!getCertificateChain(cert_buf, cert_chain)) {
+ return false;
+ }
+
+ std::stringstream cert_data;
+ for (int i = 0; i < cert_chain.size(); i++) {
+ cert_data << bin2hex(cert_chain[i]) << std::endl;
+ }
+ LOG(INFO) << cert_data.str() << "\n";
+
+ return ChainSignaturesAreValid(cert_chain, strict_issuer_check);
+}
+
+/**
+ * Below mentioned key parameters are used to create authorization list of
+ * secure key.
+ * Algorithm: AES-256
+ * Padding: PKCS7
+ * Blockmode: ECB
+ * Purpose: Encrypt, Decrypt
+ */
+keymaster::AuthorizationSet build_wrapped_key_auth_list() {
+ return keymaster::AuthorizationSet(keymaster::AuthorizationSetBuilder()
+ .AesEncryptionKey(256)
+ .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_ECB)
+ .Authorization(keymaster::TAG_PADDING, KM_PAD_PKCS7)
+ .Authorization(keymaster::TAG_NO_AUTH_REQUIRED));
+}
+
+/**
+ * Creates ASN.1 DER-encoded data corresponding to `KeyDescription` schema as
+ * AAD. See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
+ */
+CxxResult buildAsn1DerEncodedWrappedKeyDescription() {
+ CxxResult cxx_result{};
+ keymaster_error_t error;
+ cxx_result.error = KM_ERROR_OK;
+
+ keymaster::UniquePtr<TEST_KEY_DESCRIPTION, TEST_KEY_DESCRIPTION_Delete> key_description(
+ TEST_KEY_DESCRIPTION_new());
+ if (!key_description.get()) {
+ cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return cxx_result;
+ }
+
+ // Fill secure key authorizations.
+ keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list();
+ error = build_auth_list(auth_list, key_description->key_params);
+ if (error != KM_ERROR_OK) {
+ cxx_result.error = error;
+ return cxx_result;
+ }
+
+ // Fill secure key format.
+ if (!ASN1_INTEGER_set(key_description->key_format, KM_KEY_FORMAT_RAW)) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ // Perform ASN.1 DER encoding of KeyDescription.
+ int asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), nullptr);
+ if (asn1_data_len < 0) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+ std::vector<uint8_t> asn1_data(asn1_data_len, 0);
+
+ if (!asn1_data.data()) {
+ cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
+ return cxx_result;
+ }
+
+ uint8_t* p = asn1_data.data();
+ asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), &p);
+ if (asn1_data_len < 0) {
+ cxx_result.error = keymaster::TranslateLastOpenSslError();
+ return cxx_result;
+ }
+
+ std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data));
+
+ return cxx_result;
+}
+
+/**
+ * Creates wrapped key material to import in ASN.1 DER-encoded data corresponding to
+ * `SecureKeyWrapper` schema. See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper`
+ * schema.
+ */
+CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
+ rust::Vec<rust::u8> encrypted_transport_key, rust::Vec<rust::u8> iv,
+ rust::Vec<rust::u8> tag) {
+ CxxResult cxx_result{};
+ keymaster_error_t error;
+ cxx_result.error = false;
+
+ uint8_t* enc_secure_key_data = encrypted_secure_key.data();
+ int enc_secure_key_size = encrypted_secure_key.size();
+
+ uint8_t* iv_data = iv.data();
+ int iv_size = iv.size();
+
+ uint8_t* tag_data = tag.data();
+ int tag_size = tag.size();
+
+ uint8_t* enc_transport_key_data = encrypted_transport_key.data();
+ int enc_transport_key_size = encrypted_transport_key.size();
+
+ keymaster::UniquePtr<TEST_SECURE_KEY_WRAPPER, TEST_SECURE_KEY_WRAPPER_Delete> sec_key_wrapper(
+ TEST_SECURE_KEY_WRAPPER_new());
+ if (!sec_key_wrapper.get()) {
+ LOG(ERROR) << "createWrappedKey - Failed to allocate a memory";
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ // Fill version = 0
+ if (!ASN1_INTEGER_set(sec_key_wrapper->version, 0)) {
+ LOG(ERROR) << "createWrappedKey - Error while filling version: "
+ << keymaster::TranslateLastOpenSslError();
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ // Fill encrypted transport key.
+ if (enc_transport_key_size &&
+ !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_transport_key, enc_transport_key_data,
+ enc_transport_key_size)) {
+ LOG(ERROR) << "createWrappedKey - Error while filling encrypted transport key: "
+ << keymaster::TranslateLastOpenSslError();
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ // Fill encrypted secure key.
+ if (enc_secure_key_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_key,
+ enc_secure_key_data, enc_secure_key_size)) {
+ LOG(ERROR) << "createWrappedKey - Error while filling encrypted secure key: "
+ << keymaster::TranslateLastOpenSslError();
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ // Fill secure key authorization list.
+ keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list();
+ error = build_auth_list(auth_list, sec_key_wrapper->key_desc->key_params);
+ if (error != KM_ERROR_OK) {
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ // Fill secure key format.
+ if (!ASN1_INTEGER_set(sec_key_wrapper->key_desc->key_format, KM_KEY_FORMAT_RAW)) {
+ LOG(ERROR) << "createWrappedKey - Error while filling secure key format: "
+ << keymaster::TranslateLastOpenSslError();
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ // Fill initialization vector used for encrypting secure key.
+ if (iv_size &&
+ !ASN1_OCTET_STRING_set(sec_key_wrapper->initialization_vector, iv_data, iv_size)) {
+ LOG(ERROR) << "createWrappedKey - Error while filling IV: "
+ << keymaster::TranslateLastOpenSslError();
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ // Fill GCM-tag, extracted during secure key encryption.
+ if (tag_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->tag, tag_data, tag_size)) {
+ LOG(ERROR) << "createWrappedKey - Error while filling GCM-tag: "
+ << keymaster::TranslateLastOpenSslError();
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ // ASN.1 DER-encoding of secure key wrapper.
+ int asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), nullptr);
+ if (asn1_data_len < 0) {
+ LOG(ERROR) << "createWrappedKey - Error while performing DER encode: "
+ << keymaster::TranslateLastOpenSslError();
+ cxx_result.error = true;
+ return cxx_result;
+ }
+ std::vector<uint8_t> asn1_data(asn1_data_len, 0);
+
+ if (!asn1_data.data()) {
+ LOG(ERROR) << "createWrappedKey - Failed to allocate a memory for asn1_data";
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ uint8_t* p = asn1_data.data();
+ asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), &p);
+ if (asn1_data_len < 0) {
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data));
+
+ return cxx_result;
+}
+
+/**
+ * Perform EC/RSA sign operation using `EVP_PKEY`.
+ */
+bool performSignData(const char* data, size_t data_len, EVP_PKEY* pkey, unsigned char** signature,
+ size_t* signature_len) {
+ // Create the signing context
+ EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
+ if (md_ctx == NULL) {
+ LOG(ERROR) << "Failed to create signing context";
+ return false;
+ }
+
+ // Initialize the signing operation
+ if (EVP_DigestSignInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
+ LOG(ERROR) << "Failed to initialize signing operation";
+ EVP_MD_CTX_free(md_ctx);
+ return false;
+ }
+
+ // Sign the data
+ if (EVP_DigestSignUpdate(md_ctx, data, data_len) != 1) {
+ LOG(ERROR) << "Failed to sign data";
+ EVP_MD_CTX_free(md_ctx);
+ return false;
+ }
+
+ // Determine the length of the signature
+ if (EVP_DigestSignFinal(md_ctx, NULL, signature_len) != 1) {
+ LOG(ERROR) << "Failed to determine signature length";
+ EVP_MD_CTX_free(md_ctx);
+ return false;
+ }
+
+ // Allocate memory for the signature
+ *signature = (unsigned char*)malloc(*signature_len);
+ if (*signature == NULL) {
+ LOG(ERROR) << "Failed to allocate memory for the signature";
+ EVP_MD_CTX_free(md_ctx);
+ return false;
+ }
+
+ // Perform the final signing operation
+ if (EVP_DigestSignFinal(md_ctx, *signature, signature_len) != 1) {
+ LOG(ERROR) << "Failed to perform signing operation";
+ free(*signature);
+ EVP_MD_CTX_free(md_ctx);
+ return false;
+ }
+
+ EVP_MD_CTX_free(md_ctx);
+ return true;
+}
+
+/**
+ * Perform EC/RSA verify operation using `EVP_PKEY`.
+ */
+int performVerifySignature(const char* data, size_t data_len, EVP_PKEY* pkey,
+ const unsigned char* signature, size_t signature_len) {
+ // Create the verification context
+ EVP_MD_CTX* md_ctx = EVP_MD_CTX_new();
+ if (md_ctx == NULL) {
+ LOG(ERROR) << "Failed to create verification context";
+ return false;
+ }
+
+ // Initialize the verification operation
+ if (EVP_DigestVerifyInit(md_ctx, NULL, EVP_sha256(), NULL, pkey) != 1) {
+ LOG(ERROR) << "Failed to initialize verification operation";
+ EVP_MD_CTX_free(md_ctx);
+ return false;
+ }
+
+ // Verify the data
+ if (EVP_DigestVerifyUpdate(md_ctx, data, data_len) != 1) {
+ LOG(ERROR) << "Failed to verify data";
+ EVP_MD_CTX_free(md_ctx);
+ return false;
+ }
+
+ // Perform the verification operation
+ int ret = EVP_DigestVerifyFinal(md_ctx, signature, signature_len);
+ EVP_MD_CTX_free(md_ctx);
+
+ return ret == 1;
+}
+
+/**
+ * Extract the `EVP_PKEY` for the given KeyMint Key and perform Sign/Verify operations
+ * using extracted `EVP_PKEY`.
+ */
+bool performCryptoOpUsingKeystoreEngine(int64_t grant_id) {
+ const int KEY_ID_LEN = 20;
+ char key_id[KEY_ID_LEN] = "";
+ snprintf(key_id, KEY_ID_LEN, "%" PRIx64, grant_id);
+ std::string str_key = std::string(keystore2_grant_id_prefix) + key_id;
+ bool result = false;
+
+#if defined(OPENSSL_IS_BORINGSSL)
+ EVP_PKEY* evp = EVP_PKEY_from_keystore(str_key.c_str());
+ if (!evp) {
+ LOG(ERROR) << "Error while loading a key from keystore-engine";
+ return false;
+ }
+
+ int algo_type = EVP_PKEY_id(evp);
+ if (algo_type != EVP_PKEY_RSA && algo_type != EVP_PKEY_EC) {
+ LOG(ERROR) << "Unsupported Algorithm. Only RSA and EC are allowed.";
+ EVP_PKEY_free(evp);
+ return false;
+ }
+
+ unsigned char* signature = NULL;
+ size_t signature_len = 0;
+ const char* INPUT_DATA = "MY MESSAGE FOR SIGN";
+ size_t data_len = strlen(INPUT_DATA);
+ if (!performSignData(INPUT_DATA, data_len, evp, &signature, &signature_len)) {
+ LOG(ERROR) << "Failed to sign data";
+ EVP_PKEY_free(evp);
+ return false;
+ }
+
+ result = performVerifySignature(INPUT_DATA, data_len, evp, signature, signature_len);
+ if (!result) {
+ LOG(ERROR) << "Signature verification failed";
+ } else {
+ LOG(INFO) << "Signature verification success";
+ }
+
+ free(signature);
+ EVP_PKEY_free(evp);
+#endif
+ return result;
+}
+
+CxxResult getValueFromAttestRecord(rust::Vec<rust::u8> cert_buf, int32_t tag,
+ int32_t expected_sec_level) {
+ CxxResult cxx_result{};
+ cxx_result.error = false;
+
+ uint8_t* cert_data = cert_buf.data();
+ int cert_data_size = cert_buf.size();
+
+ std::vector<uint8_t> cert_bytes;
+ cert_bytes.insert(cert_bytes.end(), cert_data, (cert_data + cert_data_size));
+
+ X509_Ptr cert(parseCertBlob(cert_bytes));
+ if (!cert.get()) {
+ LOG(ERROR) << "getValueFromAttestRecord - Failed to allocate a memory for certificate";
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ ASN1_OCTET_STRING* attest_rec = getAttestationRecord(cert.get());
+ if (!attest_rec) {
+ LOG(ERROR) << "getValueFromAttestRecord - Error in getAttestationRecord: "
+ << keymaster::TranslateLastOpenSslError();
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ aidl::android::hardware::security::keymint::AuthorizationSet att_sw_enforced;
+ aidl::android::hardware::security::keymint::AuthorizationSet att_hw_enforced;
+ uint32_t att_attestation_version;
+ uint32_t att_keymint_version;
+ aidl::android::hardware::security::keymint::SecurityLevel att_attestation_security_level;
+ aidl::android::hardware::security::keymint::SecurityLevel att_keymint_security_level;
+ std::vector<uint8_t> att_challenge;
+ std::vector<uint8_t> att_unique_id;
+ std::vector<uint8_t> att_app_id;
+
+ int32_t error =
+ static_cast<int32_t>(aidl::android::hardware::security::keymint::parse_attestation_record(
+ attest_rec->data, attest_rec->length, &att_attestation_version,
+ &att_attestation_security_level, &att_keymint_version, &att_keymint_security_level,
+ &att_challenge, &att_sw_enforced, &att_hw_enforced, &att_unique_id));
+ if (error) {
+ LOG(ERROR) << "getValueFromAttestRecord - Error in parse_attestation_record: " << error;
+ cxx_result.error = true;
+ return cxx_result;
+ }
+
+ aidl::android::hardware::security::keymint::Tag auth_tag =
+ static_cast<aidl::android::hardware::security::keymint::Tag>(tag);
+ aidl::android::hardware::security::keymint::SecurityLevel tag_security_level =
+ static_cast<aidl::android::hardware::security::keymint::SecurityLevel>(expected_sec_level);
+
+ if (auth_tag == aidl::android::hardware::security::keymint::Tag::ATTESTATION_APPLICATION_ID) {
+ int pos = att_sw_enforced.find(
+ aidl::android::hardware::security::keymint::Tag::ATTESTATION_APPLICATION_ID);
+ if (pos == -1) {
+ LOG(ERROR) << "getValueFromAttestRecord - Attestation-application-id missing.";
+ cxx_result.error = true;
+ return cxx_result;
+ }
+ aidl::android::hardware::security::keymint::KeyParameter param = att_sw_enforced[pos];
+ std::vector<uint8_t> val =
+ param.value.get<aidl::android::hardware::security::keymint::KeyParameterValue::blob>();
+ std::move(val.begin(), val.end(), std::back_inserter(cxx_result.data));
+ return cxx_result;
+ }
+
+ if (auth_tag == aidl::android::hardware::security::keymint::Tag::ATTESTATION_CHALLENGE) {
+ if (att_challenge.size() == 0) {
+ LOG(ERROR) << "getValueFromAttestRecord - Attestation-challenge missing.";
+ cxx_result.error = true;
+ return cxx_result;
+ }
+ std::move(att_challenge.begin(), att_challenge.end(), std::back_inserter(cxx_result.data));
+ return cxx_result;
+ }
+
+ if (auth_tag == aidl::android::hardware::security::keymint::Tag::UNIQUE_ID) {
+ if (att_unique_id.size() == 0) {
+ LOG(ERROR) << "getValueFromAttestRecord - unsupported tag - UNIQUE_ID.";
+ cxx_result.error = true;
+ return cxx_result;
+ }
+ std::move(att_unique_id.begin(), att_unique_id.end(), std::back_inserter(cxx_result.data));
+ return cxx_result;
+ }
+
+ if (auth_tag == aidl::android::hardware::security::keymint::Tag::USAGE_COUNT_LIMIT) {
+ aidl::android::hardware::security::keymint::KeyParameter param;
+ int pos = att_hw_enforced.find(auth_tag);
+ if (tag_security_level ==
+ aidl::android::hardware::security::keymint::SecurityLevel::SOFTWARE ||
+ tag_security_level ==
+ aidl::android::hardware::security::keymint::SecurityLevel::KEYSTORE) {
+ pos = att_sw_enforced.find(auth_tag);
+ if (pos == -1) {
+ LOG(ERROR) << "USAGE_COUNT_LIMIT not found in software enforced auth list";
+ cxx_result.error = KM_ERROR_INVALID_TAG;
+ return cxx_result;
+ }
+ param = att_sw_enforced[pos];
+ } else {
+ pos = att_hw_enforced.find(auth_tag);
+ if (pos == -1) {
+ LOG(ERROR) << "USAGE_COUNT_LIMIT not found in hardware enforced auth list";
+ cxx_result.error = KM_ERROR_INVALID_TAG;
+ return cxx_result;
+ }
+ param = att_hw_enforced[pos];
+ }
+ std::string val = std::to_string(
+ param.value
+ .get<aidl::android::hardware::security::keymint::KeyParameterValue::integer>());
+ std::move(val.begin(), val.end(), std::back_inserter(cxx_result.data));
+ return cxx_result;
+ }
+
+ int pos = att_hw_enforced.find(auth_tag);
+ if (pos == -1) {
+ LOG(ERROR) << "getValueFromAttestRecord - unsupported tag.";
+ cxx_result.error = true;
+ return cxx_result;
+ }
+ aidl::android::hardware::security::keymint::KeyParameter param = att_hw_enforced[pos];
+ std::vector<uint8_t> val =
+ param.value.get<aidl::android::hardware::security::keymint::KeyParameterValue::blob>();
+ std::move(val.begin(), val.end(), std::back_inserter(cxx_result.data));
+ return cxx_result;
+}
+
+uint32_t getOsVersion() {
+ return aidl::android::hardware::security::keymint::getOsVersion();
+}
+
+uint32_t getOsPatchlevel() {
+ return aidl::android::hardware::security::keymint::getOsPatchlevel();
+}
+
+uint32_t getVendorPatchlevel() {
+ return aidl::android::hardware::security::keymint::getVendorPatchlevel();
+}
diff --git a/keystore2/test_utils/ffi_test_utils.hpp b/keystore2/test_utils/ffi_test_utils.hpp
new file mode 100644
index 00000000..c4db1ba4
--- /dev/null
+++ b/keystore2/test_utils/ffi_test_utils.hpp
@@ -0,0 +1,16 @@
+#pragma once
+
+#include "ffi_test_utils.rs.h"
+#include "rust/cxx.h"
+
+bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check);
+CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
+ rust::Vec<rust::u8> encrypted_transport_key, rust::Vec<rust::u8> iv,
+ rust::Vec<rust::u8> tag);
+CxxResult buildAsn1DerEncodedWrappedKeyDescription();
+bool performCryptoOpUsingKeystoreEngine(int64_t grant_id);
+CxxResult getValueFromAttestRecord(rust::Vec<rust::u8> cert_buf, int32_t tag,
+ int32_t expected_sec_level);
+uint32_t getOsVersion();
+uint32_t getOsPatchlevel();
+uint32_t getVendorPatchlevel();
diff --git a/keystore2/tests/ffi_test_utils.rs b/keystore2/test_utils/ffi_test_utils.rs
index 066d4a1e..1ccdcc81 100644
--- a/keystore2/tests/ffi_test_utils.rs
+++ b/keystore2/test_utils/ffi_test_utils.rs
@@ -12,13 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use keystore2_test_utils::key_generations::Error;
+//! This module implements helper methods to access the functionalities implemented in CPP.
+
+use crate::key_generations::Error;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ SecurityLevel::SecurityLevel, Tag::Tag,
+};
#[cxx::bridge]
mod ffi {
struct CxxResult {
data: Vec<u8>,
- error: i32,
+ error: bool,
}
unsafe extern "C++" {
@@ -31,20 +36,42 @@ mod ffi {
tag: Vec<u8>,
) -> CxxResult;
fn buildAsn1DerEncodedWrappedKeyDescription() -> CxxResult;
+ fn performCryptoOpUsingKeystoreEngine(grant_id: i64) -> bool;
+ fn getValueFromAttestRecord(
+ cert_buf: Vec<u8>,
+ tag: i32,
+ expected_sec_level: i32,
+ ) -> CxxResult;
+ fn getOsVersion() -> u32;
+ fn getOsPatchlevel() -> u32;
+ fn getVendorPatchlevel() -> u32;
}
}
/// Validate given certificate chain.
pub fn validate_certchain(cert_buf: &[u8]) -> Result<bool, Error> {
- if ffi::validateCertChain(cert_buf.to_vec(), cert_buf.len().try_into().unwrap(), true) {
+ validate_certchain_with_strict_issuer_check(cert_buf, true)
+}
+
+/// Validate given certificate chain with an option to validate the issuer.
+pub fn validate_certchain_with_strict_issuer_check(
+ cert_buf: &[u8],
+ strict_issuer_check: bool,
+) -> Result<bool, Error> {
+ if ffi::validateCertChain(
+ cert_buf.to_vec(),
+ cert_buf.len().try_into().unwrap(),
+ strict_issuer_check,
+ ) {
return Ok(true);
}
Err(Error::ValidateCertChainFailed)
}
+/// Collect the result from CxxResult into a Rust supported structure.
fn get_result(result: ffi::CxxResult) -> Result<Vec<u8>, Error> {
- if result.error == 0 && !result.data.is_empty() {
+ if !result.error && !result.data.is_empty() {
Ok(result.data)
} else {
Err(Error::DerEncodeFailed)
@@ -78,3 +105,40 @@ pub fn create_wrapped_key(
pub fn create_wrapped_key_additional_auth_data() -> Result<Vec<u8>, Error> {
get_result(ffi::buildAsn1DerEncodedWrappedKeyDescription())
}
+
+/// Performs crypto operation using Keystore-Engine APIs.
+pub fn perform_crypto_op_using_keystore_engine(grant_id: i64) -> Result<bool, Error> {
+ if ffi::performCryptoOpUsingKeystoreEngine(grant_id) {
+ return Ok(true);
+ }
+
+ Err(Error::Keystore2EngineOpFailed)
+}
+
+/// Get the value of the given `Tag` from attestation record.
+pub fn get_value_from_attest_record(
+ cert_buf: &[u8],
+ tag: Tag,
+ expected_sec_level: SecurityLevel,
+) -> Result<Vec<u8>, Error> {
+ let result = ffi::getValueFromAttestRecord(cert_buf.to_vec(), tag.0, expected_sec_level.0);
+ if !result.error && !result.data.is_empty() {
+ return Ok(result.data);
+ }
+ Err(Error::AttestRecordGetValueFailed)
+}
+
+/// Get OS Version
+pub fn get_os_version() -> u32 {
+ ffi::getOsVersion()
+}
+
+/// Get OS Patch Level
+pub fn get_os_patchlevel() -> u32 {
+ ffi::getOsPatchlevel()
+}
+
+/// Get vendor Patch Level
+pub fn get_vendor_patchlevel() -> u32 {
+ ffi::getVendorPatchlevel()
+}
diff --git a/keystore2/test_utils/key_generations.rs b/keystore2/test_utils/key_generations.rs
index e4c4968f..a733be39 100644
--- a/keystore2/test_utils/key_generations.rs
+++ b/keystore2/test_utils/key_generations.rs
@@ -15,15 +15,22 @@
//! This module implements test utils to generate various types of keys.
use anyhow::Result;
+use core::ops::Range;
+use nix::unistd::getuid;
+use std::collections::HashSet;
+use std::fmt::Write;
+
+use binder::ThreadState;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
ErrorCode::ErrorCode, HardwareAuthenticatorType::HardwareAuthenticatorType,
KeyOrigin::KeyOrigin, KeyParameter::KeyParameter, KeyParameterValue::KeyParameterValue,
- KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, Tag::Tag,
+ KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- AuthenticatorSpec::AuthenticatorSpec, Authorization::Authorization, Domain::Domain,
+ AuthenticatorSpec::AuthenticatorSpec, Authorization::Authorization,
+ CreateOperationResponse::CreateOperationResponse, Domain::Domain,
IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
};
@@ -31,6 +38,11 @@ use android_system_keystore2::aidl::android::system::keystore2::{
use crate::authorizations::AuthSetBuilder;
use android_system_keystore2::binder::{ExceptionCode, Result as BinderResult};
+use crate::ffi_test_utils::{
+ get_os_patchlevel, get_os_version, get_value_from_attest_record, get_vendor_patchlevel,
+ validate_certchain_with_strict_issuer_check,
+};
+
/// Shell namespace.
pub const SELINUX_SHELL_NAMESPACE: i64 = 1;
/// Vold namespace.
@@ -42,6 +54,52 @@ pub const TARGET_SU_CTX: &str = "u:r:su:s0";
/// Vold context
pub const TARGET_VOLD_CTX: &str = "u:r:vold:s0";
+/// Allowed tags in generated/imported key authorizations.
+/// See hardware/interfaces/security/keymint/aidl/android/hardware/security/keymint/Tag.aidl for the
+/// list feature tags.
+/// Note: This list need to be updated whenever a new Tag is introduced and is expected to be added
+/// in key authorizations.
+pub const ALLOWED_TAGS_IN_KEY_AUTHS: &[Tag] = &[
+ Tag::ACTIVE_DATETIME,
+ Tag::ALGORITHM,
+ Tag::ALLOW_WHILE_ON_BODY,
+ Tag::AUTH_TIMEOUT,
+ Tag::BLOCK_MODE,
+ Tag::BOOTLOADER_ONLY,
+ Tag::BOOT_PATCHLEVEL,
+ Tag::CALLER_NONCE,
+ Tag::CREATION_DATETIME,
+ Tag::DIGEST,
+ Tag::EARLY_BOOT_ONLY,
+ Tag::EC_CURVE,
+ Tag::IDENTITY_CREDENTIAL_KEY,
+ Tag::INCLUDE_UNIQUE_ID,
+ Tag::KEY_SIZE,
+ Tag::MAX_BOOT_LEVEL,
+ Tag::MAX_USES_PER_BOOT,
+ Tag::MIN_MAC_LENGTH,
+ Tag::NO_AUTH_REQUIRED,
+ Tag::ORIGIN,
+ Tag::ORIGINATION_EXPIRE_DATETIME,
+ Tag::OS_PATCHLEVEL,
+ Tag::OS_VERSION,
+ Tag::PADDING,
+ Tag::PURPOSE,
+ Tag::ROLLBACK_RESISTANCE,
+ Tag::RSA_OAEP_MGF_DIGEST,
+ Tag::RSA_PUBLIC_EXPONENT,
+ Tag::STORAGE_KEY,
+ Tag::TRUSTED_CONFIRMATION_REQUIRED,
+ Tag::TRUSTED_USER_PRESENCE_REQUIRED,
+ Tag::UNLOCKED_DEVICE_REQUIRED,
+ Tag::USAGE_COUNT_LIMIT,
+ Tag::USAGE_EXPIRE_DATETIME,
+ Tag::USER_AUTH_TYPE,
+ Tag::USER_ID,
+ Tag::USER_SECURE_ID,
+ Tag::VENDOR_PATCHLEVEL,
+];
+
/// Key parameters to generate a key.
pub struct KeyParams {
/// Key Size.
@@ -299,6 +357,15 @@ pub enum Error {
/// Error code to indicate error in ASN.1 DER-encoded data creation.
#[error("Failed to create and encode ASN.1 data.")]
DerEncodeFailed,
+ /// Error code to indicate error while using keystore-engine API.
+ #[error("Failed to perform crypto op using keystore-engine APIs.")]
+ Keystore2EngineOpFailed,
+ /// Error code to indicate error in attestation-id validation.
+ #[error("Failed to validate attestation-id.")]
+ ValidateAttestIdFailed,
+ /// Error code to indicate error in getting value from attest record.
+ #[error("Failed to get value from attest record.")]
+ AttestRecordGetValueFailed,
}
/// Keystore2 error mapping.
@@ -324,6 +391,140 @@ pub fn map_ks_error<T>(r: BinderResult<T>) -> Result<T, Error> {
})
}
+/// Indicate whether the default device is KeyMint (rather than Keymaster).
+pub fn has_default_keymint() -> bool {
+ binder::is_declared("android.hardware.security.keymint.IKeyMintDevice/default")
+ .expect("Could not check for declared keymint interface")
+}
+
+/// Verify that given key param is listed in given authorizations list.
+pub fn check_key_param(authorizations: &[Authorization], key_param: &KeyParameter) -> bool {
+ authorizations.iter().any(|auth| &auth.keyParameter == key_param)
+}
+
+/// Verify the given key authorizations with the expected authorizations.
+pub fn check_key_authorizations(
+ authorizations: &[Authorization],
+ expected_params: &[KeyParameter],
+ expected_key_origin: KeyOrigin,
+) {
+ // Make sure key authorizations contains only `ALLOWED_TAGS_IN_KEY_AUTHS`
+ authorizations.iter().all(|auth| {
+ // Ignore `INVALID` tag if the backend is Keymaster and not KeyMint.
+ // Keymaster allows INVALID tag for unsupported key parameters.
+ if !has_default_keymint() && auth.keyParameter.tag == Tag::INVALID {
+ return true;
+ }
+ assert!(
+ ALLOWED_TAGS_IN_KEY_AUTHS.contains(&auth.keyParameter.tag),
+ "key authorization is not allowed: {:#?}",
+ auth.keyParameter
+ );
+ true
+ });
+
+ //Check allowed-expected-key-parameters are present in given key authorizations list.
+ expected_params.iter().all(|key_param| {
+ // `INCLUDE_UNIQUE_ID` is not strictly expected to be in key authorizations but has been
+ // put there by some implementations so cope with that.
+ if key_param.tag == Tag::INCLUDE_UNIQUE_ID
+ && !authorizations.iter().any(|auth| auth.keyParameter.tag == key_param.tag)
+ {
+ return true;
+ }
+
+ // Ignore below parameters if the backend is Keymaster and not KeyMint.
+ // Keymaster does not support these parameters. These key parameters are introduced in
+ // KeyMint1.0.
+ if !has_default_keymint() {
+ if matches!(key_param.tag, Tag::RSA_OAEP_MGF_DIGEST | Tag::USAGE_COUNT_LIMIT) {
+ return true;
+ }
+ if key_param.tag == Tag::PURPOSE
+ && key_param.value == KeyParameterValue::KeyPurpose(KeyPurpose::ATTEST_KEY)
+ {
+ return true;
+ }
+ }
+
+ if ALLOWED_TAGS_IN_KEY_AUTHS.contains(&key_param.tag) {
+ assert!(
+ check_key_param(authorizations, key_param),
+ "Key parameter not found: {:#?}",
+ key_param
+ );
+ }
+ true
+ });
+
+ check_common_auths(authorizations, expected_key_origin);
+}
+
+/// Verify common key authorizations.
+fn check_common_auths(authorizations: &[Authorization], expected_key_origin: KeyOrigin) {
+ assert!(check_key_param(
+ authorizations,
+ &KeyParameter {
+ tag: Tag::OS_VERSION,
+ value: KeyParameterValue::Integer(get_os_version().try_into().unwrap())
+ }
+ ));
+ assert!(check_key_param(
+ authorizations,
+ &KeyParameter {
+ tag: Tag::OS_PATCHLEVEL,
+ value: KeyParameterValue::Integer(get_os_patchlevel().try_into().unwrap())
+ }
+ ));
+
+ // Access denied for finding vendor-patch-level ("ro.vendor.build.security_patch") property
+ // in a test running with `untrusted_app` context. Keeping this check to verify
+ // vendor-patch-level in tests running with `su` context.
+ if getuid().is_root() {
+ assert!(check_key_param(
+ authorizations,
+ &KeyParameter {
+ tag: Tag::VENDOR_PATCHLEVEL,
+ value: KeyParameterValue::Integer(get_vendor_patchlevel().try_into().unwrap())
+ }
+ ));
+ }
+ assert!(check_key_param(
+ authorizations,
+ &KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(expected_key_origin) }
+ ));
+ assert!(check_key_param(
+ authorizations,
+ &KeyParameter {
+ tag: Tag::USER_ID,
+ value: KeyParameterValue::Integer(
+ rustutils::users::multiuser_get_user_id(ThreadState::get_calling_uid())
+ .try_into()
+ .unwrap()
+ )
+ }
+ ));
+
+ if has_default_keymint() {
+ assert!(authorizations
+ .iter()
+ .map(|auth| &auth.keyParameter)
+ .any(|key_param| key_param.tag == Tag::CREATION_DATETIME));
+ }
+}
+
+/// Get the key `Authorization` for the given auth `Tag`.
+pub fn get_key_auth(authorizations: &[Authorization], tag: Tag) -> Option<&Authorization> {
+ let auths: Vec<&Authorization> =
+ authorizations.iter().filter(|auth| auth.keyParameter.tag == tag).collect();
+
+ if !auths.is_empty() {
+ Some(auths[0])
+ } else {
+ None
+ }
+}
+
/// Generate EC Key using given security level and domain with below key parameters and
/// optionally allow the generated key to be attested with factory provisioned attest key using
/// given challenge and application id -
@@ -367,6 +568,11 @@ pub fn generate_ec_p256_signing_key(
assert!(key_metadata.key.blob.is_some());
}
+ check_key_authorizations(
+ &key_metadata.authorizations,
+ &gen_params,
+ KeyOrigin::GENERATED,
+ );
Ok(key_metadata)
}
Err(e) => Err(e),
@@ -409,6 +615,7 @@ pub fn generate_ec_key(
} else {
assert!(key_metadata.key.blob.is_none());
}
+ check_key_authorizations(&key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
Ok(key_metadata)
}
@@ -470,6 +677,19 @@ pub fn generate_rsa_key(
|| key_metadata.key.blob.is_none()
);
+ check_key_authorizations(&key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
+ // If `RSA_OAEP_MGF_DIGEST` tag is not mentioned explicitly while generating/importing a key,
+ // then make sure `RSA_OAEP_MGF_DIGEST` tag with default value (SHA1) must not be included in
+ // key authorization list.
+ if key_params.mgf_digest.is_none() {
+ assert!(!check_key_param(
+ &key_metadata.authorizations,
+ &KeyParameter {
+ tag: Tag::RSA_OAEP_MGF_DIGEST,
+ value: KeyParameterValue::Digest(Digest::SHA1)
+ }
+ ));
+ }
Ok(key_metadata)
}
@@ -514,6 +734,7 @@ pub fn generate_sym_key(
// Should not have an attestation record.
assert!(key_metadata.certificateChain.is_none());
+ check_key_authorizations(&key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
Ok(key_metadata)
}
@@ -553,6 +774,7 @@ pub fn generate_hmac_key(
// Should not have an attestation record.
assert!(key_metadata.certificateChain.is_none());
+ check_key_authorizations(&key_metadata.authorizations, &gen_params, KeyOrigin::GENERATED);
Ok(key_metadata)
}
@@ -637,6 +859,11 @@ pub fn generate_ec_attestation_key(
// Should have an attestation record.
assert!(attestation_key_metadata.certificateChain.is_some());
+ check_key_authorizations(
+ &attestation_key_metadata.authorizations,
+ &gen_params,
+ KeyOrigin::GENERATED,
+ );
Ok(attestation_key_metadata)
}
@@ -671,20 +898,10 @@ pub fn generate_ec_256_attested_key(
// Shouldn't have an attestation record.
assert!(ec_key_metadata.certificateChain.is_none());
+ check_key_authorizations(&ec_key_metadata.authorizations, &ec_gen_params, KeyOrigin::GENERATED);
Ok(ec_key_metadata)
}
-/// Verify that given key param is listed in given authorizations list.
-pub fn check_key_param(authorizations: &[Authorization], key_param: KeyParameter) -> bool {
- for authrization in authorizations {
- if authrization.keyParameter == key_param {
- return true;
- }
- }
-
- false
-}
-
/// Imports above defined RSA key - `RSA_2048_KEY` and validates imported key parameters.
pub fn import_rsa_2048_key(
sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
@@ -706,24 +923,27 @@ pub fn import_rsa_2048_key(
assert!(key_metadata.certificate.is_some());
assert!(key_metadata.certificateChain.is_none());
+ check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+
+ // Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::RSA) }
+ &KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::RSA) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) }
+ &KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(2048) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
+ &KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter {
+ &KeyParameter {
tag: Tag::RSA_PUBLIC_EXPONENT,
value: KeyParameterValue::LongInteger(65537)
}
@@ -731,7 +951,7 @@ pub fn import_rsa_2048_key(
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter {
+ &KeyParameter {
tag: Tag::PADDING,
value: KeyParameterValue::PaddingMode(PaddingMode::RSA_PSS)
}
@@ -739,7 +959,7 @@ pub fn import_rsa_2048_key(
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+ &KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
));
Ok(key_metadata)
@@ -766,23 +986,26 @@ pub fn import_ec_p_256_key(
assert!(key_metadata.certificate.is_some());
assert!(key_metadata.certificateChain.is_none());
+ check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+
+ // Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::EC) }
+ &KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::EC) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::EC_CURVE, value: KeyParameterValue::EcCurve(EcCurve::P_256) }
+ &KeyParameter { tag: Tag::EC_CURVE, value: KeyParameterValue::EcCurve(EcCurve::P_256) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
+ &KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+ &KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
));
Ok(key_metadata)
@@ -815,28 +1038,31 @@ pub fn import_aes_key(
AES_KEY,
)?;
+ check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+
+ // Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::AES) }
+ &KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::AES) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) }
+ &KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter {
+ &KeyParameter {
tag: Tag::PADDING,
value: KeyParameterValue::PaddingMode(PaddingMode::PKCS7)
}
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(BlockMode::ECB) }
+ &KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(BlockMode::ECB) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+ &KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
));
Ok(key_metadata)
@@ -871,31 +1097,34 @@ pub fn import_3des_key(
TRIPLE_DES_KEY,
)?;
+ check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+
+ // Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter {
+ &KeyParameter {
tag: Tag::ALGORITHM,
value: KeyParameterValue::Algorithm(Algorithm::TRIPLE_DES)
}
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(168) }
+ &KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(168) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter {
+ &KeyParameter {
tag: Tag::PADDING,
value: KeyParameterValue::PaddingMode(PaddingMode::PKCS7)
}
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(BlockMode::ECB) }
+ &KeyParameter { tag: Tag::BLOCK_MODE, value: KeyParameterValue::BlockMode(BlockMode::ECB) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+ &KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
));
Ok(key_metadata)
@@ -928,21 +1157,24 @@ pub fn import_hmac_key(
HMAC_KEY,
)?;
+ check_key_authorizations(&key_metadata.authorizations, &import_params, KeyOrigin::IMPORTED);
+
+ // Check below auths explicitly, they might not be addd in import parameters.
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::HMAC) }
+ &KeyParameter { tag: Tag::ALGORITHM, value: KeyParameterValue::Algorithm(Algorithm::HMAC) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) }
+ &KeyParameter { tag: Tag::KEY_SIZE, value: KeyParameterValue::Integer(128) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
+ &KeyParameter { tag: Tag::DIGEST, value: KeyParameterValue::Digest(Digest::SHA_2_256) }
));
assert!(check_key_param(
&key_metadata.authorizations,
- KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
+ &KeyParameter { tag: Tag::ORIGIN, value: KeyParameterValue::Origin(KeyOrigin::IMPORTED) }
));
Ok(key_metadata)
@@ -1077,8 +1309,184 @@ pub fn generate_ec_agree_key(
assert!(key_metadata.key.blob.is_some());
}
+ check_key_authorizations(
+ &key_metadata.authorizations,
+ &gen_params,
+ KeyOrigin::GENERATED,
+ );
Ok(key_metadata)
}
Err(e) => Err(e),
}
}
+
+/// Helper method to import AES keys `total_count` of times.
+pub fn import_aes_keys(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ alias_prefix: String,
+ total_count: Range<i32>,
+) -> binder::Result<HashSet<String>> {
+ let mut imported_key_aliases = HashSet::new();
+
+ // Import Total number of keys with given alias prefix.
+ for count in total_count {
+ let mut alias = String::new();
+ write!(alias, "{}_{}", alias_prefix, count).unwrap();
+ imported_key_aliases.insert(alias.clone());
+
+ import_aes_key(sec_level, Domain::APP, -1, Some(alias))?;
+ }
+
+ Ok(imported_key_aliases)
+}
+
+/// Generate attested EC-P_256 key with device id attestation.
+pub fn generate_key_with_attest_id(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ algorithm: Algorithm,
+ alias: Option<String>,
+ att_challenge: &[u8],
+ attest_key: &KeyDescriptor,
+ attest_id: Tag,
+ value: Vec<u8>,
+) -> binder::Result<KeyMetadata> {
+ assert!(algorithm == Algorithm::RSA || algorithm == Algorithm::EC);
+
+ let mut ec_gen_params;
+ if algorithm == Algorithm::EC {
+ ec_gen_params = AuthSetBuilder::new()
+ .no_auth_required()
+ .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());
+ } else {
+ ec_gen_params = AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .rsa_public_exponent(65537)
+ .key_size(2048)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .padding_mode(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .attestation_challenge(att_challenge.to_vec());
+ }
+
+ match attest_id {
+ Tag::ATTESTATION_ID_BRAND => {
+ ec_gen_params = ec_gen_params.attestation_device_brand(value);
+ }
+ Tag::ATTESTATION_ID_DEVICE => {
+ ec_gen_params = ec_gen_params.attestation_device_name(value);
+ }
+ Tag::ATTESTATION_ID_PRODUCT => {
+ ec_gen_params = ec_gen_params.attestation_device_product_name(value);
+ }
+ Tag::ATTESTATION_ID_SERIAL => {
+ ec_gen_params = ec_gen_params.attestation_device_serial(value);
+ }
+ Tag::ATTESTATION_ID_MANUFACTURER => {
+ ec_gen_params = ec_gen_params.attestation_device_manufacturer(value);
+ }
+ Tag::ATTESTATION_ID_MODEL => {
+ ec_gen_params = ec_gen_params.attestation_device_model(value);
+ }
+ Tag::ATTESTATION_ID_IMEI => {
+ ec_gen_params = ec_gen_params.attestation_device_imei(value);
+ }
+ Tag::ATTESTATION_ID_SECOND_IMEI => {
+ ec_gen_params = ec_gen_params.attestation_device_second_imei(value);
+ }
+ _ => {
+ panic!("Unknown attestation id");
+ }
+ }
+
+ sec_level.generateKey(
+ &KeyDescriptor { domain: Domain::APP, nspace: -1, alias, blob: None },
+ Some(attest_key),
+ &ec_gen_params,
+ 0,
+ b"entropy",
+ )
+}
+
+/// Generate Key and validate key characteristics.
+pub fn generate_key(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ gen_params: &AuthSetBuilder,
+ alias: &str,
+) -> binder::Result<KeyMetadata> {
+ let key_metadata = sec_level.generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ None,
+ gen_params,
+ 0,
+ b"entropy",
+ )?;
+
+ if gen_params.iter().any(|kp| {
+ matches!(
+ kp.value,
+ KeyParameterValue::Algorithm(Algorithm::RSA)
+ | KeyParameterValue::Algorithm(Algorithm::EC)
+ )
+ }) {
+ assert!(key_metadata.certificate.is_some());
+ if gen_params.iter().any(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE) {
+ assert!(key_metadata.certificateChain.is_some());
+ let mut cert_chain: Vec<u8> = Vec::new();
+ cert_chain.extend(key_metadata.certificate.as_ref().unwrap());
+ cert_chain.extend(key_metadata.certificateChain.as_ref().unwrap());
+ let strict_issuer_check =
+ !(gen_params.iter().any(|kp| kp.tag == Tag::DEVICE_UNIQUE_ATTESTATION));
+ validate_certchain_with_strict_issuer_check(&cert_chain, strict_issuer_check)
+ .expect("Error while validating cert chain");
+ }
+
+ if let Some(challenge_param) =
+ gen_params.iter().find(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE)
+ {
+ if let KeyParameterValue::Blob(val) = &challenge_param.value {
+ let att_challenge = get_value_from_attest_record(
+ key_metadata.certificate.as_ref().unwrap(),
+ challenge_param.tag,
+ key_metadata.keySecurityLevel,
+ )
+ .expect("Attestation challenge verification failed.");
+ assert_eq!(&att_challenge, val);
+ }
+
+ let att_app_id = get_value_from_attest_record(
+ key_metadata.certificate.as_ref().unwrap(),
+ Tag::ATTESTATION_APPLICATION_ID,
+ SecurityLevel::KEYSTORE,
+ )
+ .expect("Attestation application id verification failed.");
+ assert!(!att_app_id.is_empty());
+ }
+ }
+ check_key_authorizations(&key_metadata.authorizations, gen_params, KeyOrigin::GENERATED);
+
+ Ok(key_metadata)
+}
+
+/// Generate a key using given authorizations and create an operation using the generated key.
+pub fn create_key_and_operation(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ gen_params: &AuthSetBuilder,
+ op_params: &AuthSetBuilder,
+ alias: &str,
+) -> binder::Result<CreateOperationResponse> {
+ let key_metadata = generate_key(sec_level, gen_params, alias)?;
+
+ sec_level.createOperation(&key_metadata.key, op_params, false)
+}
diff --git a/keystore2/test_utils/lib.rs b/keystore2/test_utils/lib.rs
index c63bfacc..8394ca1c 100644
--- a/keystore2/test_utils/lib.rs
+++ b/keystore2/test_utils/lib.rs
@@ -20,12 +20,15 @@ use std::path::{Path, PathBuf};
use std::{env::temp_dir, ops::Deref};
use android_system_keystore2::aidl::android::system::keystore2::IKeystoreService::IKeystoreService;
+use android_security_authorization::aidl::android::security::authorization::IKeystoreAuthorization::IKeystoreAuthorization;
pub mod authorizations;
+pub mod ffi_test_utils;
pub mod key_generations;
pub mod run_as;
static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
+static AUTH_SERVICE_NAME: &str = "android.security.authorization";
/// Represents the lifecycle of a temporary directory for testing.
#[derive(Debug)]
@@ -115,3 +118,8 @@ impl Deref for PathBuilder {
pub fn get_keystore_service() -> binder::Strong<dyn IKeystoreService> {
binder::get_interface(KS2_SERVICE_NAME).unwrap()
}
+
+/// Get Keystore auth service.
+pub fn get_keystore_auth_service() -> binder::Strong<dyn IKeystoreAuthorization> {
+ binder::get_interface(AUTH_SERVICE_NAME).unwrap()
+}
diff --git a/keystore2/test_utils/run_as.rs b/keystore2/test_utils/run_as.rs
index 2485ab57..d39d0697 100644
--- a/keystore2/test_utils/run_as.rs
+++ b/keystore2/test_utils/run_as.rs
@@ -29,13 +29,14 @@
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,
+ 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;
+use std::os::fd::AsRawFd;
+use std::os::fd::OwnedFd;
fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) {
setgid(gid).expect("Failed to set GID. This test might need more privileges.");
@@ -48,35 +49,23 @@ fn transition(se_context: selinux::Context, uid: Uid, gid: Gid) {
/// 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);
+struct PipeReader(OwnedFd);
impl Read for PipeReader {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
- let bytes = nix_read(self.0, buf)?;
+ let bytes = nix_read(self.0.as_raw_fd(), 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.");
- }
-}
+struct PipeWriter(OwnedFd);
impl Write for PipeWriter {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
- let written = nix_write(self.0, buf)?;
+ let written = nix_write(&self.0, buf)?;
Ok(written)
}
@@ -255,7 +244,9 @@ where
let (response_reader, mut response_writer) =
pipe_channel().expect("Failed to create cmd pipe.");
- match fork() {
+ // SAFETY: Our caller guarantees that the process only has a single thread, so calling
+ // non-async-signal-safe functions in the child is in fact safe.
+ match unsafe { fork() } {
Ok(ForkResult::Parent { child, .. }) => {
drop(response_writer);
drop(cmd_reader);
@@ -314,7 +305,9 @@ where
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() {
+ // SAFETY: Our caller guarantees that the process only has a single thread, so calling
+ // non-async-signal-safe functions in the child is in fact safe.
+ match unsafe { fork() } {
Ok(ForkResult::Parent { child, .. }) => {
drop(writer);
let status = waitpid(child, None).expect("Failed while waiting for child.");
diff --git a/keystore2/tests/Android.bp b/keystore2/tests/Android.bp
index 78dd2d72..01ea7465 100644
--- a/keystore2/tests/Android.bp
+++ b/keystore2/tests/Android.bp
@@ -23,6 +23,7 @@ package {
rust_test {
name: "keystore2_client_tests",
+ compile_multilib: "first",
defaults: [
"keymint_use_latest_hal_aidl_rust",
"keystore2_use_latest_aidl_rust",
@@ -30,71 +31,21 @@ rust_test {
srcs: ["keystore2_client_tests.rs"],
test_suites: [
"general-tests",
+ "vts",
],
test_config: "AndroidTest.xml",
rustlibs: [
- "librustutils",
+ "android.hardware.security.secureclock-V1-rust",
+ "android.security.authorization-rust",
+ "libaconfig_android_hardware_biometrics_rust",
+ "libbinder_rs",
"libkeystore2_test_utils",
- "packagemanager_aidl-rust",
"libnix",
- "libanyhow",
- "libbinder_rs",
- "liblazy_static",
- "liblibc",
- "libserde",
- "libthiserror",
- "libcxx",
"libopenssl",
- ],
- static_libs: [
- "libkeystore2_ffi_test_utils",
- "libgtest",
- "libkeymint_vts_test_utils",
- ],
- shared_libs: [
- "libcrypto",
- "libkeymaster_portable",
- "libkeymaster_messages",
- "libcppbor_external",
+ "librustutils",
+ "libserde",
+ "packagemanager_aidl-rust",
],
require_root: true,
}
-
-cc_library_static {
- name: "libkeystore2_ffi_test_utils",
- srcs: ["ffi_test_utils.cpp"],
- defaults: [
- "keymint_vts_defaults",
- "hidl_defaults",
- ],
- generated_headers: [
- "cxx-bridge-header",
- "libkeystore2_ffi_test_utils_bridge_header",
- ],
- generated_sources: ["libkeystore2_ffi_test_utils_bridge_code"],
- static_libs: [
- "libkeymint_vts_test_utils",
- ],
- shared_libs: [
- "libkeymaster_portable",
- "libkeymaster_messages",
- "libcppbor_external",
- ],
-}
-
-genrule {
- name: "libkeystore2_ffi_test_utils_bridge_code",
- tools: ["cxxbridge"],
- cmd: "$(location cxxbridge) $(in) >> $(out)",
- srcs: ["ffi_test_utils.rs"],
- out: ["libkeystore2_test_utils_cxx_generated.cc"],
-}
-
-genrule {
- name: "libkeystore2_ffi_test_utils_bridge_header",
- tools: ["cxxbridge"],
- cmd: "$(location cxxbridge) $(in) --header >> $(out)",
- srcs: ["ffi_test_utils.rs"],
- out: ["ffi_test_utils.rs.h"],
-}
diff --git a/keystore2/tests/AndroidTest.xml b/keystore2/tests/AndroidTest.xml
index 7db36f7e..dde18a9b 100644
--- a/keystore2/tests/AndroidTest.xml
+++ b/keystore2/tests/AndroidTest.xml
@@ -14,6 +14,7 @@
limitations under the License.
-->
<configuration description="Config to run keystore2_client_tests device tests.">
+ <option name="config-descriptor:metadata" key="parameter" value="not_multi_abi" />
<target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer">
</target_preparer>
diff --git a/keystore2/tests/ffi_test_utils.cpp b/keystore2/tests/ffi_test_utils.cpp
deleted file mode 100644
index de20d838..00000000
--- a/keystore2/tests/ffi_test_utils.cpp
+++ /dev/null
@@ -1,366 +0,0 @@
-#include "ffi_test_utils.hpp"
-
-#include <iostream>
-
-#include <KeyMintAidlTestBase.h>
-#include <aidl/android/hardware/security/keymint/ErrorCode.h>
-#include <keymaster/UniquePtr.h>
-
-#include <memory>
-#include <vector>
-
-#include <hardware/keymaster_defs.h>
-#include <keymaster/android_keymaster_utils.h>
-#include <keymaster/keymaster_tags.h>
-
-#include <keymaster/km_openssl/attestation_record.h>
-#include <keymaster/km_openssl/openssl_err.h>
-#include <keymaster/km_openssl/openssl_utils.h>
-#include <openssl/asn1t.h>
-
-using aidl::android::hardware::security::keymint::ErrorCode;
-
-#define TAG_SEQUENCE 0x30
-#define LENGTH_MASK 0x80
-#define LENGTH_VALUE_MASK 0x7F
-
-/**
- * ASN.1 structure for `KeyDescription` Schema.
- * See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
- * KeyDescription ::= SEQUENCE(
- * keyFormat INTEGER, # Values from KeyFormat enum.
- * keyParams AuthorizationList,
- * )
- */
-typedef struct key_description {
- ASN1_INTEGER* key_format;
- keymaster::KM_AUTH_LIST* key_params;
-} TEST_KEY_DESCRIPTION;
-
-ASN1_SEQUENCE(TEST_KEY_DESCRIPTION) = {
- ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_format, ASN1_INTEGER),
- ASN1_SIMPLE(TEST_KEY_DESCRIPTION, key_params, keymaster::KM_AUTH_LIST),
-} ASN1_SEQUENCE_END(TEST_KEY_DESCRIPTION);
-DECLARE_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION);
-
-/**
- * ASN.1 structure for `SecureKeyWrapper` Schema.
- * See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper` schema.
- * SecureKeyWrapper ::= SEQUENCE(
- * version INTEGER, # Contains value 0
- * encryptedTransportKey OCTET_STRING,
- * initializationVector OCTET_STRING,
- * keyDescription KeyDescription,
- * encryptedKey OCTET_STRING,
- * tag OCTET_STRING
- * )
- */
-typedef struct secure_key_wrapper {
- ASN1_INTEGER* version;
- ASN1_OCTET_STRING* encrypted_transport_key;
- ASN1_OCTET_STRING* initialization_vector;
- TEST_KEY_DESCRIPTION* key_desc;
- ASN1_OCTET_STRING* encrypted_key;
- ASN1_OCTET_STRING* tag;
-} TEST_SECURE_KEY_WRAPPER;
-
-ASN1_SEQUENCE(TEST_SECURE_KEY_WRAPPER) = {
- ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, version, ASN1_INTEGER),
- ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_transport_key, ASN1_OCTET_STRING),
- ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, initialization_vector, ASN1_OCTET_STRING),
- ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, key_desc, TEST_KEY_DESCRIPTION),
- ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, encrypted_key, ASN1_OCTET_STRING),
- ASN1_SIMPLE(TEST_SECURE_KEY_WRAPPER, tag, ASN1_OCTET_STRING),
-} ASN1_SEQUENCE_END(TEST_SECURE_KEY_WRAPPER);
-DECLARE_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER);
-
-IMPLEMENT_ASN1_FUNCTIONS(TEST_SECURE_KEY_WRAPPER);
-IMPLEMENT_ASN1_FUNCTIONS(TEST_KEY_DESCRIPTION);
-
-struct TEST_KEY_DESCRIPTION_Delete {
- void operator()(TEST_KEY_DESCRIPTION* p) { TEST_KEY_DESCRIPTION_free(p); }
-};
-struct TEST_SECURE_KEY_WRAPPER_Delete {
- void operator()(TEST_SECURE_KEY_WRAPPER* p) { TEST_SECURE_KEY_WRAPPER_free(p); }
-};
-
-/* This function extracts a certificate from the certs_chain_buffer at the given
- * offset. Each DER encoded certificate starts with TAG_SEQUENCE followed by the
- * total length of the certificate. The length of the certificate is determined
- * as per ASN.1 encoding rules for the length octets.
- *
- * @param certs_chain_buffer: buffer containing DER encoded X.509 certificates
- * arranged sequentially.
- * @data_size: Length of the DER encoded X.509 certificates buffer.
- * @index: DER encoded X.509 certificates buffer offset.
- * @cert: Encoded certificate to be extracted from buffer as outcome.
- * @return: ErrorCode::OK on success, otherwise ErrorCode::UNKNOWN_ERROR.
- */
-ErrorCode
-extractCertFromCertChainBuffer(uint8_t* certs_chain_buffer, int certs_chain_buffer_size, int& index,
- aidl::android::hardware::security::keymint::Certificate& cert) {
- if (index >= certs_chain_buffer_size) {
- return ErrorCode::UNKNOWN_ERROR;
- }
-
- uint32_t length = 0;
- std::vector<uint8_t> cert_bytes;
- if (certs_chain_buffer[index] == TAG_SEQUENCE) {
- // Short form. One octet. Bit 8 has value "0" and bits 7-1 give the length.
- if (0 == (certs_chain_buffer[index + 1] & LENGTH_MASK)) {
- length = (uint32_t)certs_chain_buffer[index];
- // Add SEQ and Length fields
- length += 2;
- } else {
- // Long form. Two to 127 octets. Bit 8 of first octet has value "1" and
- // bits 7-1 give the number of additional length octets. Second and following
- // octets give the actual length.
- int additionalBytes = certs_chain_buffer[index + 1] & LENGTH_VALUE_MASK;
- if (additionalBytes == 0x01) {
- length = certs_chain_buffer[index + 2];
- // Add SEQ and Length fields
- length += 3;
- } else if (additionalBytes == 0x02) {
- length = (certs_chain_buffer[index + 2] << 8 | certs_chain_buffer[index + 3]);
- // Add SEQ and Length fields
- length += 4;
- } else if (additionalBytes == 0x04) {
- length = certs_chain_buffer[index + 2] << 24;
- length |= certs_chain_buffer[index + 3] << 16;
- length |= certs_chain_buffer[index + 4] << 8;
- length |= certs_chain_buffer[index + 5];
- // Add SEQ and Length fields
- length += 6;
- } else {
- // Length is larger than uint32_t max limit.
- return ErrorCode::UNKNOWN_ERROR;
- }
- }
- cert_bytes.insert(cert_bytes.end(), (certs_chain_buffer + index),
- (certs_chain_buffer + index + length));
- index += length;
-
- for (int i = 0; i < cert_bytes.size(); i++) {
- cert.encodedCertificate = std::move(cert_bytes);
- }
- } else {
- // SEQUENCE TAG MISSING.
- return ErrorCode::UNKNOWN_ERROR;
- }
-
- return ErrorCode::OK;
-}
-
-ErrorCode getCertificateChain(
- rust::Vec<rust::u8>& chainBuffer,
- std::vector<aidl::android::hardware::security::keymint::Certificate>& certChain) {
- uint8_t* data = chainBuffer.data();
- int index = 0;
- int data_size = chainBuffer.size();
-
- while (index < data_size) {
- aidl::android::hardware::security::keymint::Certificate cert =
- aidl::android::hardware::security::keymint::Certificate();
- if (extractCertFromCertChainBuffer(data, data_size, index, cert) != ErrorCode::OK) {
- return ErrorCode::UNKNOWN_ERROR;
- }
- certChain.push_back(std::move(cert));
- }
- return ErrorCode::OK;
-}
-
-bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check) {
- std::vector<aidl::android::hardware::security::keymint::Certificate> cert_chain =
- std::vector<aidl::android::hardware::security::keymint::Certificate>();
- if (cert_len <= 0) {
- return false;
- }
- if (getCertificateChain(cert_buf, cert_chain) != ErrorCode::OK) {
- return false;
- }
-
- for (int i = 0; i < cert_chain.size(); i++) {
- std::cout << cert_chain[i].toString() << "\n";
- }
- auto result = aidl::android::hardware::security::keymint::test::ChainSignaturesAreValid(
- cert_chain, strict_issuer_check);
-
- if (result == testing::AssertionSuccess()) return true;
-
- return false;
-}
-
-/**
- * Below mentioned key parameters are used to create authorization list of
- * secure key.
- * Algorithm: AES-256
- * Padding: PKCS7
- * Blockmode: ECB
- * Purpose: Encrypt, Decrypt
- */
-keymaster::AuthorizationSet build_wrapped_key_auth_list() {
- return keymaster::AuthorizationSet(keymaster::AuthorizationSetBuilder()
- .AesEncryptionKey(256)
- .Authorization(keymaster::TAG_BLOCK_MODE, KM_MODE_ECB)
- .Authorization(keymaster::TAG_PADDING, KM_PAD_PKCS7)
- .Authorization(keymaster::TAG_NO_AUTH_REQUIRED));
-}
-
-/**
- * Creates ASN.1 DER-encoded data corresponding to `KeyDescription` schema as
- * AAD. See `IKeyMintDevice.aidl` for documentation of the `KeyDescription` schema.
- */
-CxxResult buildAsn1DerEncodedWrappedKeyDescription() {
- CxxResult cxx_result{};
- keymaster_error_t error;
- cxx_result.error = KM_ERROR_OK;
-
- keymaster::UniquePtr<TEST_KEY_DESCRIPTION, TEST_KEY_DESCRIPTION_Delete> key_description(
- TEST_KEY_DESCRIPTION_new());
- if (!key_description.get()) {
- cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
- return cxx_result;
- }
-
- // Fill secure key authorizations.
- keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list();
- error = build_auth_list(auth_list, key_description->key_params);
- if (error != KM_ERROR_OK) {
- cxx_result.error = error;
- return cxx_result;
- }
-
- // Fill secure key format.
- if (!ASN1_INTEGER_set(key_description->key_format, KM_KEY_FORMAT_RAW)) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- // Perform ASN.1 DER encoding of KeyDescription.
- int asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), nullptr);
- if (asn1_data_len < 0) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
- std::vector<uint8_t> asn1_data(asn1_data_len, 0);
-
- if (!asn1_data.data()) {
- cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
- return cxx_result;
- }
-
- uint8_t* p = asn1_data.data();
- asn1_data_len = i2d_TEST_KEY_DESCRIPTION(key_description.get(), &p);
- if (asn1_data_len < 0) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data));
-
- return cxx_result;
-}
-
-/**
- * Creates wrapped key material to import in ASN.1 DER-encoded data corresponding to
- * `SecureKeyWrapper` schema. See `IKeyMintDevice.aidl` for documentation of the `SecureKeyWrapper`
- * schema.
- */
-CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
- rust::Vec<rust::u8> encrypted_transport_key, rust::Vec<rust::u8> iv,
- rust::Vec<rust::u8> tag) {
- CxxResult cxx_result{};
- keymaster_error_t error;
- cxx_result.error = KM_ERROR_OK;
-
- uint8_t* enc_secure_key_data = encrypted_secure_key.data();
- int enc_secure_key_size = encrypted_secure_key.size();
-
- uint8_t* iv_data = iv.data();
- int iv_size = iv.size();
-
- uint8_t* tag_data = tag.data();
- int tag_size = tag.size();
-
- uint8_t* enc_transport_key_data = encrypted_transport_key.data();
- int enc_transport_key_size = encrypted_transport_key.size();
-
- keymaster::UniquePtr<TEST_SECURE_KEY_WRAPPER, TEST_SECURE_KEY_WRAPPER_Delete> sec_key_wrapper(
- TEST_SECURE_KEY_WRAPPER_new());
- if (!sec_key_wrapper.get()) {
- cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
- return cxx_result;
- }
-
- // Fill version = 0
- if (!ASN1_INTEGER_set(sec_key_wrapper->version, 0)) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- // Fill encrypted transport key.
- if (enc_transport_key_size &&
- !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_transport_key, enc_transport_key_data,
- enc_transport_key_size)) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- // Fill encrypted secure key.
- if (enc_secure_key_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->encrypted_key,
- enc_secure_key_data, enc_secure_key_size)) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- // Fill secure key authorization list.
- keymaster::AuthorizationSet auth_list = build_wrapped_key_auth_list();
- error = build_auth_list(auth_list, sec_key_wrapper->key_desc->key_params);
- if (error != KM_ERROR_OK) {
- cxx_result.error = error;
- return cxx_result;
- }
-
- // Fill secure key format.
- if (!ASN1_INTEGER_set(sec_key_wrapper->key_desc->key_format, KM_KEY_FORMAT_RAW)) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- // Fill initialization vector used for encrypting secure key.
- if (iv_size &&
- !ASN1_OCTET_STRING_set(sec_key_wrapper->initialization_vector, iv_data, iv_size)) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- // Fill GCM-tag, extracted during secure key encryption.
- if (tag_size && !ASN1_OCTET_STRING_set(sec_key_wrapper->tag, tag_data, tag_size)) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- // ASN.1 DER-encoding of secure key wrapper.
- int asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), nullptr);
- if (asn1_data_len < 0) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
- std::vector<uint8_t> asn1_data(asn1_data_len, 0);
-
- if (!asn1_data.data()) {
- cxx_result.error = KM_ERROR_MEMORY_ALLOCATION_FAILED;
- return cxx_result;
- }
-
- uint8_t* p = asn1_data.data();
- asn1_data_len = i2d_TEST_SECURE_KEY_WRAPPER(sec_key_wrapper.get(), &p);
- if (asn1_data_len < 0) {
- cxx_result.error = keymaster::TranslateLastOpenSslError();
- return cxx_result;
- }
-
- std::move(asn1_data.begin(), asn1_data.end(), std::back_inserter(cxx_result.data));
-
- return cxx_result;
-}
diff --git a/keystore2/tests/ffi_test_utils.hpp b/keystore2/tests/ffi_test_utils.hpp
deleted file mode 100644
index b8c7c483..00000000
--- a/keystore2/tests/ffi_test_utils.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include "rust/cxx.h"
-#include "ffi_test_utils.rs.h"
-
-bool validateCertChain(rust::Vec<rust::u8> cert_buf, uint32_t cert_len, bool strict_issuer_check);
-CxxResult createWrappedKey(rust::Vec<rust::u8> encrypted_secure_key,
- rust::Vec<rust::u8> encrypted_transport_key,
- rust::Vec<rust::u8> iv,
- rust::Vec<rust::u8> tag);
-CxxResult buildAsn1DerEncodedWrappedKeyDescription();
diff --git a/keystore2/tests/keystore2_client_attest_key_tests.rs b/keystore2/tests/keystore2_client_attest_key_tests.rs
index 4febd9b5..454248a3 100644
--- a/keystore2/tests/keystore2_client_attest_key_tests.rs
+++ b/keystore2/tests/keystore2_client_attest_key_tests.rs
@@ -17,21 +17,27 @@ use nix::unistd::getuid;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
- SecurityLevel::SecurityLevel,
+ SecurityLevel::SecurityLevel, Tag::Tag,
};
use android_system_keystore2::aidl::android::system::keystore2::{
- Domain::Domain, KeyDescriptor::KeyDescriptor, ResponseCode::ResponseCode,
+ Domain::Domain, IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor,
+ ResponseCode::ResponseCode,
};
use keystore2_test_utils::{
authorizations, get_keystore_service, key_generations, key_generations::Error,
};
-use crate::ffi_test_utils::validate_certchain;
+use keystore2_test_utils::ffi_test_utils::{get_value_from_attest_record, validate_certchain};
use crate::{
- keystore2_client_test_utils::app_attest_key_feature_exists,
- skip_test_if_no_app_attest_key_feature,
+ skip_device_id_attestation_tests, skip_test_if_no_app_attest_key_feature,
+ skip_test_if_no_device_id_attestation_feature,
+};
+
+use crate::keystore2_client_test_utils::{
+ app_attest_key_feature_exists, device_id_attestation_feature_exists, get_attest_id_value,
+ is_second_imei_id_attestation_required, skip_device_id_attest_tests,
};
/// Generate RSA and EC attestation keys and use them for signing RSA-signing keys.
@@ -480,3 +486,183 @@ fn keystore2_attest_symmetric_key_fail_sys_error() {
// Should not have an attestation record.
assert!(aes_key_metadata.certificateChain.is_none());
}
+
+fn get_attestation_ids(keystore2: &binder::Strong<dyn IKeystoreService>) -> Vec<(Tag, Vec<u8>)> {
+ let attest_ids = vec![
+ (Tag::ATTESTATION_ID_BRAND, "brand"),
+ (Tag::ATTESTATION_ID_DEVICE, "device"),
+ (Tag::ATTESTATION_ID_PRODUCT, "name"),
+ (Tag::ATTESTATION_ID_SERIAL, "serialno"),
+ (Tag::ATTESTATION_ID_MANUFACTURER, "manufacturer"),
+ (Tag::ATTESTATION_ID_MODEL, "model"),
+ (Tag::ATTESTATION_ID_IMEI, ""), //Get this value from Telephony service.
+ (Tag::ATTESTATION_ID_SECOND_IMEI, ""), //Get this value from Telephony service.
+ ];
+
+ let mut attest_id_params: Vec<(Tag, Vec<u8>)> = vec![];
+ for (attest_id, prop_name) in attest_ids {
+ if attest_id == Tag::ATTESTATION_ID_SECOND_IMEI
+ && !is_second_imei_id_attestation_required(keystore2)
+ {
+ continue;
+ }
+
+ if let Some(value) = get_attest_id_value(attest_id, prop_name) {
+ if !value.is_empty() {
+ attest_id_params.push((attest_id, value));
+ }
+ }
+ }
+
+ attest_id_params
+}
+
+/// Generate an attested key with attestation of the device's identifiers. Test should succeed in
+/// generating a attested key with attestation of device identifiers. Test might fail on devices
+/// which don't support device id attestation with error response code `CANNOT_ATTEST_IDS or
+/// INVALID_TAG`
+fn generate_attested_key_with_device_attest_ids(algorithm: Algorithm) {
+ skip_test_if_no_device_id_attestation_feature!();
+ skip_device_id_attestation_tests!();
+ skip_test_if_no_app_attest_key_feature!();
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let att_challenge: &[u8] = b"foo";
+
+ let attest_key_metadata =
+ key_generations::generate_attestation_key(&sec_level, algorithm, att_challenge).unwrap();
+
+ let attest_id_params = get_attestation_ids(&keystore2);
+
+ for (attest_id, value) in attest_id_params {
+ // Create RSA/EC key and use attestation key to sign it.
+ let key_alias = format!("ks_attested_test_key_{}", getuid());
+ let key_metadata =
+ key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
+ &sec_level,
+ algorithm,
+ Some(key_alias),
+ att_challenge,
+ &attest_key_metadata.key,
+ attest_id,
+ value.clone(),
+ ))
+ .unwrap();
+
+ assert!(key_metadata.certificate.is_some());
+ assert!(key_metadata.certificateChain.is_none());
+
+ let mut cert_chain: Vec<u8> = Vec::new();
+ cert_chain.extend(key_metadata.certificate.as_ref().unwrap());
+ cert_chain.extend(attest_key_metadata.certificate.as_ref().unwrap());
+ cert_chain.extend(attest_key_metadata.certificateChain.as_ref().unwrap());
+
+ validate_certchain(&cert_chain).expect("Error while validating cert chain");
+ let attest_id_value = get_value_from_attest_record(
+ key_metadata.certificate.as_ref().unwrap(),
+ attest_id,
+ SecurityLevel::TRUSTED_ENVIRONMENT,
+ )
+ .expect("Attest id verification failed.");
+ assert_eq!(attest_id_value, value);
+ }
+}
+
+#[test]
+fn keystore2_attest_ecdsa_attestation_id() {
+ generate_attested_key_with_device_attest_ids(Algorithm::EC);
+}
+
+#[test]
+fn keystore2_attest_rsa_attestation_id() {
+ generate_attested_key_with_device_attest_ids(Algorithm::RSA);
+}
+
+/// Try to generate an attested key with attestation of invalid device's identifiers. Test should
+/// fail with error response code `CANNOT_ATTEST_IDS`.
+#[test]
+fn keystore2_attest_key_fails_with_invalid_attestation_id() {
+ skip_test_if_no_device_id_attestation_feature!();
+
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let digest = Digest::SHA_2_256;
+ let att_challenge: &[u8] = b"foo";
+
+ // Create EC-Attestation key.
+ let attest_key_metadata = key_generations::generate_ec_attestation_key(
+ &sec_level,
+ att_challenge,
+ digest,
+ EcCurve::P_256,
+ )
+ .unwrap();
+
+ let attest_id_params = vec![
+ (Tag::ATTESTATION_ID_BRAND, b"invalid-brand".to_vec()),
+ (Tag::ATTESTATION_ID_DEVICE, b"invalid-device-name".to_vec()),
+ (Tag::ATTESTATION_ID_PRODUCT, b"invalid-product-name".to_vec()),
+ (Tag::ATTESTATION_ID_SERIAL, b"invalid-ro-serial".to_vec()),
+ (Tag::ATTESTATION_ID_MANUFACTURER, b"invalid-ro-product-manufacturer".to_vec()),
+ (Tag::ATTESTATION_ID_MODEL, b"invalid-ro-product-model".to_vec()),
+ (Tag::ATTESTATION_ID_IMEI, b"invalid-imei".to_vec()),
+ ];
+
+ for (attest_id, value) in attest_id_params {
+ // Create EC key and use attestation key to sign it.
+ let ec_key_alias = format!("ks_ec_attested_test_key_fail_{}{}", getuid(), digest.0);
+ let result = key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
+ &sec_level,
+ Algorithm::EC,
+ Some(ec_key_alias),
+ att_challenge,
+ &attest_key_metadata.key,
+ attest_id,
+ value,
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+ }
+}
+
+/// If `DEVICE_ID_ATTESTATION_FEATURE` is not supported then test tries to generate an attested
+/// key with attestation of valid device's identifiers. Test should fail to generate key with
+/// error code `CANNOT_ATTEST_IDS`.
+#[test]
+fn keystore2_attest_key_without_attestation_id_support_fails_with_cannot_attest_id() {
+ if device_id_attestation_feature_exists() {
+ // Skip this test on device supporting `DEVICE_ID_ATTESTATION_FEATURE`.
+ return;
+ }
+
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let att_challenge: &[u8] = b"foo";
+ let attest_key_metadata =
+ key_generations::generate_attestation_key(&sec_level, Algorithm::RSA, att_challenge)
+ .unwrap();
+
+ let attest_id_params = get_attestation_ids(&keystore2);
+ for (attest_id, value) in attest_id_params {
+ // Create RSA/EC key and use attestation key to sign it.
+ let key_alias = format!("ks_attested_test_key_{}", getuid());
+ let result = key_generations::map_ks_error(key_generations::generate_key_with_attest_id(
+ &sec_level,
+ Algorithm::RSA,
+ Some(key_alias),
+ att_challenge,
+ &attest_key_metadata.key,
+ attest_id,
+ value.clone(),
+ ));
+ assert!(
+ result.is_err(),
+ "Expected to fail as FEATURE_DEVICE_ID_ATTESTATION is not supported."
+ );
+ assert_eq!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+ }
+}
diff --git a/keystore2/tests/keystore2_client_authorizations_tests.rs b/keystore2/tests/keystore2_client_authorizations_tests.rs
new file mode 100644
index 00000000..32be99e0
--- /dev/null
+++ b/keystore2/tests/keystore2_client_authorizations_tests.rs
@@ -0,0 +1,985 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use std::time::SystemTime;
+
+use openssl::bn::{BigNum, MsbOption};
+use openssl::x509::X509NameBuilder;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
+ ErrorCode::ErrorCode, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel, Tag::Tag,
+};
+
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel, KeyDescriptor::KeyDescriptor,
+ KeyMetadata::KeyMetadata, ResponseCode::ResponseCode,
+};
+
+use aconfig_android_hardware_biometrics_rust;
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ HardwareAuthToken::HardwareAuthToken,
+ HardwareAuthenticatorType::HardwareAuthenticatorType
+};
+use android_hardware_security_secureclock::aidl::android::hardware::security::secureclock::Timestamp::Timestamp;
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_auth_service, get_keystore_service, key_generations,
+ key_generations::Error,
+};
+
+use crate::keystore2_client_test_utils::{
+ app_attest_key_feature_exists, delete_app_key, perform_sample_asym_sign_verify_op,
+ perform_sample_hmac_sign_verify_op, perform_sample_sym_key_decrypt_op,
+ perform_sample_sym_key_encrypt_op, verify_certificate_serial_num,
+ verify_certificate_subject_name, SAMPLE_PLAIN_TEXT,
+};
+
+use crate::{skip_test_if_no_app_attest_key_feature, skip_tests_if_keymaster_impl_present};
+
+use keystore2_test_utils::ffi_test_utils::get_value_from_attest_record;
+
+fn gen_key_including_unique_id(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ alias: &str,
+) -> Vec<u8> {
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .include_unique_id();
+
+ let key_metadata = key_generations::generate_key(sec_level, &gen_params, alias).unwrap();
+
+ let unique_id = get_value_from_attest_record(
+ key_metadata.certificate.as_ref().unwrap(),
+ Tag::UNIQUE_ID,
+ key_metadata.keySecurityLevel,
+ )
+ .expect("Unique id not found.");
+ assert!(!unique_id.is_empty());
+ unique_id
+}
+
+fn generate_key_and_perform_sign_verify_op_max_times(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ gen_params: &authorizations::AuthSetBuilder,
+ alias: &str,
+ max_usage_count: i32,
+) -> binder::Result<KeyMetadata> {
+ let key_metadata = key_generations::generate_key(sec_level, gen_params, alias)?;
+
+ // Use above generated key `max_usage_count` times.
+ for _ in 0..max_usage_count {
+ perform_sample_asym_sign_verify_op(sec_level, &key_metadata, None, Some(Digest::SHA_2_256));
+ }
+
+ Ok(key_metadata)
+}
+
+/// Generate a key with `USAGE_COUNT_LIMIT` and verify the key characteristics. Test should be able
+/// to use the key successfully `max_usage_count` times. After exceeding key usage `max_usage_count`
+/// times subsequent attempts to use the key in test should fail with response code `KEY_NOT_FOUND`.
+/// Test should also verify that the attest record includes `USAGE_COUNT_LIMIT` for attested keys.
+fn generate_key_and_perform_op_with_max_usage_limit(
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ gen_params: &authorizations::AuthSetBuilder,
+ alias: &str,
+ max_usage_count: i32,
+ check_attestation: bool,
+) {
+ // Generate a key and use the key for `max_usage_count` times.
+ let key_metadata = generate_key_and_perform_sign_verify_op_max_times(
+ sec_level,
+ gen_params,
+ alias,
+ max_usage_count,
+ )
+ .unwrap();
+
+ let auth = key_generations::get_key_auth(&key_metadata.authorizations, Tag::USAGE_COUNT_LIMIT)
+ .unwrap();
+ if check_attestation && key_generations::has_default_keymint() {
+ // Check usage-count-limit is included in attest-record.
+ // `USAGE_COUNT_LIMIT` is supported from KeyMint1.0
+ assert_ne!(
+ gen_params.iter().filter(|kp| kp.tag == Tag::ATTESTATION_CHALLENGE).count(),
+ 0,
+ "Attestation challenge is missing in generated key parameters."
+ );
+ let result = get_value_from_attest_record(
+ key_metadata.certificate.as_ref().unwrap(),
+ Tag::USAGE_COUNT_LIMIT,
+ auth.securityLevel,
+ )
+ .expect("Attest id verification failed.");
+ let usage_count: i32 = std::str::from_utf8(&result).unwrap().parse().unwrap();
+ assert_eq!(usage_count, max_usage_count);
+ }
+ if max_usage_count == 1 {
+ assert!(matches!(
+ auth.securityLevel,
+ SecurityLevel::KEYSTORE | SecurityLevel::TRUSTED_ENVIRONMENT
+ ));
+ } else {
+ assert_eq!(auth.securityLevel, SecurityLevel::KEYSTORE);
+ }
+
+ // Try to use the key one more time.
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::KEY_NOT_FOUND), result.unwrap_err());
+}
+
+/// Generate a key with `ACTIVE_DATETIME` set to current time. Test should successfully generate
+/// a key and verify the key characteristics. Test should be able to create a sign operation using
+/// the generated key successfully.
+#[test]
+fn keystore2_gen_key_auth_active_datetime_test_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let active_datetime = duration_since_epoch.as_millis();
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .active_date_time(active_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ alias,
+ );
+ assert!(result.is_ok());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `ACTIVE_DATETIME` set to future date and time. Test should successfully
+/// generate a key and verify the key characteristics. Try to create a sign operation
+/// using the generated key, test should fail to create an operation with error code
+/// `KEY_NOT_YET_VALID`.
+#[test]
+fn keystore2_gen_key_auth_future_active_datetime_test_op_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let future_active_datetime = duration_since_epoch.as_millis() + (24 * 60 * 60 * 1000);
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .active_date_time(future_active_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ alias,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::KEY_NOT_YET_VALID), result.unwrap_err());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `ORIGINATION_EXPIRE_DATETIME` set to future date and time. Test should
+/// successfully generate a key and verify the key characteristics. Test should be able to create
+/// sign operation using the generated key successfully.
+#[test]
+fn keystore2_gen_key_auth_future_origination_expire_datetime_test_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let origination_expire_datetime = duration_since_epoch.as_millis() + (24 * 60 * 60 * 1000);
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .origination_expire_date_time(origination_expire_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ alias,
+ );
+ assert!(result.is_ok());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `ORIGINATION_EXPIRE_DATETIME` set to current date and time. Test should
+/// successfully generate a key and verify the key characteristics. Try to create a sign operation
+/// using the generated key, test should fail to create an operation with error code
+/// `KEY_EXPIRED`.
+#[test]
+fn keystore2_gen_key_auth_origination_expire_datetime_test_op_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let origination_expire_datetime = duration_since_epoch.as_millis();
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .origination_expire_date_time(origination_expire_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ alias,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::KEY_EXPIRED), result.unwrap_err());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a HMAC key with `USAGE_EXPIRE_DATETIME` set to future date and time. Test should
+/// successfully generate a key and verify the key characteristics. Test should be able to create
+/// sign and verify operations using the generated key successfully.
+#[test]
+fn keystore2_gen_key_auth_future_usage_expire_datetime_hmac_verify_op_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let usage_expire_datetime = duration_since_epoch.as_millis() + (24 * 60 * 60 * 1000);
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::HMAC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .key_size(128)
+ .min_mac_length(256)
+ .digest(Digest::SHA_2_256)
+ .usage_expire_date_time(usage_expire_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_hmac_verify_success";
+ let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+
+ perform_sample_hmac_sign_verify_op(&sec_level, &key_metadata.key);
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `USAGE_EXPIRE_DATETIME` set to current date and time. Test should
+/// successfully generate a key and verify the key characteristics. Test should be able to create
+/// sign operation successfully and fail while performing verify operation with error code
+/// `KEY_EXPIRED`.
+#[test]
+fn keystore2_gen_key_auth_usage_expire_datetime_hmac_verify_op_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let usage_expire_datetime = duration_since_epoch.as_millis();
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::HMAC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .key_size(128)
+ .min_mac_length(256)
+ .digest(Digest::SHA_2_256)
+ .usage_expire_date_time(usage_expire_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_hamc_verify_fail";
+ let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+
+ let result = key_generations::map_ks_error(
+ sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256),
+ false,
+ ),
+ );
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::KEY_EXPIRED), result.unwrap_err());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate AES key with `USAGE_EXPIRE_DATETIME` set to future date and time. Test should
+/// successfully generate a key and verify the key characteristics. Test should be able to create
+/// Encrypt and Decrypt operations successfully.
+#[test]
+fn keystore2_gen_key_auth_usage_future_expire_datetime_decrypt_op_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let usage_expire_datetime = duration_since_epoch.as_millis() + (24 * 60 * 60 * 1000);
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::AES)
+ .purpose(KeyPurpose::ENCRYPT)
+ .purpose(KeyPurpose::DECRYPT)
+ .key_size(128)
+ .padding_mode(PaddingMode::PKCS7)
+ .block_mode(BlockMode::ECB)
+ .usage_expire_date_time(usage_expire_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_test";
+ let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+ let cipher_text = perform_sample_sym_key_encrypt_op(
+ &sec_level,
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+
+ assert!(cipher_text.is_some());
+
+ let plain_text = perform_sample_sym_key_decrypt_op(
+ &sec_level,
+ &cipher_text.unwrap(),
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+ assert!(plain_text.is_some());
+ assert_eq!(plain_text.unwrap(), SAMPLE_PLAIN_TEXT.to_vec());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate AES key with `USAGE_EXPIRE_DATETIME` set to current date and time. Test should
+/// successfully generate a key and verify the key characteristics. Test should be able to create
+/// Encrypt operation successfully and fail while performing decrypt operation with error code
+/// `KEY_EXPIRED`.
+#[test]
+fn keystore2_gen_key_auth_usage_expire_datetime_decrypt_op_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let usage_expire_datetime = duration_since_epoch.as_millis();
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::AES)
+ .purpose(KeyPurpose::ENCRYPT)
+ .purpose(KeyPurpose::DECRYPT)
+ .key_size(128)
+ .padding_mode(PaddingMode::PKCS7)
+ .block_mode(BlockMode::ECB)
+ .usage_expire_date_time(usage_expire_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_test";
+ let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+ let cipher_text = perform_sample_sym_key_encrypt_op(
+ &sec_level,
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ )
+ .unwrap();
+
+ assert!(cipher_text.is_some());
+
+ let result = key_generations::map_ks_error(perform_sample_sym_key_decrypt_op(
+ &sec_level,
+ &cipher_text.unwrap(),
+ PaddingMode::PKCS7,
+ BlockMode::ECB,
+ &mut None,
+ None,
+ &key_metadata.key,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::KEY_EXPIRED), result.unwrap_err());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `EARLY_BOOT_ONLY`. Test should successfully generate
+/// a key and verify the key characteristics. Test should fail with error code `EARLY_BOOT_ENDED`
+/// during creation of an operation using this key.
+#[test]
+fn keystore2_gen_key_auth_early_boot_only_op_fail() {
+ skip_tests_if_keymaster_impl_present!();
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .early_boot_only();
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ alias,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::EARLY_BOOT_ENDED), result.unwrap_err());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `MAX_USES_PER_BOOT`. Test should successfully generate
+/// a key and verify the key characteristics. Test should be able to use the key successfully
+/// `MAX_USES_COUNT` times. After exceeding key usage `MAX_USES_COUNT` times
+/// subsequent attempts to use the key in test should fail with error code MAX_OPS_EXCEEDED.
+#[test]
+fn keystore2_gen_key_auth_max_uses_per_boot() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ const MAX_USES_COUNT: i32 = 3;
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .max_uses_per_boot(MAX_USES_COUNT);
+
+ let alias = "ks_test_auth_tags_test";
+ // Generate a key and use the key for `MAX_USES_COUNT` times.
+ let key_metadata = generate_key_and_perform_sign_verify_op_max_times(
+ &sec_level,
+ &gen_params,
+ alias,
+ MAX_USES_COUNT,
+ )
+ .unwrap();
+
+ // Try to use the key one more time.
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(Digest::SHA_2_256),
+ false,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::KEY_MAX_OPS_EXCEEDED), result.unwrap_err());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `USAGE_COUNT_LIMIT`. Test should successfully generate
+/// a key and verify the key characteristics. Test should be able to use the key successfully
+/// `MAX_USES_COUNT` times. After exceeding key usage `MAX_USES_COUNT` times
+/// subsequent attempts to use the key in test should fail with response code `KEY_NOT_FOUND`.
+/// Test should also verify that the attest record includes `USAGE_COUNT_LIMIT`.
+#[test]
+fn keystore2_gen_key_auth_usage_count_limit() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ const MAX_USES_COUNT: i32 = 3;
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .usage_count_limit(MAX_USES_COUNT);
+
+ let alias = "ks_test_auth_tags_test";
+ generate_key_and_perform_op_with_max_usage_limit(
+ &sec_level,
+ &gen_params,
+ alias,
+ MAX_USES_COUNT,
+ true,
+ );
+}
+
+/// Generate a key with `USAGE_COUNT_LIMIT`. Test should successfully generate
+/// a key and verify the key characteristics. Test should be able to use the key successfully
+/// `MAX_USES_COUNT` times. After exceeding key usage `MAX_USES_COUNT` times
+/// subsequent attempts to use the key in test should fail with response code `KEY_NOT_FOUND`.
+/// Test should also verify that the attest record includes `USAGE_COUNT_LIMIT`.
+#[test]
+fn keystore2_gen_key_auth_usage_count_limit_one() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ const MAX_USES_COUNT: i32 = 1;
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .usage_count_limit(MAX_USES_COUNT);
+
+ let alias = "ks_test_auth_tags_test";
+ generate_key_and_perform_op_with_max_usage_limit(
+ &sec_level,
+ &gen_params,
+ alias,
+ MAX_USES_COUNT,
+ true,
+ );
+}
+
+/// Generate a non-attested key with `USAGE_COUNT_LIMIT`. Test should successfully generate
+/// a key and verify the key characteristics. Test should be able to use the key successfully
+/// `MAX_USES_COUNT` times. After exceeding key usage `MAX_USES_COUNT` times
+/// subsequent attempts to use the key in test should fail with response code `KEY_NOT_FOUND`.
+#[test]
+fn keystore2_gen_non_attested_key_auth_usage_count_limit() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ const MAX_USES_COUNT: i32 = 2;
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .usage_count_limit(MAX_USES_COUNT);
+
+ let alias = "ks_test_auth_tags_test";
+ generate_key_and_perform_op_with_max_usage_limit(
+ &sec_level,
+ &gen_params,
+ alias,
+ MAX_USES_COUNT,
+ false,
+ );
+}
+
+/// Try to generate a key with `Tag::CREATION_DATETIME` set to valid value. Test should fail
+/// to generate a key with `INVALID_ARGUMENT` error as Keystore2 backend doesn't allow user to
+/// specify `CREATION_DATETIME`.
+#[test]
+fn keystore2_gen_key_auth_creation_date_time_test_fail_with_invalid_arg_error() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let duration_since_epoch = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap();
+ let creation_datetime = duration_since_epoch.as_millis();
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .creation_date_time(creation_datetime.try_into().unwrap());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::map_ks_error(sec_level.generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Generate a key with `Tag::INCLUDE_UNIQUE_ID` set. Test should verify that `Tag::UNIQUE_ID` is
+/// included in attest record and it remains the same for new keys generated.
+#[test]
+fn keystore2_gen_key_auth_include_unique_id_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias_first = "ks_test_auth_tags_test_1";
+ let unique_id_first = gen_key_including_unique_id(&sec_level, alias_first);
+
+ let alias_second = "ks_test_auth_tags_test_2";
+ let unique_id_second = gen_key_including_unique_id(&sec_level, alias_second);
+
+ assert_eq!(unique_id_first, unique_id_second);
+
+ delete_app_key(&keystore2, alias_first).unwrap();
+ delete_app_key(&keystore2, alias_second).unwrap();
+}
+
+/// Generate a key with `APPLICATION_DATA`. Test should create an operation using the
+/// same `APPLICATION_DATA` successfully.
+#[test]
+fn keystore2_gen_key_auth_app_data_test_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .app_data(b"app-data".to_vec());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256)
+ .app_data(b"app-data".to_vec()),
+ alias,
+ );
+ assert!(result.is_ok());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `APPLICATION_DATA`. Try to create an operation using the
+/// different `APPLICATION_DATA`, test should fail to create an operation with error code
+/// `INVALID_KEY_BLOB`.
+#[test]
+fn keystore2_gen_key_auth_app_data_test_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .app_data(b"app-data".to_vec());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256)
+ .app_data(b"invalid-app-data".to_vec()),
+ alias,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_KEY_BLOB), result.unwrap_err());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `APPLICATION_ID`. Test should create an operation using the
+/// same `APPLICATION_ID` successfully.
+#[test]
+fn keystore2_gen_key_auth_app_id_test_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .app_id(b"app-id".to_vec());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256)
+ .app_id(b"app-id".to_vec()),
+ alias,
+ );
+ assert!(result.is_ok());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate a key with `APPLICATION_ID`. Try to create an operation using the
+/// different `APPLICATION_ID`, test should fail to create an operation with error code
+/// `INVALID_KEY_BLOB`.
+#[test]
+fn keystore2_gen_key_auth_app_id_test_fail() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .app_id(b"app-id".to_vec());
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::map_ks_error(key_generations::create_key_and_operation(
+ &sec_level,
+ &gen_params,
+ &authorizations::AuthSetBuilder::new()
+ .purpose(KeyPurpose::SIGN)
+ .digest(Digest::SHA_2_256)
+ .app_id(b"invalid-app-id".to_vec()),
+ alias,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_KEY_BLOB), result.unwrap_err());
+ delete_app_key(&keystore2, alias).unwrap();
+}
+
+/// Generate an attestation-key without specifying `APPLICATION_ID` and `APPLICATION_DATA`.
+/// Test should be able to generate a new key with specifying app-id and app-data using previously
+/// generated attestation-key.
+#[test]
+fn keystore2_gen_attested_key_auth_app_id_app_data_test_success() {
+ skip_test_if_no_app_attest_key_feature!();
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Generate attestation key.
+ let attest_gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::ATTEST_KEY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec());
+ let attest_alias = "ks_test_auth_tags_attest_key";
+ let attest_key_metadata =
+ key_generations::generate_key(&sec_level, &attest_gen_params, attest_alias).unwrap();
+
+ // Generate attested key.
+ let alias = "ks_test_auth_tags_attested_key";
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"bar".to_vec())
+ .app_id(b"app-id".to_vec())
+ .app_data(b"app-data".to_vec());
+
+ let result = sec_level.generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ Some(&attest_key_metadata.key),
+ &gen_params,
+ 0,
+ b"entropy",
+ );
+
+ assert!(result.is_ok());
+ delete_app_key(&keystore2, alias).unwrap();
+ delete_app_key(&keystore2, attest_alias).unwrap();
+}
+
+/// Generate an attestation-key with specifying `APPLICATION_ID` and `APPLICATION_DATA`.
+/// Test should try to generate an attested key using previously generated attestation-key without
+/// specifying app-id and app-data. Test should fail to generate a new key with error code
+/// `INVALID_KEY_BLOB`.
+/// It is an oversight of the Keystore API that `APPLICATION_ID` and `APPLICATION_DATA` tags cannot
+/// be provided to generateKey for an attestation key that was generated with them.
+#[test]
+fn keystore2_gen_attestation_key_with_auth_app_id_app_data_test_fail() {
+ skip_test_if_no_app_attest_key_feature!();
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Generate attestation key.
+ let attest_gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::ATTEST_KEY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .app_id(b"app-id".to_vec())
+ .app_data(b"app-data".to_vec());
+ let attest_alias = "ks_test_auth_tags_attest_key";
+ let attest_key_metadata =
+ key_generations::generate_key(&sec_level, &attest_gen_params, attest_alias).unwrap();
+
+ // Generate new key using above generated attestation key without providing app-id and app-data.
+ let alias = "ks_test_auth_tags_attested_key";
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec());
+
+ let result = key_generations::map_ks_error(sec_level.generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ Some(&attest_key_metadata.key),
+ &gen_params,
+ 0,
+ b"entropy",
+ ));
+
+ assert!(result.is_err());
+ assert_eq!(Error::Km(ErrorCode::INVALID_KEY_BLOB), result.unwrap_err());
+ delete_app_key(&keystore2, attest_alias).unwrap();
+}
+
+fn add_hardware_token(auth_type: HardwareAuthenticatorType) {
+ let keystore_auth = get_keystore_auth_service();
+
+ let token = HardwareAuthToken {
+ challenge: 0,
+ userId: 0,
+ authenticatorId: 0,
+ authenticatorType: auth_type,
+ timestamp: Timestamp { milliSeconds: 500 },
+ mac: vec![],
+ };
+ keystore_auth.addAuthToken(&token).unwrap();
+}
+
+#[test]
+fn keystore2_flagged_off_get_last_auth_password_permission_denied() {
+ if aconfig_android_hardware_biometrics_rust::last_authentication_time() {
+ return;
+ }
+
+ let keystore_auth = get_keystore_auth_service();
+
+ let result = keystore_auth.getLastAuthTime(0, &[HardwareAuthenticatorType::PASSWORD]);
+
+ assert!(result.is_err());
+ assert_eq!(result.unwrap_err().service_specific_error(), ResponseCode::PERMISSION_DENIED.0);
+}
+
+#[test]
+fn keystore2_flagged_on_get_last_auth_password_success() {
+ if !aconfig_android_hardware_biometrics_rust::last_authentication_time() {
+ return;
+ }
+
+ let keystore_auth = get_keystore_auth_service();
+
+ add_hardware_token(HardwareAuthenticatorType::PASSWORD);
+ assert!(keystore_auth.getLastAuthTime(0, &[HardwareAuthenticatorType::PASSWORD]).unwrap() > 0);
+}
+
+#[test]
+fn keystore2_flagged_on_get_last_auth_fingerprint_success() {
+ if !aconfig_android_hardware_biometrics_rust::last_authentication_time() {
+ return;
+ }
+
+ let keystore_auth = get_keystore_auth_service();
+
+ add_hardware_token(HardwareAuthenticatorType::FINGERPRINT);
+ assert!(
+ keystore_auth.getLastAuthTime(0, &[HardwareAuthenticatorType::FINGERPRINT]).unwrap() > 0
+ );
+}
+
+/// Generate a key with specifying `CERTIFICATE_SUBJECT and CERTIFICATE_SERIAL`. Test should
+/// generate a key successfully and verify the specified key parameters.
+#[test]
+fn keystore2_gen_key_auth_serial_number_subject_test_success() {
+ skip_tests_if_keymaster_impl_present!();
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let cert_subject = "test cert subject";
+ let mut x509_name = X509NameBuilder::new().unwrap();
+ x509_name.append_entry_by_text("CN", cert_subject).unwrap();
+ let x509_name = x509_name.build().to_der().unwrap();
+
+ let mut serial = BigNum::new().unwrap();
+ serial.rand(159, MsbOption::MAYBE_ZERO, false).unwrap();
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .cert_subject_name(x509_name)
+ .cert_serial(serial.to_vec());
+
+ let alias = "ks_test_auth_tags_test";
+ let key_metadata = key_generations::generate_key(&sec_level, &gen_params, alias).unwrap();
+ verify_certificate_subject_name(
+ key_metadata.certificate.as_ref().unwrap(),
+ cert_subject.as_bytes(),
+ );
+ verify_certificate_serial_num(key_metadata.certificate.as_ref().unwrap(), &serial);
+ delete_app_key(&keystore2, alias).unwrap();
+}
diff --git a/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
new file mode 100644
index 00000000..b784adf4
--- /dev/null
+++ b/keystore2/tests/keystore2_client_device_unique_attestation_tests.rs
@@ -0,0 +1,412 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, ErrorCode::ErrorCode,
+ KeyPurpose::KeyPurpose, PaddingMode::PaddingMode, SecurityLevel::SecurityLevel, Tag::Tag,
+};
+
+use keystore2_test_utils::{
+ authorizations, get_keystore_service, key_generations, key_generations::Error,
+};
+
+use keystore2_test_utils::ffi_test_utils::get_value_from_attest_record;
+
+use crate::keystore2_client_test_utils::{
+ delete_app_key, get_attest_id_value, is_second_imei_id_attestation_required,
+ perform_sample_asym_sign_verify_op,
+};
+
+use crate::skip_tests_if_keymaster_impl_present;
+
+/// This macro is used for generating device unique attested EC key with device id attestation.
+macro_rules! test_ec_key_device_unique_attestation_id {
+ ( $test_name:ident, $tag:expr, $prop_name:expr ) => {
+ #[test]
+ fn $test_name() {
+ generate_ec_key_device_unique_attested_with_id_attest($tag, $prop_name);
+ }
+ };
+}
+
+/// This macro is used for generating device unique attested RSA key with device id attestation.
+macro_rules! test_rsa_key_device_unique_attestation_id {
+ ( $test_name:ident, $tag:expr, $prop_name:expr ) => {
+ #[test]
+ fn $test_name() {
+ generate_rsa_key_device_unique_attested_with_id_attest($tag, $prop_name);
+ }
+ };
+}
+
+fn generate_ec_key_device_unique_attested_with_id_attest(attest_id_tag: Tag, prop_name: &str) {
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .device_unique_attestation();
+ generate_device_unique_attested_key_with_device_attest_ids(
+ gen_params,
+ attest_id_tag,
+ prop_name,
+ );
+}
+
+fn generate_rsa_key_device_unique_attested_with_id_attest(attest_id_tag: Tag, prop_name: &str) {
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .rsa_public_exponent(65537)
+ .key_size(2048)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .padding_mode(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .attestation_challenge(b"foo".to_vec())
+ .device_unique_attestation();
+ generate_device_unique_attested_key_with_device_attest_ids(
+ gen_params,
+ attest_id_tag,
+ prop_name,
+ );
+}
+
+fn add_attest_id_auth(
+ gen_params: authorizations::AuthSetBuilder,
+ attest_id_tag: Tag,
+ value: Vec<u8>,
+) -> authorizations::AuthSetBuilder {
+ match attest_id_tag {
+ Tag::ATTESTATION_ID_BRAND => gen_params.attestation_device_brand(value),
+ Tag::ATTESTATION_ID_DEVICE => gen_params.attestation_device_name(value),
+ Tag::ATTESTATION_ID_PRODUCT => gen_params.attestation_device_product_name(value),
+ Tag::ATTESTATION_ID_SERIAL => gen_params.attestation_device_serial(value),
+ Tag::ATTESTATION_ID_MANUFACTURER => gen_params.attestation_device_manufacturer(value),
+ Tag::ATTESTATION_ID_MODEL => gen_params.attestation_device_model(value),
+ Tag::ATTESTATION_ID_IMEI => gen_params.attestation_device_imei(value),
+ Tag::ATTESTATION_ID_SECOND_IMEI => gen_params.attestation_device_second_imei(value),
+ _ => {
+ panic!("Unknown attestation id");
+ }
+ }
+}
+
+/// Generate a device unique attested key with attestation of the device's identifiers. Test should
+/// succeed in generating a attested key with attestation of device identifiers. Test might fail on
+/// devices which don't support device id attestation with error response code `CANNOT_ATTEST_IDS`.
+fn generate_device_unique_attested_key_with_device_attest_ids(
+ gen_params: authorizations::AuthSetBuilder,
+ attest_id: Tag,
+ prop_name: &str,
+) {
+ let keystore2 = get_keystore_service();
+ let result =
+ key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
+ if result.is_err() {
+ assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+ return;
+ }
+ let sec_level = result.unwrap();
+
+ if attest_id == Tag::ATTESTATION_ID_SECOND_IMEI
+ && !is_second_imei_id_attestation_required(&keystore2)
+ {
+ return;
+ }
+
+ if let Some(value) = get_attest_id_value(attest_id, prop_name) {
+ if value.is_empty() {
+ return;
+ }
+ let gen_params = add_attest_id_auth(gen_params, attest_id, value.clone());
+ let alias = "ks_test_device_unique_attest_id_test";
+ match key_generations::map_ks_error(key_generations::generate_key(
+ &sec_level,
+ &gen_params,
+ alias,
+ )) {
+ Ok(key_metadata) => {
+ let attest_id_value = get_value_from_attest_record(
+ key_metadata.certificate.as_ref().unwrap(),
+ attest_id,
+ key_metadata.keySecurityLevel,
+ )
+ .expect("Attest id verification failed.");
+ assert_eq!(attest_id_value, value);
+ delete_app_key(&keystore2, alias).unwrap();
+ }
+ Err(e) => {
+ assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+ }
+ }
+ }
+}
+
+/// Try generate a key with `DEVICE_UNIQUE_ATTESTATION` using `TRUSTED_ENVIRONMENT` security level.
+/// Test should fail to generate a key with error code `INVALID_ARGUMENT`
+#[test]
+fn keystore2_gen_key_device_unique_attest_with_default_sec_level_unimplemented() {
+ skip_tests_if_keymaster_impl_present!();
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .device_unique_attestation();
+
+ let alias = "ks_test_auth_tags_test";
+ let result = key_generations::map_ks_error(key_generations::generate_key(
+ &sec_level,
+ &gen_params,
+ alias,
+ ));
+ assert!(result.is_err());
+ assert!(matches!(
+ result.unwrap_err(),
+ Error::Km(ErrorCode::INVALID_ARGUMENT) | Error::Km(ErrorCode::UNSUPPORTED_TAG)
+ ));
+}
+
+/// Generate a EC key with `DEVICE_UNIQUE_ATTESTATION` using `STRONGBOX` security level.
+/// Test should create a key successfully, verify key characteristics, cert-chain signatures and
+/// use it for performing an operation.
+#[test]
+fn keystore2_gen_ec_key_device_unique_attest_with_strongbox_sec_level_test_success() {
+ let keystore2 = get_keystore_service();
+ let result =
+ key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
+ if result.is_err() {
+ assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+ return;
+ }
+
+ let sec_level = result.unwrap();
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .device_unique_attestation();
+
+ let alias = "ks_device_unique_ec_key_attest_test";
+ match key_generations::map_ks_error(key_generations::generate_key(
+ &sec_level,
+ &gen_params,
+ alias,
+ )) {
+ Ok(key_metadata) => {
+ perform_sample_asym_sign_verify_op(
+ &sec_level,
+ &key_metadata,
+ None,
+ Some(Digest::SHA_2_256),
+ );
+ delete_app_key(&keystore2, alias).unwrap();
+ }
+ Err(e) => {
+ assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+ }
+ }
+}
+
+/// Generate a RSA key with `DEVICE_UNIQUE_ATTESTATION` using `STRONGBOX` security level.
+/// Test should create a key successfully, verify key characteristics, cert-chain signatures and
+/// use it for performing an operation.
+#[test]
+fn keystore2_gen_rsa_key_device_unique_attest_with_strongbox_sec_level_test_success() {
+ let keystore2 = get_keystore_service();
+ let result =
+ key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
+ if result.is_err() {
+ assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+ return;
+ }
+
+ let sec_level = result.unwrap();
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .rsa_public_exponent(65537)
+ .key_size(2048)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .padding_mode(PaddingMode::RSA_PKCS1_1_5_SIGN)
+ .attestation_challenge(b"foo".to_vec())
+ .device_unique_attestation();
+
+ let alias = "ks_device_unique_rsa_key_attest_test";
+ match key_generations::map_ks_error(key_generations::generate_key(
+ &sec_level,
+ &gen_params,
+ alias,
+ )) {
+ Ok(key_metadata) => {
+ perform_sample_asym_sign_verify_op(
+ &sec_level,
+ &key_metadata,
+ Some(PaddingMode::RSA_PKCS1_1_5_SIGN),
+ Some(Digest::SHA_2_256),
+ );
+ delete_app_key(&keystore2, alias).unwrap();
+ }
+ Err(e) => {
+ assert_eq!(e, Error::Km(ErrorCode::CANNOT_ATTEST_IDS));
+ }
+ }
+}
+
+/// Try to generate a device unique attested key with attestation of invalid device's identifiers.
+/// Test should fail with error response code `CANNOT_ATTEST_IDS`.
+#[test]
+fn keystore2_device_unique_attest_key_fails_with_invalid_attestation_id() {
+ let keystore2 = get_keystore_service();
+ let result =
+ key_generations::map_ks_error(keystore2.getSecurityLevel(SecurityLevel::STRONGBOX));
+ if result.is_err() {
+ assert_eq!(Error::Km(ErrorCode::HARDWARE_TYPE_UNAVAILABLE), result.unwrap_err());
+ return;
+ }
+
+ let sec_level = result.unwrap();
+ let attest_id_params = vec![
+ (Tag::ATTESTATION_ID_BRAND, b"invalid-brand".to_vec()),
+ (Tag::ATTESTATION_ID_DEVICE, b"invalid-device-name".to_vec()),
+ (Tag::ATTESTATION_ID_PRODUCT, b"invalid-product-name".to_vec()),
+ (Tag::ATTESTATION_ID_SERIAL, b"invalid-ro-serial".to_vec()),
+ (Tag::ATTESTATION_ID_MANUFACTURER, b"invalid-ro-product-manufacturer".to_vec()),
+ (Tag::ATTESTATION_ID_MODEL, b"invalid-ro-product-model".to_vec()),
+ (Tag::ATTESTATION_ID_IMEI, b"invalid-imei".to_vec()),
+ ];
+
+ for (attest_id, value) in attest_id_params {
+ let gen_params = authorizations::AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::SHA_2_256)
+ .ec_curve(EcCurve::P_256)
+ .attestation_challenge(b"foo".to_vec())
+ .device_unique_attestation();
+ let alias = "ks_ec_device_unique_attested_test_key_fail";
+ let gen_params = add_attest_id_auth(gen_params, attest_id, value.clone());
+
+ let result = key_generations::map_ks_error(key_generations::generate_key(
+ &sec_level,
+ &gen_params,
+ alias,
+ ));
+ assert!(result.is_err());
+ assert!(matches!(result.unwrap_err(), Error::Km(ErrorCode::CANNOT_ATTEST_IDS)));
+ }
+}
+
+// Below macros generate tests for generating device unique attested EC keys with attestation
+// of the device's identifiers.
+test_ec_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_ecdsa_attest_id_brand,
+ Tag::ATTESTATION_ID_BRAND,
+ "brand"
+);
+test_ec_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_ecdsa_attest_id_device,
+ Tag::ATTESTATION_ID_DEVICE,
+ "device"
+);
+test_ec_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_ecdsa_attest_id_product,
+ Tag::ATTESTATION_ID_PRODUCT,
+ "name"
+);
+test_ec_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_ecdsa_attest_id_serial,
+ Tag::ATTESTATION_ID_SERIAL,
+ "serialno"
+);
+test_ec_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_ecdsa_attest_id_manufacturer,
+ Tag::ATTESTATION_ID_MANUFACTURER,
+ "manufacturer"
+);
+test_ec_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_ecdsa_attest_id_model,
+ Tag::ATTESTATION_ID_MODEL,
+ "model"
+);
+test_ec_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_ecdsa_attest_id_imei,
+ Tag::ATTESTATION_ID_IMEI,
+ ""
+);
+test_ec_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_ecdsa_attest_id_second_imei,
+ Tag::ATTESTATION_ID_SECOND_IMEI,
+ ""
+);
+
+// Below macros generate tests for generating device unique attested RSA keys with attestation
+// of the device's identifiers.
+test_rsa_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_rsa_attest_id_brand,
+ Tag::ATTESTATION_ID_BRAND,
+ "brand"
+);
+test_rsa_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_rsa_attest_id_device,
+ Tag::ATTESTATION_ID_DEVICE,
+ "device"
+);
+test_rsa_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_rsa_attest_id_product,
+ Tag::ATTESTATION_ID_PRODUCT,
+ "name"
+);
+test_rsa_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_rsa_attest_id_serial,
+ Tag::ATTESTATION_ID_SERIAL,
+ "serialno"
+);
+test_rsa_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_rsa_attest_id_manufacturer,
+ Tag::ATTESTATION_ID_MANUFACTURER,
+ "manufacturer"
+);
+test_rsa_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_rsa_attest_id_model,
+ Tag::ATTESTATION_ID_MODEL,
+ "model"
+);
+test_rsa_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_rsa_attest_id_imei,
+ Tag::ATTESTATION_ID_IMEI,
+ ""
+);
+test_rsa_key_device_unique_attestation_id!(
+ keystore2_device_unique_attest_rsa_attest_id_second_imei,
+ Tag::ATTESTATION_ID_SECOND_IMEI,
+ ""
+);
diff --git a/keystore2/tests/keystore2_client_ec_key_tests.rs b/keystore2/tests/keystore2_client_ec_key_tests.rs
index c2034ded..f2c6d0f9 100644
--- a/keystore2/tests/keystore2_client_ec_key_tests.rs
+++ b/keystore2/tests/keystore2_client_ec_key_tests.rs
@@ -30,8 +30,8 @@ use keystore2_test_utils::{
};
use crate::keystore2_client_test_utils::{
- delete_app_key, execute_op_run_as_child, perform_sample_sign_operation, BarrierReached,
- ForcedOp, TestOutcome,
+ delete_app_key, execute_op_run_as_child, get_vsr_api_level, perform_sample_sign_operation,
+ BarrierReached, ForcedOp, TestOutcome,
};
macro_rules! test_ec_sign_key_op_success {
@@ -374,13 +374,18 @@ fn keystore2_ec_25519_generate_key_fail() {
)
.unwrap();
- let result = key_generations::map_ks_error(sec_level.createOperation(
- &key_metadata.key,
- &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
- false,
- ));
- assert!(result.is_err());
- assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+ // The KeyMint v2 API added `CURVE_25519` and specified that "Ed25519 keys only support
+ // Digest::NONE". However, this was not checked at the time so we can only be strict about
+ // checking this for more recent implementations.
+ if get_vsr_api_level() >= 35 {
+ let result = key_generations::map_ks_error(sec_level.createOperation(
+ &key_metadata.key,
+ &authorizations::AuthSetBuilder::new().purpose(KeyPurpose::SIGN).digest(digest),
+ false,
+ ));
+ assert!(result.is_err(), "unexpected success for digest {digest:?}");
+ assert_eq!(Error::Km(ErrorCode::UNSUPPORTED_DIGEST), result.unwrap_err());
+ }
}
}
@@ -432,15 +437,18 @@ fn keystore2_key_owner_validation() {
// Client#1: Generate a key and create an operation using generated key.
// Wait until the parent notifies to continue. Once the parent notifies, this operation
// is expected to be completed successfully.
- let mut child_handle = execute_op_run_as_child(
- TARGET_CTX,
- Domain::APP,
- -1,
- Some(alias.to_string()),
- Uid::from_raw(uid1),
- Gid::from_raw(gid1),
- ForcedOp(false),
- );
+ // SAFETY: The test is run in a separate process with no other threads.
+ let mut child_handle = unsafe {
+ execute_op_run_as_child(
+ TARGET_CTX,
+ Domain::APP,
+ -1,
+ Some(alias.to_string()),
+ Uid::from_raw(uid1),
+ Gid::from_raw(gid1),
+ ForcedOp(false),
+ )
+ };
// Wait until (client#1) child process notifies us to continue, so that there will be a key
// generated by client#1.
@@ -450,6 +458,7 @@ fn keystore2_key_owner_validation() {
const APPLICATION_ID_2: u32 = 10602;
let uid2 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_2;
let gid2 = USER_ID * AID_USER_OFFSET + APPLICATION_ID_2;
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(TARGET_CTX, Uid::from_raw(uid2), Gid::from_raw(gid2), move || {
let keystore2_inst = get_keystore_service();
diff --git a/keystore2/tests/keystore2_client_grant_key_tests.rs b/keystore2/tests/keystore2_client_grant_key_tests.rs
index bde872d0..516869a1 100644
--- a/keystore2/tests/keystore2_client_grant_key_tests.rs
+++ b/keystore2/tests/keystore2_client_grant_key_tests.rs
@@ -114,6 +114,7 @@ fn keystore2_grant_key_with_perm_none() {
static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static GRANTEE_GID: u32 = GRANTEE_UID;
+ // SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let empty_access_vector = KeyPermission::NONE.0;
@@ -132,6 +133,7 @@ fn keystore2_grant_key_with_perm_none() {
// In grantee context try to load the key, it should fail to load the granted key as it is
// granted with empty access vector.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -169,6 +171,7 @@ fn keystore2_grant_get_info_use_key_perm() {
static GRANTEE_GID: u32 = GRANTEE_UID;
// Generate a key and grant it to a user with GET_INFO|USE key permissions.
+ // SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0;
@@ -185,6 +188,7 @@ fn keystore2_grant_get_info_use_key_perm() {
};
// In grantee context load the key and try to perform crypto operation.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -251,6 +255,7 @@ fn keystore2_grant_delete_key_success() {
static ALIAS: &str = "ks_grant_key_delete_success";
// Generate a key and grant it to a user with DELETE permission.
+ // SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
@@ -270,6 +275,7 @@ fn keystore2_grant_delete_key_success() {
};
// Grantee context, delete the key.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -290,6 +296,7 @@ fn keystore2_grant_delete_key_success() {
};
// Verify whether key got deleted in grantor's context.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), move || {
let keystore2_inst = get_keystore_service();
@@ -325,6 +332,7 @@ fn keystore2_grant_key_fails_with_permission_denied() {
static SEC_GRANTEE_GID: u32 = SEC_GRANTEE_UID;
// Generate a key and grant it to a user with GET_INFO permission.
+ // SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
@@ -345,6 +353,7 @@ fn keystore2_grant_key_fails_with_permission_denied() {
};
// Grantee context, load the granted key and try to grant it to `SEC_GRANTEE_UID` grantee.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -375,6 +384,7 @@ fn keystore2_grant_key_fails_with_permission_denied() {
};
// Make sure second grantee shouldn't have access to the above granted key.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -457,6 +467,7 @@ fn keystore2_ungrant_key_success() {
static GRANTEE_GID: u32 = GRANTEE_UID;
// Generate a key and grant it to a user with GET_INFO permission.
+ // SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
@@ -492,6 +503,7 @@ fn keystore2_ungrant_key_success() {
};
// Grantee context, try to load the ungranted key.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -527,6 +539,7 @@ fn keystore2_ungrant_fails_with_non_existing_key_expect_key_not_found_error() {
static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static GRANTEE_GID: u32 = GRANTEE_UID;
+ // SAFETY: The test is run in a separate process with no other threads.
let grant_key_nspace = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
@@ -576,6 +589,7 @@ fn keystore2_ungrant_fails_with_non_existing_key_expect_key_not_found_error() {
// Make sure grant did not persist, try to access the earlier granted key in grantee context.
// Grantee context should fail to load the granted key as its associated key is deleted in
// grantor context.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -614,6 +628,7 @@ fn keystore2_grant_key_to_multi_users_success() {
static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
// Generate a key and grant it to multiple users with GET_INFO|USE permissions.
+ // SAFETY: The test is run in a separate process with no other threads.
let mut grant_keys = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
@@ -636,6 +651,7 @@ fn keystore2_grant_key_to_multi_users_success() {
&[(GRANTEE_1_UID, GRANTEE_1_GID), (GRANTEE_2_UID, GRANTEE_2_GID)]
{
let grant_key_nspace = grant_keys.remove(0);
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -678,6 +694,7 @@ fn keystore2_grant_key_to_multi_users_delete_fails_with_key_not_found_error() {
static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
// Generate a key and grant it to multiple users with GET_INFO permission.
+ // SAFETY: The test is run in a separate process with no other threads.
let mut grant_keys = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
@@ -699,6 +716,7 @@ fn keystore2_grant_key_to_multi_users_delete_fails_with_key_not_found_error() {
// Grantee #1 context
let grant_key1_nspace = grant_keys.remove(0);
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -733,6 +751,7 @@ fn keystore2_grant_key_to_multi_users_delete_fails_with_key_not_found_error() {
// Grantee #2 context
let grant_key2_nspace = grant_keys.remove(0);
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
diff --git a/keystore2/tests/keystore2_client_import_keys_tests.rs b/keystore2/tests/keystore2_client_import_keys_tests.rs
index ecba402a..bf787d29 100644
--- a/keystore2/tests/keystore2_client_import_keys_tests.rs
+++ b/keystore2/tests/keystore2_client_import_keys_tests.rs
@@ -32,10 +32,12 @@ use keystore2_test_utils::{
authorizations, get_keystore_service, key_generations, key_generations::Error,
};
-use crate::ffi_test_utils::{create_wrapped_key, create_wrapped_key_additional_auth_data};
+use keystore2_test_utils::ffi_test_utils::{
+ create_wrapped_key, create_wrapped_key_additional_auth_data,
+};
use crate::keystore2_client_test_utils::{
- encrypt_secure_key, encrypt_transport_key, has_default_keymint,
+ encrypt_secure_key, encrypt_transport_key, get_vsr_api_level,
perform_sample_asym_sign_verify_op, perform_sample_hmac_sign_verify_op,
perform_sample_sym_key_decrypt_op, perform_sample_sym_key_encrypt_op, SAMPLE_PLAIN_TEXT,
};
@@ -286,7 +288,7 @@ fn keystore2_rsa_import_key_with_multipurpose_fails_incompt_purpose_error() {
key_generations::RSA_2048_KEY,
));
- if has_default_keymint() {
+ if key_generations::has_default_keymint() {
assert!(result.is_err());
assert_eq!(Error::Km(ErrorCode::INCOMPATIBLE_PURPOSE), result.unwrap_err());
} else {
@@ -304,6 +306,13 @@ fn keystore2_import_ec_key_success() {
let alias = format!("ks_ec_key_test_import_1_{}{}", getuid(), 256);
+ if get_vsr_api_level() < 35 {
+ // The KeyMint spec was previously not clear as to whether EC_CURVE was optional on import
+ // of EC keys. However, this was not checked at the time so we can only be strict about
+ // checking this for implementations at VSR-V or later.
+ println!("Skipping EC_CURVE on import only strict >= VSR-V");
+ return;
+ }
// Don't specify ec-curve.
let import_params = authorizations::AuthSetBuilder::new()
.no_auth_required()
diff --git a/keystore2/tests/keystore2_client_keystore_engine_tests.rs b/keystore2/tests/keystore2_client_keystore_engine_tests.rs
new file mode 100644
index 00000000..4651931b
--- /dev/null
+++ b/keystore2/tests/keystore2_client_keystore_engine_tests.rs
@@ -0,0 +1,305 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+use nix::unistd::{Gid, Uid};
+use rustutils::users::AID_USER_OFFSET;
+
+use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
+ Algorithm::Algorithm, Digest::Digest, EcCurve::EcCurve, KeyPurpose::KeyPurpose,
+ PaddingMode::PaddingMode, SecurityLevel::SecurityLevel,
+};
+use android_system_keystore2::aidl::android::system::keystore2::{
+ Domain::Domain, IKeystoreSecurityLevel::IKeystoreSecurityLevel,
+ IKeystoreService::IKeystoreService, KeyDescriptor::KeyDescriptor, KeyPermission::KeyPermission,
+};
+
+use keystore2_test_utils::{authorizations::AuthSetBuilder, get_keystore_service, run_as};
+
+use keystore2_test_utils::ffi_test_utils::perform_crypto_op_using_keystore_engine;
+
+use openssl::x509::X509;
+
+fn generate_rsa_key_and_grant_to_user(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ alias: &str,
+ grantee_uid: i32,
+ access_vector: i32,
+) -> binder::Result<KeyDescriptor> {
+ let gen_params = AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::RSA)
+ .rsa_public_exponent(65537)
+ .key_size(2048)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .padding_mode(PaddingMode::NONE)
+ .digest(Digest::NONE);
+
+ let key_metadata = sec_level
+ .generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ )
+ .expect("Failed to generate RSA Key.");
+
+ assert!(key_metadata.certificate.is_some());
+
+ keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+}
+
+fn generate_ec_key_and_grant_to_user(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ alias: &str,
+ grantee_uid: i32,
+ access_vector: i32,
+) -> binder::Result<KeyDescriptor> {
+ let gen_params = AuthSetBuilder::new()
+ .no_auth_required()
+ .algorithm(Algorithm::EC)
+ .purpose(KeyPurpose::SIGN)
+ .purpose(KeyPurpose::VERIFY)
+ .digest(Digest::NONE)
+ .ec_curve(EcCurve::P_256);
+
+ let key_metadata = sec_level
+ .generateKey(
+ &KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ },
+ None,
+ &gen_params,
+ 0,
+ b"entropy",
+ )
+ .expect("Failed to generate EC Key.");
+
+ assert!(key_metadata.certificate.is_some());
+
+ keystore2.grant(&key_metadata.key, grantee_uid, access_vector)
+}
+
+fn generate_key_and_grant_to_user(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
+ alias: &str,
+ grantee_uid: u32,
+ algo: Algorithm,
+) -> Result<i64, Box<dyn std::error::Error>> {
+ let access_vector = KeyPermission::GET_INFO.0 | KeyPermission::USE.0 | KeyPermission::DELETE.0;
+
+ assert!(matches!(algo, Algorithm::RSA | Algorithm::EC));
+
+ let grant_key = match algo {
+ Algorithm::RSA => generate_rsa_key_and_grant_to_user(
+ keystore2,
+ sec_level,
+ alias,
+ grantee_uid.try_into().unwrap(),
+ access_vector,
+ )
+ .unwrap(),
+ Algorithm::EC => generate_ec_key_and_grant_to_user(
+ keystore2,
+ sec_level,
+ alias,
+ grantee_uid.try_into().unwrap(),
+ access_vector,
+ )
+ .unwrap(),
+ _ => panic!("Unsupported algorithms"),
+ };
+
+ assert_eq!(grant_key.domain, Domain::GRANT);
+
+ Ok(grant_key.nspace)
+}
+
+fn perform_crypto_op_using_granted_key(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ grant_key_nspace: i64,
+) {
+ // Load the granted key from Keystore2-Engine API and perform crypto operations.
+ assert!(perform_crypto_op_using_keystore_engine(grant_key_nspace).unwrap());
+
+ // Delete the granted key.
+ keystore2
+ .deleteKey(&KeyDescriptor {
+ domain: Domain::GRANT,
+ nspace: grant_key_nspace,
+ alias: None,
+ blob: None,
+ })
+ .unwrap();
+}
+
+#[test]
+fn keystore2_perofrm_crypto_op_using_keystore2_engine_rsa_key_success() {
+ static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ // Generate a key and grant it to a user with GET_INFO|USE|DELETE key permissions.
+ // SAFETY: The test is run in a separate process with no other threads.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "keystore2_engine_rsa_key";
+ generate_key_and_grant_to_user(
+ &keystore2,
+ &sec_level,
+ alias,
+ GRANTEE_UID,
+ Algorithm::RSA,
+ )
+ .unwrap()
+ })
+ };
+
+ // In grantee context load the key and try to perform crypto operation.
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
+ },
+ )
+ };
+}
+
+#[test]
+fn keystore2_perofrm_crypto_op_using_keystore2_engine_ec_key_success() {
+ static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ // Generate a key and grant it to a user with GET_INFO|USE|DELETE key permissions.
+ // SAFETY: The test is run in a separate process with no other threads.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "keystore2_engine_ec_test_key";
+ generate_key_and_grant_to_user(
+ &keystore2,
+ &sec_level,
+ alias,
+ GRANTEE_UID,
+ Algorithm::EC,
+ )
+ .unwrap()
+ })
+ };
+
+ // In grantee context load the key and try to perform crypto operation.
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
+ },
+ )
+ };
+}
+
+#[test]
+fn keystore2_perofrm_crypto_op_using_keystore2_engine_pem_pub_key_success() {
+ static TARGET_SU_CTX: &str = "u:r:su:s0";
+
+ static GRANTEE_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ const USER_ID: u32 = 99;
+ const APPLICATION_ID: u32 = 10001;
+ static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static GRANTEE_GID: u32 = GRANTEE_UID;
+
+ // Generate a key and re-encode it's certificate as PEM and update it and
+ // grant it to a user with GET_INFO|USE|DELETE key permissions.
+ // SAFETY: The test is run in a separate process with no other threads.
+ let grant_key_nspace = unsafe {
+ run_as::run_as(TARGET_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+ let alias = "keystore2_engine_rsa_pem_pub_key";
+ let grant_key_nspace = generate_key_and_grant_to_user(
+ &keystore2,
+ &sec_level,
+ alias,
+ GRANTEE_UID,
+ Algorithm::RSA,
+ )
+ .unwrap();
+
+ // Update certificate with encodeed PEM data.
+ let key_entry_response = keystore2
+ .getKeyEntry(&KeyDescriptor {
+ domain: Domain::APP,
+ nspace: -1,
+ alias: Some(alias.to_string()),
+ blob: None,
+ })
+ .unwrap();
+ let cert_bytes = key_entry_response.metadata.certificate.as_ref().unwrap();
+ let cert = X509::from_der(cert_bytes.as_ref()).unwrap();
+ let cert_pem = cert.to_pem().unwrap();
+ keystore2
+ .updateSubcomponent(&key_entry_response.metadata.key, Some(&cert_pem), None)
+ .expect("updateSubcomponent failed.");
+
+ grant_key_nspace
+ })
+ };
+
+ // In grantee context load the key and try to perform crypto operation.
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(
+ GRANTEE_CTX,
+ Uid::from_raw(GRANTEE_UID),
+ Gid::from_raw(GRANTEE_GID),
+ move || {
+ let keystore2 = get_keystore_service();
+ perform_crypto_op_using_granted_key(&keystore2, grant_key_nspace);
+ },
+ )
+ };
+}
diff --git a/keystore2/tests/keystore2_client_list_entries_tests.rs b/keystore2/tests/keystore2_client_list_entries_tests.rs
index 3b656c3d..8b3f7001 100644
--- a/keystore2/tests/keystore2_client_list_entries_tests.rs
+++ b/keystore2/tests/keystore2_client_list_entries_tests.rs
@@ -23,7 +23,7 @@ use android_system_keystore2::aidl::android::system::keystore2::{
KeyPermission::KeyPermission, ResponseCode::ResponseCode,
};
-use crate::keystore2_client_test_utils::delete_app_key;
+use crate::keystore2_client_test_utils::{delete_all_entries, delete_app_key, verify_aliases};
use keystore2_test_utils::{get_keystore_service, key_generations, key_generations::Error, run_as};
/// Try to find a key with given key parameters using `listEntries` API.
@@ -60,6 +60,7 @@ fn keystore2_list_entries_success() {
static GRANTEE_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static GRANTEE_GID: u32 = GRANTEE_UID;
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
@@ -113,6 +114,7 @@ fn keystore2_list_entries_success() {
};
// In user context validate list of key entries associated with it.
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -138,7 +140,7 @@ fn keystore2_list_entries_success() {
let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
assert_eq!(1, key_descriptors.len());
- let key = key_descriptors.get(0).unwrap();
+ let key = key_descriptors.first().unwrap();
assert_eq!(key.alias, Some(alias));
assert_eq!(key.nspace, GRANTEE_UID.try_into().unwrap());
assert_eq!(key.domain, Domain::APP);
@@ -161,6 +163,7 @@ fn keystore2_list_entries_fails_perm_denied() {
let agid = 91 * AID_USER_OFFSET + 10001;
static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
let keystore2 = get_keystore_service();
@@ -198,6 +201,7 @@ fn keystore2_list_entries_with_long_aliases_success() {
static CLIENT_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
static CLIENT_GID: u32 = CLIENT_UID;
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
let keystore2 = get_keystore_service();
@@ -251,3 +255,471 @@ fn keystore2_list_entries_with_long_aliases_success() {
})
};
}
+
+/// Import large number of Keystore entries with long aliases such that the
+/// aliases list would exceed the binder transaction size limit.
+/// Try to list aliases of all the entries in the keystore using `listEntriesBatched` API.
+#[test]
+fn keystore2_list_entries_batched_with_long_aliases_success() {
+ static CLIENT_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID: u32 = 92;
+ const APPLICATION_ID: u32 = 10002;
+ static CLIENT_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static CLIENT_GID: u32 = CLIENT_UID;
+
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Make sure there are no keystore entries exist before adding new entries.
+ delete_all_entries(&keystore2);
+
+ // Import 100 keys with aliases of length 6000.
+ let mut imported_key_aliases =
+ key_generations::import_aes_keys(&sec_level, "X".repeat(6000), 1..101).unwrap();
+ assert_eq!(
+ keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ 100,
+ "Error while importing keys"
+ );
+
+ let mut start_past_alias = None;
+ let mut alias;
+ while !imported_key_aliases.is_empty() {
+ let key_descriptors =
+ keystore2.listEntriesBatched(Domain::APP, -1, start_past_alias).unwrap();
+
+ // Check retrieved key entries list is a subset of imported keys list.
+ assert!(key_descriptors
+ .iter()
+ .all(|key| imported_key_aliases.contains(key.alias.as_ref().unwrap())));
+
+ alias = key_descriptors.last().unwrap().alias.clone().unwrap();
+ start_past_alias = Some(alias.as_ref());
+ // Delete the listed key entries from imported keys list.
+ key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
+ assert!(imported_key_aliases.remove(&alias));
+ });
+ }
+
+ assert!(imported_key_aliases.is_empty());
+ delete_all_entries(&keystore2);
+ assert_eq!(
+ keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ 0,
+ "Error while doing cleanup"
+ );
+ })
+ };
+}
+
+/// Import keys from multiple processes with same user context and try to list the keystore entries
+/// using `listEntriesBatched` API.
+/// - Create two processes sharing user-id.
+/// - From process-1, import 3 keys and try to list the keys using `listEntriesBatched`
+/// without `startingPastAlias`, it should list all the 3 entries.
+/// - From process-2, import another 5 keys and try to list the keys using `listEntriesBatched`
+/// with the alias of the last key listed in process-1 as `startingPastAlias`. It should list
+/// all the entries whose alias is greater than the provided `startingPastAlias`.
+/// - From process-2 try to list all entries accessible to it by using `listEntriesBatched` with
+/// `startingPastAlias` as None. It should list all the keys imported in process-1 and process-2.
+#[test]
+fn keystore2_list_entries_batched_with_multi_procs_success() {
+ static CLIENT_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID: u32 = 92;
+ const APPLICATION_ID: u32 = 10002;
+ static CLIENT_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static CLIENT_GID: u32 = CLIENT_UID;
+ static ALIAS_PREFIX: &str = "key_test_batch_list";
+
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Make sure there are no keystore entries exist before adding new entries.
+ delete_all_entries(&keystore2);
+
+ // Import 3 keys with below aliases -
+ // [key_test_batch_list_1, key_test_batch_list_2, key_test_batch_list_3]
+ let imported_key_aliases =
+ key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 1..4)
+ .unwrap();
+ assert_eq!(
+ keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ 3,
+ "Error while importing keys"
+ );
+
+ // List all entries in keystore for this user-id.
+ let key_descriptors = keystore2.listEntriesBatched(Domain::APP, -1, None).unwrap();
+ assert_eq!(key_descriptors.len(), 3);
+
+ // Makes sure all listed aliases are matching with imported keys aliases.
+ assert!(key_descriptors
+ .iter()
+ .all(|key| imported_key_aliases.contains(key.alias.as_ref().unwrap())));
+ })
+ };
+
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Import another 5 keys with below aliases -
+ // [ key_test_batch_list_4, key_test_batch_list_5, key_test_batch_list_6,
+ // key_test_batch_list_7, key_test_batch_list_8 ]
+ let mut imported_key_aliases =
+ key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 4..9)
+ .unwrap();
+
+ // Above context already 3 keys are imported, in this context 5 keys are imported,
+ // total 8 keystore entries are expected to be present in Keystore for this user-id.
+ assert_eq!(
+ keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ 8,
+ "Error while importing keys"
+ );
+
+ // List keystore entries with `start_past_alias` as "key_test_batch_list_3".
+ // `listEntriesBatched` should list all the keystore entries with
+ // alias > "key_test_batch_list_3".
+ let key_descriptors = keystore2
+ .listEntriesBatched(Domain::APP, -1, Some("key_test_batch_list_3"))
+ .unwrap();
+ assert_eq!(key_descriptors.len(), 5);
+
+ // Make sure above listed aliases are matching with imported keys aliases.
+ assert!(key_descriptors
+ .iter()
+ .all(|key| imported_key_aliases.contains(key.alias.as_ref().unwrap())));
+
+ // List all keystore entries with `start_past_alias` as `None`.
+ // `listEntriesBatched` should list all the keystore entries.
+ let key_descriptors = keystore2.listEntriesBatched(Domain::APP, -1, None).unwrap();
+ assert_eq!(key_descriptors.len(), 8);
+
+ // Include previously imported keys aliases as well
+ imported_key_aliases.insert(ALIAS_PREFIX.to_owned() + "_1");
+ imported_key_aliases.insert(ALIAS_PREFIX.to_owned() + "_2");
+ imported_key_aliases.insert(ALIAS_PREFIX.to_owned() + "_3");
+
+ // Make sure all the above listed aliases are matching with imported keys aliases.
+ assert!(key_descriptors
+ .iter()
+ .all(|key| imported_key_aliases.contains(key.alias.as_ref().unwrap())));
+
+ delete_all_entries(&keystore2);
+ assert_eq!(
+ keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ 0,
+ "Error while doing cleanup"
+ );
+ })
+ };
+}
+
+#[test]
+fn keystore2_list_entries_batched_with_empty_keystore_success() {
+ static CLIENT_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID: u32 = 92;
+ const APPLICATION_ID: u32 = 10002;
+ static CLIENT_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static CLIENT_GID: u32 = CLIENT_UID;
+
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
+ let keystore2 = get_keystore_service();
+
+ // Make sure there are no keystore entries exist before adding new entries.
+ delete_all_entries(&keystore2);
+
+ // List all entries in keystore for this user-id, pass startingPastAlias = None
+ let key_descriptors = keystore2.listEntriesBatched(Domain::APP, -1, None).unwrap();
+ assert_eq!(key_descriptors.len(), 0);
+
+ // List all entries in keystore for this user-id, pass startingPastAlias = <random value>
+ let key_descriptors =
+ keystore2.listEntriesBatched(Domain::APP, -1, Some("startingPastAlias")).unwrap();
+ assert_eq!(key_descriptors.len(), 0);
+ })
+ };
+}
+
+/// Import a key with SELINUX as domain, list aliases using `listEntriesBatched`.
+/// Test should successfully list the imported key.
+#[test]
+fn keystore2_list_entries_batched_with_selinux_domain_success() {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ let alias = "test_selinux_key_list_alias_batched";
+ let _result = keystore2.deleteKey(&KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias.to_string()),
+ blob: None,
+ });
+
+ let initial_count = keystore2
+ .getNumberOfEntries(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE)
+ .unwrap();
+
+ key_generations::import_aes_key(
+ &sec_level,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias.to_string()),
+ )
+ .unwrap();
+
+ assert_eq!(
+ keystore2
+ .getNumberOfEntries(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE)
+ .unwrap(),
+ initial_count + 1,
+ "Error while getting number of keystore entries accessible."
+ );
+
+ let key_descriptors = keystore2
+ .listEntriesBatched(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE, None)
+ .unwrap();
+ assert_eq!(key_descriptors.len(), (initial_count + 1) as usize);
+
+ let count =
+ key_descriptors.into_iter().map(|key| key.alias.unwrap()).filter(|a| a == alias).count();
+ assert_eq!(count, 1);
+
+ keystore2
+ .deleteKey(&KeyDescriptor {
+ domain: Domain::SELINUX,
+ nspace: key_generations::SELINUX_SHELL_NAMESPACE,
+ alias: Some(alias.to_string()),
+ blob: None,
+ })
+ .unwrap();
+}
+
+#[test]
+fn keystore2_list_entries_batched_validate_count_and_order_success() {
+ static CLIENT_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ const USER_ID: u32 = 92;
+ const APPLICATION_ID: u32 = 10002;
+ static CLIENT_UID: u32 = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ static CLIENT_GID: u32 = CLIENT_UID;
+ static ALIAS_PREFIX: &str = "key_test_batch_list";
+
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(CLIENT_CTX, Uid::from_raw(CLIENT_UID), Gid::from_raw(CLIENT_GID), || {
+ let keystore2 = get_keystore_service();
+ let sec_level = keystore2.getSecurityLevel(SecurityLevel::TRUSTED_ENVIRONMENT).unwrap();
+
+ // Make sure there are no keystore entries exist before adding new entries.
+ delete_all_entries(&keystore2);
+
+ // Import keys with below mentioned aliases -
+ // [
+ // key_test_batch_list_1,
+ // key_test_batch_list_2,
+ // key_test_batch_list_3,
+ // key_test_batch_list_4,
+ // key_test_batch_list_5,
+ // key_test_batch_list_10,
+ // key_test_batch_list_11,
+ // key_test_batch_list_12,
+ // key_test_batch_list_21,
+ // key_test_batch_list_22,
+ // ]
+ let _imported_key_aliases =
+ key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 1..6)
+ .unwrap();
+ assert_eq!(
+ keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ 5,
+ "Error while importing keys"
+ );
+ let _imported_key_aliases =
+ key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 10..13)
+ .unwrap();
+ assert_eq!(
+ keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ 8,
+ "Error while importing keys"
+ );
+ let _imported_key_aliases =
+ key_generations::import_aes_keys(&sec_level, ALIAS_PREFIX.to_string(), 21..23)
+ .unwrap();
+ assert_eq!(
+ keystore2.getNumberOfEntries(Domain::APP, -1).unwrap(),
+ 10,
+ "Error while importing keys"
+ );
+
+ // List the aliases using given `startingPastAlias` and verify the listed
+ // aliases with the expected list of aliases.
+ verify_aliases(&keystore2, Some(format!("{}{}", ALIAS_PREFIX, "_5").as_str()), vec![]);
+
+ verify_aliases(
+ &keystore2,
+ Some(format!("{}{}", ALIAS_PREFIX, "_4").as_str()),
+ vec![ALIAS_PREFIX.to_owned() + "_5"],
+ );
+
+ verify_aliases(
+ &keystore2,
+ Some(format!("{}{}", ALIAS_PREFIX, "_3").as_str()),
+ vec![ALIAS_PREFIX.to_owned() + "_4", ALIAS_PREFIX.to_owned() + "_5"],
+ );
+
+ verify_aliases(
+ &keystore2,
+ Some(format!("{}{}", ALIAS_PREFIX, "_2").as_str()),
+ vec![
+ ALIAS_PREFIX.to_owned() + "_21",
+ ALIAS_PREFIX.to_owned() + "_22",
+ ALIAS_PREFIX.to_owned() + "_3",
+ ALIAS_PREFIX.to_owned() + "_4",
+ ALIAS_PREFIX.to_owned() + "_5",
+ ],
+ );
+
+ verify_aliases(
+ &keystore2,
+ Some(format!("{}{}", ALIAS_PREFIX, "_1").as_str()),
+ vec![
+ ALIAS_PREFIX.to_owned() + "_10",
+ ALIAS_PREFIX.to_owned() + "_11",
+ ALIAS_PREFIX.to_owned() + "_12",
+ ALIAS_PREFIX.to_owned() + "_2",
+ ALIAS_PREFIX.to_owned() + "_21",
+ ALIAS_PREFIX.to_owned() + "_22",
+ ALIAS_PREFIX.to_owned() + "_3",
+ ALIAS_PREFIX.to_owned() + "_4",
+ ALIAS_PREFIX.to_owned() + "_5",
+ ],
+ );
+
+ verify_aliases(
+ &keystore2,
+ Some(ALIAS_PREFIX),
+ vec![
+ ALIAS_PREFIX.to_owned() + "_1",
+ ALIAS_PREFIX.to_owned() + "_10",
+ ALIAS_PREFIX.to_owned() + "_11",
+ ALIAS_PREFIX.to_owned() + "_12",
+ ALIAS_PREFIX.to_owned() + "_2",
+ ALIAS_PREFIX.to_owned() + "_21",
+ ALIAS_PREFIX.to_owned() + "_22",
+ ALIAS_PREFIX.to_owned() + "_3",
+ ALIAS_PREFIX.to_owned() + "_4",
+ ALIAS_PREFIX.to_owned() + "_5",
+ ],
+ );
+
+ verify_aliases(
+ &keystore2,
+ None,
+ vec![
+ ALIAS_PREFIX.to_owned() + "_1",
+ ALIAS_PREFIX.to_owned() + "_10",
+ ALIAS_PREFIX.to_owned() + "_11",
+ ALIAS_PREFIX.to_owned() + "_12",
+ ALIAS_PREFIX.to_owned() + "_2",
+ ALIAS_PREFIX.to_owned() + "_21",
+ ALIAS_PREFIX.to_owned() + "_22",
+ ALIAS_PREFIX.to_owned() + "_3",
+ ALIAS_PREFIX.to_owned() + "_4",
+ ALIAS_PREFIX.to_owned() + "_5",
+ ],
+ );
+ })
+ };
+}
+
+/// Try to list the key entries with domain SELINUX from user context where user doesn't possesses
+/// `GET_INFO` permission for specified namespace. Test should fail to list key entries with error
+/// response code `PERMISSION_DENIED`.
+#[test]
+fn keystore2_list_entries_batched_fails_perm_denied() {
+ let auid = 91 * AID_USER_OFFSET + 10001;
+ let agid = 91 * AID_USER_OFFSET + 10001;
+ static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.listEntriesBatched(
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ })
+ };
+}
+
+/// Try to list key entries with domain BLOB. Test should fail with error response code
+/// `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_list_entries_batched_fails_invalid_arg() {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(keystore2.listEntriesBatched(
+ Domain::BLOB,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ None,
+ ));
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
+
+/// Try to get the number of key entries with domain SELINUX from user context where user doesn't
+/// possesses `GET_INFO` permission for specified namespace. Test should fail to list key entries
+/// with error response code `PERMISSION_DENIED`.
+#[test]
+fn keystore2_get_number_of_entries_fails_perm_denied() {
+ let auid = 91 * AID_USER_OFFSET + 10001;
+ let agid = 91 * AID_USER_OFFSET + 10001;
+ static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
+
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe {
+ run_as::run_as(TARGET_CTX, Uid::from_raw(auid), Gid::from_raw(agid), move || {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(
+ keystore2
+ .getNumberOfEntries(Domain::SELINUX, key_generations::SELINUX_SHELL_NAMESPACE),
+ );
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::PERMISSION_DENIED), result.unwrap_err());
+ })
+ };
+}
+
+/// Try to get number of key entries with domain BLOB. Test should fail with error response code
+/// `INVALID_ARGUMENT`.
+#[test]
+fn keystore2_get_number_of_entries_fails_invalid_arg() {
+ let keystore2 = get_keystore_service();
+
+ let result = key_generations::map_ks_error(
+ keystore2.getNumberOfEntries(Domain::BLOB, key_generations::SELINUX_SHELL_NAMESPACE),
+ );
+ assert!(result.is_err());
+ assert_eq!(Error::Rc(ResponseCode::INVALID_ARGUMENT), result.unwrap_err());
+}
diff --git a/keystore2/tests/keystore2_client_operation_tests.rs b/keystore2/tests/keystore2_client_operation_tests.rs
index 97149004..89b5a319 100644
--- a/keystore2/tests/keystore2_client_operation_tests.rs
+++ b/keystore2/tests/keystore2_client_operation_tests.rs
@@ -36,7 +36,11 @@ use crate::keystore2_client_test_utils::{
/// Create `max_ops` number child processes with the given context and perform an operation under each
/// child process.
-pub fn create_operations(
+///
+/// # Safety
+///
+/// Must be called from a process with no other threads.
+pub unsafe fn create_operations(
target_ctx: &'static str,
forced_op: ForcedOp,
max_ops: i32,
@@ -45,8 +49,8 @@ pub fn create_operations(
let base_gid = 99 * AID_USER_OFFSET + 10001;
let base_uid = 99 * AID_USER_OFFSET + 10001;
(0..max_ops)
- .into_iter()
- .map(|i| {
+ // SAFETY: The caller guarantees that there are no other threads.
+ .map(|i| unsafe {
execute_op_run_as_child(
target_ctx,
Domain::APP,
@@ -88,7 +92,8 @@ fn keystore2_backend_busy_test() {
const MAX_OPS: i32 = 100;
static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
- let mut child_handles = create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS);
+ // SAFETY: The test is run in a separate process with no other threads.
+ let mut child_handles = unsafe { create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS) };
// Wait until all child procs notifies us to continue,
// so that there are definitely enough operations outstanding to trigger a BACKEND_BUSY.
@@ -121,7 +126,8 @@ fn keystore2_forced_op_after_backendbusy_test() {
static TARGET_CTX: &str = "u:r:untrusted_app:s0:c91,c256,c10,c20";
// Create regular operations.
- let mut child_handles = create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS);
+ // SAFETY: The test is run in a separate process with no other threads.
+ let mut child_handles = unsafe { create_operations(TARGET_CTX, ForcedOp(false), MAX_OPS) };
// Wait until all child procs notifies us to continue, so that there are enough
// operations outstanding to trigger a BACKEND_BUSY.
@@ -132,6 +138,7 @@ fn keystore2_forced_op_after_backendbusy_test() {
// Create a forced operation.
let auid = 99 * AID_USER_OFFSET + 10604;
let agid = 99 * AID_USER_OFFSET + 10604;
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
key_generations::TARGET_VOLD_CTX,
@@ -204,15 +211,18 @@ fn keystore2_max_forced_ops_test() {
// Create initial forced operation in a child process
// and wait for the parent to notify to perform operation.
let alias = format!("ks_forced_op_key_{}", getuid());
- let mut first_op_handle = execute_op_run_as_child(
- key_generations::TARGET_SU_CTX,
- Domain::SELINUX,
- key_generations::SELINUX_SHELL_NAMESPACE,
- Some(alias),
- Uid::from_raw(auid),
- Gid::from_raw(agid),
- ForcedOp(true),
- );
+ // SAFETY: The test is run in a separate process with no other threads.
+ let mut first_op_handle = unsafe {
+ execute_op_run_as_child(
+ key_generations::TARGET_SU_CTX,
+ Domain::SELINUX,
+ key_generations::SELINUX_SHELL_NAMESPACE,
+ Some(alias),
+ Uid::from_raw(auid),
+ Gid::from_raw(agid),
+ ForcedOp(true),
+ )
+ };
// Wait until above child proc notifies us to continue, so that there is definitely a forced
// operation outstanding to perform a operation.
@@ -220,7 +230,8 @@ fn keystore2_max_forced_ops_test() {
// Create MAX_OPS number of forced operations.
let mut child_handles =
- create_operations(key_generations::TARGET_SU_CTX, ForcedOp(true), MAX_OPS);
+ // SAFETY: The test is run in a separate process with no other threads.
+ unsafe { create_operations(key_generations::TARGET_SU_CTX, ForcedOp(true), MAX_OPS) };
// Wait until all child procs notifies us to continue, so that there are enough operations
// outstanding to trigger a BACKEND_BUSY.
@@ -283,15 +294,18 @@ fn keystore2_ops_prune_test() {
// Create an operation in an untrusted_app context. Wait until the parent notifies to continue.
// Once the parent notifies, this operation is expected to be completed successfully.
let alias = format!("ks_reg_op_key_{}", getuid());
- let mut child_handle = execute_op_run_as_child(
- TARGET_CTX,
- Domain::APP,
- -1,
- Some(alias),
- Uid::from_raw(uid),
- Gid::from_raw(gid),
- ForcedOp(false),
- );
+ // SAFETY: The test is run in a separate process with no other threads.
+ let mut child_handle = unsafe {
+ execute_op_run_as_child(
+ TARGET_CTX,
+ Domain::APP,
+ -1,
+ Some(alias),
+ Uid::from_raw(uid),
+ Gid::from_raw(gid),
+ ForcedOp(false),
+ )
+ };
// Wait until child process notifies us to continue, so that an operation from child process is
// outstanding to complete the operation.
@@ -312,7 +326,6 @@ fn keystore2_ops_prune_test() {
// Create multiple operations in this process to trigger cannibalizing sibling operations.
let mut ops: Vec<binder::Result<CreateOperationResponse>> = (0..MAX_OPS)
- .into_iter()
.map(|_| {
sec_level.createOperation(
&key_metadata.key,
@@ -379,6 +392,7 @@ fn keystore2_forced_op_perm_denied_test() {
let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
for context in TARGET_CTXS.iter() {
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(context, Uid::from_raw(uid), Gid::from_raw(gid), move || {
let alias = format!("ks_app_forced_op_test_key_{}", getuid());
@@ -408,6 +422,7 @@ fn keystore2_forced_op_success_test() {
let uid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
let gid = USER_ID * AID_USER_OFFSET + APPLICATION_ID;
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(TARGET_CTX, Uid::from_raw(uid), Gid::from_raw(gid), move || {
let alias = format!("ks_vold_forced_op_key_{}", getuid());
diff --git a/keystore2/tests/keystore2_client_test_utils.rs b/keystore2/tests/keystore2_client_test_utils.rs
index 58e6b7d3..7534da3a 100644
--- a/keystore2/tests/keystore2_client_test_utils.rs
+++ b/keystore2/tests/keystore2_client_test_utils.rs
@@ -15,9 +15,14 @@
use nix::unistd::{Gid, Uid};
use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
+use std::process::{Command, Output};
+
+use openssl::bn::BigNum;
use openssl::encrypt::Encrypter;
use openssl::error::ErrorStack;
use openssl::hash::MessageDigest;
+use openssl::nid::Nid;
use openssl::pkey::PKey;
use openssl::pkey::Public;
use openssl::rsa::Padding;
@@ -66,6 +71,7 @@ pub const SAMPLE_PLAIN_TEXT: &[u8] = b"my message 11111";
pub const PACKAGE_MANAGER_NATIVE_SERVICE: &str = "package_native";
pub const APP_ATTEST_KEY_FEATURE: &str = "android.hardware.keystore.app_attest_key";
+pub const DEVICE_ID_ATTESTATION_FEATURE: &str = "android.software.device_id_attestation";
/// Determines whether app_attest_key_feature is supported or not.
pub fn app_attest_key_feature_exists() -> bool {
@@ -75,6 +81,27 @@ pub fn app_attest_key_feature_exists() -> bool {
pm.hasSystemFeature(APP_ATTEST_KEY_FEATURE, 0).expect("hasSystemFeature failed.")
}
+/// Determines whether device_id_attestation is supported or not.
+pub fn device_id_attestation_feature_exists() -> bool {
+ let pm = wait_for_interface::<dyn IPackageManagerNative>(PACKAGE_MANAGER_NATIVE_SERVICE)
+ .expect("Failed to get package manager native service.");
+
+ pm.hasSystemFeature(DEVICE_ID_ATTESTATION_FEATURE, 0).expect("hasSystemFeature failed.")
+}
+
+/// Determines whether to skip device id attestation tests on GSI build with API level < 34.
+pub fn skip_device_id_attest_tests() -> bool {
+ // b/298586194, there are some devices launched with Android T, and they will be receiving
+ // only system update and not vendor update, newly added attestation properties
+ // (ro.product.*_for_attestation) reading logic would not be available for such devices
+ // hence skipping this test for such scenario.
+
+ // This file is only present on GSI builds.
+ let gsi_marker = PathBuf::from("/system/system_ext/etc/init/init.gsi.rc");
+
+ get_vsr_api_level() < 34 && gsi_marker.as_path().is_file()
+}
+
#[macro_export]
macro_rules! skip_test_if_no_app_attest_key_feature {
() => {
@@ -84,10 +111,31 @@ macro_rules! skip_test_if_no_app_attest_key_feature {
};
}
-/// Indicate whether the default device is KeyMint (rather than Keymaster).
-pub fn has_default_keymint() -> bool {
- binder::is_declared("android.hardware.security.keymint.IKeyMintDevice/default")
- .expect("Could not check for declared keymint interface")
+#[macro_export]
+macro_rules! skip_test_if_no_device_id_attestation_feature {
+ () => {
+ if !device_id_attestation_feature_exists() {
+ return;
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! skip_device_id_attestation_tests {
+ () => {
+ if skip_device_id_attest_tests() {
+ return;
+ }
+ };
+}
+
+#[macro_export]
+macro_rules! skip_tests_if_keymaster_impl_present {
+ () => {
+ if !key_generations::has_default_keymint() {
+ return;
+ }
+ };
}
/// Generate EC key and grant it to the list of users with given access vector.
@@ -246,7 +294,11 @@ pub fn perform_sample_asym_sign_verify_op(
}
/// Create new operation on child proc and perform simple operation after parent notification.
-pub fn execute_op_run_as_child(
+///
+/// # Safety
+///
+/// Must only be called from a single-threaded process.
+pub unsafe fn execute_op_run_as_child(
target_ctx: &'static str,
domain: Domain,
nspace: i64,
@@ -255,6 +307,7 @@ pub fn execute_op_run_as_child(
agid: Gid,
forced_op: ForcedOp,
) -> run_as::ChildHandle<TestOutcome, BarrierReached> {
+ // SAFETY: The caller guarantees that there are no other threads.
unsafe {
run_as::run_as_child(target_ctx, auid, agid, move |reader, writer| {
let result = key_generations::map_ks_error(create_signing_operation(
@@ -376,6 +429,17 @@ pub fn delete_app_key(
})
}
+/// Deletes all entries from keystore.
+pub fn delete_all_entries(keystore2: &binder::Strong<dyn IKeystoreService>) {
+ while keystore2.getNumberOfEntries(Domain::APP, -1).unwrap() != 0 {
+ let key_descriptors = keystore2.listEntries(Domain::APP, -1).unwrap();
+ key_descriptors.into_iter().map(|key| key.alias.unwrap()).for_each(|alias| {
+ delete_app_key(keystore2, &alias).unwrap();
+ });
+ }
+ assert!(keystore2.getNumberOfEntries(Domain::APP, -1).unwrap() == 0);
+}
+
/// Encrypt the secure key with given transport key.
pub fn encrypt_secure_key(
sec_level: &binder::Strong<dyn IKeystoreSecurityLevel>,
@@ -417,3 +481,126 @@ pub fn encrypt_transport_key(
Ok(encoded.to_vec())
}
+
+/// List aliases using given `startingPastAlias` and verify that the fetched list is matching with
+/// the expected list of aliases.
+pub fn verify_aliases(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+ starting_past_alias: Option<&str>,
+ expected_aliases: Vec<String>,
+) {
+ let key_descriptors =
+ keystore2.listEntriesBatched(Domain::APP, -1, starting_past_alias).unwrap();
+
+ assert_eq!(key_descriptors.len(), expected_aliases.len());
+ assert!(key_descriptors
+ .iter()
+ .all(|key| expected_aliases.contains(key.alias.as_ref().unwrap())));
+}
+
+// Get the value of the given system property, if the given system property doesn't exist
+// then returns an empty byte vector.
+pub fn get_system_prop(name: &str) -> Vec<u8> {
+ match rustutils::system_properties::read(name) {
+ Ok(Some(value)) => {
+ return value.as_bytes().to_vec();
+ }
+ _ => {
+ vec![]
+ }
+ }
+}
+
+fn get_integer_system_prop(name: &str) -> Option<i32> {
+ let val = get_system_prop(name);
+ if val.is_empty() {
+ return None;
+ }
+ let val = std::str::from_utf8(&val).ok()?;
+ val.parse::<i32>().ok()
+}
+
+pub fn get_vsr_api_level() -> i32 {
+ if let Some(api_level) = get_integer_system_prop("ro.vendor.api_level") {
+ return api_level;
+ }
+
+ let vendor_api_level = get_integer_system_prop("ro.board.api_level")
+ .or_else(|| get_integer_system_prop("ro.board.first_api_level"));
+ let product_api_level = get_integer_system_prop("ro.product.first_api_level")
+ .or_else(|| get_integer_system_prop("ro.build.version.sdk"));
+
+ match (vendor_api_level, product_api_level) {
+ (Some(v), Some(p)) => std::cmp::min(v, p),
+ (Some(v), None) => v,
+ (None, Some(p)) => p,
+ _ => panic!("Could not determine VSR API level"),
+ }
+}
+
+/// Determines whether the SECOND-IMEI can be used as device attest-id.
+pub fn is_second_imei_id_attestation_required(
+ keystore2: &binder::Strong<dyn IKeystoreService>,
+) -> bool {
+ keystore2.getInterfaceVersion().unwrap() >= 3 && get_vsr_api_level() > 33
+}
+
+/// Run a service command and collect the output.
+pub fn run_service_command(command: &[&str]) -> std::io::Result<Output> {
+ Command::new("cmd").args(command).output()
+}
+
+/// Get IMEI from telephony service.
+pub fn get_imei(slot: i32) -> Option<Vec<u8>> {
+ let mut cmd = vec!["phone", "get-imei"];
+ let slot_str = slot.to_string();
+ cmd.push(slot_str.as_str());
+ let output = run_service_command(&cmd).unwrap();
+ if output.status.success() {
+ let stdout = String::from_utf8(output.stdout).unwrap();
+ let mut split_out = stdout.split_whitespace();
+ let imei = split_out.next_back().unwrap();
+ if imei == "null" {
+ return None;
+ }
+ return Some(imei.as_bytes().to_vec());
+ }
+
+ None
+}
+
+/// Get value of the given attestation id.
+pub fn get_attest_id_value(attest_id: Tag, prop_name: &str) -> Option<Vec<u8>> {
+ match attest_id {
+ Tag::ATTESTATION_ID_IMEI => get_imei(0),
+ Tag::ATTESTATION_ID_SECOND_IMEI => get_imei(1),
+ Tag::ATTESTATION_ID_SERIAL => Some(get_system_prop(format!("ro.{}", prop_name).as_str())),
+ _ => {
+ let prop_val =
+ get_system_prop(format!("ro.product.{}_for_attestation", prop_name).as_str());
+ if !prop_val.is_empty() {
+ Some(prop_val)
+ } else {
+ let prop_val = get_system_prop(format!("ro.product.vendor.{}", prop_name).as_str());
+ if !prop_val.is_empty() {
+ Some(prop_val)
+ } else {
+ Some(get_system_prop(format!("ro.product.{}", prop_name).as_str()))
+ }
+ }
+ }
+ }
+}
+
+pub fn verify_certificate_subject_name(cert_bytes: &[u8], expected_subject: &[u8]) {
+ let cert = X509::from_der(cert_bytes).unwrap();
+ let subject = cert.subject_name();
+ let cn = subject.entries_by_nid(Nid::COMMONNAME).next().unwrap();
+ assert_eq!(cn.data().as_slice(), expected_subject);
+}
+
+pub fn verify_certificate_serial_num(cert_bytes: &[u8], expected_serial_num: &BigNum) {
+ let cert = X509::from_der(cert_bytes).unwrap();
+ let serial_num = cert.serial_number();
+ assert_eq!(serial_num.to_bn().as_ref().unwrap(), expected_serial_num);
+}
diff --git a/keystore2/tests/keystore2_client_tests.rs b/keystore2/tests/keystore2_client_tests.rs
index 07a298a8..a0c140a0 100644
--- a/keystore2/tests/keystore2_client_tests.rs
+++ b/keystore2/tests/keystore2_client_tests.rs
@@ -12,17 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-pub mod ffi_test_utils;
pub mod keystore2_client_3des_key_tests;
pub mod keystore2_client_aes_key_tests;
pub mod keystore2_client_attest_key_tests;
+pub mod keystore2_client_authorizations_tests;
pub mod keystore2_client_delete_key_tests;
+pub mod keystore2_client_device_unique_attestation_tests;
pub mod keystore2_client_ec_key_tests;
pub mod keystore2_client_grant_key_tests;
pub mod keystore2_client_hmac_key_tests;
pub mod keystore2_client_import_keys_tests;
pub mod keystore2_client_key_agreement_tests;
pub mod keystore2_client_key_id_domain_tests;
+pub mod keystore2_client_keystore_engine_tests;
pub mod keystore2_client_list_entries_tests;
pub mod keystore2_client_operation_tests;
pub mod keystore2_client_rsa_key_tests;
diff --git a/keystore2/tests/keystore2_client_update_subcomponent_tests.rs b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
index 0be092f8..d9576a84 100644
--- a/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
+++ b/keystore2/tests/keystore2_client_update_subcomponent_tests.rs
@@ -167,6 +167,7 @@ fn keystore2_update_subcomponent_fails_permission_denied() {
static GRANTEE_2_GID: u32 = GRANTEE_2_UID;
// Generate a key and grant it to multiple users with different access permissions.
+ // SAFETY: The test is run in a separate process with no other threads.
let mut granted_keys = unsafe {
run_as::run_as(GRANTOR_SU_CTX, Uid::from_raw(0), Gid::from_raw(0), || {
let keystore2 = get_keystore_service();
@@ -205,6 +206,7 @@ fn keystore2_update_subcomponent_fails_permission_denied() {
// Grantee context, try to update the key public certs, permission denied error is expected.
let granted_key1_nspace = granted_keys.remove(0);
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
@@ -234,6 +236,7 @@ fn keystore2_update_subcomponent_fails_permission_denied() {
// Grantee context, update granted key public certs. Update should happen successfully.
let granted_key2_nspace = granted_keys.remove(0);
+ // SAFETY: The test is run in a separate process with no other threads.
unsafe {
run_as::run_as(
GRANTEE_CTX,
diff --git a/keystore2/tests/legacy_blobs/Android.bp b/keystore2/tests/legacy_blobs/Android.bp
index 92f2cc34..0f310f51 100644
--- a/keystore2/tests/legacy_blobs/Android.bp
+++ b/keystore2/tests/legacy_blobs/Android.bp
@@ -31,17 +31,17 @@ rust_test {
test_config: "AndroidTest.xml",
rustlibs: [
- "libkeystore2_with_test_utils",
- "libkeystore2_crypto_rust",
- "android.security.maintenance-rust",
"android.security.authorization-rust",
- "librustutils",
- "libkeystore2_test_utils",
- "libnix",
+ "android.security.maintenance-rust",
"libanyhow",
"libbinder_rs",
+ "libkeystore2_crypto_rust",
+ "libkeystore2_test_utils",
+ "libkeystore2_with_test_utils",
"liblazy_static",
"liblibc",
+ "libnix",
+ "librustutils",
"libserde",
"libthiserror",
],
diff --git a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
index 63122fe0..3be99ee3 100644
--- a/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
+++ b/keystore2/tests/legacy_blobs/keystore2_legacy_blob_tests.rs
@@ -25,12 +25,10 @@ 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_maintenance::aidl::android::security::maintenance::IKeystoreMaintenance::IKeystoreMaintenance;
use android_security_authorization::aidl::android::security::authorization::{
- IKeystoreAuthorization::IKeystoreAuthorization, LockScreenEvent::LockScreenEvent,
+ IKeystoreAuthorization::IKeystoreAuthorization,
};
use keystore2::key_parameter::KeyParameter as KsKeyparameter;
@@ -48,6 +46,10 @@ static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
static AUTH_SERVICE_NAME: &str = "android.security.authorization";
const SELINUX_SHELL_NAMESPACE: i64 = 1;
+fn rkp_only() -> bool {
+ matches!(rustutils::system_properties::read("remote_provisioning.tee.rkp_only"), Ok(Some(v)) if v == "1")
+}
+
fn get_maintenance() -> binder::Strong<dyn IKeystoreMaintenance> {
binder::get_interface(USER_MANAGER_SERVICE_NAME).unwrap()
}
@@ -164,19 +166,19 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
.getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
.unwrap();
// Generate Key BLOB and prepare legacy keystore blob files.
- let att_challenge: &[u8] = b"foo";
+ let att_challenge: Option<&[u8]> = if rkp_only() { None } else { Some(b"foo") };
let key_metadata = key_generations::generate_ec_p256_signing_key(
&sec_level,
Domain::BLOB,
SELINUX_SHELL_NAMESPACE,
None,
- Some(att_challenge),
+ att_challenge,
)
.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(SUPERKEY_SALT, 32).unwrap());
+ let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
let super_key =
TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
@@ -214,14 +216,12 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
.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();
+ if let Some(chain) = key_metadata.certificateChain.as_ref() {
+ 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(), chain).unwrap();
+ }
}
// Keystore2 disables the legacy importer when it finds the legacy database empty.
@@ -231,8 +231,7 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
keystore2_restart_service();
let auth_service = get_authorization();
- match auth_service.onLockScreenEvent(LockScreenEvent::UNLOCK, 99, Some(PASSWORD), None)
- {
+ match auth_service.onDeviceUnlocked(99, Some(PASSWORD)) {
Ok(result) => {
println!("Unlock Result: {:?}", result);
}
@@ -241,9 +240,6 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
}
}
- 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);
@@ -252,7 +248,7 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
KeygenResult {
cert: key_metadata.certificate.unwrap(),
- cert_chain: key_metadata.certificateChain.unwrap(),
+ cert_chain: key_metadata.certificateChain.unwrap_or_default(),
key_parameters: key_params,
}
})
@@ -281,7 +277,7 @@ fn keystore2_encrypted_characteristics() -> anyhow::Result<()> {
gen_key_result.cert
);
assert_eq!(
- key_entry_response.metadata.certificateChain.unwrap(),
+ key_entry_response.metadata.certificateChain.unwrap_or_default(),
gen_key_result.cert_chain
);
assert_eq!(key_entry_response.metadata.key.domain, Domain::KEY_ID);
@@ -421,19 +417,19 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
.getSecurityLevel(SecurityLevel::SecurityLevel::TRUSTED_ENVIRONMENT)
.unwrap();
// Generate Key BLOB and prepare legacy keystore blob files.
- let att_challenge: &[u8] = b"foo";
+ let att_challenge: Option<&[u8]> = if rkp_only() { None } else { Some(b"foo") };
let key_metadata = key_generations::generate_ec_p256_signing_key(
&sec_level,
Domain::BLOB,
SELINUX_SHELL_NAMESPACE,
None,
- Some(att_challenge),
+ att_challenge,
)
.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(SUPERKEY_SALT, 32).unwrap());
+ let pw_key = TestKey(pw.derive_key_pbkdf2(SUPERKEY_SALT, 32).unwrap());
let super_key =
TestKey(pw_key.decrypt(SUPERKEY_PAYLOAD, SUPERKEY_IV, SUPERKEY_TAG).unwrap());
@@ -474,15 +470,12 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
.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();
+ if let Some(chain) = key_metadata.certificateChain.as_ref() {
+ 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, chain).unwrap();
+ }
}
// Keystore2 disables the legacy importer when it finds the legacy database empty.
@@ -492,8 +485,7 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
keystore2_restart_service();
let auth_service = get_authorization();
- match auth_service.onLockScreenEvent(LockScreenEvent::UNLOCK, 98, Some(PASSWORD), None)
- {
+ match auth_service.onDeviceUnlocked(98, Some(PASSWORD)) {
Ok(result) => {
println!("Unlock Result: {:?}", result);
}
@@ -502,9 +494,6 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
}
}
- 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);
@@ -513,7 +502,7 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
KeygenResult {
cert: key_metadata.certificate.unwrap(),
- cert_chain: key_metadata.certificateChain.unwrap(),
+ cert_chain: key_metadata.certificateChain.unwrap_or_default(),
key_parameters: key_params,
}
})
@@ -542,7 +531,7 @@ fn keystore2_encrypted_certificates() -> anyhow::Result<()> {
gen_key_result.cert
);
assert_eq!(
- key_entry_response.metadata.certificateChain.unwrap(),
+ key_entry_response.metadata.certificateChain.unwrap_or_default(),
gen_key_result.cert_chain
);
diff --git a/keystore2/watchdog/Android.bp b/keystore2/watchdog/Android.bp
new file mode 100644
index 00000000..5074388c
--- /dev/null
+++ b/keystore2/watchdog/Android.bp
@@ -0,0 +1,49 @@
+// Copyright 2023, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+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_defaults {
+ name: "libwatchdog_defaults",
+ crate_name: "watchdog_rs",
+ srcs: ["src/lib.rs"],
+ rustlibs: [
+ "liblog_rust",
+ ],
+}
+
+rust_library {
+ name: "libwatchdog_rs",
+ defaults: ["libwatchdog_defaults"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.virt",
+ ],
+}
+
+rust_test {
+ name: "libwatchdog_rs.test",
+ defaults: ["libwatchdog_defaults"],
+ test_suites: ["general-tests"],
+ rustlibs: [
+ "libandroid_logger",
+ ],
+}
diff --git a/keystore2/src/watchdog.rs b/keystore2/watchdog/src/lib.rs
index 01043c55..fa4620a8 100644
--- a/keystore2/src/watchdog.rs
+++ b/keystore2/watchdog/src/lib.rs
@@ -335,7 +335,7 @@ mod tests {
android_logger::init_once(
android_logger::Config::default()
.with_tag("keystore2_watchdog_tests")
- .with_min_level(log::Level::Debug),
+ .with_max_level(log::LevelFilter::Debug),
);
let wd = Watchdog::new(Watchdog::NOISY_REPORT_TIMEOUT.checked_mul(3).unwrap());
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index f56cfab3..6901b174 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -142,6 +142,8 @@ cc_binary {
"libfsverity",
"liblogwrap",
"libprotobuf-cpp-lite",
+ "libstatspull",
+ "libstatssocket",
"libutils",
],
}
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
index 8fe0816c..bb2da5a1 100644
--- a/ondevice-signing/CertUtils.cpp
+++ b/ondevice-signing/CertUtils.cpp
@@ -21,12 +21,10 @@
#include <openssl/bn.h>
#include <openssl/crypto.h>
-#include <openssl/pkcs7.h>
#include <openssl/rsa.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
-#include <optional>
#include <vector>
#include "KeyConstants.h"
@@ -56,12 +54,6 @@ static Result<bssl::UniquePtr<X509>> loadX509(const std::string& path) {
return cert;
}
-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));
if (!ex) {
@@ -93,20 +85,6 @@ static Result<bssl::UniquePtr<RSA>> getRsaFromModulus(const std::vector<uint8_t>
return rsaPubkey;
}
-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.
@@ -122,21 +100,6 @@ static Result<bssl::UniquePtr<EVP_PKEY>> modulusToRsaPkey(const std::vector<uint
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;
-}
-
Result<void> verifySignature(const std::string& message, const std::string& signature,
const std::vector<uint8_t>& publicKey) {
auto rsaKey = getRsaFromModulus(publicKey);
@@ -156,34 +119,14 @@ Result<void> verifySignature(const std::string& message, const std::string& sign
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";
+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 {};
-}
-
-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;
-
bssl::UniquePtr<X509> x509(X509_new());
if (!x509) {
return Error() << "Unable to allocate x509 container";
@@ -191,7 +134,7 @@ static Result<void> createCertificate(
X509_set_version(x509.get(), 2);
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);
+ ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), kRootSubject.serialNumber);
bssl::UniquePtr<X509_ALGOR> algor(X509_ALGOR_new());
if (!algor ||
@@ -201,7 +144,7 @@ static Result<void> createCertificate(
return Error() << "Unable to set x509 signature algorithm";
}
- if (!X509_set_pubkey(x509.get(), publicKey)) {
+ if (!X509_set_pubkey(x509.get(), rsa_pkey.value().get())) {
return Error() << "Unable to set x509 public key";
}
@@ -211,44 +154,15 @@ static Result<void> createCertificate(
}
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());
+ addNameEntry(subjectName, "CN", kRootSubject.commonName);
+ if (!X509_set_issuer_name(x509.get(), subjectName)) {
+ 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(&context, x509.get(), NID_basic_constraints, basicConstraints);
- add_ext(&context, x509.get(), NID_key_usage, keyUsage);
+ X509V3_CTX context = {};
+ X509V3_set_ctx(&context, x509.get(), x509.get(), nullptr, nullptr, 0);
+ add_ext(&context, x509.get(), NID_basic_constraints, "CA:TRUE");
+ add_ext(&context, x509.get(), NID_key_usage, "critical,keyCertSign,cRLSign,digitalSignature");
add_ext(&context, x509.get(), NID_subject_key_identifier, "hash");
add_ext(&context, x509.get(), NID_authority_key_identifier, "keyid:always");
@@ -280,31 +194,7 @@ static Result<void> createCertificate(
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) {
+static Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
if (pkey == nullptr) {
return Error() << "Failed to extract public key from x509 cert";
}
@@ -325,14 +215,6 @@ Result<std::vector<uint8_t>> extractPublicKey(EVP_PKEY* pkey) {
return pubKey;
}
-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()));
-
- return extractPublicKey(public_key.get());
-}
-
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()));
@@ -351,121 +233,3 @@ Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path) {
}
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();
- }
-
- // 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";
- }
-
- 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;
-}
-
-Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest,
- const CertSubject& signer) {
- 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;
- size_t pkcs7_data_len, name_der_len;
- 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_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);
- CBB_init(&out, 1024);
-
- if (!CBB_add_asn1(&out, &outer_seq, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&outer_seq, NID_pkcs7_signed) ||
- !CBB_add_asn1(&outer_seq, &wrapped_seq,
- CBS_ASN1_CONTEXT_SPECIFIC | CBS_ASN1_CONSTRUCTED | 0) ||
- // See https://tools.ietf.org/html/rfc2315#section-9.1
- !CBB_add_asn1(&wrapped_seq, &seq, CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1_uint64(&seq, 1 /* version */) ||
- !CBB_add_asn1(&seq, &digest_algos_set, CBS_ASN1_SET) ||
- !CBB_add_asn1(&digest_algos_set, &digest_algo, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&digest_algo, NID_sha256) ||
- !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
- !CBB_add_asn1(&seq, &content_info, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&content_info, NID_pkcs7_data) ||
- !CBB_add_asn1(&seq, &signer_infos, CBS_ASN1_SET) ||
- !CBB_add_asn1(&signer_infos, &signer_info, CBS_ASN1_SEQUENCE) ||
- !CBB_add_asn1_uint64(&signer_info, 1 /* version */) ||
- !CBB_add_asn1(&signer_info, &issuer_and_serial, CBS_ASN1_SEQUENCE) ||
- !CBB_add_bytes(&issuer_and_serial, name_der, name_der_len) ||
- !BN_marshal_asn1(&issuer_and_serial, serial) ||
- !CBB_add_asn1(&signer_info, &digest_algo, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&digest_algo, NID_sha256) ||
- !CBB_add_asn1(&digest_algo, &null, CBS_ASN1_NULL) ||
- !CBB_add_asn1(&signer_info, &sign_algo, CBS_ASN1_SEQUENCE) ||
- !OBJ_nid2cbb(&sign_algo, sig_nid) || !CBB_add_asn1(&sign_algo, &null, CBS_ASN1_NULL) ||
- !CBB_add_asn1(&signer_info, &signature, CBS_ASN1_OCTETSTRING) ||
- !CBB_add_bytes(&signature, signed_digest.data(), signed_digest.size()) ||
- !CBB_finish(&out, &pkcs7_data, &pkcs7_data_len)) {
- return Error() << "Failed to create PKCS7 certificate.";
- }
-
- return std::vector<uint8_t>(&pkcs7_data[0], &pkcs7_data[pkcs7_data_len]);
-}
diff --git a/ondevice-signing/OWNERS b/ondevice-signing/OWNERS
index 72a8eb5f..f406fce3 100644
--- a/ondevice-signing/OWNERS
+++ b/ondevice-signing/OWNERS
@@ -1,3 +1,3 @@
maco@google.com
ngeoffray@google.com
-oth@google.com
+jiakaiz@google.com
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 0b631daa..43de67cb 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -19,12 +19,12 @@
#include <map>
#include <span>
#include <string>
+#include <vector>
#include <fcntl.h>
#include <linux/fs.h>
#include <sys/stat.h>
#include <sys/types.h>
-#include <sys/wait.h>
#include "android-base/errors.h"
#include <android-base/file.h>
@@ -42,7 +42,6 @@ using android::base::Error;
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() {
@@ -285,41 +284,3 @@ Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
return {};
}
-
-Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName) {
- const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", keyName};
-
- 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);
- close(fd);
- int argc = arraysize(argv);
- char* argv_child[argc + 1];
- memcpy(argv_child, argv, argc * sizeof(char*));
- argv_child[argc] = nullptr;
- execvp(argv_child[0], argv_child);
- PLOG(ERROR) << "exec in ForkExecvp";
- _exit(EXIT_FAILURE);
- } else {
- close(fd);
- }
- if (pid == -1) {
- return ErrnoError() << "Failed to fork.";
- }
- int status;
- if (waitpid(pid, &status, 0) == -1) {
- return ErrnoError() << "waitpid() failed.";
- }
- if (!WIFEXITED(status)) {
- return Error() << kFsVerityInitPath << ": abnormal process exit";
- }
- if (WEXITSTATUS(status) != 0) {
- return Error() << kFsVerityInitPath << " exited with " << WEXITSTATUS(status);
- }
-
- return {};
-}
diff --git a/ondevice-signing/include/CertUtils.h b/ondevice-signing/include/CertUtils.h
index fe703fa0..9b9d2cc4 100644
--- a/ondevice-signing/include/CertUtils.h
+++ b/ondevice-signing/include/CertUtils.h
@@ -34,39 +34,18 @@ struct CertSubject {
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.
+// This is our self-signed cert.
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>>
extractPublicKeyFromX509(const std::vector<uint8_t>& x509);
-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/include/VerityUtils.h b/ondevice-signing/include/VerityUtils.h
index 626bbdb4..71f78cf3 100644
--- a/ondevice-signing/include/VerityUtils.h
+++ b/ondevice-signing/include/VerityUtils.h
@@ -22,7 +22,6 @@
#include <string>
#include <vector>
-android::base::Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName);
android::base::Result<std::vector<uint8_t>> createDigest(const std::string& path);
android::base::Result<std::string> enableFsVerity(int fd);
bool SupportsFsVerity();
@@ -34,7 +33,7 @@ verifyAllFilesInVerity(const std::string& path);
android::base::Result<std::map<std::string, std::string>>
addFilesToVerityRecursive(const std::string& path);
-// Enable verity on the provided file, using the given PKCS7 signature.
+// Enable verity on the provided file.
android::base::Result<void> enableFsVerity(const std::string& path);
android::base::Result<void>
diff --git a/ondevice-signing/odsign.rc b/ondevice-signing/odsign.rc
index b96c62ff..b95cf9db 100644
--- a/ondevice-signing/odsign.rc
+++ b/ondevice-signing/odsign.rc
@@ -3,13 +3,10 @@ service odsign /system/bin/odsign
user root
group system
disabled # does not start with the core class
- # Explicitly specify empty capabilities, otherwise odsign will inherit all
- # the capabilities from init.
- # Note: whether a process can use capabilities is controlled by SELinux, so
- # inheriting all the capabilities from init is not a security issue.
- # However, for defense-in-depth and just for the sake of bookkeeping it's
- # better to explicitly state that odsign doesn't need any capabilities.
- capabilities
+ # We need SYS_NICE in order to allow the crosvm child process to use it.
+ # (b/322197421). odsign itself never uses it (and isn't allowed to by
+ # SELinux).
+ capabilities SYS_NICE
# Note that odsign is not oneshot, but stopped manually when it exits. This
# ensures that if odsign crashes during a module update, apexd will detect
diff --git a/ondevice-signing/tests/Android.bp b/ondevice-signing/tests/Android.bp
index 40272200..bcfe8e40 100644
--- a/ondevice-signing/tests/Android.bp
+++ b/ondevice-signing/tests/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_art_mainline",
// 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"
@@ -22,23 +23,23 @@ package {
}
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",
- ],
+ 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/prng_seeder/Android.bp b/prng_seeder/Android.bp
index 763aaa01..4f9b7e14 100644
--- a/prng_seeder/Android.bp
+++ b/prng_seeder/Android.bp
@@ -13,6 +13,7 @@
// limitations under the License.
package {
+ default_team: "trendy_team_java_core_libraries",
// See: http://go/android-license-faq
// SPDX-license-identifier-Apache-2.0
default_applicable_licenses: ["system_security_license"],
@@ -36,7 +37,7 @@ rust_defaults {
edition: "2021",
rustlibs: [
"libanyhow",
- "libbssl_ffi",
+ "libbssl_sys",
"libclap",
"libcutils_socket_bindgen",
"liblogger",
@@ -70,7 +71,7 @@ rust_test {
srcs: ["src/main.rs"],
rustlibs: [
"libanyhow",
- "libbssl_ffi",
+ "libbssl_sys",
"libclap",
"libcutils_socket_bindgen",
"liblogger",
diff --git a/prng_seeder/OWNERS b/prng_seeder/OWNERS
index 9202b90e..51b7f38c 100644
--- a/prng_seeder/OWNERS
+++ b/prng_seeder/OWNERS
@@ -1,2 +1,2 @@
paulcrowley@google.com
-prb@google.com \ No newline at end of file
+prb@google.com
diff --git a/prng_seeder/src/conditioner.rs b/prng_seeder/src/conditioner.rs
index eca8af88..ec1181bd 100644
--- a/prng_seeder/src/conditioner.rs
+++ b/prng_seeder/src/conditioner.rs
@@ -12,11 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use std::{fs::File, io::Read, os::unix::io::AsRawFd};
+use std::{fs::File, io::Read};
use anyhow::{ensure, Context, Result};
use log::debug;
-use nix::fcntl::{fcntl, FcntlArg::F_SETFL, OFlag};
use tokio::io::AsyncReadExt;
use crate::drbg;
@@ -34,8 +33,6 @@ impl ConditionerBuilder {
let mut et: drbg::Entropy = [0; drbg::ENTROPY_LEN];
hwrng.read_exact(&mut et).context("hwrng.read_exact in new")?;
let rg = drbg::Drbg::new(&et)?;
- fcntl(hwrng.as_raw_fd(), F_SETFL(OFlag::O_NONBLOCK))
- .context("setting O_NONBLOCK on hwrng")?;
Ok(ConditionerBuilder { hwrng, rg })
}
diff --git a/prng_seeder/src/cutils_socket.rs b/prng_seeder/src/cutils_socket.rs
index ab2c8698..b408be60 100644
--- a/prng_seeder/src/cutils_socket.rs
+++ b/prng_seeder/src/cutils_socket.rs
@@ -19,7 +19,11 @@ use anyhow::{ensure, Result};
pub fn android_get_control_socket(name: &str) -> Result<UnixListener> {
let name = CString::new(name)?;
+ // SAFETY: name is a valid C string, and android_get_control_socket doesn't retain it after it
+ // returns.
let fd = unsafe { cutils_socket_bindgen::android_get_control_socket(name.as_ptr()) };
ensure!(fd >= 0, "android_get_control_socket failed");
+ // SAFETY: android_get_control_socket either returns a valid and open FD or -1, and we checked
+ // that it's not -1.
Ok(unsafe { UnixListener::from_raw_fd(fd) })
}
diff --git a/prng_seeder/src/drbg.rs b/prng_seeder/src/drbg.rs
index 89c5a888..71903530 100644
--- a/prng_seeder/src/drbg.rs
+++ b/prng_seeder/src/drbg.rs
@@ -13,7 +13,6 @@
// limitations under the License.
use anyhow::{ensure, Result};
-use bssl_ffi as bssl_sys;
pub const ENTROPY_LEN: usize = bssl_sys::CTR_DRBG_ENTROPY_LEN as usize;
@@ -23,6 +22,9 @@ pub struct Drbg(*mut bssl_sys::CTR_DRBG_STATE);
impl Drbg {
pub fn new(entropy: &Entropy) -> Result<Drbg> {
+ // SAFETY: entropy must be a valid pointer because it comes from a reference, and a null
+ // pointer is allowed for personalization. CTR_DRBG_new doesn't retain the entropy pointer
+ // for use after it returns.
let p = unsafe { bssl_sys::CTR_DRBG_new(entropy.as_ptr(), std::ptr::null(), 0) };
ensure!(!p.is_null(), "CTR_DRBG_new failed");
Ok(Drbg(p))
@@ -30,6 +32,9 @@ impl Drbg {
pub fn reseed(&mut self, entropy: &Entropy) -> Result<()> {
ensure!(
+ // SAFETY: We know that self.0 is valid because it was initialised from CTR_DRBG_new in
+ // Drbg::new above. The entropy pointer must be valid because it comes from a reference,
+ // and CTR_DRBG_reseed doesn't retain it after it returns.
unsafe { bssl_sys::CTR_DRBG_reseed(self.0, entropy.as_ptr(), std::ptr::null(), 0) }
== 1,
"CTR_DRBG_reseed failed"
@@ -39,6 +44,10 @@ impl Drbg {
pub fn generate(&mut self, buf: &mut [u8]) -> Result<()> {
ensure!(
+ // SAFETY: We know that self.0 is valid because it was initialised from CTR_DRBG_new in
+ // Drbg::new above. The out pointer and length must be valid and unaliased because they
+ // come from a mutable slice reference, and CTR_DRBG_generate doesn't retain them after
+ // it returns.
unsafe {
bssl_sys::CTR_DRBG_generate(
self.0,
@@ -56,10 +65,13 @@ impl Drbg {
impl Drop for Drbg {
fn drop(&mut self) {
+ // SAFETY: We know that self.0 is valid because it was initialised from CTR_DRBG_new in
+ // Drbg::new above, and this is the only place that frees it.
unsafe {
bssl_sys::CTR_DRBG_free(self.0);
}
}
}
+// SAFETY: CTR_DRBG functions can be called from any thread.
unsafe impl Send for Drbg {}
diff --git a/prng_seeder/src/main.rs b/prng_seeder/src/main.rs
index 924481ac..cb7f38d7 100644
--- a/prng_seeder/src/main.rs
+++ b/prng_seeder/src/main.rs
@@ -31,7 +31,7 @@ use std::{
use anyhow::{ensure, Context, Result};
use clap::Parser;
-use log::{error, info, Level};
+use log::{error, info, LevelFilter};
use nix::sys::signal;
use tokio::{io::AsyncWriteExt, net::UnixListener as TokioUnixListener};
@@ -48,7 +48,9 @@ struct Cli {
fn configure_logging() -> Result<()> {
ensure!(
logger::init(
- logger::Config::default().with_tag_on_device("prng_seeder").with_min_level(Level::Info)
+ logger::Config::default()
+ .with_tag_on_device("prng_seeder")
+ .with_max_level(LevelFilter::Info)
),
"log configuration failed"
);
@@ -70,6 +72,7 @@ fn get_socket(path: &Path) -> Result<UnixListener> {
fn setup() -> Result<(ConditionerBuilder, UnixListener)> {
configure_logging()?;
let cli = Cli::try_parse()?;
+ // SAFETY: Nothing else sets the signal handler, so either it was set here or it is the default.
unsafe { signal::signal(signal::Signal::SIGPIPE, signal::SigHandler::SigIgn) }
.context("In setup, setting SIGPIPE to SIG_IGN")?;
diff --git a/provisioner/Android.bp b/provisioner/Android.bp
index b5489738..ede1ae6c 100644
--- a/provisioner/Android.bp
+++ b/provisioner/Android.bp
@@ -15,6 +15,7 @@
//
package {
+ default_team: "trendy_team_foundation_security_rust_pkvm_",
// 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"
@@ -23,44 +24,26 @@ package {
default_applicable_licenses: ["system_security_license"],
}
-aidl_interface {
- name: "android.security.provisioner",
- unstable: true,
- local_include_dir: "binder",
- srcs: [
- "binder/android/security/provisioner/*.aidl",
- ],
- backend: {
- java: {
- platform_apis: true,
- },
- cpp: {
- enabled: false,
- },
- ndk: {
- enabled: false,
- },
- },
-}
-
cc_defaults {
name: "rkp_factory_extraction_defaults",
defaults: [
"keymint_use_latest_hal_aidl_ndk_static",
],
shared_libs: [
- "libbinder",
"libbinder_ndk",
"libcrypto",
"liblog",
],
static_libs: [
+ "android.hardware.common-V2-ndk",
+ "android.hardware.drm-V1-ndk",
"android.hardware.security.rkp-V3-ndk",
"libbase",
- "libcppbor_external",
+ "libcppbor",
"libcppcose_rkp",
"libjsoncpp",
"libkeymint_remote_prov_support",
+ "libmediadrmrkp",
],
}
@@ -97,4 +80,39 @@ cc_binary {
"libgflags",
"librkp_factory_extraction",
],
+ dist: {
+ targets: [
+ "dist_files",
+ "rkp_factory_extraction_tool",
+ ],
+ dest: "rkp_factory_extraction_tool",
+ },
+ compile_multilib: "both",
+ multilib: {
+ lib64: {
+ suffix: "64",
+ },
+ },
+ target: {
+ android_arm: {
+ dist: {
+ dir: "rkp/arm",
+ },
+ },
+ android_arm64: {
+ dist: {
+ dir: "rkp/arm64",
+ },
+ },
+ android_x86: {
+ dist: {
+ dir: "rkp/x86",
+ },
+ },
+ android_x86_64: {
+ dist: {
+ dir: "rkp/x86_64",
+ },
+ },
+ },
}
diff --git a/provisioner/binder/android/security/provisioner/IProvisionerService.aidl b/provisioner/binder/android/security/provisioner/IProvisionerService.aidl
deleted file mode 100644
index f81e9abe..00000000
--- a/provisioner/binder/android/security/provisioner/IProvisionerService.aidl
+++ /dev/null
@@ -1,27 +0,0 @@
-/**
- * Copyright (c) 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.
- */
-
-package android.security.provisioner;
-
-/**
- * @hide
- */
-interface IProvisionerService {
- byte[] getCertificateRequest(in boolean testMode,
- in int keyCount,
- in byte[] endpointEncryptionKey,
- in byte[] challenge) = 0;
-}
diff --git a/provisioner/rkp_factory_extraction_lib.cpp b/provisioner/rkp_factory_extraction_lib.cpp
index d85e85f6..b7e1e340 100644
--- a/provisioner/rkp_factory_extraction_lib.cpp
+++ b/provisioner/rkp_factory_extraction_lib.cpp
@@ -17,6 +17,7 @@
#include "rkp_factory_extraction_lib.h"
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android-base/properties.h>
#include <android/binder_manager.h>
#include <cppbor.h>
#include <cstddef>
@@ -143,7 +144,7 @@ CborResult<Array> getCsrV1(std::string_view componentName, IRemotelyProvisionedC
::ndk::ScopedAStatus status = irpc->getHardwareInfo(&hwInfo);
if (!status.isOk()) {
std::cerr << "Failed to get hardware info for '" << componentName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+ << "'. Description: " << status.getDescription() << "." << std::endl;
exit(-1);
}
@@ -154,7 +155,7 @@ CborResult<Array> getCsrV1(std::string_view componentName, IRemotelyProvisionedC
&keysToSignMac);
if (!status.isOk()) {
std::cerr << "Bundle extraction failed for '" << componentName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+ << "'. Description: " << status.getDescription() << "." << std::endl;
exit(-1);
}
return composeCertificateRequestV1(protectedData, verifiedDeviceInfo, challenge, keysToSignMac,
@@ -170,7 +171,7 @@ void selfTestGetCsrV1(std::string_view componentName, IRemotelyProvisionedCompon
::ndk::ScopedAStatus status = irpc->getHardwareInfo(&hwInfo);
if (!status.isOk()) {
std::cerr << "Failed to get hardware info for '" << componentName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+ << "'. Description: " << status.getDescription() << "." << std::endl;
exit(-1);
}
@@ -186,7 +187,7 @@ void selfTestGetCsrV1(std::string_view componentName, IRemotelyProvisionedCompon
&protectedData, &keysToSignMac);
if (!status.isOk()) {
std::cerr << "Error generating test cert chain for '" << componentName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+ << "'. Description: " << status.getDescription() << "." << std::endl;
exit(-1);
}
@@ -194,10 +195,16 @@ void selfTestGetCsrV1(std::string_view componentName, IRemotelyProvisionedCompon
protectedData, *eekChain, eekId,
hwInfo.supportedEekCurve, irpc, challenge);
- std::cout << "Self test successful." << std::endl;
+ if (!result) {
+ std::cerr << "Self test failed for IRemotelyProvisionedComponent '" << componentName
+ << "'. Error message: '" << result.message() << "'." << std::endl;
+ exit(-1);
+ }
}
CborResult<Array> composeCertificateRequestV3(const std::vector<uint8_t>& csr) {
+ const std::string kFingerprintProp = "ro.build.fingerprint";
+
auto [parsedCsr, _, csrErrMsg] = cppbor::parse(csr);
if (!parsedCsr) {
return {nullptr, csrErrMsg};
@@ -206,26 +213,18 @@ CborResult<Array> composeCertificateRequestV3(const std::vector<uint8_t>& csr) {
return {nullptr, "CSR is not a CBOR array."};
}
- return {std::unique_ptr<Array>(parsedCsr.release()->asArray()), ""};
-}
-
-CborResult<cppbor::Array> getCsrV3(std::string_view componentName,
- IRemotelyProvisionedComponent* irpc) {
- std::vector<uint8_t> csr;
- std::vector<MacedPublicKey> emptyKeys;
- const std::vector<uint8_t> challenge = generateChallenge();
-
- auto status = irpc->generateCertificateRequestV2(emptyKeys, challenge, &csr);
- if (!status.isOk()) {
- std::cerr << "Bundle extraction failed for '" << componentName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
- exit(-1);
+ if (!::android::base::WaitForPropertyCreation(kFingerprintProp)) {
+ return {nullptr, "Unable to read build fingerprint"};
}
- return composeCertificateRequestV3(csr);
+ Map unverifiedDeviceInfo =
+ Map().add("fingerprint", ::android::base::GetProperty(kFingerprintProp, /*default=*/""));
+ parsedCsr->asArray()->add(std::move(unverifiedDeviceInfo));
+ return {std::unique_ptr<Array>(parsedCsr.release()->asArray()), ""};
}
-void selfTestGetCsrV3(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+CborResult<cppbor::Array> getCsrV3(std::string_view componentName,
+ IRemotelyProvisionedComponent* irpc, bool selfTest) {
std::vector<uint8_t> csr;
std::vector<MacedPublicKey> emptyKeys;
const std::vector<uint8_t> challenge = generateChallenge();
@@ -233,48 +232,38 @@ void selfTestGetCsrV3(std::string_view componentName, IRemotelyProvisionedCompon
auto status = irpc->generateCertificateRequestV2(emptyKeys, challenge, &csr);
if (!status.isOk()) {
std::cerr << "Bundle extraction failed for '" << componentName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+ << "'. Description: " << status.getDescription() << "." << std::endl;
exit(-1);
}
- auto result = verifyFactoryCsr(/*keysToSign=*/cppbor::Array(), csr, irpc, challenge);
- if (!result) {
- std::cerr << "Self test failed for '" << componentName
- << "'. Error message: " << result.message() << "." << std::endl;
- exit(-1);
+ if (selfTest) {
+ auto result = verifyFactoryCsr(/*keysToSign=*/cppbor::Array(), csr, irpc, challenge);
+ if (!result) {
+ std::cerr << "Self test failed for IRemotelyProvisionedComponent '" << componentName
+ << "'. Error message: '" << result.message() << "'." << std::endl;
+ exit(-1);
+ }
}
- std::cout << "Self test successful." << std::endl;
+ return composeCertificateRequestV3(csr);
}
-CborResult<Array> getCsr(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
+CborResult<Array> getCsr(std::string_view componentName, IRemotelyProvisionedComponent* irpc,
+ bool selfTest) {
RpcHardwareInfo hwInfo;
auto status = irpc->getHardwareInfo(&hwInfo);
if (!status.isOk()) {
std::cerr << "Failed to get hardware info for '" << componentName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
+ << "'. Description: " << status.getDescription() << "." << std::endl;
exit(-1);
}
if (hwInfo.versionNumber < kVersionWithoutSuperencryption) {
+ if (selfTest) {
+ selfTestGetCsrV1(componentName, irpc);
+ }
return getCsrV1(componentName, irpc);
} else {
- return getCsrV3(componentName, irpc);
- }
-}
-
-void selfTestGetCsr(std::string_view componentName, IRemotelyProvisionedComponent* irpc) {
- RpcHardwareInfo hwInfo;
- auto status = irpc->getHardwareInfo(&hwInfo);
- if (!status.isOk()) {
- std::cerr << "Failed to get hardware info for '" << componentName
- << "'. Error code: " << status.getServiceSpecificError() << "." << std::endl;
- exit(-1);
- }
-
- if (hwInfo.versionNumber < kVersionWithoutSuperencryption) {
- selfTestGetCsrV1(componentName, irpc);
- } else {
- selfTestGetCsrV3(componentName, irpc);
+ return getCsrV3(componentName, irpc, selfTest);
}
}
diff --git a/provisioner/rkp_factory_extraction_lib.h b/provisioner/rkp_factory_extraction_lib.h
index a2183380..ae8ea6b6 100644
--- a/provisioner/rkp_factory_extraction_lib.h
+++ b/provisioner/rkp_factory_extraction_lib.h
@@ -46,7 +46,8 @@ std::vector<uint8_t> generateChallenge();
// what went wrong.
CborResult<cppbor::Array>
getCsr(std::string_view componentName,
- aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent* irpc);
+ aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent* irpc,
+ bool selfTest);
// Generates a test certificate chain and validates it, exiting the process on error.
void selfTestGetCsr(
diff --git a/provisioner/rkp_factory_extraction_lib_test.cpp b/provisioner/rkp_factory_extraction_lib_test.cpp
index 05509b36..3fe88da8 100644
--- a/provisioner/rkp_factory_extraction_lib_test.cpp
+++ b/provisioner/rkp_factory_extraction_lib_test.cpp
@@ -22,6 +22,7 @@
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <aidl/android/hardware/security/keymint/MacedPublicKey.h>
#include <aidl/android/hardware/security/keymint/RpcHardwareInfo.h>
+#include <android-base/properties.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -179,7 +180,8 @@ TEST(LibRkpFactoryExtractionTests, GetCsrWithV2Hal) {
SetArgPointee<6>(kFakeMac), //
Return(ByMove(ScopedAStatus::ok())))); //
- auto [csr, csrErrMsg] = getCsr("mock component name", mockRpc.get());
+ auto [csr, csrErrMsg] = getCsr("mock component name", mockRpc.get(),
+ /*selfTest=*/false);
ASSERT_THAT(csr, NotNull()) << csrErrMsg;
ASSERT_THAT(csr->asArray(), Pointee(Property(&Array::size, Eq(4))));
@@ -248,12 +250,19 @@ TEST(LibRkpFactoryExtractionTests, GetCsrWithV3Hal) {
.WillOnce(DoAll(SaveArg<1>(&challenge), SetArgPointee<2>(kCsr),
Return(ByMove(ScopedAStatus::ok()))));
- auto [csr, csrErrMsg] = getCsr("mock component name", mockRpc.get());
+ auto [csr, csrErrMsg] = getCsr("mock component name", mockRpc.get(),
+ /*selfTest=*/false);
ASSERT_THAT(csr, NotNull()) << csrErrMsg;
- ASSERT_THAT(csr, Pointee(Property(&Array::size, Eq(4))));
+ ASSERT_THAT(csr, Pointee(Property(&Array::size, Eq(5))));
EXPECT_THAT(csr->get(0 /* version */), Pointee(Eq(Uint(3))));
EXPECT_THAT(csr->get(1)->asMap(), NotNull());
EXPECT_THAT(csr->get(2)->asArray(), NotNull());
EXPECT_THAT(csr->get(3)->asArray(), NotNull());
+
+ const Map* unverifedDeviceInfo = csr->get(4)->asMap();
+ ASSERT_THAT(unverifedDeviceInfo, NotNull());
+ EXPECT_THAT(unverifedDeviceInfo->get("fingerprint"), NotNull());
+ const Tstr fingerprint(android::base::GetProperty("ro.build.fingerprint", ""));
+ EXPECT_THAT(*unverifedDeviceInfo->get("fingerprint")->asTstr(), Eq(fingerprint));
}
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index 2aeabe0a..0a3a59a6 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <aidl/android/hardware/drm/IDrmFactory.h>
#include <aidl/android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
#include <android/binder_manager.h>
#include <cppbor.h>
@@ -26,8 +27,10 @@
#include <string>
#include <vector>
+#include "DrmRkpAdapter.h"
#include "rkp_factory_extraction_lib.h"
+using aidl::android::hardware::drm::IDrmFactory;
using aidl::android::hardware::security::keymint::IRemotelyProvisionedComponent;
using aidl::android::hardware::security::keymint::remote_prov::jsonEncodeCsrWithBuild;
@@ -35,10 +38,12 @@ using namespace cppbor;
using namespace cppcose;
DEFINE_string(output_format, "build+csr", "How to format the output. Defaults to 'build+csr'.");
-DEFINE_bool(self_test, false,
- "If true, the tool does not output CSR data, but instead performs a self-test, "
- "validating a test payload for correctness. This may be used to verify a device on the "
- "factory line before attempting to upload the output to the device info service.");
+DEFINE_bool(self_test, true,
+ "If true, this tool performs a self-test, validating the payload for correctness. "
+ "This checks that the device on the factory line is producing valid output "
+ "before attempting to upload the output to the device info service.");
+DEFINE_string(serialno_prop, "ro.serialno",
+ "The property of getting serial number. Defaults to 'ro.serialno'.");
namespace {
@@ -47,12 +52,16 @@ constexpr std::string_view kBinaryCsrOutput = "csr"; // Just the raw csr as
constexpr std::string_view kBuildPlusCsr = "build+csr"; // Text-encoded (JSON) build
// fingerprint plus CSR.
+std::string getFullServiceName(const char* descriptor, const char* name) {
+ return std::string(descriptor) + "/" + name;
+}
+
void writeOutput(const std::string instance_name, 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(instance_name, csr, FLAGS_serialno_prop);
if (!error.empty()) {
std::cerr << "Error JSON encoding the output: " << error;
exit(1);
@@ -67,12 +76,21 @@ void writeOutput(const std::string instance_name, const Array& csr) {
}
}
+void getCsrForIRpc(const char* descriptor, const char* name, IRemotelyProvisionedComponent* irpc) {
+ auto [request, errMsg] = getCsr(name, irpc, FLAGS_self_test);
+ auto fullName = getFullServiceName(descriptor, name);
+ if (!request) {
+ std::cerr << "Unable to build CSR for '" << fullName << ": " << errMsg << std::endl;
+ exit(-1);
+ }
+
+ writeOutput(std::string(name), *request);
+}
+
// Callback for AServiceManager_forEachDeclaredInstance that writes out a CSR
// for every IRemotelyProvisionedComponent.
void getCsrForInstance(const char* name, void* /*context*/) {
- const std::vector<uint8_t> challenge = generateChallenge();
-
- auto fullName = std::string(IRemotelyProvisionedComponent::descriptor) + "/" + name;
+ auto fullName = getFullServiceName(IRemotelyProvisionedComponent::descriptor, name);
AIBinder* rkpAiBinder = AServiceManager_getService(fullName.c_str());
::ndk::SpAIBinder rkp_binder(rkpAiBinder);
auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
@@ -81,17 +99,7 @@ void getCsrForInstance(const char* name, void* /*context*/) {
exit(-1);
}
- if (FLAGS_self_test) {
- selfTestGetCsr(name, rkp_service.get());
- } else {
- auto [request, errMsg] = getCsr(name, rkp_service.get());
- if (!request) {
- std::cerr << "Unable to build CSR for '" << fullName << ": " << errMsg << std::endl;
- exit(-1);
- }
-
- writeOutput(std::string(name), *request);
- }
+ getCsrForIRpc(IRemotelyProvisionedComponent::descriptor, name, rkp_service.get());
}
} // namespace
@@ -102,5 +110,10 @@ int main(int argc, char** argv) {
AServiceManager_forEachDeclaredInstance(IRemotelyProvisionedComponent::descriptor,
/*context=*/nullptr, getCsrForInstance);
+ // Append drm csr's
+ for (auto const& e : android::mediadrm::getDrmRemotelyProvisionedComponents()) {
+ getCsrForIRpc(IDrmFactory::descriptor, e.first.c_str(), e.second.get());
+ }
+
return 0;
}
diff --git a/provisioner/support/Android.bp b/provisioner/support/Android.bp
new file mode 100644
index 00000000..24cfd03c
--- /dev/null
+++ b/provisioner/support/Android.bp
@@ -0,0 +1,64 @@
+// Copyright (C) 2023 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_team: "trendy_team_android_hardware_backed_security",
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "hardware_interfaces_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["system_security_license"],
+}
+
+cc_defaults {
+ name: "librkp_support_defaults",
+ static_libs: [
+ "android.hardware.security.rkp-V3-cpp",
+ "android.security.rkp_aidl-cpp",
+ ],
+ shared_libs: [
+ "libbase",
+ "libbinder",
+ "libutils",
+ ],
+ cflags: [
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
+
+cc_library {
+ name: "librkp_support",
+ defaults: ["librkp_support_defaults"],
+ srcs: [
+ "rkpd_client.cpp",
+ ],
+ export_include_dirs: ["include"],
+}
+
+cc_test {
+ name: "librkp_support_test",
+ defaults: [
+ "librkp_support_defaults",
+ "use_libaidlvintf_gtest_helper_static",
+ ],
+ srcs: ["test.cpp"],
+ static_libs: [
+ "librkp_support",
+ ],
+ test_suites: ["general-tests"],
+ require_root: true,
+}
diff --git a/provisioner/support/TEST_MAPPING b/provisioner/support/TEST_MAPPING
new file mode 100644
index 00000000..fc301043
--- /dev/null
+++ b/provisioner/support/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "librkp_support_test"
+ }
+ ]
+}
diff --git a/identity/RemotelyProvisionedKey.h b/provisioner/support/include/rkp/support/rkpd_client.h
index e7ddfca5..5a7fe6e4 100644
--- a/identity/RemotelyProvisionedKey.h
+++ b/provisioner/support/include/rkp/support/rkpd_client.h
@@ -20,19 +20,20 @@
#include <optional>
#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <android/security/rkp/RemotelyProvisionedKey.h>
-namespace android {
-namespace security {
-namespace identity {
+namespace android::security::rkp::support {
using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
using ::android::security::rkp::RemotelyProvisionedKey;
-std::optional<std::string> getRpcId(const sp<IRemotelyProvisionedComponent>& rpc);
-
+// Callers of getRpcKeyFuture() and getRpcKey() need at least two threads to
+// retrieve the key, one to asynchronously handle binder callbacks and one to
+// wait on the future.
std::optional<std::future<std::optional<RemotelyProvisionedKey>>>
getRpcKeyFuture(const sp<IRemotelyProvisionedComponent>& rpc, int32_t keyId);
-} // namespace identity
-} // namespace security
-} // namespace android
+std::optional<RemotelyProvisionedKey> getRpcKey(const sp<IRemotelyProvisionedComponent>& rpc,
+ int32_t keyId, int32_t timeout_sec = 10);
+
+} // namespace android::security::rkp::support
diff --git a/identity/RemotelyProvisionedKey.cpp b/provisioner/support/rkpd_client.cpp
index 784a6803..de1e3bbe 100644
--- a/identity/RemotelyProvisionedKey.cpp
+++ b/provisioner/support/rkpd_client.cpp
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#define LOG_TAG "credstore"
+#define LOG_TAG "rkpd_client"
#include <atomic>
@@ -25,13 +25,9 @@
#include <android/security/rkp/IRemoteProvisioning.h>
#include <binder/IServiceManager.h>
#include <binder/Status.h>
-#include <vintf/VintfObject.h>
+#include <rkp/support/rkpd_client.h>
-#include "RemotelyProvisionedKey.h"
-
-namespace android {
-namespace security {
-namespace identity {
+namespace android::security::rkp::support {
namespace {
using ::android::binder::Status;
@@ -46,13 +42,28 @@ using ::android::security::rkp::RemotelyProvisionedKey;
constexpr const char* kRemoteProvisioningServiceName = "remote_provisioning";
+std::optional<std::string> getRpcId(const sp<IRemotelyProvisionedComponent>& rpc) {
+ RpcHardwareInfo rpcHwInfo;
+ Status status = rpc->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. "
+ << "This is a bug in the vendor implementation.";
+ return std::nullopt;
+ }
+
+ return *rpcHwInfo.uniqueId;
+}
+
std::optional<String16> findRpcNameById(std::string_view targetRpcId) {
- auto deviceManifest = vintf::VintfObject::GetDeviceHalManifest();
- auto instances = deviceManifest->getAidlInstances("android.hardware.security.keymint",
- "IRemotelyProvisionedComponent");
- for (const std::string& instance : instances) {
- auto rpcName =
- IRemotelyProvisionedComponent::descriptor + String16("/") + String16(instance.c_str());
+ auto instances = android::defaultServiceManager()->getDeclaredInstances(
+ IRemotelyProvisionedComponent::descriptor);
+ for (const auto& instance : instances) {
+ auto rpcName = IRemotelyProvisionedComponent::descriptor + String16("/") + instance;
sp<IRemotelyProvisionedComponent> rpc =
android::waitForService<IRemotelyProvisionedComponent>(rpcName);
@@ -157,24 +168,6 @@ class GetRegistrationCallback : public BnGetRegistrationCallback {
} // namespace
-std::optional<std::string> getRpcId(const sp<IRemotelyProvisionedComponent>& rpc) {
- RpcHardwareInfo rpcHwInfo;
- Status status = rpc->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;
- }
-
- return *rpcHwInfo.uniqueId;
-}
-
std::optional<std::future<std::optional<RemotelyProvisionedKey>>>
getRpcKeyFuture(const sp<IRemotelyProvisionedComponent>& rpc, int32_t keyId) {
std::promise<std::optional<RemotelyProvisionedKey>> keyPromise;
@@ -203,6 +196,21 @@ getRpcKeyFuture(const sp<IRemotelyProvisionedComponent>& rpc, int32_t keyId) {
return keyFuture;
}
-} // namespace identity
-} // namespace security
-} // namespace android
+std::optional<RemotelyProvisionedKey> getRpcKey(const sp<IRemotelyProvisionedComponent>& rpc,
+ int32_t keyId, int32_t timeout_sec) {
+ auto rpcKeyFuture = getRpcKeyFuture(rpc, keyId);
+ if (!rpcKeyFuture) {
+ LOG(ERROR) << "Failed getRpcKeyFuture()";
+ return std::nullopt;
+ }
+
+ auto timeout = std::chrono::seconds(timeout_sec);
+ if (rpcKeyFuture->wait_for(timeout) != std::future_status::ready) {
+ LOG(ERROR) << "Waiting for remotely provisioned attestation key timed out";
+ return std::nullopt;
+ }
+
+ return rpcKeyFuture->get();
+}
+
+} // namespace android::security::rkp::support
diff --git a/provisioner/support/test.cpp b/provisioner/support/test.cpp
new file mode 100644
index 00000000..0e6e2f46
--- /dev/null
+++ b/provisioner/support/test.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <aidl/Gtest.h>
+#include <aidl/Vintf.h>
+#include <android/hardware/security/keymint/IRemotelyProvisionedComponent.h>
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <gtest/gtest.h>
+#include <rkp/support/rkpd_client.h>
+
+using ::android::getAidlHalInstanceNames;
+using ::android::sp;
+using ::android::String16;
+using ::android::hardware::security::keymint::IRemotelyProvisionedComponent;
+using ::android::security::rkp::RemotelyProvisionedKey;
+using ::android::security::rkp::support::getRpcKey;
+
+// TODO(b/272600606): Add tests for error cases
+class RkpdClientTest : public testing::TestWithParam<std::string> {
+ public:
+ virtual void SetUp() override {
+ auto rpcName = String16(GetParam().c_str());
+ String16 avfName = String16(IRemotelyProvisionedComponent::descriptor) + String16("/avf");
+ if (avfName == rpcName) {
+ GTEST_SKIP() << "Skipping test for avf";
+ }
+ rpc_ = android::waitForService<IRemotelyProvisionedComponent>(rpcName);
+ ASSERT_NE(rpc_, nullptr);
+ }
+
+ sp<IRemotelyProvisionedComponent> rpc_;
+};
+
+TEST_P(RkpdClientTest, getRpcKey) {
+ std::optional<RemotelyProvisionedKey> key = getRpcKey(rpc_, /*keyId=*/0);
+
+ ASSERT_TRUE(key.has_value()) << "Failed to get remotely provisioned attestation key";
+ ASSERT_FALSE(key->keyBlob.empty()) << "Key blob is empty";
+ ASSERT_FALSE(key->encodedCertChain.empty()) << "Certificate is empty";
+}
+
+INSTANTIATE_TEST_SUITE_P(
+ PerInstance, RkpdClientTest,
+ testing::ValuesIn(getAidlHalInstanceNames(IRemotelyProvisionedComponent::descriptor)),
+ ::android::PrintInstanceNameToString);
+
+int main(int argc, char** argv) {
+ testing::InitGoogleTest(&argc, argv);
+
+ // We need one thread to issue requests to RKPD and one to handle
+ // asynchronous responses from RKPD.
+ android::ProcessState::self()->setThreadPoolMaxThreadCount(2);
+ android::ProcessState::self()->startThreadPool();
+ return RUN_ALL_TESTS();
+}