Merge "Revert "Revert "Keystore 2.0: Add CREATE_DATETIME unconditionally."""
diff --git a/OWNERS b/OWNERS
index 93c024d..563a78c 100644
--- a/OWNERS
+++ b/OWNERS
@@ -1,7 +1,9 @@
+alanstokes@google.com
cbrubaker@google.com
hasinitg@google.com
jbires@google.com
jdanis@google.com
+jeffv@google.com
kroot@google.com
sethmo@google.com
swillden@google.com
diff --git a/fsverity/Android.bp b/fsverity/Android.bp
new file mode 100644
index 0000000..5fb38ae
--- /dev/null
+++ b/fsverity/Android.bp
@@ -0,0 +1,43 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "system_security_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["system_security_license"],
+}
+
+python_library_host {
+ name: "fsverity_digests_proto_python",
+ srcs: [
+ "fsverity_digests.proto",
+ ],
+ required: [
+ "fsverity",
+ ],
+ proto: {
+ canonical_path_from_root: false,
+ },
+ version: {
+ py2: {
+ enabled: true,
+ },
+ py3: {
+ enabled: true,
+ },
+ },
+}
diff --git a/fsverity/AndroidManifest.xml b/fsverity/AndroidManifest.xml
new file mode 100644
index 0000000..434955c
--- /dev/null
+++ b/fsverity/AndroidManifest.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.security.fsverity_metadata" />
diff --git a/fsverity/OWNERS b/fsverity/OWNERS
new file mode 100644
index 0000000..f9e7b25
--- /dev/null
+++ b/fsverity/OWNERS
@@ -0,0 +1,5 @@
+alanstokes@google.com
+ebiggers@google.com
+jeffv@google.com
+jiyong@google.com
+victorhsieh@google.com
diff --git a/fsverity/fsverity_digests.proto b/fsverity/fsverity_digests.proto
new file mode 100644
index 0000000..816ae61
--- /dev/null
+++ b/fsverity/fsverity_digests.proto
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+syntax = "proto3";
+
+package android.security.fsverity;
+
+message FSVerityDigests {
+ message Digest {
+ bytes digest = 1;
+ string hash_alg = 2;
+ }
+ map<string, Digest> digests = 1;
+}
diff --git a/fsverity_init/Android.bp b/fsverity_init/Android.bp
index 39d4e6b..83c5945 100644
--- a/fsverity_init/Android.bp
+++ b/fsverity_init/Android.bp
@@ -10,17 +10,34 @@
cc_binary {
name: "fsverity_init",
srcs: [
- "fsverity_init.cpp",
+ "main.cpp",
],
static_libs: [
"libc++fs",
+ "libfsverity_init",
+ "libmini_keyctl_static",
+ ],
+ shared_libs: [
+ "libbase",
+ "libkeyutils",
+ "liblog",
+ ],
+ 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",
- "liblogwrap",
],
cflags: ["-Werror", "-Wall", "-Wextra"],
+ export_include_dirs: ["include"],
+ recovery_available: true,
}
diff --git a/fsverity_init/OWNERS b/fsverity_init/OWNERS
new file mode 100644
index 0000000..f9e7b25
--- /dev/null
+++ b/fsverity_init/OWNERS
@@ -0,0 +1,5 @@
+alanstokes@google.com
+ebiggers@google.com
+jeffv@google.com
+jiyong@google.com
+victorhsieh@google.com
diff --git a/fsverity_init/fsverity_init.cpp b/fsverity_init/fsverity_init.cpp
index 7bc6022..61f84dd 100644
--- a/fsverity_init/fsverity_init.cpp
+++ b/fsverity_init/fsverity_init.cpp
@@ -81,47 +81,3 @@
LoadKeyFromDirectory(keyring_id, "fsv_system_", "/system/etc/security/fsverity");
LoadKeyFromDirectory(keyring_id, "fsv_product_", "/product/etc/security/fsverity");
}
-
-int main(int argc, const char** argv) {
- if (argc < 2) {
- LOG(ERROR) << "Not enough arguments";
- return -1;
- }
-
- key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
- if (keyring_id < 0) {
- LOG(ERROR) << "Failed to find .fs-verity keyring id";
- return -1;
- }
-
- const std::string_view command = argv[1];
-
- if (command == "--load-verified-keys") {
- LoadKeyFromVerifiedPartitions(keyring_id);
- } else if (command == "--load-extra-key") {
- if (argc != 3) {
- LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
- return -1;
- }
- if (!LoadKeyFromStdin(keyring_id, argv[2])) {
- return -1;
- }
- } else if (command == "--lock") {
- // Requires files backed by fs-verity to be verified with a key in .fs-verity
- // keyring.
- if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
- PLOG(ERROR) << "Failed to enforce fs-verity signature";
- }
-
- if (!android::base::GetBoolProperty("ro.debuggable", false)) {
- if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
- PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
- }
- }
- } else {
- LOG(ERROR) << "Unknown argument(s).";
- return -1;
- }
-
- return 0;
-}
diff --git a/fsverity_init/include/fsverity_init.h b/fsverity_init/include/fsverity_init.h
new file mode 100644
index 0000000..c3bc93b
--- /dev/null
+++ b/fsverity_init/include/fsverity_init.h
@@ -0,0 +1,21 @@
+/*
+ * 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
new file mode 100644
index 0000000..3f75dca
--- /dev/null
+++ b/fsverity_init/main.cpp
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <string>
+
+#include <android-base/file.h>
+#include <android-base/logging.h>
+#include <android-base/properties.h>
+#include <fsverity_init.h>
+#include <log/log.h>
+#include <mini_keyctl_utils.h>
+
+int main(int argc, const char** argv) {
+ if (argc < 2) {
+ LOG(ERROR) << "Not enough arguments";
+ return -1;
+ }
+
+ key_serial_t keyring_id = android::GetKeyringId(".fs-verity");
+ if (keyring_id < 0) {
+ LOG(ERROR) << "Failed to find .fs-verity keyring id";
+ return -1;
+ }
+
+ const std::string_view command = argv[1];
+
+ if (command == "--load-verified-keys") {
+ LoadKeyFromVerifiedPartitions(keyring_id);
+ } else if (command == "--load-extra-key") {
+ if (argc != 3) {
+ LOG(ERROR) << "--load-extra-key requires <key_name> argument.";
+ return -1;
+ }
+ if (!LoadKeyFromStdin(keyring_id, argv[2])) {
+ return -1;
+ }
+ } else if (command == "--lock") {
+ // Requires files backed by fs-verity to be verified with a key in .fs-verity
+ // keyring.
+ if (!android::base::WriteStringToFile("1", "/proc/sys/fs/verity/require_signatures")) {
+ PLOG(ERROR) << "Failed to enforce fs-verity signature";
+ }
+
+ if (!android::base::GetBoolProperty("ro.debuggable", false)) {
+ if (keyctl_restrict_keyring(keyring_id, nullptr, nullptr) < 0) {
+ PLOG(ERROR) << "Cannot restrict .fs-verity keyring";
+ }
+ }
+ } else {
+ LOG(ERROR) << "Unknown argument(s).";
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/identity/Android.bp b/identity/Android.bp
index ecdf9a4..ddb4335 100644
--- a/identity/Android.bp
+++ b/identity/Android.bp
@@ -24,7 +24,10 @@
cc_binary {
name: "credstore",
- defaults: ["identity_defaults"],
+ defaults: [
+ "identity_defaults",
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ ],
srcs: [
"main.cpp",
@@ -48,14 +51,13 @@
"android.hardware.identity-support-lib",
"libkeymaster4support",
"libkeystore-attestation-application-id",
- "android.hardware.security.keymint-V1-ndk",
"android.security.authorization-ndk",
],
static_libs: [
"android.hardware.identity-V3-cpp",
"android.hardware.keymaster-V3-cpp",
"libcppbor_external",
- ]
+ ],
}
filegroup {
diff --git a/keystore/Android.bp b/keystore/Android.bp
index ad4b4b1..892c5b4 100644
--- a/keystore/Android.bp
+++ b/keystore/Android.bp
@@ -36,7 +36,10 @@
cc_binary {
name: "keystore_cli_v2",
- defaults: ["keystore_defaults"],
+ defaults: [
+ "keystore_defaults",
+ "keystore2_use_latest_aidl_ndk_shared",
+ ],
cflags: [
"-DKEYMASTER_NAME_TAGS",
@@ -48,7 +51,6 @@
],
shared_libs: [
"android.security.apc-ndk",
- "android.system.keystore2-V1-ndk",
"libbinder",
"libbinder_ndk",
"libchrome",
@@ -63,7 +65,7 @@
// Library used by both keystore and credstore for generating the ASN.1 stored
// in Tag::ATTESTATION_APPLICATION_ID
-cc_library_shared {
+cc_library {
name: "libkeystore-attestation-application-id",
defaults: ["keystore_defaults"],
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index 1cfb4f6..1e9126d 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -19,6 +19,7 @@
#include <iostream>
#include <memory>
#include <string>
+#include <variant>
#include <vector>
#include <base/command_line.h>
diff --git a/keystore/tests/fuzzer/Android.bp b/keystore/tests/fuzzer/Android.bp
new file mode 100644
index 0000000..589cef7
--- /dev/null
+++ b/keystore/tests/fuzzer/Android.bp
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "system_security_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["system_security_license"],
+}
+
+cc_fuzz {
+ name: "keystoreGetWifiHidl_fuzzer",
+ vendor: true,
+ srcs: [
+ "keystoreGetWifiHidl_fuzzer.cpp",
+ ],
+ static_libs: [
+ "libkeystore-wifi-hidl",
+ "libutils",
+ ],
+ shared_libs: [
+ "android.system.wifi.keystore@1.0",
+ "libhidlbase",
+ "liblog",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
+
+cc_defaults {
+ name: "keystoreAttestation_defaults",
+ static_libs: [
+ "libkeystore-attestation-application-id",
+ "liblog",
+ "libutils",
+ "libbase",
+ "libhidlbase",
+ ],
+ shared_libs: [
+ "libbinder",
+ "libcrypto",
+ ],
+ fuzz_config: {
+ cc: [
+ "android-media-fuzzing-reports@google.com",
+ ],
+ componentid: 155276,
+ },
+}
+
+cc_fuzz {
+ name: "keystoreSignature_fuzzer",
+ srcs: [
+ "keystoreSignature_fuzzer.cpp",
+ ],
+ defaults: [
+ "keystoreAttestation_defaults",
+ ],
+}
+
+cc_fuzz {
+ name: "keystorePackageInfo_fuzzer",
+ srcs: [
+ "keystorePackageInfo_fuzzer.cpp",
+ ],
+ defaults: [
+ "keystoreAttestation_defaults",
+ ],
+}
+
+cc_fuzz {
+ name: "keystoreApplicationId_fuzzer",
+ srcs: [
+ "keystoreApplicationId_fuzzer.cpp",
+ ],
+ defaults: [
+ "keystoreAttestation_defaults",
+ ],
+}
+
+cc_fuzz {
+ name: "keystoreAttestationId_fuzzer",
+ srcs: [
+ "keystoreAttestationId_fuzzer.cpp",
+ ],
+ defaults: [
+ "keystoreAttestation_defaults",
+ ],
+}
diff --git a/keystore/tests/fuzzer/README.md b/keystore/tests/fuzzer/README.md
new file mode 100644
index 0000000..25d53ab
--- /dev/null
+++ b/keystore/tests/fuzzer/README.md
@@ -0,0 +1,103 @@
+# Fuzzer for libkeystore
+## Table of contents
++ [libkeystore-get-wifi-hidl](#libkeystore-get-wifi-hidl)
++ [libkeystore_attestation_application_id](#libkeystore_attestation_application_id)
+
+# <a name="libkeystore-get-wifi-hidl"></a> Fuzzer for libkeystore-get-wifi-hidl
+## Plugin Design Considerations
+The fuzzer plugin for libkeystore-get-wifi-hidl is designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libkeystore-get-wifi-hidl supports the following parameters:
+1. Key (parameter name: `key`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `key` | `String` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the libkeystore-get-wifi-hidl module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build keystoreGetWifiHidl_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) keystoreGetWifiHidl_fuzzer
+```
+#### Steps to run
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreGetWifiHidl_fuzzer/keystoreGetWifiHidl_fuzzer
+```
+
+# <a name="libkeystore_attestation_application_id"></a> Fuzzer for libkeystore_attestation_application_id
+## Plugin Design Considerations
+The fuzzer plugin for libkeystore-attestation-application-id are designed based on the understanding of the library and tries to achieve the following:
+
+##### Maximize code coverage
+The configuration parameters are not hardcoded, but instead selected based on
+incoming data. This ensures more code paths are reached by the fuzzer.
+
+libkeystore-attestation-application-id supports the following parameters:
+1. Package Name (parameter name: `packageName`)
+2. Version Code (parameter name: `versionCode`)
+3. Uid (parameter name: `uid`)
+
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `packageName` | `String` | Value obtained from FuzzedDataProvider|
+| `versionCode` | `INT64_MIN` to `INT64_MAX` | Value obtained from FuzzedDataProvider|
+| `uid` | `0` to `1000` | Value obtained from FuzzedDataProvider|
+
+This also ensures that the plugin is always deterministic for any given input.
+
+##### Maximize utilization of input data
+The plugins feed the entire input data to the libkeystore_attestation_application_id module.
+This ensures that the plugin tolerates any kind of input (empty, huge,
+malformed, etc) and doesnt `exit()` on any input and thereby increasing the
+chance of identifying vulnerabilities.
+
+## Build
+
+This describes steps to build keystoreSignature_fuzzer, keystorePackageInfo_fuzzer, keystoreApplicationId_fuzzer and keystoreAttestationId_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) keystoreSignature_fuzzer
+ $ mm -j$(nproc) keystorePackageInfo_fuzzer
+ $ mm -j$(nproc) keystoreApplicationId_fuzzer
+ $ mm -j$(nproc) keystoreAttestationId_fuzzer
+```
+#### Steps to run
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreSignature_fuzzer/keystoreSignature_fuzzer
+ $ adb shell /data/fuzz/${TARGET_ARCH}/keystorePackageInfo_fuzzer/keystorePackageInfo_fuzzer
+ $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreApplicationId_fuzzer/keystoreApplicationId_fuzzer
+ $ adb shell /data/fuzz/${TARGET_ARCH}/keystoreAttestationId_fuzzer/keystoreAttestationId_fuzzer
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
new file mode 100644
index 0000000..0eddb9a
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreApplicationId_fuzzer.cpp
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "keystoreCommon.h"
+#include <keystore/KeyAttestationApplicationId.h>
+
+using ::security::keymaster::KeyAttestationApplicationId;
+
+constexpr size_t kPackageVectorSizeMin = 1;
+constexpr size_t kPackageVectorSizeMax = 10;
+
+class KeystoreApplicationId {
+ public:
+ void process(const uint8_t* data, size_t size);
+ ~KeystoreApplicationId() {}
+
+ private:
+ void invokeApplicationId();
+ std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystoreApplicationId::invokeApplicationId() {
+ std::optional<KeyAttestationApplicationId> applicationId;
+ bool shouldUsePackageInfoVector = mFdp->ConsumeBool();
+ if (shouldUsePackageInfoVector) {
+ KeyAttestationApplicationId::PackageInfoVector packageInfoVector;
+ int32_t packageVectorSize =
+ mFdp->ConsumeIntegralInRange<int32_t>(kPackageVectorSizeMin, kPackageVectorSizeMax);
+ for (int32_t packageSize = 0; packageSize < packageVectorSize; ++packageSize) {
+ auto packageInfoData = initPackageInfoData(mFdp.get());
+ packageInfoVector.push_back(make_optional<KeyAttestationPackageInfo>(
+ String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
+ packageInfoData.sharedSignaturesVector));
+ }
+ applicationId = KeyAttestationApplicationId(std::move(packageInfoVector));
+ } else {
+ auto packageInfoData = initPackageInfoData(mFdp.get());
+ applicationId = KeyAttestationApplicationId(make_optional<KeyAttestationPackageInfo>(
+ String16((packageInfoData.packageName).c_str()), packageInfoData.versionCode,
+ packageInfoData.sharedSignaturesVector));
+ }
+ invokeReadWriteParcel(&applicationId.value());
+}
+
+void KeystoreApplicationId::process(const uint8_t* data, size_t size) {
+ mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+ invokeApplicationId();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ KeystoreApplicationId keystoreApplicationId;
+ keystoreApplicationId.process(data, size);
+ return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp b/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp
new file mode 100644
index 0000000..581da46
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreAttestationId_fuzzer.cpp
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <keystore/keystore_attestation_id.h>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ uint32_t uid = fdp.ConsumeIntegral<uint32_t>();
+ auto result = android::security::gather_attestation_application_id(uid);
+ result.isOk();
+ result.status();
+ result.value();
+ return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreCommon.h b/keystore/tests/fuzzer/keystoreCommon.h
new file mode 100644
index 0000000..7af3ba8
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreCommon.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifndef KEYSTORECOMMON_H
+#define KEYSTORECOMMON_H
+
+#include <binder/Parcel.h>
+#include <binder/Parcelable.h>
+#include <keystore/KeyAttestationPackageInfo.h>
+#include <keystore/Signature.h>
+#include <vector>
+
+#include "fuzzer/FuzzedDataProvider.h"
+
+using namespace android;
+using namespace std;
+using ::content::pm::Signature;
+using ::security::keymaster::KeyAttestationPackageInfo;
+
+constexpr size_t kSignatureSizeMin = 1;
+constexpr size_t kSignatureSizeMax = 1000;
+constexpr size_t kRandomStringLength = 256;
+constexpr size_t kSignatureVectorSizeMin = 1;
+constexpr size_t kSignatureVectorSizeMax = 1000;
+
+struct PackageInfoData {
+ string packageName;
+ int64_t versionCode;
+ KeyAttestationPackageInfo::SharedSignaturesVector sharedSignaturesVector;
+};
+
+inline void invokeReadWriteParcel(Parcelable* obj) {
+ Parcel parcel;
+ obj->writeToParcel(&parcel);
+ parcel.setDataPosition(0);
+ obj->readFromParcel(&parcel);
+}
+
+inline vector<uint8_t> initSignatureData(FuzzedDataProvider* fdp) {
+ size_t signatureSize = fdp->ConsumeIntegralInRange(kSignatureSizeMin, kSignatureSizeMax);
+ vector<uint8_t> signatureData = fdp->ConsumeBytes<uint8_t>(signatureSize);
+ return signatureData;
+}
+
+inline PackageInfoData initPackageInfoData(FuzzedDataProvider* fdp) {
+ PackageInfoData packageInfoData;
+ packageInfoData.packageName = fdp->ConsumeRandomLengthString(kRandomStringLength);
+ packageInfoData.versionCode = fdp->ConsumeIntegral<int64_t>();
+ size_t signatureVectorSize =
+ fdp->ConsumeIntegralInRange(kSignatureVectorSizeMin, kSignatureVectorSizeMax);
+ KeyAttestationPackageInfo::SignaturesVector signatureVector;
+ for (size_t size = 0; size < signatureVectorSize; ++size) {
+ bool shouldUseParameterizedConstructor = fdp->ConsumeBool();
+ if (shouldUseParameterizedConstructor) {
+ vector<uint8_t> signatureData = initSignatureData(fdp);
+ signatureVector.push_back(make_optional<Signature>(signatureData));
+ } else {
+ signatureVector.push_back(std::nullopt);
+ }
+ }
+ packageInfoData.sharedSignaturesVector =
+ make_shared<KeyAttestationPackageInfo::SignaturesVector>(move(signatureVector));
+ return packageInfoData;
+}
+#endif // KEYSTORECOMMON_H
diff --git a/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp b/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp
new file mode 100644
index 0000000..1e033c8
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreGetWifiHidl_fuzzer.cpp
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "fuzzer/FuzzedDataProvider.h"
+#include <inttypes.h>
+#include <keystore/keystore_get.h>
+
+using namespace std;
+
+constexpr int32_t kMaxKeySize = 256;
+const string kValidStrKeyPrefix[] = {"USRSKEY_",
+ "PLATFORM_VPN_",
+ "USRPKEY_",
+ "CACERT_",
+ "VPN_"
+ "USRCERT_",
+ "WIFI_"};
+constexpr char kStrGrantKeyPrefix[] = "ks2_keystore-engine_grant_id:";
+constexpr char kStrKeySuffix[] = "LOCKDOWN_VPN";
+constexpr size_t kGrantIdSize = 20;
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ FuzzedDataProvider fdp = FuzzedDataProvider(data, size);
+ size_t keyLength = fdp.ConsumeIntegralInRange<size_t>(0, kMaxKeySize);
+ bool usePrefix = fdp.ConsumeBool();
+ string strKeyPrefix;
+ size_t strKeyPrefixLength = 0;
+ size_t strKeySuffixLength = min(fdp.remaining_bytes(), keyLength);
+ if (usePrefix) {
+ strKeyPrefix = fdp.PickValueInArray(kValidStrKeyPrefix);
+ strKeyPrefixLength = sizeof(strKeyPrefix);
+ strKeySuffixLength =
+ (strKeySuffixLength > strKeyPrefixLength) ? strKeySuffixLength - strKeyPrefixLength : 0;
+ }
+ string strKeySuffix =
+ fdp.ConsumeBool() ? string(kStrKeySuffix) : fdp.ConsumeBytesAsString(strKeySuffixLength);
+ string strKey;
+ strKey = usePrefix ? strKeyPrefix + strKeySuffix : strKeySuffix;
+ if (fdp.ConsumeBool()) {
+ uint64_t grant = fdp.ConsumeIntegral<uint64_t>();
+ char grantId[kGrantIdSize] = "";
+ snprintf(grantId, kGrantIdSize, "%" PRIx64, grant);
+ strKey = strKey + string(kStrGrantKeyPrefix) + grantId;
+ }
+ const char* key = strKey.c_str();
+ uint8_t* value = nullptr;
+ keystore_get(key, strlen(key), &value);
+ free(value);
+ return 0;
+}
diff --git a/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
new file mode 100644
index 0000000..63899ff
--- /dev/null
+++ b/keystore/tests/fuzzer/keystorePackageInfo_fuzzer.cpp
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "keystoreCommon.h"
+
+class KeystorePackageInfoFuzzer {
+ public:
+ void process(const uint8_t* data, size_t size);
+ ~KeystorePackageInfoFuzzer() {}
+
+ private:
+ void invokePackageInfo();
+ std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystorePackageInfoFuzzer::invokePackageInfo() {
+ auto packageInfoData = initPackageInfoData(mFdp.get());
+ KeyAttestationPackageInfo packageInfo(String16((packageInfoData.packageName).c_str()),
+ packageInfoData.versionCode,
+ packageInfoData.sharedSignaturesVector);
+ invokeReadWriteParcel(&packageInfo);
+}
+
+void KeystorePackageInfoFuzzer::process(const uint8_t* data, size_t size) {
+ mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+ invokePackageInfo();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ KeystorePackageInfoFuzzer keystorePackageInfoFuzzer;
+ keystorePackageInfoFuzzer.process(data, size);
+ return 0;
+}
diff --git a/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
new file mode 100644
index 0000000..b8f8a73
--- /dev/null
+++ b/keystore/tests/fuzzer/keystoreSignature_fuzzer.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#include "keystoreCommon.h"
+#include <keystore/Signature.h>
+
+class KeystoreSignatureFuzzer {
+ public:
+ void process(const uint8_t* data, size_t size);
+ ~KeystoreSignatureFuzzer() {}
+
+ private:
+ void invokeSignature();
+ std::unique_ptr<FuzzedDataProvider> mFdp;
+};
+
+void KeystoreSignatureFuzzer::invokeSignature() {
+ std::optional<Signature> signature;
+ bool shouldUseParameterizedConstructor = mFdp->ConsumeBool();
+ if (shouldUseParameterizedConstructor) {
+ std::vector<uint8_t> signatureData = initSignatureData(mFdp.get());
+ signature = Signature(signatureData);
+ } else {
+ signature = Signature();
+ }
+ invokeReadWriteParcel(&signature.value());
+}
+
+void KeystoreSignatureFuzzer::process(const uint8_t* data, size_t size) {
+ mFdp = std::make_unique<FuzzedDataProvider>(data, size);
+ invokeSignature();
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ KeystoreSignatureFuzzer keystoreSignatureFuzzer;
+ keystoreSignatureFuzzer.process(data, size);
+ return 0;
+}
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index e8cdb6c..520237a 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -25,9 +25,11 @@
name: "libkeystore2_defaults",
crate_name: "keystore2",
srcs: ["src/lib.rs"],
+ defaults: [
+ "keymint_use_latest_hal_aidl_rust",
+ ],
rustlibs: [
- "android.hardware.security.keymint-V1-rust",
"android.hardware.security.secureclock-V1-rust",
"android.hardware.security.sharedsecret-V1-rust",
"android.os.permissions_aidl-rust",
@@ -37,7 +39,7 @@
"android.security.maintenance-rust",
"android.security.metrics-rust",
"android.security.remoteprovisioning-rust",
- "android.system.keystore2-V1-rust",
+ "android.system.keystore2-V2-rust",
"libanyhow",
"libbinder_rs",
"libkeystore2_aaid-rust",
@@ -52,6 +54,8 @@
"liblog_rust",
"librand",
"librustutils",
+ "libserde",
+ "libserde_cbor",
"libthiserror",
],
shared_libs: [
@@ -149,28 +153,6 @@
// selection available in the build system.
prefer_rlib: true,
- // TODO(b/187412695)
- // This is a hack to work around the build system not installing
- // dynamic dependencies of rlibs to the device. This section should
- // be removed once that works correctly.
- shared_libs: [
- "android.hardware.confirmationui@1.0",
- "android.hardware.security.sharedsecret-V1-ndk",
- "android.security.compat-ndk",
- "libc",
- "libdl_android",
- "libdl",
- "libkeymint",
- "libkeystore2_aaid",
- "libkeystore2_apc_compat",
- "libkeystore2_crypto",
- "libkm_compat_service",
- "libkm_compat",
- "libm",
- "libstatspull",
- "libstatssocket",
- ],
-
vintf_fragments: ["android.system.keystore2-service.xml"],
required: ["keystore_cli_v2"],
diff --git a/keystore2/TEST_MAPPING b/keystore2/TEST_MAPPING
index 127ff1e..049adc7 100644
--- a/keystore2/TEST_MAPPING
+++ b/keystore2/TEST_MAPPING
@@ -14,6 +14,19 @@
},
{
"name": "CtsIdentityTestCases"
+ },
+ {
+ "name": "CtsKeystoreTestCases",
+ "options": [
+ {
+ "include-annotation": "android.platform.test.annotations.RequiresDevice"
+ }
+ ]
+ }
+ ],
+ "postsubmit": [
+ {
+ "name": "CtsKeystorePerformanceTestCases"
}
]
}
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index 4a7b7b4..7eb2b83 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -24,7 +24,7 @@
aidl_interface {
name: "android.security.attestationmanager",
srcs: [ "android/security/attestationmanager/*.aidl", ],
- imports: [ "android.hardware.security.keymint-V1" ],
+ imports: [ "android.hardware.security.keymint-V2" ],
unstable: true,
backend: {
java: {
@@ -45,7 +45,7 @@
name: "android.security.authorization",
srcs: [ "android/security/authorization/*.aidl" ],
imports: [
- "android.hardware.security.keymint-V1",
+ "android.hardware.security.keymint-V2",
"android.hardware.security.secureclock-V1",
],
unstable: true,
@@ -86,7 +86,7 @@
name: "android.security.compat",
srcs: [ "android/security/compat/*.aidl" ],
imports: [
- "android.hardware.security.keymint-V1",
+ "android.hardware.security.keymint-V2",
"android.hardware.security.secureclock-V1",
"android.hardware.security.sharedsecret-V1",
],
@@ -110,7 +110,7 @@
name: "android.security.remoteprovisioning",
srcs: [ "android/security/remoteprovisioning/*.aidl" ],
imports: [
- "android.hardware.security.keymint-V1",
+ "android.hardware.security.keymint-V2",
],
unstable: true,
backend: {
@@ -132,7 +132,7 @@
name: "android.security.maintenance",
srcs: [ "android/security/maintenance/*.aidl" ],
imports: [
- "android.system.keystore2-V1",
+ "android.system.keystore2-V2",
],
unstable: true,
backend: {
@@ -173,7 +173,7 @@
name: "android.security.metrics",
srcs: [ "android/security/metrics/*.aidl" ],
imports: [
- "android.system.keystore2-V1",
+ "android.system.keystore2-V2",
],
unstable: true,
backend: {
@@ -191,3 +191,19 @@
},
}
+// cc_defaults that includes the latest Keystore2 AIDL library.
+// Modules that depend on KeyMint directly can include this cc_defaults to avoid
+// managing dependency versions explicitly.
+cc_defaults {
+ name: "keystore2_use_latest_aidl_ndk_static",
+ static_libs: [
+ "android.system.keystore2-V2-ndk",
+ ],
+}
+
+cc_defaults {
+ name: "keystore2_use_latest_aidl_ndk_shared",
+ shared_libs: [
+ "android.system.keystore2-V2-ndk",
+ ],
+}
diff --git a/keystore2/aidl/android/security/metrics/EcCurve.aidl b/keystore2/aidl/android/security/metrics/EcCurve.aidl
index b190d83..7b1a5a2 100644
--- a/keystore2/aidl/android/security/metrics/EcCurve.aidl
+++ b/keystore2/aidl/android/security/metrics/EcCurve.aidl
@@ -29,4 +29,5 @@
P_256 = 2,
P_384 = 3,
P_521 = 4,
+ CURVE_25519 = 5,
}
\ No newline at end of file
diff --git a/keystore2/android.system.keystore2-service.xml b/keystore2/android.system.keystore2-service.xml
index 6b8d0cb..20c2fba 100644
--- a/keystore2/android.system.keystore2-service.xml
+++ b/keystore2/android.system.keystore2-service.xml
@@ -1,6 +1,7 @@
<manifest version="1.0" type="framework">
<hal format="aidl">
<name>android.system.keystore2</name>
+ <version>2</version>
<interface>
<name>IKeystoreService</name>
<instance>default</instance>
diff --git a/keystore2/src/crypto/error.rs b/keystore2/src/crypto/error.rs
index a369012..c6476f9 100644
--- a/keystore2/src/crypto/error.rs
+++ b/keystore2/src/crypto/error.rs
@@ -13,6 +13,7 @@
// limitations under the License.
//! This module implements Error for the keystore2_crypto library.
+use crate::zvec;
/// Crypto specific error codes.
#[derive(Debug, thiserror::Error, Eq, PartialEq)]
@@ -93,4 +94,8 @@
/// This is returned if the C implementation of extractSubjectFromCertificate failed.
#[error("Failed to extract certificate subject.")]
ExtractSubjectFailed,
+
+ /// Zvec error.
+ #[error(transparent)]
+ ZVec(#[from] zvec::Error),
}
diff --git a/keystore2/src/crypto/lib.rs b/keystore2/src/crypto/lib.rs
index 5f8a2ef..92da965 100644
--- a/keystore2/src/crypto/lib.rs
+++ b/keystore2/src/crypto/lib.rs
@@ -16,7 +16,7 @@
//! Keystore 2.0.
mod error;
-mod zvec;
+pub mod zvec;
pub use error::Error;
use keystore2_crypto_bindgen::{
extractSubjectFromCertificate, generateKeyFromPassword, randomBytes, AES_gcm_decrypt,
diff --git a/keystore2/src/crypto/zvec.rs b/keystore2/src/crypto/zvec.rs
index 78b474e..5a173c3 100644
--- a/keystore2/src/crypto/zvec.rs
+++ b/keystore2/src/crypto/zvec.rs
@@ -12,7 +12,9 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-use crate::error::Error;
+//! Implements ZVec, a vector that is mlocked during its lifetime and zeroed
+//! when dropped.
+
use nix::sys::mman::{mlock, munlock};
use std::convert::TryFrom;
use std::fmt;
@@ -29,6 +31,14 @@
len: usize,
}
+/// ZVec specific error codes.
+#[derive(Debug, thiserror::Error, Eq, PartialEq)]
+pub enum Error {
+ /// Underlying libc error.
+ #[error(transparent)]
+ NixError(#[from] nix::Error),
+}
+
impl ZVec {
/// Create a ZVec with the given size.
pub fn new(size: usize) -> Result<Self, Error> {
@@ -48,6 +58,14 @@
self.len = len;
}
}
+
+ /// Attempts to make a clone of the Zvec. This may fail due trying to mlock
+ /// the new memory region.
+ pub fn try_clone(&self) -> Result<Self, Error> {
+ let mut result = Self::new(self.len())?;
+ result[..].copy_from_slice(&self[..]);
+ Ok(result)
+ }
}
impl Drop for ZVec {
diff --git a/keystore2/src/enforcements.rs b/keystore2/src/enforcements.rs
index 997e739..2407525 100644
--- a/keystore2/src/enforcements.rs
+++ b/keystore2/src/enforcements.rs
@@ -629,7 +629,7 @@
let hat_and_last_off_body = if need_auth_token {
let hat_and_last_off_body = Self::find_auth_token(|hat: &AuthTokenEntry| {
- if let (Some(auth_type), true) = (user_auth_type, has_sids) {
+ if let (Some(auth_type), true) = (user_auth_type, timeout_bound) {
hat.satisfies(&user_secure_ids, auth_type)
} else {
unlocked_device_required
diff --git a/keystore2/src/km_compat/Android.bp b/keystore2/src/km_compat/Android.bp
index 32406ae..806f3dc 100644
--- a/keystore2/src/km_compat/Android.bp
+++ b/keystore2/src/km_compat/Android.bp
@@ -25,9 +25,10 @@
name: "libkeystore2_km_compat",
crate_name: "keystore2_km_compat",
srcs: ["lib.rs"],
-
+ defaults: [
+ "keymint_use_latest_hal_aidl_rust",
+ ],
rustlibs: [
- "android.hardware.security.keymint-V1-rust",
"android.security.compat-rust",
],
shared_libs: [
@@ -41,8 +42,10 @@
srcs: ["lib.rs"],
test_suites: ["general-tests"],
auto_gen_config: true,
+ defaults: [
+ "keymint_use_latest_hal_aidl_rust",
+ ],
rustlibs: [
- "android.hardware.security.keymint-V1-rust",
"android.security.compat-rust",
],
shared_libs: [
@@ -53,15 +56,17 @@
cc_library {
name: "libkm_compat",
srcs: ["km_compat.cpp"],
+ defaults: [
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ "keystore2_use_latest_aidl_ndk_shared",
+ ],
shared_libs: [
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
- "android.hardware.security.keymint-V1-ndk",
"android.hardware.security.secureclock-V1-ndk",
"android.hardware.security.sharedsecret-V1-ndk",
"android.security.compat-ndk",
- "android.system.keystore2-V1-ndk",
"libbase",
"libbinder_ndk",
"libcrypto",
@@ -77,8 +82,10 @@
cc_library {
name: "libkm_compat_service",
srcs: ["km_compat_service.cpp"],
+ defaults: [
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ ],
shared_libs: [
- "android.hardware.security.keymint-V1-ndk",
"android.hardware.security.secureclock-V1-ndk",
"android.hardware.security.sharedsecret-V1-ndk",
"android.security.compat-ndk",
@@ -103,15 +110,17 @@
"parameter_conversion_test.cpp",
"slot_test.cpp",
],
+ defaults: [
+ "keymint_use_latest_hal_aidl_ndk_shared",
+ "keystore2_use_latest_aidl_ndk_shared",
+ ],
shared_libs: [
"android.hardware.keymaster@3.0",
"android.hardware.keymaster@4.0",
"android.hardware.keymaster@4.1",
- "android.hardware.security.keymint-V1-ndk",
"android.hardware.security.secureclock-V1-ndk",
"android.hardware.security.sharedsecret-V1-ndk",
"android.security.compat-ndk",
- "android.system.keystore2-V1-ndk",
"libbase",
"libbinder_ndk",
"libcrypto",
diff --git a/keystore2/src/km_compat/km_compat.cpp b/keystore2/src/km_compat/km_compat.cpp
index 40ca554..bb60047 100644
--- a/keystore2/src/km_compat/km_compat.cpp
+++ b/keystore2/src/km_compat/km_compat.cpp
@@ -762,7 +762,21 @@
return convertErrorCode(errorCode);
}
-ScopedAStatus KeyMintOperation::update(const std::vector<uint8_t>& input,
+void KeyMintOperation::setUpdateBuffer(std::vector<uint8_t> data) {
+ mUpdateBuffer = std::move(data);
+}
+
+const std::vector<uint8_t>&
+KeyMintOperation::getExtendedUpdateBuffer(const std::vector<uint8_t>& suffix) {
+ if (mUpdateBuffer.empty()) {
+ return suffix;
+ } else {
+ mUpdateBuffer.insert(mUpdateBuffer.end(), suffix.begin(), suffix.end());
+ return mUpdateBuffer;
+ }
+}
+
+ScopedAStatus KeyMintOperation::update(const std::vector<uint8_t>& input_raw,
const std::optional<HardwareAuthToken>& optAuthToken,
const std::optional<TimeStampToken>& optTimeStampToken,
std::vector<uint8_t>* out_output) {
@@ -772,8 +786,10 @@
size_t inputPos = 0;
*out_output = {};
KMV1::ErrorCode errorCode = KMV1::ErrorCode::OK;
+ auto input = getExtendedUpdateBuffer(input_raw);
while (inputPos < input.size() && errorCode == KMV1::ErrorCode::OK) {
+ uint32_t consumed = 0;
auto result =
mDevice->update(mOperationHandle, {} /* inParams */,
{input.begin() + inputPos, input.end()}, authToken, verificationToken,
@@ -781,13 +797,22 @@
const hidl_vec<uint8_t>& output) {
errorCode = convert(error);
out_output->insert(out_output->end(), output.begin(), output.end());
- inputPos += inputConsumed;
+ consumed = inputConsumed;
});
if (!result.isOk()) {
LOG(ERROR) << __func__ << " transaction failed. " << result.description();
errorCode = KMV1::ErrorCode::UNKNOWN_ERROR;
}
+
+ if (errorCode == KMV1::ErrorCode::OK && consumed == 0) {
+ // Some very old KM implementations do not buffer sub blocks in certain block modes,
+ // instead, the simply return consumed == 0. So we buffer the input here in the
+ // hope that we complete the bock in a future call to update.
+ setUpdateBuffer({input.begin() + inputPos, input.end()});
+ return convertErrorCode(errorCode);
+ }
+ inputPos += consumed;
}
if (errorCode != KMV1::ErrorCode::OK) mOperationSlot.freeSlot();
@@ -802,7 +827,8 @@
const std::optional<TimeStampToken>& in_timeStampToken,
const std::optional<std::vector<uint8_t>>& in_confirmationToken,
std::vector<uint8_t>* out_output) {
- auto input = in_input.value_or(std::vector<uint8_t>());
+ auto input_raw = in_input.value_or(std::vector<uint8_t>());
+ auto input = getExtendedUpdateBuffer(input_raw);
auto signature = in_signature.value_or(std::vector<uint8_t>());
V4_0_HardwareAuthToken authToken = convertAuthTokenToLegacy(in_authToken);
V4_0_VerificationToken verificationToken = convertTimestampTokenToLegacy(in_timeStampToken);
diff --git a/keystore2/src/km_compat/km_compat.h b/keystore2/src/km_compat/km_compat.h
index 2d892da..70c7b86 100644
--- a/keystore2/src/km_compat/km_compat.h
+++ b/keystore2/src/km_compat/km_compat.h
@@ -140,11 +140,6 @@
};
class KeyMintOperation : public aidl::android::hardware::security::keymint::BnKeyMintOperation {
- private:
- ::android::sp<Keymaster> mDevice;
- uint64_t mOperationHandle;
- OperationSlot mOperationSlot;
-
public:
KeyMintOperation(::android::sp<Keymaster> device, uint64_t operationHandle,
OperationSlots* slots, bool isActive)
@@ -168,6 +163,25 @@
std::vector<uint8_t>* output) override;
ScopedAStatus abort();
+
+ private:
+ /**
+ * Sets mUpdateBuffer to the given value.
+ * @param data
+ */
+ void setUpdateBuffer(std::vector<uint8_t> data);
+ /**
+ * If mUpdateBuffer is not empty, suffix is appended to mUpdateBuffer, and a reference to
+ * mUpdateBuffer is returned. Otherwise a reference to suffix is returned.
+ * @param suffix
+ * @return
+ */
+ const std::vector<uint8_t>& getExtendedUpdateBuffer(const std::vector<uint8_t>& suffix);
+
+ std::vector<uint8_t> mUpdateBuffer;
+ ::android::sp<Keymaster> mDevice;
+ uint64_t mOperationHandle;
+ OperationSlot mOperationSlot;
};
class SharedSecret : public aidl::android::hardware::security::sharedsecret::BnSharedSecret {
diff --git a/keystore2/src/km_compat/km_compat_type_conversion.h b/keystore2/src/km_compat/km_compat_type_conversion.h
index de09477..33248a4 100644
--- a/keystore2/src/km_compat/km_compat_type_conversion.h
+++ b/keystore2/src/km_compat/km_compat_type_conversion.h
@@ -16,6 +16,9 @@
#pragma once
+#include <optional>
+
+#include <aidl/android/hardware/security/keymint/EcCurve.h>
#include <aidl/android/hardware/security/keymint/ErrorCode.h>
#include <keymasterV4_1/keymaster_tags.h>
#include <keymint_support/keymint_tags.h>
@@ -278,7 +281,7 @@
}
}
-static V4_0::EcCurve convert(KMV1::EcCurve e) {
+static std::optional<V4_0::EcCurve> convert(KMV1::EcCurve e) {
switch (e) {
case KMV1::EcCurve::P_224:
return V4_0::EcCurve::P_224;
@@ -288,7 +291,11 @@
return V4_0::EcCurve::P_384;
case KMV1::EcCurve::P_521:
return V4_0::EcCurve::P_521;
+ case KMV1::EcCurve::CURVE_25519:
+ // KeyMaster did not support curve 25519
+ return std::nullopt;
}
+ return std::nullopt;
}
static KMV1::EcCurve convert(V4_0::EcCurve e) {
@@ -490,7 +497,9 @@
break;
case KMV1::Tag::EC_CURVE:
if (auto v = KMV1::authorizationValue(KMV1::TAG_EC_CURVE, kp)) {
- return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, convert(v->get()));
+ if (auto curve = convert(v->get())) {
+ return V4_0::makeKeyParameter(V4_0::TAG_EC_CURVE, curve.value());
+ }
}
break;
case KMV1::Tag::RSA_PUBLIC_EXPONENT:
diff --git a/keystore2/src/maintenance.rs b/keystore2/src/maintenance.rs
index 3f6cf36..57abc26 100644
--- a/keystore2/src/maintenance.rs
+++ b/keystore2/src/maintenance.rs
@@ -215,6 +215,8 @@
fn migrate_key_namespace(source: &KeyDescriptor, destination: &KeyDescriptor) -> Result<()> {
let caller_uid = ThreadState::get_calling_uid();
+ let migrate_any_key_permission =
+ check_keystore_permission(KeystorePerm::MigrateAnyKey).is_ok();
DB.with(|db| {
let key_id_guard = match source.domain {
@@ -227,9 +229,12 @@
KeyEntryLoadBits::NONE,
caller_uid,
|k, av| {
- check_key_permission(KeyPerm::Use, k, &av)?;
- check_key_permission(KeyPerm::Delete, k, &av)?;
- check_key_permission(KeyPerm::Grant, k, &av)
+ if !migrate_any_key_permission {
+ check_key_permission(KeyPerm::Use, k, &av)?;
+ check_key_permission(KeyPerm::Delete, k, &av)?;
+ check_key_permission(KeyPerm::Grant, k, &av)?;
+ }
+ Ok(())
},
)
})
@@ -245,7 +250,10 @@
};
db.borrow_mut().migrate_key_namespace(key_id_guard, destination, caller_uid, |k| {
- check_key_permission(KeyPerm::Rebind, k, &None)
+ if !migrate_any_key_permission {
+ check_key_permission(KeyPerm::Rebind, k, &None)?;
+ }
+ Ok(())
})
})
}
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
index 741d65e..b18d84c 100644
--- a/keystore2/src/metrics_store.rs
+++ b/keystore2/src/metrics_store.rs
@@ -17,7 +17,7 @@
//! stores them in an in-memory store.
//! 2. Returns the collected metrics when requested by the statsd proxy.
-use crate::error::get_error_code;
+use crate::error::{get_error_code, Error};
use crate::globals::DB;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
use crate::operation::Outcome;
@@ -44,6 +44,7 @@
RkpPoolStats::RkpPoolStats, SecurityLevel::SecurityLevel as MetricsSecurityLevel,
Storage::Storage as MetricsStorage,
};
+use android_system_keystore2::aidl::android::system::keystore2::ResponseCode::ResponseCode;
use anyhow::{Context, Result};
use lazy_static::lazy_static;
use rustutils::system_properties::PropertyWatcherError;
@@ -287,6 +288,7 @@
EcCurve::P_256 => MetricsEcCurve::P_256,
EcCurve::P_384 => MetricsEcCurve::P_384,
EcCurve::P_521 => MetricsEcCurve::P_521,
+ EcCurve::CURVE_25519 => MetricsEcCurve::CURVE_25519,
_ => MetricsEcCurve::EC_CURVE_UNSPECIFIED,
}
}
@@ -560,10 +562,14 @@
fn pull_attestation_pool_stats() -> Result<Vec<KeystoreAtom>> {
let mut atoms = Vec::<KeystoreAtom>::new();
for sec_level in &[SecurityLevel::TRUSTED_ENVIRONMENT, SecurityLevel::STRONGBOX] {
+ // set the expired_by date to be three days from now
let expired_by = SystemTime::now()
+ .checked_add(Duration::from_secs(60 * 60 * 24 * 3))
+ .ok_or(Error::Rc(ResponseCode::SYSTEM_ERROR))
+ .context("In pull_attestation_pool_stats: Failed to compute expired by system time.")?
.duration_since(UNIX_EPOCH)
- .unwrap_or_else(|_| Duration::new(0, 0))
- .as_secs() as i64;
+ .context("In pull_attestation_pool_stats: Failed to compute expired by duration.")?
+ .as_millis() as i64;
let result = get_pool_status(expired_by, *sec_level);
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index f280341..e6d61b0 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -145,6 +145,10 @@
/// Checked when IKeystoreMaintenance::deleteAllKeys is called.
#[selinux(name = delete_all_keys)]
DeleteAllKeys,
+ /// Checked when migrating any key from any namespace to any other namespace. It was
+ /// introduced for migrating keys when an app leaves a sharedUserId.
+ #[selinux(name = migrate_any_key)]
+ MigrateAnyKey,
}
);
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index a19462b..66e1988 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -38,6 +38,8 @@
};
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, KeystoreDB, Uuid};
@@ -56,6 +58,11 @@
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 {
@@ -261,6 +268,27 @@
Ok(BnRemoteProvisioning::new_binder(result, BinderFeatures::default()))
}
+ fn extract_payload_from_cose_mac(data: &[u8]) -> Result<Value> {
+ let cose_mac0: Vec<Value> = serde_cbor::from_slice(data).context(
+ "In extract_payload_from_cose_mac: COSE_Mac0 returned from IRPC cannot be parsed",
+ )?;
+ if cose_mac0.len() != COSE_MAC0_LEN {
+ return Err(error::Error::sys()).context(format!(
+ "In extract_payload_from_cose_mac: COSE_Mac0 has improper length. \
+ Expected: {}, Actual: {}",
+ COSE_MAC0_LEN,
+ cose_mac0.len(),
+ ));
+ }
+ match &cose_mac0[COSE_MAC0_PAYLOAD] {
+ Value::Bytes(key) => Ok(serde_cbor::from_slice(key)
+ .context("In extract_payload_from_cose_mac: COSE_Mac0 payload is malformed.")?),
+ _ => Err(error::Error::sys()).context(
+ "In extract_payload_from_cose_mac: COSE_Mac0 payload is the wrong type.",
+ )?,
+ }
+ }
+
/// Generates a CBOR blob which will be assembled by the calling code into a larger
/// CBOR blob intended for delivery to a provisioning serever. This blob will contain
/// `num_csr` certificate signing requests for attestation keys generated in the TEE,
@@ -290,7 +318,7 @@
.map(|key| MacedPublicKey { macedKey: key.to_vec() })
.collect())
})?;
- let mut mac = map_rem_prov_error(dev.generateCertificateRequest(
+ let mac = map_rem_prov_error(dev.generateCertificateRequest(
test_mode,
&keys_to_sign,
eek,
@@ -299,30 +327,16 @@
protected_data,
))
.context("In generate_csr: Failed to generate csr")?;
- // TODO(b/180392379): Replace this manual CBOR generation with the cbor-serde crate as well.
- // This generates an array consisting of the mac and the public key Maps.
- // Just generate the actual MacedPublicKeys structure when the crate is
- // available.
- let mut cose_mac_0: Vec<u8> = vec![
- (0b100_00000 | (keys_to_sign.len() + 1)) as u8,
- 0b010_11000, // mac
- (mac.len() as u8),
- ];
- cose_mac_0.append(&mut mac);
- // If this is a test mode key, there is an extra 6 bytes added as an additional entry in
- // the COSE_Key struct to denote that.
- let test_mode_entry_shift = if test_mode { 0 } else { 6 };
- let byte_dist_mac0_payload = 8;
- let cose_key_size = 83 - test_mode_entry_shift;
+ let mut mac_and_keys: Vec<Value> = vec![Value::from(mac)];
for maced_public_key in keys_to_sign {
- if maced_public_key.macedKey.len() > cose_key_size + byte_dist_mac0_payload {
- cose_mac_0.extend_from_slice(
- &maced_public_key.macedKey
- [byte_dist_mac0_payload..cose_key_size + byte_dist_mac0_payload],
- );
- }
+ mac_and_keys.push(
+ Self::extract_payload_from_cose_mac(&maced_public_key.macedKey)
+ .context("In generate_csr: Failed to get the payload from the COSE_Mac0")?,
+ )
}
- Ok(cose_mac_0)
+ let cbor_array: Value = Value::Array(mac_and_keys);
+ serde_cbor::to_vec(&cbor_array)
+ .context("In generate_csr: Failed to serialize the mac and keys array")
}
/// Provisions a certificate chain for a key whose CSR was included in generate_csr. The
@@ -351,6 +365,66 @@
})
}
+ fn parse_cose_mac0_for_coords(data: &[u8]) -> Result<Vec<u8>> {
+ let cose_mac0: Vec<Value> = serde_cbor::from_slice(data).context(
+ "In parse_cose_mac0_for_coords: COSE_Mac0 returned from IRPC cannot be parsed",
+ )?;
+ if cose_mac0.len() != COSE_MAC0_LEN {
+ return Err(error::Error::sys()).context(format!(
+ "In parse_cose_mac0_for_coords: COSE_Mac0 has improper length. \
+ Expected: {}, Actual: {}",
+ COSE_MAC0_LEN,
+ cose_mac0.len(),
+ ));
+ }
+ let cose_key: BTreeMap<Value, Value> = match &cose_mac0[COSE_MAC0_PAYLOAD] {
+ Value::Bytes(key) => serde_cbor::from_slice(key)
+ .context("In parse_cose_mac0_for_coords: COSE_Key is malformed.")?,
+ _ => Err(error::Error::sys())
+ .context("In parse_cose_mac0_for_coords: COSE_Mac0 payload is the wrong type.")?,
+ };
+ if !cose_key.contains_key(&COSE_KEY_XCOORD) || !cose_key.contains_key(&COSE_KEY_YCOORD) {
+ return Err(error::Error::sys()).context(
+ "In parse_cose_mac0_for_coords: \
+ COSE_Key returned from IRPC is lacking required fields",
+ );
+ }
+ let mut raw_key: Vec<u8> = vec![0; 64];
+ match &cose_key[&COSE_KEY_XCOORD] {
+ Value::Bytes(x_coord) if x_coord.len() == 32 => {
+ raw_key[0..32].clone_from_slice(x_coord)
+ }
+ Value::Bytes(x_coord) => {
+ return Err(error::Error::sys()).context(format!(
+ "In parse_cose_mac0_for_coords: COSE_Key X-coordinate is not the right length. \
+ Expected: 32; Actual: {}",
+ x_coord.len()
+ ))
+ }
+ _ => {
+ return Err(error::Error::sys())
+ .context("In parse_cose_mac0_for_coords: COSE_Key X-coordinate is not a bstr")
+ }
+ }
+ match &cose_key[&COSE_KEY_YCOORD] {
+ Value::Bytes(y_coord) if y_coord.len() == 32 => {
+ raw_key[32..64].clone_from_slice(y_coord)
+ }
+ Value::Bytes(y_coord) => {
+ return Err(error::Error::sys()).context(format!(
+ "In parse_cose_mac0_for_coords: COSE_Key Y-coordinate is not the right length. \
+ Expected: 32; Actual: {}",
+ y_coord.len()
+ ))
+ }
+ _ => {
+ return Err(error::Error::sys())
+ .context("In parse_cose_mac0_for_coords: COSE_Key Y-coordinate is not a bstr")
+ }
+ }
+ Ok(raw_key)
+ }
+
/// 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,
@@ -362,18 +436,8 @@
let priv_key =
map_rem_prov_error(dev.generateEcdsaP256KeyPair(is_test_mode, &mut maced_key))
.context("In generate_key_pair: Failed to generated ECDSA keypair.")?;
- // TODO(b/180392379): This is a brittle hack that relies on the consistent formatting of
- // the returned CBOR blob in order to extract the public key.
- let data = &maced_key.macedKey;
- if data.len() < 85 {
- return Err(error::Error::sys()).context(concat!(
- "In generate_key_pair: CBOR blob returned from",
- "RemotelyProvisionedComponent is definitely malformatted or empty."
- ));
- }
- let mut raw_key: Vec<u8> = vec![0; 64];
- raw_key[0..32].clone_from_slice(&data[18..18 + 32]);
- raw_key[32..64].clone_from_slice(&data[53..53 + 32]);
+ let raw_key = Self::parse_cose_mac0_for_coords(&maced_key.macedKey)
+ .context("In generate_key_pair: Failed to parse raw key")?;
DB.with::<_, Result<()>>(|db| {
let mut db = db.borrow_mut();
db.create_attestation_key_entry(&maced_key.macedKey, &raw_key, &priv_key, &uuid)
@@ -489,3 +553,108 @@
map_or_log_err(self.delete_all_keys(), Ok)
}
}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+ use serde_cbor::Value;
+ use std::collections::BTreeMap;
+
+ #[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(())
+ }
+}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index efa0389..42d3074 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -74,6 +74,34 @@
],
}
+cc_library {
+ name: "libsigningutils",
+ defaults: [
+ "odsign_flags_defaults",
+ ],
+ cpp_std: "experimental",
+ srcs: [
+ "CertUtils.cpp",
+ "VerityUtils.cpp",
+ ],
+
+ static_libs: [
+ "libc++fs",
+ "lib_compos_proto",
+ ],
+
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ "libcrypto_utils",
+ "libfsverity",
+ "libprotobuf-cpp-lite",
+ "libutils",
+ ],
+ export_include_dirs: ["include"],
+ recovery_available: true,
+}
+
cc_binary {
name: "odsign",
defaults: [
@@ -82,21 +110,19 @@
cpp_std: "experimental",
init_rc: ["odsign.rc"],
srcs: [
- "odsign_main.cpp",
- "CertUtils.cpp",
"KeystoreKey.cpp",
"KeystoreHmacKey.cpp",
- "VerityUtils.cpp",
+ "odsign_main.cpp",
],
header_libs: ["odrefresh_headers"],
static_libs: [
"libc++fs",
+ "libsigningutils",
"lib_odsign_proto",
"lib_compos_proto",
],
-
shared_libs: [
"android.system.keystore2-V1-cpp",
"android.hardware.security.keymint-V1-cpp",
diff --git a/ondevice-signing/TEST_MAPPING b/ondevice-signing/TEST_MAPPING
new file mode 100644
index 0000000..4b2c8c6
--- /dev/null
+++ b/ondevice-signing/TEST_MAPPING
@@ -0,0 +1,7 @@
+{
+ "presubmit": [
+ {
+ "name": "libsigningutils_test"
+ }
+ ]
+}
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 2beb7eb..54490bd 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -14,6 +14,7 @@
* limitations under the License.
*/
+#include <charconv>
#include <filesystem>
#include <map>
#include <span>
@@ -33,7 +34,6 @@
#include "CertUtils.h"
#include "SigningKey.h"
-#include "compos_signature.pb.h"
#define FS_VERITY_MAX_DIGEST_SIZE 64
@@ -42,14 +42,7 @@
using android::base::Result;
using android::base::unique_fd;
-using compos::proto::Signature;
-
static const char* kFsVerityInitPath = "/system/bin/fsverity_init";
-static const char* kSignatureExtension = ".signature";
-
-static bool isSignatureFile(const std::filesystem::path& path) {
- return path.extension().native() == kSignatureExtension;
-}
static std::string toHex(std::span<const uint8_t> data) {
std::stringstream ss;
@@ -59,6 +52,23 @@
return ss.str();
}
+static std::vector<uint8_t> fromHex(std::string_view hex) {
+ if (hex.size() % 2 != 0) {
+ return {};
+ }
+ std::vector<uint8_t> result;
+ result.reserve(hex.size() / 2);
+ for (size_t i = 0; i < hex.size(); i += 2) {
+ uint8_t byte;
+ auto conversion_result = std::from_chars(&hex[i], &hex[i + 2], byte, 16);
+ if (conversion_result.ptr != &hex[i + 2] || conversion_result.ec != std::errc()) {
+ return {};
+ }
+ result.push_back(byte);
+ }
+ return result;
+}
+
static int read_callback(void* file, void* buf, size_t count) {
int* fd = (int*)file;
if (TEMP_FAILURE_RETRY(read(*fd, buf, count)) < 0) return errno ? -errno : -EIO;
@@ -155,15 +165,10 @@
return {};
}
-Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (!fd.ok()) {
- return ErrnoError() << "Failed to open " << path;
- }
-
- auto digest = createDigest(fd.get());
+Result<std::string> enableFsVerity(int fd, const SigningKey& key) {
+ auto digest = createDigest(fd);
if (!digest.ok()) {
- return Error() << digest.error() << ": " << path;
+ return Error() << digest.error();
}
auto signed_digest = signDigest(key, digest.value());
@@ -176,36 +181,26 @@
return pkcs7_data.error();
}
- auto enabled = enableFsVerity(fd.get(), pkcs7_data.value());
+ auto enabled = enableFsVerity(fd, pkcs7_data.value());
if (!enabled.ok()) {
- return Error() << enabled.error() << ": " << path;
+ return Error() << enabled.error();
}
// Return the root hash as a hex string
return toHex(digest.value());
}
-Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
- const SigningKey& key) {
- std::map<std::string, std::string> digests;
-
- std::error_code ec;
- auto it = std::filesystem::recursive_directory_iterator(path, ec);
- for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
- if (it->is_regular_file()) {
- LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
- auto result = enableFsVerity(it->path(), key);
- if (!result.ok()) {
- return result.error();
- }
- digests[it->path()] = *result;
- }
- }
- if (ec) {
- return Error() << "Failed to iterate " << path << ": " << ec.message();
+Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (!fd.ok()) {
+ return ErrnoError() << "Failed to open " << path;
}
- return digests;
+ auto enableStatus = enableFsVerity(fd.get(), key);
+ if (!enableStatus.ok()) {
+ return Error() << path << ": " << enableStatus.error();
+ }
+ return enableStatus;
}
Result<std::string> isFileInVerity(int fd) {
@@ -228,7 +223,7 @@
return ErrnoError() << "Failed to open " << path;
}
- auto digest = isFileInVerity(fd);
+ auto digest = isFileInVerity(fd.get());
if (!digest.ok()) {
return Error() << digest.error() << ": " << path;
}
@@ -236,6 +231,39 @@
return digest;
}
+Result<std::map<std::string, std::string>> addFilesToVerityRecursive(const std::string& path,
+ const SigningKey& key) {
+ std::map<std::string, std::string> digests;
+
+ std::error_code ec;
+ auto it = std::filesystem::recursive_directory_iterator(path, ec);
+ for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
+ if (it->is_regular_file()) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(it->path().c_str(), O_RDONLY | O_CLOEXEC)));
+ if (!fd.ok()) {
+ return ErrnoError() << "Failed to open " << path;
+ }
+ auto digest = isFileInVerity(fd);
+ if (!digest.ok()) {
+ LOG(INFO) << "Adding " << it->path() << " to fs-verity...";
+ auto result = enableFsVerity(fd, key);
+ if (!result.ok()) {
+ return result.error();
+ }
+ digests[it->path()] = *result;
+ } else {
+ LOG(INFO) << it->path() << " was already in fs-verity.";
+ digests[it->path()] = *digest;
+ }
+ }
+ }
+ if (ec) {
+ return Error() << "Failed to iterate " << path << ": " << ec.message();
+ }
+
+ return digests;
+}
+
Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
std::map<std::string, std::string> digests;
std::error_code ec;
@@ -267,99 +295,62 @@
return digests;
}
-Result<Signature> readSignature(const std::filesystem::path& signature_path) {
- unique_fd fd(TEMP_FAILURE_RETRY(open(signature_path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd == -1) {
- return ErrnoError();
- }
- Signature signature;
- if (!signature.ParseFromFileDescriptor(fd.get())) {
- return Error() << "Failed to parse";
- }
- return signature;
-}
-
-Result<std::map<std::string, std::string>>
-verifyAllFilesUsingCompOs(const std::string& directory_path,
- const std::vector<uint8_t>& compos_key) {
- std::map<std::string, std::string> new_digests;
- std::vector<std::filesystem::path> signature_files;
-
+Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
+ std::map<std::string, std::string> digests,
+ const SigningKey& signing_key) {
std::error_code ec;
auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
+ size_t verified_count = 0;
for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
auto& path = it->path();
if (it->is_regular_file()) {
- if (isSignatureFile(path)) {
- continue;
+ auto entry = digests.find(path);
+ if (entry == digests.end()) {
+ return Error() << "Unexpected file found: " << path;
}
+ auto& compos_digest = entry->second;
unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
if (!fd.ok()) {
return ErrnoError() << "Can't open " << path;
}
- auto signature_path = path;
- signature_path += kSignatureExtension;
- auto signature = readSignature(signature_path);
- if (!signature.ok()) {
- return Error() << "Invalid signature " << signature_path << ": "
- << signature.error();
- }
- signature_files.push_back(signature_path);
-
- // Note that these values are not yet trusted.
- auto& raw_digest = signature->digest();
- auto& raw_signature = signature->signature();
-
- // Re-construct the fsverity_formatted_digest that was signed, so we
- // can verify the signature.
- std::vector<uint8_t> buffer(sizeof(fsverity_formatted_digest) + raw_digest.size());
- auto signed_data = new (buffer.data()) fsverity_formatted_digest;
- memcpy(signed_data->magic, "FSVerity", sizeof signed_data->magic);
- signed_data->digest_algorithm = __cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
- signed_data->digest_size = __cpu_to_le16(raw_digest.size());
- memcpy(signed_data->digest, raw_digest.data(), raw_digest.size());
-
- // Make sure the signature matches the CompOs public key, and not some other
- // fs-verity trusted key.
- std::string to_verify(reinterpret_cast<char*>(buffer.data()), buffer.size());
-
- auto verified = verifyRsaPublicKeySignature(to_verify, raw_signature, compos_key);
- if (!verified.ok()) {
- return Error() << verified.error() << ": " << path;
- }
-
- std::span<const uint8_t> digest_bytes(
- reinterpret_cast<const uint8_t*>(raw_digest.data()), raw_digest.size());
- std::string compos_digest = toHex(digest_bytes);
-
auto verity_digest = isFileInVerity(fd);
if (verity_digest.ok()) {
// The file is already in fs-verity. We need to make sure it was signed
- // by CompOs, so we just check that it has the digest we expect.
- if (verity_digest.value() != compos_digest) {
- return Error() << "fs-verity digest does not match signature file: " << path;
+ // by CompOS, so we just check that it has the digest we expect.
+ if (verity_digest.value() == compos_digest) {
+ ++verified_count;
+ } else {
+ return Error() << "fs-verity digest does not match CompOS digest: " << path;
}
} else {
- // Not in fs-verity yet. But we have a valid signature of some
- // digest. If it's not the correct digest for the file then
- // enabling fs-verity will fail, so we don't need to check it
- // explicitly ourselves. Otherwise we should be good.
- std::vector<uint8_t> signature_bytes(raw_signature.begin(), raw_signature.end());
- auto pkcs7 = createPkcs7(signature_bytes, kCompOsSubject);
- if (!pkcs7.ok()) {
- return Error() << pkcs7.error() << ": " << path;
- }
-
+ // Not in fs-verity yet. We know the digest CompOS provided; If
+ // it's not the correct digest for the file then enabling
+ // fs-verity will fail, so we don't need to check it explicitly
+ // ourselves. Otherwise we should be good.
LOG(INFO) << "Adding " << path << " to fs-verity...";
- auto enabled = enableFsVerity(fd, pkcs7.value());
- if (!enabled.ok()) {
- return Error() << enabled.error() << ": " << path;
- }
- }
- new_digests[path] = compos_digest;
+ auto digest_bytes = fromHex(compos_digest);
+ if (digest_bytes.empty()) {
+ return Error() << "Invalid digest " << compos_digest;
+ }
+ auto signed_digest = signDigest(signing_key, digest_bytes);
+ if (!signed_digest.ok()) {
+ return signed_digest.error();
+ }
+
+ auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject);
+ if (!pkcs7_data.ok()) {
+ return pkcs7_data.error();
+ }
+
+ auto enabled = enableFsVerity(fd, pkcs7_data.value());
+ if (!enabled.ok()) {
+ return Error() << enabled.error();
+ }
+ ++verified_count;
+ }
} else if (it->is_directory()) {
// These are fine to ignore
} else if (it->is_symlink()) {
@@ -371,18 +362,12 @@
if (ec) {
return Error() << "Failed to iterate " << directory_path << ": " << ec.message();
}
-
- // Delete the signature files now that they have served their purpose. (ART
- // has no use for them, and their presence could cause verification to fail
- // on subsequent boots.)
- for (auto& signature_path : signature_files) {
- std::filesystem::remove(signature_path, ec);
- if (ec) {
- return Error() << "Failed to delete " << signature_path << ": " << ec.message();
- }
+ // Make sure all the files we expected have been seen
+ if (verified_count != digests.size()) {
+ return Error() << "Verified " << verified_count << "files, but expected " << digests.size();
}
- return new_digests;
+ return {};
}
Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName) {
diff --git a/ondevice-signing/CertUtils.h b/ondevice-signing/include/CertUtils.h
similarity index 100%
rename from ondevice-signing/CertUtils.h
rename to ondevice-signing/include/CertUtils.h
diff --git a/ondevice-signing/SigningKey.h b/ondevice-signing/include/SigningKey.h
similarity index 100%
rename from ondevice-signing/SigningKey.h
rename to ondevice-signing/include/SigningKey.h
diff --git a/ondevice-signing/VerityUtils.h b/ondevice-signing/include/VerityUtils.h
similarity index 70%
rename from ondevice-signing/VerityUtils.h
rename to ondevice-signing/include/VerityUtils.h
index 8d8e62c..7715628 100644
--- a/ondevice-signing/VerityUtils.h
+++ b/ondevice-signing/include/VerityUtils.h
@@ -18,6 +18,10 @@
#include <android-base/result.h>
+#include <map>
+#include <string>
+#include <vector>
+
#include "SigningKey.h"
android::base::Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName);
@@ -25,8 +29,11 @@
android::base::Result<std::map<std::string, std::string>>
verifyAllFilesInVerity(const std::string& path);
+// Note that this function will skip files that are already in fs-verity, and
+// for those files it will return the existing digest.
android::base::Result<std::map<std::string, std::string>>
addFilesToVerityRecursive(const std::string& path, const SigningKey& key);
-android::base::Result<std::map<std::string, std::string>>
-verifyAllFilesUsingCompOs(const std::string& path, const std::vector<uint8_t>& compos_key);
+android::base::Result<void> verifyAllFilesUsingCompOs(const std::string& directory_path,
+ std::map<std::string, std::string> digests,
+ const SigningKey& signing_key);
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 6ea20ea..ec4a997 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -65,8 +65,14 @@
"/data/misc/apexdata/com.android.compos/current/key.pubkey";
const std::string kCompOsPendingPublicKey =
"/data/misc/apexdata/com.android.compos/pending/key.pubkey";
-
const std::string kCompOsPendingArtifactsDir = "/data/misc/apexdata/com.android.art/compos-pending";
+const std::string kCompOsInfo = kArtArtifactsDir + "/compos.info";
+const std::string kCompOsInfoSignature = kCompOsInfo + ".signature";
+
+constexpr const char* kCompOsPendingInfoPath =
+ "/data/misc/apexdata/com.android.art/compos-pending/compos.info";
+constexpr const char* kCompOsPendingInfoSignaturePath =
+ "/data/misc/apexdata/com.android.art/compos-pending/compos.info.signature";
constexpr const char* kOdsignVerificationDoneProp = "odsign.verification.done";
constexpr const char* kOdsignKeyDoneProp = "odsign.key.done";
@@ -241,7 +247,7 @@
auto existing_key =
extractRsaPublicKeyFromLeafCert(signingKey, kCompOsCert, kCompOsSubject.commonName);
if (existing_key.ok()) {
- LOG(INFO) << "Found and verified existing CompOs public key certificate: "
+ LOG(INFO) << "Found and verified existing CompOS public key certificate: "
<< kCompOsCert;
return existing_key.value();
}
@@ -254,7 +260,7 @@
}
if (!verified) {
- return Error() << "No valid CompOs key present.";
+ return Error() << "No valid CompOS key present.";
}
// If the pending key was verified it will have been promoted to current, so
@@ -262,7 +268,7 @@
auto publicKey = readBytesFromFile(kCompOsCurrentPublicKey);
if (publicKey.empty()) {
// This shouldn`t really happen.
- return Error() << "Failed to read CompOs key.";
+ return Error() << "Failed to read CompOS key.";
}
// One way or another we now have a valid public key. Persist a certificate so
@@ -274,10 +280,10 @@
auto certStatus = createLeafCertificate(kCompOsSubject, publicKey, signFunction,
kSigningKeyCert, kCompOsCert);
if (!certStatus.ok()) {
- return Error() << "Failed to create CompOs cert: " << certStatus.error();
+ return Error() << "Failed to create CompOS cert: " << certStatus.error();
}
- LOG(INFO) << "Verified key, wrote new CompOs cert";
+ LOG(INFO) << "Verified key, wrote new CompOS cert";
return publicKey;
}
@@ -447,23 +453,58 @@
if (!cert_add_result.ok()) {
// Best efforts only - nothing we can do if deletion fails.
unlink(kCompOsCert.c_str());
- return Error() << "Failed to add CompOs certificate to fs-verity keyring: "
+ return Error() << "Failed to add CompOS certificate to fs-verity keyring: "
<< cert_add_result.error();
}
return publicKey;
}
+Result<OdsignInfo> getComposInfo(const std::vector<uint8_t>& compos_key) {
+ std::string compos_signature;
+ if (!android::base::ReadFileToString(kCompOsInfoSignature, &compos_signature)) {
+ return ErrnoError() << "Failed to read " << kCompOsInfoSignature;
+ }
+
+ std::string compos_info_str;
+ if (!android::base::ReadFileToString(kCompOsInfo, &compos_info_str)) {
+ return ErrnoError() << "Failed to read " << kCompOsInfo;
+ }
+
+ // Delete the files - if they're valid we don't need them any more, and
+ // they'd confuse artifact verification; if they're not we never need to
+ // look at them again.
+ if (unlink(kCompOsInfo.c_str()) != 0 || unlink(kCompOsInfoSignature.c_str()) != 0) {
+ return ErrnoError() << "Unable to delete CompOS info/signature file";
+ }
+
+ // Verify the signature
+ auto verified = verifyRsaPublicKeySignature(compos_info_str, compos_signature, compos_key);
+ if (!verified.ok()) {
+ return Error() << kCompOsInfoSignature << " does not match.";
+ } else {
+ LOG(INFO) << kCompOsInfoSignature << " matches.";
+ }
+
+ OdsignInfo compos_info;
+ if (!compos_info.ParseFromString(compos_info_str)) {
+ return Error() << "Failed to parse " << kCompOsInfo;
+ }
+
+ LOG(INFO) << "Loaded " << kCompOsInfo;
+ return compos_info;
+}
+
art::odrefresh::ExitCode checkCompOsPendingArtifacts(const std::vector<uint8_t>& compos_key,
- const SigningKey& signingKey,
+ const SigningKey& signing_key,
bool* digests_verified) {
if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
return art::odrefresh::ExitCode::kCompilationRequired;
}
- // CompOs has generated some artifacts that may, or may not, match the
+ // CompOS has generated some artifacts that may, or may not, match the
// current state. But if there are already valid artifacts present the
- // CompOs ones are redundant.
+ // CompOS ones are redundant.
art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
if (odrefresh_status != art::odrefresh::ExitCode::kCompilationRequired) {
if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
@@ -473,7 +514,14 @@
return odrefresh_status;
}
- // No useful current artifacts, lets see if the CompOs ones are ok
+ // No useful current artifacts, lets see if the CompOS ones are ok
+ if (access(kCompOsPendingInfoPath, R_OK) != 0 ||
+ access(kCompOsPendingInfoSignaturePath, R_OK) != 0) {
+ LOG(INFO) << "Missing CompOS info/signature, deleting pending artifacts";
+ removeDirectory(kCompOsPendingArtifactsDir);
+ return art::odrefresh::ExitCode::kCompilationRequired;
+ }
+
LOG(INFO) << "Current artifacts are out of date, switching to pending artifacts";
removeDirectory(kArtArtifactsDir);
if (!rename(kCompOsPendingArtifactsDir, kArtArtifactsDir)) {
@@ -481,9 +529,6 @@
return art::odrefresh::ExitCode::kCompilationRequired;
}
- // TODO: Make sure that we check here that the contents of the artifacts
- // correspond to their filenames (and extensions) - the CompOs signatures
- // can't guarantee that.
odrefresh_status = checkArtifacts();
if (odrefresh_status != art::odrefresh::ExitCode::kOkay) {
LOG(WARNING) << "Pending artifacts are not OK";
@@ -492,24 +537,28 @@
// The artifacts appear to be up to date - but we haven't
// verified that they are genuine yet.
- Result<std::map<std::string, std::string>> digests =
- verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_key);
- if (digests.ok()) {
- auto persisted = persistDigests(digests.value(), signingKey);
-
- // Having signed the digests (or failed to), we're done with the signing key.
- SetProperty(kOdsignKeyDoneProp, "1");
-
- if (persisted.ok()) {
- *digests_verified = true;
- LOG(INFO) << "Pending artifacts successfully verified.";
- return art::odrefresh::ExitCode::kOkay;
- } else {
- LOG(WARNING) << persisted.error();
- }
+ auto compos_info = getComposInfo(compos_key);
+ if (!compos_info.ok()) {
+ LOG(WARNING) << compos_info.error();
} else {
- LOG(WARNING) << "Pending artifact verification failed: " << digests.error();
+ std::map<std::string, std::string> compos_digests(compos_info->file_hashes().begin(),
+ compos_info->file_hashes().end());
+
+ auto status = verifyAllFilesUsingCompOs(kArtArtifactsDir, compos_digests, signing_key);
+ if (!status.ok()) {
+ LOG(WARNING) << status.error();
+ } else {
+ auto persisted = persistDigests(compos_digests, signing_key);
+
+ if (!persisted.ok()) {
+ LOG(WARNING) << persisted.error();
+ } else {
+ LOG(INFO) << "Pending artifacts successfully verified.";
+ *digests_verified = true;
+ return art::odrefresh::ExitCode::kOkay;
+ }
+ }
}
// We can't use the existing artifacts, so we will need to generate new
diff --git a/ondevice-signing/proto/Android.bp b/ondevice-signing/proto/Android.bp
index c042b8e..d6e3354 100644
--- a/ondevice-signing/proto/Android.bp
+++ b/ondevice-signing/proto/Android.bp
@@ -26,6 +26,21 @@
type: "lite",
},
srcs: ["odsign_info.proto"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ ],
+}
+
+rust_protobuf {
+ name: "libodsign_proto_rust",
+ crate_name: "odsign_proto",
+ protos: ["odsign_info.proto"],
+ source_stem: "odsign_proto",
+ host_supported: true,
+ apex_available: [
+ "com.android.compos",
+ ],
}
cc_library_static {
@@ -40,4 +55,5 @@
"//apex_available:platform",
"com.android.compos",
],
+ recovery_available: true,
}
diff --git a/ondevice-signing/tests/Android.bp b/ondevice-signing/tests/Android.bp
new file mode 100644
index 0000000..4027220
--- /dev/null
+++ b/ondevice-signing/tests/Android.bp
@@ -0,0 +1,44 @@
+// Copyright (C) 2021 The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ // See: http://go/android-license-faq
+ // A large-scale-change added 'default_applicable_licenses' to import
+ // all of the 'license_kinds' from "system_security_license"
+ // to get the below license kinds:
+ // SPDX-license-identifier-Apache-2.0
+ default_applicable_licenses: ["system_security_license"],
+}
+
+cc_test {
+ name: "libsigningutils_test",
+ srcs: ["SigningUtilsTest.cpp"],
+ test_suites: ["device-tests"],
+ compile_multilib: "both",
+ defaults: [
+ "odsign_flags_defaults",
+ ],
+ static_libs: [
+ "libsigningutils",
+ ],
+ shared_libs: [
+ "libbase",
+ "libcrypto",
+ ],
+ data: [
+ "test_file",
+ "test_file.sig",
+ "SigningUtils.cert.der",
+ ],
+}
diff --git a/ondevice-signing/tests/SigningUtils.cert.der b/ondevice-signing/tests/SigningUtils.cert.der
new file mode 100644
index 0000000..0703d59
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtils.cert.der
Binary files differ
diff --git a/ondevice-signing/tests/SigningUtils.pem b/ondevice-signing/tests/SigningUtils.pem
new file mode 100644
index 0000000..01dfa5e
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtils.pem
@@ -0,0 +1,51 @@
+-----BEGIN RSA PRIVATE KEY-----
+MIIJKAIBAAKCAgEAwyOUvcTpPuPxw2I9E28uhXT9pbJAgNE0dc1emOWRfapZ5iow
+6PC7am47DfHyyZ/dwtw0vvi3QWCLIpsyFQbspO5XfmJH7mHBTvG8SkV40rFjptf8
+iwMl5i1ZUc5LChxuRh6DIS1fT2tFiXQ8iX3FUNNqjj/tOOtp45JEsgFK7WDW8YU3
+UseUn2BpkccUHMbe78EkEb4F6/AJWISk2INMOMPzRPUwdE3eUPVrufkp6jVJs69I
+c6skRvAAqpiOx4rYpVzs3V5PuzhmCr7jt8hTQ0DA6q501UT06og6gIKPdqLjugKK
+jt88XNXhhofckSbIH7edwa20LhvE4vtM/x7E0u+sCJozr0uyNgRXWs6imsVrC7Hb
+cclecBUFqETVzP6RG49cCXVkuOkSNBm3HLt4291+O821gn3yygTE9bpf9uuDHSb1
+l13MOMbVeT3ipaRGMwD1bp7kBW3cEZ2zrQ4WSwwtADeWI6Z0k5SpcHfLxp+6dhPN
+JtqBmgUTHuycmvoAPZK6XMnV0wCo2gUYS2q3Vc9yqsJRG0q2CKhOtKnuEmSqcjcC
+52OnP06M5pLfvms+qoOLVUHMhidMtYs3yRPZ1ZPzScQhjg64QDsRiG+PC0lL/NFz
+XPFcgzSsoIJ8UNZNjw7l5hh9NehJam2pqMizIcECl3gwrqGtq7Qo/AIXc4sCAwEA
+AQKCAgBqkCqw+zBYvMgQ57vsugGQtdOyQcaB0j0wu6cWHf+2vWl8jLvK6XOfanTr
+Z54rRxcmS3SueUox9JPmoRPXccGXS+URyn/3iQC0qMQnVwrlHCQMP9TU4TI4Ibmu
+N9a4vc/mkNERNCLhTvZZWtWYS8uOGPYOmpBkTgK0WPMUtioBuamHmTUeCol6A3+D
+MVElaeDi0vlsivXW421nHoCbEBB2y2M03CTKzp9CXNOoao3eLZ2C94y8RdB4wKXM
+g6UtCQDIRRfAx7kIx4LKCXZ3rXjyuBDh18VLle2diilQdnv70HZF5Q9feD8Rf2c6
+PUVRKvmMgIww8Tf9GgMJ5SwmAdp/VblgcKsdjr8tnT7V9ljkYCL4K3H4FT5LKXjI
+FiGyqBie8jvLYCLx6DlVTIi+Q/kvxaFT9twazVlz9jfgufQ4ICBKlNp4A6kpwrzb
+9QnMrHI+gTOrCCEeklg90M+xk5UERueLPnXYbvAG8cv1FeVpi9ldSQds5VHN1ZJ4
+hWWeWfGgIDiNeZuKL141NLS/sX9rCGEsQyVLTKkSDIgh5ncepVcXhB40GZFfggml
+A3HfNRN5lHLwH5+JKWlVx7PfUGPOTgx62i7HF+qcV3bQJfqnAMLPA7hh1bo8xIPp
+hiAbaZkwCGlNCwadzOq6U99Dx7eorwfAgGDKWnAr/rMaK9n+OQKCAQEA8UItzBLq
+yyJ+Xj7n/M3x/+v2E6dcDYH64oeMpVEEdH6cSSNcdUm0vgX4p25W0uY5kgj1RsXV
+gOk+9W6cM84p+2+DIGI8fydnuIv8q6TFiouDCY+T3FVdj4MasNZuAeHR7c6Coc5N
+Eckv3F1sfoGlH4z2AzppY0T3VX5TkKkh719X7A/cpIirZBRLfddDA3Hr6pm/vgSo
+mX1X5BhjrujasRoZhU2RsXnAflXp18aP67zvlRlzGrpMGeueRjp2NXF3pItiMAjN
+EOSoY3elEi6Um+WomFGLsY5SAuId+TJy8SqsGKWNHN+UPx4tGYQmEPA1e76Nu5Ex
+xDxpHIWyZ4Hz/wKCAQEAzw/9bPFgihzSnrBbJiFZVGPhFxw39aeuB5/3ZMPjknfK
+fyWonbhrJF14/86JSd16Xime6J4a3OsXKzTo36sLBDsYfYXof7bwfKk/GUryMFOG
+0aZqiZbiRQ8uCfzd+MtnNxO0WxiyZvj8i7hMjK50yBhRs/5YAHSoLxFOfVVSLNAi
+nhIDqtzeLA2W05PGusgz/0w8FHI6J64jZY4EQLgr43K6DXoFLQjtsl8JZfMO9fEc
+0j5vRytXtYlTSlQEtKeQ8cvpcqbY0gmrEasZ4v1jEzemXzvFkx+ck+Ayl/vHx60A
+AZCom66BudFArAnuVdrMAWUH+78Xf2l5en7kz40QdQKCAQBWxkran+M7dQimtVGT
+qC9msWQs5YFCioHGgKKhw2Yq0G8+Dy3uMbiEsHkjH5iy+oOydu5hqj6Ew2AVvtcH
++xs2iIFNYIgJ5A52XkNfKUCz+EIFalLwaPPh7nHnMPkYTDTJqAFsWVt3DjnctO2V
+AuR1WKoTtyq4vdGIOour+GlwQ4bILVxbAZ1Dvdj5RjegQZVtKCfDHMHXkzHNpMgV
+3ULreEu9mozQnM4ToqsdJRoW3DoAEstHzcIZgJnJALYLuughktCaHlBDxzqZrCr/
+QynIeO4O+yWXk20EBHhrbS3SeFq18rWysOgNW7k0+EcIyJ00CPHJiQuxXVkhHSVx
+/VfZAoIBAGA1isg642No/wgS41c1OZ93hRfK2cl/ruIGFtowFqZwmJs5cT5PeSD9
+eYJKggnbKcdkyVxGUi8B4NMHk4iRnd3KY5e3R49H/je+H/5tj1ibBtKU432oqNvz
+sK2dW7oFMKEru6p0MDieSiHVcWQQj1yFyDi83kDf82FjRjgAE92Um/EcZ63VUDnh
+2onWaQlSiq59ypCpfpH/XJ0MPrefm2zkWsR2RL9nHaK6e9Bt/i6SaJTbw7Kq1ecY
+tqWbolAaZ8OhvoeyNJ5rNZxRBwcsOwOr4NbxG90/W+5txrRNnccOgCk6AM3NaKNh
+Mg590sr7jby8J9h2MsHVzUb4fPJfFh0CggEBANS+aqEzWflHMOR4knhM7QHHaTfS
+4wwR3zL3/tSaV/cxNGehtjyEg85/aKklt/ude3/sm/Z0m6jQEMWgAt5iwuBovPA+
+1/rGkWTHL+dQr0i81C3b/Ymx7izL7BobaYV0o00EoKotC0NP5qR0fBKSkTfFqAYG
+SxnHtw/vduxu2H6TyIrdtvNNqc1PbHdzDI/FwzcWZFNyHBzSWwZxB5w+21uRUayv
+Iz3zcytrZZbAuOjCnhxNL/6XgcttqWSVFB4Ul1xiXrXDx2Xq+FfM40UF7oKGd+Kt
+B0wMqoZJj+0CdFfRZxHA6/n8v1Al+8lYo8smp+R9fR6qZKcugEFgdVkIl7E=
+-----END RSA PRIVATE KEY-----
diff --git a/ondevice-signing/tests/SigningUtilsTest.cpp b/ondevice-signing/tests/SigningUtilsTest.cpp
new file mode 100644
index 0000000..10f7629
--- /dev/null
+++ b/ondevice-signing/tests/SigningUtilsTest.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <android-base/file.h>
+#include <gtest/gtest.h>
+
+#include "CertUtils.h"
+#include "VerityUtils.h"
+
+// These files were created using the following commands:
+// openssl genrsa -out SigningUtils.pem 4096
+// openssl req -new -x509 -key SigningUtils.pem -out SigningUtils.cert.pem
+// openssl x509 -in SigningUtils.cert.pem -out SigningUtils.cert.der -outform DER
+// head -c 4096 </dev/urandom >test_file
+// openssl dgst -sign SigningUtils.pem -keyform PEM -sha256 -out test_file.sig -binary test_file
+const std::string kTestCert = "SigningUtils.cert.der";
+const std::string kTestFile = "test_file";
+const std::string kTestFileSignature = "test_file.sig";
+
+TEST(SigningUtilsTest, CheckVerifySignature) {
+ std::string signature;
+ std::string sigFile = android::base::GetExecutableDirectory() + "/" + kTestFileSignature;
+ ASSERT_TRUE(android::base::ReadFileToString(sigFile, &signature));
+
+ std::string data;
+ std::string testFile = android::base::GetExecutableDirectory() + "/" + kTestFile;
+ ASSERT_TRUE(android::base::ReadFileToString(testFile, &data));
+
+ std::string testCert = android::base::GetExecutableDirectory() + "/" + kTestCert;
+ auto trustedKey = extractPublicKeyFromX509(testCert.c_str());
+ ASSERT_TRUE(trustedKey.ok());
+
+ auto result = verifySignature(data, signature, *trustedKey);
+ ASSERT_TRUE(result.ok());
+}
diff --git a/ondevice-signing/tests/test_file b/ondevice-signing/tests/test_file
new file mode 100644
index 0000000..8a121be
--- /dev/null
+++ b/ondevice-signing/tests/test_file
Binary files differ
diff --git a/ondevice-signing/tests/test_file.sig b/ondevice-signing/tests/test_file.sig
new file mode 100644
index 0000000..ffd95dc
--- /dev/null
+++ b/ondevice-signing/tests/test_file.sig
Binary files differ
diff --git a/provisioner/rkp_factory_extraction_tool.cpp b/provisioner/rkp_factory_extraction_tool.cpp
index 2e59dbd..9786c3d 100644
--- a/provisioner/rkp_factory_extraction_tool.cpp
+++ b/provisioner/rkp_factory_extraction_tool.cpp
@@ -77,9 +77,13 @@
uint8_t* writePtr = challenge.data();
while (bytesRemaining > 0) {
int bytesRead = getrandom(writePtr, bytesRemaining, /*flags=*/0);
- if (bytesRead < 0 && errno != EINTR) {
- std::cerr << errno << ": " << strerror(errno) << std::endl;
- exit(-1);
+ if (bytesRead < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ std::cerr << errno << ": " << strerror(errno) << std::endl;
+ exit(-1);
+ }
}
bytesRemaining -= bytesRead;
writePtr += bytesRead;
@@ -158,7 +162,7 @@
auto rkp_service = IRemotelyProvisionedComponent::fromBinder(rkp_binder);
if (!rkp_service) {
std::cerr << "Unable to get binder object for '" << fullName << "', skipping.";
- return;
+ exit(-1);
}
std::vector<uint8_t> keysToSignMac;