Merge changes from topic "full_spec_upgrade"
* changes:
Always fall back to factory key on any RKP error
Adding plumbing for supported EC curve on impl
diff --git a/keystore2/Android.bp b/keystore2/Android.bp
index 9dc34d2..0069f95 100644
--- a/keystore2/Android.bp
+++ b/keystore2/Android.bp
@@ -35,6 +35,7 @@
"android.security.authorization-rust",
"android.security.compat-rust",
"android.security.maintenance-rust",
+ "android.security.metrics-rust",
"android.security.remoteprovisioning-rust",
"android.system.keystore2-V1-rust",
"libanyhow",
@@ -54,9 +55,6 @@
"liblog_rust",
"librand",
"librusqlite",
- "libstatslog_rust",
- "libstatslog_rust_header",
- "libstatspull_rust",
"libthiserror",
],
shared_libs: [
diff --git a/keystore2/aidl/Android.bp b/keystore2/aidl/Android.bp
index 5e4e22c..5416024 100644
--- a/keystore2/aidl/Android.bp
+++ b/keystore2/aidl/Android.bp
@@ -169,3 +169,25 @@
},
}
+aidl_interface {
+ name: "android.security.metrics",
+ srcs: [ "android/security/metrics/*.aidl" ],
+ imports: [
+ "android.system.keystore2-V1",
+ ],
+ unstable: true,
+ backend: {
+ java: {
+ platform_apis: true,
+ srcs_available: true,
+ },
+ rust: {
+ enabled: true,
+ },
+ ndk: {
+ enabled: true,
+ apps_enabled: false,
+ }
+ },
+}
+
diff --git a/keystore2/aidl/android/security/metrics/Algorithm.aidl b/keystore2/aidl/android/security/metrics/Algorithm.aidl
new file mode 100644
index 0000000..8e8d107
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Algorithm.aidl
@@ -0,0 +1,40 @@
+/*
+ * 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;
+
+/**
+ * Algorithm enum as defined in stats/enums/system/security/keystore2/enums.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum Algorithm {
+ /** ALGORITHM is prepended because UNSPECIFIED exists in other enums as well. */
+ ALGORITHM_UNSPECIFIED = 0,
+
+ /** Asymmetric algorithms. */
+ RSA = 1,
+
+ /** 2 removed, do not reuse. */
+ EC = 3,
+
+ /** Block cipher algorithms. */
+ AES = 32,
+ TRIPLE_DES = 33,
+
+ /** MAC algorithms. */
+ HMAC = 128,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/AtomID.aidl b/keystore2/aidl/android/security/metrics/AtomID.aidl
new file mode 100644
index 0000000..166e753
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/AtomID.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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;
+
+/**
+ * Atom IDs as defined in frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum AtomID {
+ STORAGE_STATS = 10103,
+ RKP_POOL_STATS = 10104,
+ KEY_CREATION_WITH_GENERAL_INFO = 10118,
+ KEY_CREATION_WITH_AUTH_INFO = 10119,
+ KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO = 10120,
+ KEYSTORE2_ATOM_WITH_OVERFLOW = 10121,
+ KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO = 10122,
+ 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/CrashStats.aidl b/keystore2/aidl/android/security/metrics/CrashStats.aidl
new file mode 100644
index 0000000..8ca043b
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/CrashStats.aidl
@@ -0,0 +1,23 @@
+/*
+ * 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;
+
+/** @hide */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable CrashStats {
+ int count_of_crash_events;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/EcCurve.aidl b/keystore2/aidl/android/security/metrics/EcCurve.aidl
new file mode 100644
index 0000000..b190d83
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/EcCurve.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * EcCurve enum as defined in Keystore2KeyCreationWithGeneralInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum EcCurve {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to the keymint spec. */
+ EC_CURVE_UNSPECIFIED = 0,
+ P_224 = 1,
+ P_256 = 2,
+ P_384 = 3,
+ P_521 = 4,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/HardwareAuthenticatorType.aidl b/keystore2/aidl/android/security/metrics/HardwareAuthenticatorType.aidl
new file mode 100644
index 0000000..b13f6ea
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/HardwareAuthenticatorType.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * HardwareAuthenticatorType enum as defined in Keystore2KeyCreationWithAuthInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum HardwareAuthenticatorType {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to keymint spec. */
+ AUTH_TYPE_UNSPECIFIED = 0,
+ NONE = 1,
+ PASSWORD = 2,
+ FINGERPRINT = 3,
+ ANY = 5,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/IKeystoreMetrics.aidl b/keystore2/aidl/android/security/metrics/IKeystoreMetrics.aidl
new file mode 100644
index 0000000..342cf01
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/IKeystoreMetrics.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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.KeystoreAtom;
+import android.security.metrics.AtomID;
+
+/**
+ * IKeystoreMetrics interface exposes the method for system server to pull metrics from keystore.
+ * @hide
+ */
+interface IKeystoreMetrics {
+ /**
+ * Allows the metrics routing proxy to pull the metrics from keystore.
+ *
+ * @return an array of KeystoreAtom objects with the atomID. There can be multiple atom objects
+ * for the same atomID, encapsulating different combinations of values for the atom fields.
+ * If there is no atom object found for the atomID in the metrics store, an empty array is
+ * returned.
+ *
+ * Callers require 'PullMetrics' permission.
+ *
+ * @param atomID - ID of the atom to be pulled.
+ *
+ * Errors are reported as service specific errors.
+ */
+ KeystoreAtom[] pullMetrics(in AtomID atomID);
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyCreationWithAuthInfo.aidl b/keystore2/aidl/android/security/metrics/KeyCreationWithAuthInfo.aidl
new file mode 100644
index 0000000..ff200bc
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyCreationWithAuthInfo.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.HardwareAuthenticatorType;
+import android.security.metrics.SecurityLevel;
+
+/**
+ * Atom that encapsulates authentication related information in key creation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyCreationWithAuthInfo {
+ HardwareAuthenticatorType user_auth_type;
+ /**
+ * Base 10 logarithm of time out in seconds.
+ * Logarithm is taken in order to reduce the cardinaltiy.
+ */
+ int log10_auth_key_timeout_seconds;
+ SecurityLevel security_level;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyCreationWithGeneralInfo.aidl b/keystore2/aidl/android/security/metrics/KeyCreationWithGeneralInfo.aidl
new file mode 100644
index 0000000..74cd9ef
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyCreationWithGeneralInfo.aidl
@@ -0,0 +1,35 @@
+/*
+ * 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.Algorithm;
+import android.security.metrics.EcCurve;
+import android.security.metrics.KeyOrigin;
+
+/**
+ * Atom that encapsulates a set of general information in key creation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyCreationWithGeneralInfo {
+ Algorithm algorithm;
+ int key_size;
+ EcCurve ec_curve;
+ KeyOrigin key_origin;
+ int error_code;
+ boolean attestation_requested = false;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyCreationWithPurposeAndModesInfo.aidl b/keystore2/aidl/android/security/metrics/KeyCreationWithPurposeAndModesInfo.aidl
new file mode 100644
index 0000000..dda61c4
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyCreationWithPurposeAndModesInfo.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.Algorithm;
+
+/**
+ * Atom that encapsulates the repeated fields in key creation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyCreationWithPurposeAndModesInfo {
+ Algorithm algorithm;
+ int purpose_bitmap;
+ int padding_mode_bitmap;
+ int digest_bitmap;
+ int block_mode_bitmap;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyOperationWithGeneralInfo.aidl b/keystore2/aidl/android/security/metrics/KeyOperationWithGeneralInfo.aidl
new file mode 100644
index 0000000..d70aaf3
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyOperationWithGeneralInfo.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.Outcome;
+import android.security.metrics.SecurityLevel;
+
+/**
+ * Atom that encapsulates a set of general information in key operation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyOperationWithGeneralInfo {
+ Outcome outcome;
+ int error_code;
+ boolean key_upgraded;
+ SecurityLevel security_level;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyOperationWithPurposeAndModesInfo.aidl b/keystore2/aidl/android/security/metrics/KeyOperationWithPurposeAndModesInfo.aidl
new file mode 100644
index 0000000..e3769e1
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyOperationWithPurposeAndModesInfo.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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.Purpose;
+
+/**
+ * Atom that encapsulates the purpose, padding mode, digest and block mode fields in key operations.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeyOperationWithPurposeAndModesInfo {
+ Purpose purpose;
+ int padding_mode_bitmap;
+ int digest_bitmap;
+ int block_mode_bitmap;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeyOrigin.aidl b/keystore2/aidl/android/security/metrics/KeyOrigin.aidl
new file mode 100644
index 0000000..b472bc3
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeyOrigin.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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;
+
+/**
+ * KeyOrigin enum as defined in Keystore2KeyCreationWithGeneralInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum KeyOrigin {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to keymint spec. */
+ ORIGIN_UNSPECIFIED = 0,
+
+ /** Generated in KeyMint. Should not exist outside the TEE. */
+ GENERATED = 1,
+
+ /** Derived inside KeyMint. Likely exists off-device. */
+ DERIVED = 2,
+
+ /** Imported into KeyMint. Existed as cleartext in Android. */
+ IMPORTED = 3,
+
+ /** Previously used for another purpose that is now obsolete. */
+ RESERVED = 4,
+
+ /** Securely imported into KeyMint. */
+ SECURELY_IMPORTED = 5,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/Keystore2AtomWithOverflow.aidl b/keystore2/aidl/android/security/metrics/Keystore2AtomWithOverflow.aidl
new file mode 100644
index 0000000..f2ac399
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Keystore2AtomWithOverflow.aidl
@@ -0,0 +1,34 @@
+/*
+ * 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.AtomID;
+
+/**
+ * Logs the atom id of the atoms associated with key creation/operation events, that have reached
+ * the maximum storage limit allocated for different atom objects of that atom,
+ * in keystore in-memory store.
+ *
+ * Size of the storage bucket for each atom is limited considering their expected cardinaltity.
+ * This limit may exceed if the dimensions of the atoms take a large number of unexpected
+ * combinations. This atom is used to track such cases.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable Keystore2AtomWithOverflow {
+ AtomID atom_id;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl b/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
new file mode 100644
index 0000000..266267a
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeystoreAtom.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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.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.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable KeystoreAtom {
+ KeystoreAtomPayload payload;
+ int count;
+}
diff --git a/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl b/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
new file mode 100644
index 0000000..a3e4dd6
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/KeystoreAtomPayload.aidl
@@ -0,0 +1,43 @@
+/*
+ * 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.KeyCreationWithGeneralInfo;
+import android.security.metrics.KeyCreationWithPurposeAndModesInfo;
+import android.security.metrics.KeyCreationWithAuthInfo;
+import android.security.metrics.KeyOperationWithGeneralInfo;
+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;
+ Keystore2AtomWithOverflow keystore2AtomWithOverflow;
+ KeyOperationWithPurposeAndModesInfo keyOperationWithPurposeAndModesInfo;
+ KeyOperationWithGeneralInfo keyOperationWithGeneralInfo;
+ RkpErrorStats rkpErrorStats;
+ CrashStats crashStats;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/Outcome.aidl b/keystore2/aidl/android/security/metrics/Outcome.aidl
new file mode 100644
index 0000000..006548b
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Outcome.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * Outcome enum as defined in Keystore2KeyOperationWithGeneralInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum Outcome {
+ OUTCOME_UNSPECIFIED = 0,
+ DROPPED = 1,
+ SUCCESS = 2,
+ ABORT = 3,
+ PRUNED = 4,
+ ERROR = 5,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/PoolStatus.aidl b/keystore2/aidl/android/security/metrics/PoolStatus.aidl
new file mode 100644
index 0000000..3530163
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/PoolStatus.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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/Purpose.aidl b/keystore2/aidl/android/security/metrics/Purpose.aidl
new file mode 100644
index 0000000..f003cea
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Purpose.aidl
@@ -0,0 +1,54 @@
+/*
+ * 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;
+
+/**
+ * Purpose enum as defined in Keystore2KeyOperationWithPurposeAndModesInfo of
+ * frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum Purpose {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to keymint spec. */
+ KEY_PURPOSE_UNSPECIFIED = 0,
+
+ /** Usable with RSA, 3DES and AES keys. */
+ ENCRYPT = 1,
+
+ /** Usable with RSA, 3DES and AES keys. */
+ DECRYPT = 2,
+
+ /** Usable with RSA, EC and HMAC keys. */
+ SIGN = 3,
+
+ /** Usable with RSA, EC and HMAC keys. */
+ VERIFY = 4,
+
+ /** 4 is reserved */
+
+ /** Usable with RSA keys. */
+ WRAP_KEY = 6,
+
+ /** Key Agreement, usable with EC keys. */
+ AGREE_KEY = 7,
+
+ /**
+ * Usable as an attestation signing key. Keys with this purpose must not have any other
+ * purpose.
+ */
+ ATTEST_KEY = 8,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/RkpError.aidl b/keystore2/aidl/android/security/metrics/RkpError.aidl
new file mode 100644
index 0000000..c33703d
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/RkpError.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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;
+
+/**
+ * KeyOrigin enum as defined in RkpErrorStats of frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum RkpError {
+ RKP_ERROR_UNSPECIFIED = 0,
+
+ /** The key pool is out of keys. */
+ OUT_OF_KEYS = 1,
+
+ /** Falling back to factory provisioned keys during hybrid mode. */
+ FALL_BACK_DURING_HYBRID = 2,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl b/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl
new file mode 100644
index 0000000..616d129
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/RkpErrorStats.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.RkpError;
+/**
+ * Atom that encapsulates error information in remote key provisioning events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable RkpErrorStats {
+ RkpError rkpError;
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl b/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl
new file mode 100644
index 0000000..016b6ff
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/RkpPoolStats.aidl
@@ -0,0 +1,32 @@
+/*
+ * 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/metrics/SecurityLevel.aidl b/keystore2/aidl/android/security/metrics/SecurityLevel.aidl
new file mode 100644
index 0000000..f627be2
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/SecurityLevel.aidl
@@ -0,0 +1,31 @@
+/*
+ * 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;
+
+/**
+ * SecurityLevel enum as defined in stats/enums/system/security/keystore2/enums.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum SecurityLevel {
+ /** Unspecified takes 0. Other values are incremented by 1 compared to keymint spec. */
+ SECURITY_LEVEL_UNSPECIFIED = 0,
+ SECURITY_LEVEL_SOFTWARE = 1,
+ SECURITY_LEVEL_TRUSTED_ENVIRONMENT = 2,
+ SECURITY_LEVEL_STRONGBOX = 3,
+ SECURITY_LEVEL_KEYSTORE = 4,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/Storage.aidl b/keystore2/aidl/android/security/metrics/Storage.aidl
new file mode 100644
index 0000000..1ba6e1f
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/Storage.aidl
@@ -0,0 +1,42 @@
+/*
+ * 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;
+
+/**
+ * Storage enum as defined in Keystore2StorageStats of frameworks/proto_logging/stats/atoms.proto.
+ * @hide
+ */
+@Backing(type="int")
+enum Storage {
+ STORAGE_UNSPECIFIED = 0,
+ KEY_ENTRY = 1,
+ KEY_ENTRY_ID_INDEX = 2,
+ KEY_ENTRY_DOMAIN_NAMESPACE_INDEX = 3,
+ BLOB_ENTRY = 4,
+ BLOB_ENTRY_KEY_ENTRY_ID_INDEX = 5,
+ KEY_PARAMETER = 6,
+ KEY_PARAMETER_KEY_ENTRY_ID_INDEX = 7,
+ KEY_METADATA = 8,
+ KEY_METADATA_KEY_ENTRY_ID_INDEX = 9,
+ GRANT = 10,
+ AUTH_TOKEN = 11,
+ BLOB_METADATA = 12,
+ BLOB_METADATA_BLOB_ENTRY_ID_INDEX =13,
+ METADATA = 14,
+ DATABASE = 15,
+ LEGACY_STORAGE = 16,
+}
\ No newline at end of file
diff --git a/keystore2/aidl/android/security/metrics/StorageStats.aidl b/keystore2/aidl/android/security/metrics/StorageStats.aidl
new file mode 100644
index 0000000..6822e86
--- /dev/null
+++ b/keystore2/aidl/android/security/metrics/StorageStats.aidl
@@ -0,0 +1,30 @@
+/*
+ * 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.Storage;
+
+/**
+ * Atom that encapsulates a set of general information in key creation events.
+ * @hide
+ */
+@RustDerive(Clone=true, Eq=true, PartialEq=true, Ord=true, PartialOrd=true, Hash=true)
+parcelable StorageStats {
+ Storage storage_type;
+ int size;
+ int unused_size;
+}
\ No newline at end of file
diff --git a/keystore2/src/async_task.rs b/keystore2/src/async_task.rs
index 45f0274..e130024 100644
--- a/keystore2/src/async_task.rs
+++ b/keystore2/src/async_task.rs
@@ -19,7 +19,6 @@
//! processed all tasks before it terminates.
//! Note that low priority tasks are processed only when the high priority queue is empty.
-use crate::utils::watchdog as wd;
use std::{any::Any, any::TypeId, time::Duration};
use std::{
collections::{HashMap, VecDeque},
@@ -241,14 +240,11 @@
// Now that the lock has been dropped, perform the action.
match action {
Action::QueuedFn(f) => {
- let _wd = wd::watch_millis("async_task thread: calling queued fn", 500);
f(&mut shelf);
done_idle = false;
}
Action::IdleFns(idle_fns) => {
for idle_fn in idle_fns {
- let _wd =
- wd::watch_millis("async_task thread: calling idle_fn", 500);
idle_fn(&mut shelf);
}
done_idle = true;
diff --git a/keystore2/src/database.rs b/keystore2/src/database.rs
index 36c722a..33c2c32 100644
--- a/keystore2/src/database.rs
+++ b/keystore2/src/database.rs
@@ -47,6 +47,7 @@
use crate::impl_metadata; // This is in db_utils.rs
use crate::key_parameter::{KeyParameter, Tag};
+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::{
@@ -69,8 +70,10 @@
use android_security_remoteprovisioning::aidl::android::security::remoteprovisioning::{
AttestationPoolStatus::AttestationPoolStatus,
};
-use statslog_rust::keystore2_storage_stats::{
- Keystore2StorageStats, StorageType as StatsdStorageType,
+use android_security_metrics::aidl::android::security::metrics::{
+ StorageStats::StorageStats,
+ Storage::Storage as MetricsStorage,
+ RkpError::RkpError as MetricsRkpError,
};
use keystore2_crypto::ZVec;
@@ -1021,23 +1024,23 @@
fn do_table_size_query(
&mut self,
- storage_type: StatsdStorageType,
+ storage_type: MetricsStorage,
query: &str,
params: &[&str],
- ) -> Result<Keystore2StorageStats> {
+ ) -> Result<StorageStats> {
let (total, unused) = self.with_transaction(TransactionBehavior::Deferred, |tx| {
tx.query_row(query, params_from_iter(params), |row| Ok((row.get(0)?, row.get(1)?)))
.with_context(|| {
- format!("get_storage_stat: Error size of storage type {}", storage_type as i32)
+ format!("get_storage_stat: Error size of storage type {}", storage_type.0)
})
.no_gc()
})?;
- Ok(Keystore2StorageStats { storage_type, size: total, unused_size: unused })
+ Ok(StorageStats { storage_type, size: total, unused_size: unused })
}
- fn get_total_size(&mut self) -> Result<Keystore2StorageStats> {
+ fn get_total_size(&mut self) -> Result<StorageStats> {
self.do_table_size_query(
- StatsdStorageType::Database,
+ MetricsStorage::DATABASE,
"SELECT page_count * page_size, freelist_count * page_size
FROM pragma_page_count('persistent'),
pragma_page_size('persistent'),
@@ -1048,10 +1051,10 @@
fn get_table_size(
&mut self,
- storage_type: StatsdStorageType,
+ storage_type: MetricsStorage,
schema: &str,
table: &str,
- ) -> Result<Keystore2StorageStats> {
+ ) -> Result<StorageStats> {
self.do_table_size_query(
storage_type,
"SELECT pgsize,unused FROM dbstat(?1)
@@ -1063,63 +1066,57 @@
/// Fetches a storage statisitics atom for a given storage type. For storage
/// types that map to a table, information about the table's storage is
/// returned. Requests for storage types that are not DB tables return None.
- pub fn get_storage_stat(
- &mut self,
- storage_type: StatsdStorageType,
- ) -> Result<Keystore2StorageStats> {
+ pub fn get_storage_stat(&mut self, storage_type: MetricsStorage) -> Result<StorageStats> {
let _wp = wd::watch_millis("KeystoreDB::get_storage_stat", 500);
match storage_type {
- StatsdStorageType::Database => self.get_total_size(),
- StatsdStorageType::KeyEntry => {
+ MetricsStorage::DATABASE => self.get_total_size(),
+ MetricsStorage::KEY_ENTRY => {
self.get_table_size(storage_type, "persistent", "keyentry")
}
- StatsdStorageType::KeyEntryIdIndex => {
+ MetricsStorage::KEY_ENTRY_ID_INDEX => {
self.get_table_size(storage_type, "persistent", "keyentry_id_index")
}
- StatsdStorageType::KeyEntryDomainNamespaceIndex => {
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX => {
self.get_table_size(storage_type, "persistent", "keyentry_domain_namespace_index")
}
- StatsdStorageType::BlobEntry => {
+ MetricsStorage::BLOB_ENTRY => {
self.get_table_size(storage_type, "persistent", "blobentry")
}
- StatsdStorageType::BlobEntryKeyEntryIdIndex => {
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX => {
self.get_table_size(storage_type, "persistent", "blobentry_keyentryid_index")
}
- StatsdStorageType::KeyParameter => {
+ MetricsStorage::KEY_PARAMETER => {
self.get_table_size(storage_type, "persistent", "keyparameter")
}
- StatsdStorageType::KeyParameterKeyEntryIdIndex => {
+ MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX => {
self.get_table_size(storage_type, "persistent", "keyparameter_keyentryid_index")
}
- StatsdStorageType::KeyMetadata => {
+ MetricsStorage::KEY_METADATA => {
self.get_table_size(storage_type, "persistent", "keymetadata")
}
- StatsdStorageType::KeyMetadataKeyEntryIdIndex => {
+ MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX => {
self.get_table_size(storage_type, "persistent", "keymetadata_keyentryid_index")
}
- StatsdStorageType::Grant => self.get_table_size(storage_type, "persistent", "grant"),
- StatsdStorageType::AuthToken => {
+ MetricsStorage::GRANT => self.get_table_size(storage_type, "persistent", "grant"),
+ MetricsStorage::AUTH_TOKEN => {
// Since the table is actually a BTreeMap now, unused_size is not meaningfully
// reportable
// Size provided is only an approximation
- Ok(Keystore2StorageStats {
+ Ok(StorageStats {
storage_type,
size: (self.perboot.auth_tokens_len() * std::mem::size_of::<AuthTokenEntry>())
- as i64,
+ as i32,
unused_size: 0,
})
}
- StatsdStorageType::BlobMetadata => {
+ MetricsStorage::BLOB_METADATA => {
self.get_table_size(storage_type, "persistent", "blobmetadata")
}
- StatsdStorageType::BlobMetadataBlobEntryIdIndex => {
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX => {
self.get_table_size(storage_type, "persistent", "blobmetadata_blobentryid_index")
}
- _ => Err(anyhow::Error::msg(format!(
- "Unsupported storage type: {}",
- storage_type as i32
- ))),
+ _ => Err(anyhow::Error::msg(format!("Unsupported storage type: {}", storage_type.0))),
}
}
@@ -1834,6 +1831,7 @@
)
.context("Failed to assign attestation key")?;
if result == 0 {
+ log_rkp_error_stats(MetricsRkpError::OUT_OF_KEYS);
return Err(KsError::Rc(ResponseCode::OUT_OF_KEYS)).context("Out of keys.");
} else if result > 1 {
return Err(KsError::sys())
@@ -5497,21 +5495,21 @@
Ok(())
}
- fn get_valid_statsd_storage_types() -> Vec<StatsdStorageType> {
+ fn get_valid_statsd_storage_types() -> Vec<MetricsStorage> {
vec![
- StatsdStorageType::KeyEntry,
- StatsdStorageType::KeyEntryIdIndex,
- StatsdStorageType::KeyEntryDomainNamespaceIndex,
- StatsdStorageType::BlobEntry,
- StatsdStorageType::BlobEntryKeyEntryIdIndex,
- StatsdStorageType::KeyParameter,
- StatsdStorageType::KeyParameterKeyEntryIdIndex,
- StatsdStorageType::KeyMetadata,
- StatsdStorageType::KeyMetadataKeyEntryIdIndex,
- StatsdStorageType::Grant,
- StatsdStorageType::AuthToken,
- StatsdStorageType::BlobMetadata,
- StatsdStorageType::BlobMetadataBlobEntryIdIndex,
+ MetricsStorage::KEY_ENTRY,
+ MetricsStorage::KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
+ MetricsStorage::BLOB_ENTRY,
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_PARAMETER,
+ MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_METADATA,
+ MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::GRANT,
+ MetricsStorage::AUTH_TOKEN,
+ MetricsStorage::BLOB_METADATA,
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
]
}
@@ -5519,7 +5517,7 @@
/// that are supported by the DB. Check for reasonable values.
#[test]
fn test_query_all_valid_table_sizes() -> Result<()> {
- const PAGE_SIZE: i64 = 4096;
+ const PAGE_SIZE: i32 = 4096;
let mut db = new_test_db()?;
@@ -5527,7 +5525,7 @@
let stat = db.get_storage_stat(t)?;
// AuthToken can be less than a page since it's in a btree, not sqlite
// TODO(b/187474736) stop using if-let here
- if let StatsdStorageType::AuthToken = t {
+ if let MetricsStorage::AUTH_TOKEN = t {
} else {
assert!(stat.size >= PAGE_SIZE);
}
@@ -5537,35 +5535,35 @@
Ok(())
}
- fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, Keystore2StorageStats> {
+ fn get_storage_stats_map(db: &mut KeystoreDB) -> BTreeMap<i32, StorageStats> {
get_valid_statsd_storage_types()
.into_iter()
- .map(|t| (t as i32, db.get_storage_stat(t).unwrap()))
+ .map(|t| (t.0, db.get_storage_stat(t).unwrap()))
.collect()
}
fn assert_storage_increased(
db: &mut KeystoreDB,
- increased_storage_types: Vec<StatsdStorageType>,
- baseline: &mut BTreeMap<i32, Keystore2StorageStats>,
+ increased_storage_types: Vec<MetricsStorage>,
+ baseline: &mut BTreeMap<i32, StorageStats>,
) {
for storage in increased_storage_types {
// Verify the expected storage increased.
let new = db.get_storage_stat(storage).unwrap();
- let storage = storage as i32;
- let old = &baseline[&storage];
- assert!(new.size >= old.size, "{}: {} >= {}", storage, new.size, old.size);
+ let storage = storage;
+ let old = &baseline[&storage.0];
+ assert!(new.size >= old.size, "{}: {} >= {}", storage.0, new.size, old.size);
assert!(
new.unused_size <= old.unused_size,
"{}: {} <= {}",
- storage,
+ storage.0,
new.unused_size,
old.unused_size
);
// Update the baseline with the new value so that it succeeds in the
// later comparison.
- baseline.insert(storage, new);
+ baseline.insert(storage.0, new);
}
// Get an updated map of the storage and verify there were no unexpected changes.
@@ -5573,7 +5571,7 @@
assert_eq!(updated_stats.len(), baseline.len());
for &k in baseline.keys() {
- let stringify = |map: &BTreeMap<i32, Keystore2StorageStats>| -> String {
+ let stringify = |map: &BTreeMap<i32, StorageStats>| -> String {
let mut s = String::new();
for &k in map.keys() {
writeln!(&mut s, " {}: {}, {}", &k, map[&k].size, map[&k].unused_size)
@@ -5601,9 +5599,9 @@
assert_storage_increased(
&mut db,
vec![
- StatsdStorageType::KeyEntry,
- StatsdStorageType::KeyEntryIdIndex,
- StatsdStorageType::KeyEntryDomainNamespaceIndex,
+ MetricsStorage::KEY_ENTRY,
+ MetricsStorage::KEY_ENTRY_ID_INDEX,
+ MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX,
],
&mut working_stats,
);
@@ -5614,10 +5612,10 @@
assert_storage_increased(
&mut db,
vec![
- StatsdStorageType::BlobEntry,
- StatsdStorageType::BlobEntryKeyEntryIdIndex,
- StatsdStorageType::BlobMetadata,
- StatsdStorageType::BlobMetadataBlobEntryIdIndex,
+ MetricsStorage::BLOB_ENTRY,
+ MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX,
+ MetricsStorage::BLOB_METADATA,
+ MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX,
],
&mut working_stats,
);
@@ -5626,7 +5624,7 @@
db.insert_keyparameter(&key_id, ¶ms)?;
assert_storage_increased(
&mut db,
- vec![StatsdStorageType::KeyParameter, StatsdStorageType::KeyParameterKeyEntryIdIndex],
+ vec![MetricsStorage::KEY_PARAMETER, MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX],
&mut working_stats,
);
@@ -5635,7 +5633,7 @@
db.insert_key_metadata(&key_id, &metadata)?;
assert_storage_increased(
&mut db,
- vec![StatsdStorageType::KeyMetadata, StatsdStorageType::KeyMetadataKeyEntryIdIndex],
+ vec![MetricsStorage::KEY_METADATA, MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX],
&mut working_stats,
);
@@ -5643,7 +5641,7 @@
for stat in working_stats.values() {
sum += stat.size;
}
- let total = db.get_storage_stat(StatsdStorageType::Database)?.size;
+ let total = db.get_storage_stat(MetricsStorage::DATABASE)?.size;
assert!(sum <= total, "Expected sum <= total. sum: {}, total: {}", sum, total);
Ok(())
@@ -5661,7 +5659,7 @@
timestamp: Timestamp { milliSeconds: 10 },
mac: b"mac".to_vec(),
});
- assert_storage_increased(&mut db, vec![StatsdStorageType::AuthToken], &mut working_stats);
+ assert_storage_increased(&mut db, vec![MetricsStorage::AUTH_TOKEN], &mut working_stats);
Ok(())
}
@@ -5685,7 +5683,7 @@
|_, _| Ok(()),
)?;
- assert_storage_increased(&mut db, vec![StatsdStorageType::Grant], &mut working_stats);
+ assert_storage_increased(&mut db, vec![MetricsStorage::GRANT], &mut working_stats);
Ok(())
}
diff --git a/keystore2/src/fuzzers/Android.bp b/keystore2/src/fuzzers/Android.bp
new file mode 100644
index 0000000..384ab77
--- /dev/null
+++ b/keystore2/src/fuzzers/Android.bp
@@ -0,0 +1,29 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package {
+ default_applicable_licenses: ["Android-Apache-2.0"],
+}
+
+rust_fuzz {
+ name: "legacy_blob_fuzzer",
+ srcs: ["legacy_blob_fuzzer.rs"],
+ rustlibs: [
+ "libkeystore2",
+ ],
+ fuzz_config: {
+ fuzz_on_haiku_device: true,
+ fuzz_on_haiku_host: false,
+ },
+}
diff --git a/keystore2/src/fuzzers/legacy_blob_fuzzer.rs b/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
new file mode 100644
index 0000000..5c89ca4
--- /dev/null
+++ b/keystore2/src/fuzzers/legacy_blob_fuzzer.rs
@@ -0,0 +1,24 @@
+// Copyright 2021, The Android Open Source Project
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#![allow(missing_docs)]
+#![no_main]
+#[macro_use]
+extern crate libfuzzer_sys;
+use keystore2::legacy_blob::LegacyBlobLoader;
+
+fuzz_target!(|data: &[u8]| {
+ let string = data.iter().filter_map(|c| std::char::from_u32(*c as u32)).collect::<String>();
+ let _res = LegacyBlobLoader::decode_alias(&string);
+});
diff --git a/keystore2/src/keystore2_main.rs b/keystore2/src/keystore2_main.rs
index dab6867..cf2ba04 100644
--- a/keystore2/src/keystore2_main.rs
+++ b/keystore2/src/keystore2_main.rs
@@ -17,7 +17,8 @@
use keystore2::entropy;
use keystore2::globals::ENFORCEMENTS;
use keystore2::maintenance::Maintenance;
-use keystore2::metrics;
+use keystore2::metrics::Metrics;
+use keystore2::metrics_store;
use keystore2::remote_provisioning::RemoteProvisioningService;
use keystore2::service::KeystoreService;
use keystore2::{apc::ApcManager, shared_secret_negotiation};
@@ -29,6 +30,7 @@
static KS2_SERVICE_NAME: &str = "android.system.keystore2.IKeystoreService/default";
static APC_SERVICE_NAME: &str = "android.security.apc";
static AUTHORIZATION_SERVICE_NAME: &str = "android.security.authorization";
+static METRICS_SERVICE_NAME: &str = "android.security.metrics";
static REMOTE_PROVISIONING_SERVICE_NAME: &str = "android.security.remoteprovisioning";
static USER_MANAGER_SERVICE_NAME: &str = "android.security.maintenance";
static LEGACY_KEYSTORE_SERVICE_NAME: &str = "android.security.legacykeystore";
@@ -50,6 +52,9 @@
let mut args = std::env::args();
args.next().expect("That's odd. How is there not even a first argument?");
+ // Write/update keystore.crash_count system property.
+ metrics_store::update_keystore_crash_sysprop();
+
// Keystore 2.0 cannot change to the database directory (typically /data/misc/keystore) on
// startup as Keystore 1.0 did because Keystore 2.0 is intended to run much earlier than
// Keystore 1.0. Instead we set a global variable to the database path.
@@ -109,6 +114,13 @@
},
);
+ let metrics_service = Metrics::new_native_binder().unwrap_or_else(|e| {
+ panic!("Failed to create service {} because of {:?}.", METRICS_SERVICE_NAME, e);
+ });
+ binder::add_service(METRICS_SERVICE_NAME, metrics_service.as_binder()).unwrap_or_else(|e| {
+ 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.
if let Ok(remote_provisioning_service) = RemoteProvisioningService::new_native_binder() {
@@ -133,13 +145,6 @@
},
);
- std::thread::spawn(|| {
- match metrics::register_pull_metrics_callbacks() {
- Err(e) => error!("register_pull_metrics_callbacks failed: {:?}.", e),
- _ => info!("Pull metrics callbacks successfully registered."),
- };
- });
-
info!("Successfully registered Keystore 2.0 service.");
info!("Joining thread pool now.");
diff --git a/keystore2/src/lib.rs b/keystore2/src/lib.rs
index 51316d7..8b629b1 100644
--- a/keystore2/src/lib.rs
+++ b/keystore2/src/lib.rs
@@ -32,6 +32,7 @@
pub mod legacy_migrator;
pub mod maintenance;
pub mod metrics;
+pub mod metrics_store;
pub mod operation;
pub mod permission;
pub mod raw_device;
diff --git a/keystore2/src/metrics.rs b/keystore2/src/metrics.rs
index 07c3d64..42295b7 100644
--- a/keystore2/src/metrics.rs
+++ b/keystore2/src/metrics.rs
@@ -12,496 +12,45 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-//! This module provides convenience functions for keystore2 logging.
-use crate::error::get_error_code;
-use crate::globals::{DB, LOGS_HANDLER};
-use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
-use crate::operation::Outcome;
-use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
- Algorithm::Algorithm, BlockMode::BlockMode, Digest::Digest, EcCurve::EcCurve,
- HardwareAuthenticatorType::HardwareAuthenticatorType, KeyOrigin::KeyOrigin,
- KeyParameter::KeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
- SecurityLevel::SecurityLevel,
+//! This module implements the IKeystoreMetrics AIDL interface, which exposes the API method for the
+//! proxy in the system server to pull the aggregated metrics in keystore.
+use crate::error::map_or_log_err;
+use crate::metrics_store::METRICS_STORE;
+use crate::permission::KeystorePerm;
+use crate::utils::{check_keystore_permission, watchdog as wd};
+use android_security_metrics::aidl::android::security::metrics::{
+ AtomID::AtomID,
+ IKeystoreMetrics::{BnKeystoreMetrics, IKeystoreMetrics},
+ KeystoreAtom::KeystoreAtom,
};
-use anyhow::Result;
-use keystore2_system_property::PropertyWatcher;
-use statslog_rust::{
- keystore2_key_creation_event_reported::{
- Algorithm as StatsdAlgorithm, EcCurve as StatsdEcCurve, KeyOrigin as StatsdKeyOrigin,
- Keystore2KeyCreationEventReported, SecurityLevel as StatsdKeyCreationSecurityLevel,
- UserAuthType as StatsdUserAuthType,
- },
- keystore2_key_operation_event_reported::{
- Keystore2KeyOperationEventReported, Outcome as StatsdOutcome, Purpose as StatsdKeyPurpose,
- SecurityLevel as StatsdKeyOperationSecurityLevel,
- },
- keystore2_storage_stats::StorageType as StatsdStorageType,
-};
-use statslog_rust_header::Atoms;
-use statspull_rust::{set_pull_atom_callback, StatsPullResult};
+use android_security_metrics::binder::{BinderFeatures, Interface, Result as BinderResult, Strong};
+use anyhow::{Context, Result};
-fn create_default_key_creation_atom() -> Keystore2KeyCreationEventReported {
- // If a value is not present, fields represented by bitmaps and i32 fields
- // will take 0, except error_code which defaults to 1 indicating NO_ERROR and key_size,
- // and auth_time_out which default to -1.
- // The boolean fields are set to false by default.
- // Some keymint enums do have 0 as an enum variant value. In such cases, the corresponding
- // enum variant value in atoms.proto is incremented by 1, in order to have 0 as the reserved
- // value for unspecified fields.
- Keystore2KeyCreationEventReported {
- algorithm: StatsdAlgorithm::AlgorithmUnspecified,
- key_size: -1,
- key_origin: StatsdKeyOrigin::OriginUnspecified,
- user_auth_type: StatsdUserAuthType::AuthTypeUnspecified,
- user_auth_key_timeout_seconds: -1,
- padding_mode_bitmap: 0,
- digest_bitmap: 0,
- block_mode_bitmap: 0,
- purpose_bitmap: 0,
- ec_curve: StatsdEcCurve::EcCurveUnspecified,
- // as per keystore2/ResponseCode.aidl, 1 is reserved for NO_ERROR
- error_code: 1,
- attestation_requested: false,
- security_level: StatsdKeyCreationSecurityLevel::SecurityLevelUnspecified,
+/// This struct is defined to implement IKeystoreMetrics AIDL interface.
+pub struct Metrics;
+
+impl Metrics {
+ /// Create a new instance of Keystore Metrics service.
+ pub fn new_native_binder() -> Result<Strong<dyn IKeystoreMetrics>> {
+ Ok(BnKeystoreMetrics::new_binder(
+ Self,
+ BinderFeatures { set_requesting_sid: true, ..BinderFeatures::default() },
+ ))
+ }
+
+ fn pull_metrics(&self, atom_id: AtomID) -> Result<Vec<KeystoreAtom>> {
+ // Check permission. Function should return if this failed. Therefore having '?' at the end
+ // is very important.
+ check_keystore_permission(KeystorePerm::pull_metrics()).context("In pull_metrics.")?;
+ METRICS_STORE.get_atoms(atom_id)
}
}
-fn create_default_key_operation_atom() -> Keystore2KeyOperationEventReported {
- Keystore2KeyOperationEventReported {
- purpose: StatsdKeyPurpose::KeyPurposeUnspecified,
- padding_mode_bitmap: 0,
- digest_bitmap: 0,
- block_mode_bitmap: 0,
- outcome: StatsdOutcome::OutcomeUnspecified,
- error_code: 1,
- key_upgraded: false,
- security_level: StatsdKeyOperationSecurityLevel::SecurityLevelUnspecified,
+impl Interface for Metrics {}
+
+impl IKeystoreMetrics for Metrics {
+ fn pullMetrics(&self, atom_id: AtomID) -> BinderResult<Vec<KeystoreAtom>> {
+ let _wp = wd::watch_millis("IKeystoreMetrics::pullMetrics", 500);
+ map_or_log_err(self.pull_metrics(atom_id), Ok)
}
}
-
-/// Log key creation events via statsd API.
-pub fn log_key_creation_event_stats<U>(
- sec_level: SecurityLevel,
- key_params: &[KeyParameter],
- result: &Result<U>,
-) {
- let key_creation_event_stats =
- construct_key_creation_event_stats(sec_level, key_params, result);
-
- LOGS_HANDLER.queue_lo(move |_| {
- let logging_result = key_creation_event_stats.stats_write();
-
- if let Err(e) = logging_result {
- log::error!("Error in logging key creation event in the async task. {:?}", e);
- }
- });
-}
-
-/// Log key operation events via statsd API.
-pub fn log_key_operation_event_stats(
- sec_level: SecurityLevel,
- key_purpose: KeyPurpose,
- op_params: &[KeyParameter],
- op_outcome: &Outcome,
- key_upgraded: bool,
-) {
- let key_operation_event_stats = construct_key_operation_event_stats(
- sec_level,
- key_purpose,
- op_params,
- op_outcome,
- key_upgraded,
- );
-
- LOGS_HANDLER.queue_lo(move |_| {
- let logging_result = key_operation_event_stats.stats_write();
-
- if let Err(e) = logging_result {
- log::error!("Error in logging key operation event in the async task. {:?}", e);
- }
- });
-}
-
-fn construct_key_creation_event_stats<U>(
- sec_level: SecurityLevel,
- key_params: &[KeyParameter],
- result: &Result<U>,
-) -> Keystore2KeyCreationEventReported {
- let mut key_creation_event_atom = create_default_key_creation_atom();
-
- if let Err(ref e) = result {
- key_creation_event_atom.error_code = get_error_code(e);
- }
-
- key_creation_event_atom.security_level = match sec_level {
- SecurityLevel::SOFTWARE => StatsdKeyCreationSecurityLevel::SecurityLevelSoftware,
- SecurityLevel::TRUSTED_ENVIRONMENT => {
- StatsdKeyCreationSecurityLevel::SecurityLevelTrustedEnvironment
- }
- SecurityLevel::STRONGBOX => StatsdKeyCreationSecurityLevel::SecurityLevelStrongbox,
- //KEYSTORE is not a valid variant here
- _ => StatsdKeyCreationSecurityLevel::SecurityLevelUnspecified,
- };
-
- for key_param in key_params.iter().map(KsKeyParamValue::from) {
- match key_param {
- KsKeyParamValue::Algorithm(a) => {
- key_creation_event_atom.algorithm = match a {
- Algorithm::RSA => StatsdAlgorithm::Rsa,
- Algorithm::EC => StatsdAlgorithm::Ec,
- Algorithm::AES => StatsdAlgorithm::Aes,
- Algorithm::TRIPLE_DES => StatsdAlgorithm::TripleDes,
- Algorithm::HMAC => StatsdAlgorithm::Hmac,
- _ => StatsdAlgorithm::AlgorithmUnspecified,
- }
- }
- KsKeyParamValue::KeySize(s) => {
- key_creation_event_atom.key_size = s;
- }
- KsKeyParamValue::KeyOrigin(o) => {
- key_creation_event_atom.key_origin = match o {
- KeyOrigin::GENERATED => StatsdKeyOrigin::Generated,
- KeyOrigin::DERIVED => StatsdKeyOrigin::Derived,
- KeyOrigin::IMPORTED => StatsdKeyOrigin::Imported,
- KeyOrigin::RESERVED => StatsdKeyOrigin::Reserved,
- KeyOrigin::SECURELY_IMPORTED => StatsdKeyOrigin::SecurelyImported,
- _ => StatsdKeyOrigin::OriginUnspecified,
- }
- }
- KsKeyParamValue::HardwareAuthenticatorType(a) => {
- key_creation_event_atom.user_auth_type = match a {
- HardwareAuthenticatorType::NONE => StatsdUserAuthType::None,
- HardwareAuthenticatorType::PASSWORD => StatsdUserAuthType::Password,
- HardwareAuthenticatorType::FINGERPRINT => StatsdUserAuthType::Fingerprint,
- HardwareAuthenticatorType::ANY => StatsdUserAuthType::Any,
- _ => StatsdUserAuthType::AuthTypeUnspecified,
- }
- }
- KsKeyParamValue::AuthTimeout(t) => {
- key_creation_event_atom.user_auth_key_timeout_seconds = t;
- }
- KsKeyParamValue::PaddingMode(p) => {
- key_creation_event_atom.padding_mode_bitmap =
- compute_padding_mode_bitmap(&key_creation_event_atom.padding_mode_bitmap, p);
- }
- KsKeyParamValue::Digest(d) => {
- key_creation_event_atom.digest_bitmap =
- compute_digest_bitmap(&key_creation_event_atom.digest_bitmap, d);
- }
- KsKeyParamValue::BlockMode(b) => {
- key_creation_event_atom.block_mode_bitmap =
- compute_block_mode_bitmap(&key_creation_event_atom.block_mode_bitmap, b);
- }
- KsKeyParamValue::KeyPurpose(k) => {
- key_creation_event_atom.purpose_bitmap =
- compute_purpose_bitmap(&key_creation_event_atom.purpose_bitmap, k);
- }
- KsKeyParamValue::EcCurve(e) => {
- key_creation_event_atom.ec_curve = match e {
- EcCurve::P_224 => StatsdEcCurve::P224,
- EcCurve::P_256 => StatsdEcCurve::P256,
- EcCurve::P_384 => StatsdEcCurve::P384,
- EcCurve::P_521 => StatsdEcCurve::P521,
- _ => StatsdEcCurve::EcCurveUnspecified,
- }
- }
- KsKeyParamValue::AttestationChallenge(_) => {
- key_creation_event_atom.attestation_requested = true;
- }
- _ => {}
- }
- }
- key_creation_event_atom
-}
-
-fn construct_key_operation_event_stats(
- sec_level: SecurityLevel,
- key_purpose: KeyPurpose,
- op_params: &[KeyParameter],
- op_outcome: &Outcome,
- key_upgraded: bool,
-) -> Keystore2KeyOperationEventReported {
- let mut key_operation_event_atom = create_default_key_operation_atom();
-
- key_operation_event_atom.security_level = match sec_level {
- SecurityLevel::SOFTWARE => StatsdKeyOperationSecurityLevel::SecurityLevelSoftware,
- SecurityLevel::TRUSTED_ENVIRONMENT => {
- StatsdKeyOperationSecurityLevel::SecurityLevelTrustedEnvironment
- }
- SecurityLevel::STRONGBOX => StatsdKeyOperationSecurityLevel::SecurityLevelStrongbox,
- //KEYSTORE is not a valid variant here
- _ => StatsdKeyOperationSecurityLevel::SecurityLevelUnspecified,
- };
-
- key_operation_event_atom.key_upgraded = key_upgraded;
-
- key_operation_event_atom.purpose = match key_purpose {
- KeyPurpose::ENCRYPT => StatsdKeyPurpose::Encrypt,
- KeyPurpose::DECRYPT => StatsdKeyPurpose::Decrypt,
- KeyPurpose::SIGN => StatsdKeyPurpose::Sign,
- KeyPurpose::VERIFY => StatsdKeyPurpose::Verify,
- KeyPurpose::WRAP_KEY => StatsdKeyPurpose::WrapKey,
- KeyPurpose::AGREE_KEY => StatsdKeyPurpose::AgreeKey,
- KeyPurpose::ATTEST_KEY => StatsdKeyPurpose::AttestKey,
- _ => StatsdKeyPurpose::KeyPurposeUnspecified,
- };
-
- key_operation_event_atom.outcome = match op_outcome {
- Outcome::Unknown | Outcome::Dropped => StatsdOutcome::Dropped,
- Outcome::Success => StatsdOutcome::Success,
- Outcome::Abort => StatsdOutcome::Abort,
- Outcome::Pruned => StatsdOutcome::Pruned,
- Outcome::ErrorCode(e) => {
- key_operation_event_atom.error_code = e.0;
- StatsdOutcome::Error
- }
- };
-
- for key_param in op_params.iter().map(KsKeyParamValue::from) {
- match key_param {
- KsKeyParamValue::PaddingMode(p) => {
- key_operation_event_atom.padding_mode_bitmap =
- compute_padding_mode_bitmap(&key_operation_event_atom.padding_mode_bitmap, p);
- }
- KsKeyParamValue::Digest(d) => {
- key_operation_event_atom.digest_bitmap =
- compute_digest_bitmap(&key_operation_event_atom.digest_bitmap, d);
- }
- KsKeyParamValue::BlockMode(b) => {
- key_operation_event_atom.block_mode_bitmap =
- compute_block_mode_bitmap(&key_operation_event_atom.block_mode_bitmap, b);
- }
- _ => {}
- }
- }
-
- key_operation_event_atom
-}
-
-fn compute_purpose_bitmap(purpose_bitmap: &i32, purpose: KeyPurpose) -> i32 {
- let mut bitmap = *purpose_bitmap;
- match purpose {
- KeyPurpose::ENCRYPT => {
- bitmap |= 1 << KeyPurposeBitPosition::ENCRYPT_BIT_POS as i32;
- }
- KeyPurpose::DECRYPT => {
- bitmap |= 1 << KeyPurposeBitPosition::DECRYPT_BIT_POS as i32;
- }
- KeyPurpose::SIGN => {
- bitmap |= 1 << KeyPurposeBitPosition::SIGN_BIT_POS as i32;
- }
- KeyPurpose::VERIFY => {
- bitmap |= 1 << KeyPurposeBitPosition::VERIFY_BIT_POS as i32;
- }
- KeyPurpose::WRAP_KEY => {
- bitmap |= 1 << KeyPurposeBitPosition::WRAP_KEY_BIT_POS as i32;
- }
- KeyPurpose::AGREE_KEY => {
- bitmap |= 1 << KeyPurposeBitPosition::AGREE_KEY_BIT_POS as i32;
- }
- KeyPurpose::ATTEST_KEY => {
- bitmap |= 1 << KeyPurposeBitPosition::ATTEST_KEY_BIT_POS as i32;
- }
- _ => {}
- }
- bitmap
-}
-
-fn compute_padding_mode_bitmap(padding_mode_bitmap: &i32, padding_mode: PaddingMode) -> i32 {
- let mut bitmap = *padding_mode_bitmap;
- match padding_mode {
- PaddingMode::NONE => {
- bitmap |= 1 << PaddingModeBitPosition::NONE_BIT_POSITION as i32;
- }
- PaddingMode::RSA_OAEP => {
- bitmap |= 1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS as i32;
- }
- PaddingMode::RSA_PSS => {
- bitmap |= 1 << PaddingModeBitPosition::RSA_PSS_BIT_POS as i32;
- }
- PaddingMode::RSA_PKCS1_1_5_ENCRYPT => {
- bitmap |= 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS as i32;
- }
- PaddingMode::RSA_PKCS1_1_5_SIGN => {
- bitmap |= 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS as i32;
- }
- PaddingMode::PKCS7 => {
- bitmap |= 1 << PaddingModeBitPosition::PKCS7_BIT_POS as i32;
- }
- _ => {}
- }
- bitmap
-}
-
-fn compute_digest_bitmap(digest_bitmap: &i32, digest: Digest) -> i32 {
- let mut bitmap = *digest_bitmap;
- match digest {
- Digest::NONE => {
- bitmap |= 1 << DigestBitPosition::NONE_BIT_POSITION as i32;
- }
- Digest::MD5 => {
- bitmap |= 1 << DigestBitPosition::MD5_BIT_POS as i32;
- }
- Digest::SHA1 => {
- bitmap |= 1 << DigestBitPosition::SHA_1_BIT_POS as i32;
- }
- Digest::SHA_2_224 => {
- bitmap |= 1 << DigestBitPosition::SHA_2_224_BIT_POS as i32;
- }
- Digest::SHA_2_256 => {
- bitmap |= 1 << DigestBitPosition::SHA_2_256_BIT_POS as i32;
- }
- Digest::SHA_2_384 => {
- bitmap |= 1 << DigestBitPosition::SHA_2_384_BIT_POS as i32;
- }
- Digest::SHA_2_512 => {
- bitmap |= 1 << DigestBitPosition::SHA_2_512_BIT_POS as i32;
- }
- _ => {}
- }
- bitmap
-}
-
-fn compute_block_mode_bitmap(block_mode_bitmap: &i32, block_mode: BlockMode) -> i32 {
- let mut bitmap = *block_mode_bitmap;
- match block_mode {
- BlockMode::ECB => {
- bitmap |= 1 << BlockModeBitPosition::ECB_BIT_POS as i32;
- }
- BlockMode::CBC => {
- bitmap |= 1 << BlockModeBitPosition::CBC_BIT_POS as i32;
- }
- BlockMode::CTR => {
- bitmap |= 1 << BlockModeBitPosition::CTR_BIT_POS as i32;
- }
- BlockMode::GCM => {
- bitmap |= 1 << BlockModeBitPosition::GCM_BIT_POS as i32;
- }
- _ => {}
- }
- bitmap
-}
-
-/// Registers pull metrics callbacks
-pub fn register_pull_metrics_callbacks() -> Result<()> {
- // Before registering the callbacks with statsd, we have to wait for the system to finish
- // booting up. This avoids possible races that may occur at startup. For example, statsd
- // depends on a companion service, and if registration happens too soon it will fail since
- // the companion service isn't up yet.
- let mut watcher = PropertyWatcher::new("sys.boot_completed")?;
- loop {
- watcher.wait()?;
- let value = watcher.read(|_name, value| Ok(value.trim().to_string()));
- if value? == "1" {
- set_pull_atom_callback(Atoms::Keystore2StorageStats, None, pull_metrics_callback);
- break;
- }
- }
- Ok(())
-}
-
-fn pull_metrics_callback() -> StatsPullResult {
- let mut result = StatsPullResult::new();
- let mut append = |stat| {
- match stat {
- Ok(s) => result.push(Box::new(s)),
- Err(error) => {
- log::error!("pull_metrics_callback: Error getting storage stat: {}", error)
- }
- };
- };
- DB.with(|db| {
- let mut db = db.borrow_mut();
- append(db.get_storage_stat(StatsdStorageType::Database));
- append(db.get_storage_stat(StatsdStorageType::KeyEntry));
- append(db.get_storage_stat(StatsdStorageType::KeyEntryIdIndex));
- append(db.get_storage_stat(StatsdStorageType::KeyEntryDomainNamespaceIndex));
- append(db.get_storage_stat(StatsdStorageType::BlobEntry));
- append(db.get_storage_stat(StatsdStorageType::BlobEntryKeyEntryIdIndex));
- append(db.get_storage_stat(StatsdStorageType::KeyParameter));
- append(db.get_storage_stat(StatsdStorageType::KeyParameterKeyEntryIdIndex));
- append(db.get_storage_stat(StatsdStorageType::KeyMetadata));
- append(db.get_storage_stat(StatsdStorageType::KeyMetadataKeyEntryIdIndex));
- append(db.get_storage_stat(StatsdStorageType::Grant));
- append(db.get_storage_stat(StatsdStorageType::AuthToken));
- append(db.get_storage_stat(StatsdStorageType::BlobMetadata));
- append(db.get_storage_stat(StatsdStorageType::BlobMetadataBlobEntryIdIndex));
- });
- result
-}
-
-/// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
-/// is represented using a bitmap.
-#[allow(non_camel_case_types)]
-#[repr(i32)]
-pub enum PaddingModeBitPosition {
- ///Bit position in the PaddingMode bitmap for NONE.
- NONE_BIT_POSITION = 0,
- ///Bit position in the PaddingMode bitmap for RSA_OAEP.
- RSA_OAEP_BIT_POS = 1,
- ///Bit position in the PaddingMode bitmap for RSA_PSS.
- RSA_PSS_BIT_POS = 2,
- ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_ENCRYPT.
- RSA_PKCS1_1_5_ENCRYPT_BIT_POS = 3,
- ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_SIGN.
- RSA_PKCS1_1_5_SIGN_BIT_POS = 4,
- ///Bit position in the PaddingMode bitmap for RSA_PKCS7.
- PKCS7_BIT_POS = 5,
-}
-
-/// Enum defining the bit position for each digest type. Since digest can be repeatable in
-/// key parameters, it is represented using a bitmap.
-#[allow(non_camel_case_types)]
-#[repr(i32)]
-pub enum DigestBitPosition {
- ///Bit position in the Digest bitmap for NONE.
- NONE_BIT_POSITION = 0,
- ///Bit position in the Digest bitmap for MD5.
- MD5_BIT_POS = 1,
- ///Bit position in the Digest bitmap for SHA1.
- SHA_1_BIT_POS = 2,
- ///Bit position in the Digest bitmap for SHA_2_224.
- SHA_2_224_BIT_POS = 3,
- ///Bit position in the Digest bitmap for SHA_2_256.
- SHA_2_256_BIT_POS = 4,
- ///Bit position in the Digest bitmap for SHA_2_384.
- SHA_2_384_BIT_POS = 5,
- ///Bit position in the Digest bitmap for SHA_2_512.
- SHA_2_512_BIT_POS = 6,
-}
-
-/// Enum defining the bit position for each block mode type. Since block mode can be repeatable in
-/// key parameters, it is represented using a bitmap.
-#[allow(non_camel_case_types)]
-#[repr(i32)]
-enum BlockModeBitPosition {
- ///Bit position in the BlockMode bitmap for ECB.
- ECB_BIT_POS = 1,
- ///Bit position in the BlockMode bitmap for CBC.
- CBC_BIT_POS = 2,
- ///Bit position in the BlockMode bitmap for CTR.
- CTR_BIT_POS = 3,
- ///Bit position in the BlockMode bitmap for GCM.
- GCM_BIT_POS = 4,
-}
-
-/// Enum defining the bit position for each key purpose. Since key purpose can be repeatable in
-/// key parameters, it is represented using a bitmap.
-#[allow(non_camel_case_types)]
-#[repr(i32)]
-enum KeyPurposeBitPosition {
- ///Bit position in the KeyPurpose bitmap for Encrypt.
- ENCRYPT_BIT_POS = 1,
- ///Bit position in the KeyPurpose bitmap for Decrypt.
- DECRYPT_BIT_POS = 2,
- ///Bit position in the KeyPurpose bitmap for Sign.
- SIGN_BIT_POS = 3,
- ///Bit position in the KeyPurpose bitmap for Verify.
- VERIFY_BIT_POS = 4,
- ///Bit position in the KeyPurpose bitmap for Wrap Key.
- WRAP_KEY_BIT_POS = 5,
- ///Bit position in the KeyPurpose bitmap for Agree Key.
- AGREE_KEY_BIT_POS = 6,
- ///Bit position in the KeyPurpose bitmap for Attest Key.
- ATTEST_KEY_BIT_POS = 7,
-}
diff --git a/keystore2/src/metrics_store.rs b/keystore2/src/metrics_store.rs
new file mode 100644
index 0000000..32067b9
--- /dev/null
+++ b/keystore2/src/metrics_store.rs
@@ -0,0 +1,724 @@
+// 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 is the metrics store module of keystore. It does the following tasks:
+//! 1. Processes the data about keystore events asynchronously, and
+//! 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::globals::DB;
+use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
+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,
+ KeyParameter::KeyParameter, KeyPurpose::KeyPurpose, PaddingMode::PaddingMode,
+ SecurityLevel::SecurityLevel,
+};
+use android_security_metrics::aidl::android::security::metrics::{
+ Algorithm::Algorithm as MetricsAlgorithm, AtomID::AtomID, CrashStats::CrashStats,
+ EcCurve::EcCurve as MetricsEcCurve,
+ HardwareAuthenticatorType::HardwareAuthenticatorType as MetricsHardwareAuthenticatorType,
+ KeyCreationWithAuthInfo::KeyCreationWithAuthInfo,
+ KeyCreationWithGeneralInfo::KeyCreationWithGeneralInfo,
+ KeyCreationWithPurposeAndModesInfo::KeyCreationWithPurposeAndModesInfo,
+ KeyOperationWithGeneralInfo::KeyOperationWithGeneralInfo,
+ KeyOperationWithPurposeAndModesInfo::KeyOperationWithPurposeAndModesInfo,
+ KeyOrigin::KeyOrigin as MetricsKeyOrigin, Keystore2AtomWithOverflow::Keystore2AtomWithOverflow,
+ 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,
+};
+use anyhow::{Context, Result};
+use keystore2_system_property::{write, PropertyWatcher, PropertyWatcherError};
+use lazy_static::lazy_static;
+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.
+const KEYSTORE_CRASH_COUNT_PROPERTY: &str = "keystore.crash_count";
+
+lazy_static! {
+ /// Singleton for MetricsStore.
+ pub static ref METRICS_STORE: MetricsStore = Default::default();
+}
+
+/// MetricsStore stores the <atom object, count> as <key, value> in the inner hash map,
+/// indexed by the atom id, in the outer hash map.
+/// There can be different atom objects with the same atom id based on the values assigned to the
+/// fields of the atom objects. When an atom object with a particular combination of field values is
+/// inserted, we first check if that atom object is in the inner hash map. If one exists, count
+/// is inceremented. Otherwise, the atom object is inserted with count = 1. Note that count field
+/// of the atom object itself is set to 0 while the object is stored in the hash map. When the atom
+/// objects are queried by the atom id, the corresponding atom objects are retrieved, cloned, and
+/// the count field of the cloned objects is set to the corresponding value field in the inner hash
+/// map before the query result is returned.
+#[derive(Default)]
+pub struct MetricsStore {
+ metrics_store: Mutex<HashMap<AtomID, HashMap<KeystoreAtomPayload, i32>>>,
+}
+
+impl MetricsStore {
+ /// There are some atoms whose maximum cardinality exceeds the cardinality limits tolerated
+ /// by statsd. Statsd tolerates cardinality between 200-300. Therefore, the in-memory storage
+ /// limit for a single atom is set to 250. If the number of atom objects created for a
+ /// particular atom exceeds this limit, an overflow atom object is created to track the ID of
+ /// such atoms.
+ const SINGLE_ATOM_STORE_MAX_SIZE: usize = 250;
+
+ /// Return a vector of atom objects with the given atom ID, if one exists in the metrics_store.
+ /// If any atom object does not exist in the metrics_store for the given atom ID, return an
+ /// empty vector.
+ pub fn get_atoms(&self, atom_id: AtomID) -> Result<Vec<KeystoreAtom>> {
+ // StorageStats is an original pulled atom (i.e. not a pushed atom converted to a
+ // pulledd atom). Therefore, it is handled separately.
+ if AtomID::STORAGE_STATS == atom_id {
+ 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()
+ }]);
+ }
+
+ // It is safe to call unwrap here since the lock can not be poisoned based on its usage
+ // in this module and the lock is not acquired in the same thread before.
+ let metrics_store_guard = self.metrics_store.lock().unwrap();
+ metrics_store_guard.get(&atom_id).map_or(Ok(Vec::<KeystoreAtom>::new()), |atom_count_map| {
+ Ok(atom_count_map
+ .iter()
+ .map(|(atom, count)| KeystoreAtom { payload: atom.clone(), count: *count })
+ .collect())
+ })
+ }
+
+ /// Insert an atom object to the metrics_store indexed by the atom ID.
+ fn insert_atom(&self, atom_id: AtomID, atom: KeystoreAtomPayload) {
+ // 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);
+ 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);
+
+ if overflow_atom_count_map.len() < MetricsStore::SINGLE_ATOM_STORE_MAX_SIZE {
+ let overflow_atom = Keystore2AtomWithOverflow { atom_id };
+ let atom_count = overflow_atom_count_map
+ .entry(KeystoreAtomPayload::Keystore2AtomWithOverflow(overflow_atom))
+ .or_insert(0);
+ *atom_count += 1;
+ } else {
+ // This is a rare case, if at all.
+ log::error!("In insert_atom: Maximum storage limit reached for overflow atom.")
+ }
+ }
+ }
+}
+
+/// Log key creation events to be sent to statsd.
+pub fn log_key_creation_event_stats<U>(
+ sec_level: SecurityLevel,
+ key_params: &[KeyParameter],
+ result: &Result<U>,
+) {
+ let (
+ key_creation_with_general_info,
+ key_creation_with_auth_info,
+ key_creation_with_purpose_and_modes_info,
+ ) = process_key_creation_event_stats(sec_level, key_params, result);
+
+ METRICS_STORE
+ .insert_atom(AtomID::KEY_CREATION_WITH_GENERAL_INFO, key_creation_with_general_info);
+ METRICS_STORE.insert_atom(AtomID::KEY_CREATION_WITH_AUTH_INFO, key_creation_with_auth_info);
+ METRICS_STORE.insert_atom(
+ AtomID::KEY_CREATION_WITH_PURPOSE_AND_MODES_INFO,
+ key_creation_with_purpose_and_modes_info,
+ );
+}
+
+// Process the statistics related to key creations and return the three atom objects related to key
+// creations: i) KeyCreationWithGeneralInfo ii) KeyCreationWithAuthInfo
+// iii) KeyCreationWithPurposeAndModesInfo
+fn process_key_creation_event_stats<U>(
+ sec_level: SecurityLevel,
+ key_params: &[KeyParameter],
+ result: &Result<U>,
+) -> (KeystoreAtomPayload, KeystoreAtomPayload, KeystoreAtomPayload) {
+ // In the default atom objects, fields represented by bitmaps and i32 fields
+ // will take 0, except error_code which defaults to 1 indicating NO_ERROR and key_size,
+ // and auth_time_out which defaults to -1.
+ // The boolean fields are set to false by default.
+ // Some keymint enums do have 0 as an enum variant value. In such cases, the corresponding
+ // enum variant value in atoms.proto is incremented by 1, in order to have 0 as the reserved
+ // value for unspecified fields.
+ let mut key_creation_with_general_info = KeyCreationWithGeneralInfo {
+ algorithm: MetricsAlgorithm::ALGORITHM_UNSPECIFIED,
+ key_size: -1,
+ ec_curve: MetricsEcCurve::EC_CURVE_UNSPECIFIED,
+ key_origin: MetricsKeyOrigin::ORIGIN_UNSPECIFIED,
+ error_code: 1,
+ // Default for bool is false (for attestation_requested field).
+ ..Default::default()
+ };
+
+ let mut key_creation_with_auth_info = KeyCreationWithAuthInfo {
+ user_auth_type: MetricsHardwareAuthenticatorType::AUTH_TYPE_UNSPECIFIED,
+ log10_auth_key_timeout_seconds: -1,
+ security_level: MetricsSecurityLevel::SECURITY_LEVEL_UNSPECIFIED,
+ };
+
+ let mut key_creation_with_purpose_and_modes_info = KeyCreationWithPurposeAndModesInfo {
+ algorithm: MetricsAlgorithm::ALGORITHM_UNSPECIFIED,
+ // Default for i32 is 0 (for the remaining bitmap fields).
+ ..Default::default()
+ };
+
+ if let Err(ref e) = result {
+ key_creation_with_general_info.error_code = get_error_code(e);
+ }
+
+ key_creation_with_auth_info.security_level = process_security_level(sec_level);
+
+ for key_param in key_params.iter().map(KsKeyParamValue::from) {
+ match key_param {
+ KsKeyParamValue::Algorithm(a) => {
+ let algorithm = match a {
+ Algorithm::RSA => MetricsAlgorithm::RSA,
+ Algorithm::EC => MetricsAlgorithm::EC,
+ Algorithm::AES => MetricsAlgorithm::AES,
+ Algorithm::TRIPLE_DES => MetricsAlgorithm::TRIPLE_DES,
+ Algorithm::HMAC => MetricsAlgorithm::HMAC,
+ _ => MetricsAlgorithm::ALGORITHM_UNSPECIFIED,
+ };
+ key_creation_with_general_info.algorithm = algorithm;
+ key_creation_with_purpose_and_modes_info.algorithm = algorithm;
+ }
+ KsKeyParamValue::KeySize(s) => {
+ key_creation_with_general_info.key_size = s;
+ }
+ KsKeyParamValue::KeyOrigin(o) => {
+ key_creation_with_general_info.key_origin = match o {
+ KeyOrigin::GENERATED => MetricsKeyOrigin::GENERATED,
+ KeyOrigin::DERIVED => MetricsKeyOrigin::DERIVED,
+ KeyOrigin::IMPORTED => MetricsKeyOrigin::IMPORTED,
+ KeyOrigin::RESERVED => MetricsKeyOrigin::RESERVED,
+ KeyOrigin::SECURELY_IMPORTED => MetricsKeyOrigin::SECURELY_IMPORTED,
+ _ => MetricsKeyOrigin::ORIGIN_UNSPECIFIED,
+ }
+ }
+ KsKeyParamValue::HardwareAuthenticatorType(a) => {
+ key_creation_with_auth_info.user_auth_type = match a {
+ HardwareAuthenticatorType::NONE => MetricsHardwareAuthenticatorType::NONE,
+ HardwareAuthenticatorType::PASSWORD => {
+ MetricsHardwareAuthenticatorType::PASSWORD
+ }
+ HardwareAuthenticatorType::FINGERPRINT => {
+ MetricsHardwareAuthenticatorType::FINGERPRINT
+ }
+ HardwareAuthenticatorType::ANY => MetricsHardwareAuthenticatorType::ANY,
+ _ => MetricsHardwareAuthenticatorType::AUTH_TYPE_UNSPECIFIED,
+ }
+ }
+ KsKeyParamValue::AuthTimeout(t) => {
+ key_creation_with_auth_info.log10_auth_key_timeout_seconds =
+ f32::log10(t as f32) as i32;
+ }
+ KsKeyParamValue::PaddingMode(p) => {
+ compute_padding_mode_bitmap(
+ &mut key_creation_with_purpose_and_modes_info.padding_mode_bitmap,
+ p,
+ );
+ }
+ KsKeyParamValue::Digest(d) => {
+ // key_creation_with_purpose_and_modes_info.digest_bitmap =
+ compute_digest_bitmap(
+ &mut key_creation_with_purpose_and_modes_info.digest_bitmap,
+ d,
+ );
+ }
+ KsKeyParamValue::BlockMode(b) => {
+ compute_block_mode_bitmap(
+ &mut key_creation_with_purpose_and_modes_info.block_mode_bitmap,
+ b,
+ );
+ }
+ KsKeyParamValue::KeyPurpose(k) => {
+ compute_purpose_bitmap(
+ &mut key_creation_with_purpose_and_modes_info.purpose_bitmap,
+ k,
+ );
+ }
+ KsKeyParamValue::EcCurve(e) => {
+ key_creation_with_general_info.ec_curve = match e {
+ EcCurve::P_224 => MetricsEcCurve::P_224,
+ EcCurve::P_256 => MetricsEcCurve::P_256,
+ EcCurve::P_384 => MetricsEcCurve::P_384,
+ EcCurve::P_521 => MetricsEcCurve::P_521,
+ _ => MetricsEcCurve::EC_CURVE_UNSPECIFIED,
+ }
+ }
+ KsKeyParamValue::AttestationChallenge(_) => {
+ key_creation_with_general_info.attestation_requested = true;
+ }
+ _ => {}
+ }
+ }
+ if key_creation_with_general_info.algorithm == MetricsAlgorithm::EC {
+ // Do not record key sizes if Algorithm = EC, in order to reduce cardinality.
+ key_creation_with_general_info.key_size = -1;
+ }
+
+ (
+ KeystoreAtomPayload::KeyCreationWithGeneralInfo(key_creation_with_general_info),
+ KeystoreAtomPayload::KeyCreationWithAuthInfo(key_creation_with_auth_info),
+ KeystoreAtomPayload::KeyCreationWithPurposeAndModesInfo(
+ key_creation_with_purpose_and_modes_info,
+ ),
+ )
+}
+
+/// Log key operation events to be sent to statsd.
+pub fn log_key_operation_event_stats(
+ sec_level: SecurityLevel,
+ key_purpose: KeyPurpose,
+ op_params: &[KeyParameter],
+ op_outcome: &Outcome,
+ key_upgraded: bool,
+) {
+ let (key_operation_with_general_info, key_operation_with_purpose_and_modes_info) =
+ process_key_operation_event_stats(
+ sec_level,
+ key_purpose,
+ op_params,
+ op_outcome,
+ key_upgraded,
+ );
+ METRICS_STORE
+ .insert_atom(AtomID::KEY_OPERATION_WITH_GENERAL_INFO, key_operation_with_general_info);
+ METRICS_STORE.insert_atom(
+ AtomID::KEY_OPERATION_WITH_PURPOSE_AND_MODES_INFO,
+ key_operation_with_purpose_and_modes_info,
+ );
+}
+
+// Process the statistics related to key operations and return the two atom objects related to key
+// operations: i) KeyOperationWithGeneralInfo ii) KeyOperationWithPurposeAndModesInfo
+fn process_key_operation_event_stats(
+ sec_level: SecurityLevel,
+ key_purpose: KeyPurpose,
+ op_params: &[KeyParameter],
+ op_outcome: &Outcome,
+ key_upgraded: bool,
+) -> (KeystoreAtomPayload, KeystoreAtomPayload) {
+ let mut key_operation_with_general_info = KeyOperationWithGeneralInfo {
+ outcome: MetricsOutcome::OUTCOME_UNSPECIFIED,
+ error_code: 1,
+ security_level: MetricsSecurityLevel::SECURITY_LEVEL_UNSPECIFIED,
+ // Default for bool is false (for key_upgraded field).
+ ..Default::default()
+ };
+
+ let mut key_operation_with_purpose_and_modes_info = KeyOperationWithPurposeAndModesInfo {
+ purpose: MetricsPurpose::KEY_PURPOSE_UNSPECIFIED,
+ // Default for i32 is 0 (for the remaining bitmap fields).
+ ..Default::default()
+ };
+
+ key_operation_with_general_info.security_level = process_security_level(sec_level);
+
+ key_operation_with_general_info.key_upgraded = key_upgraded;
+
+ key_operation_with_purpose_and_modes_info.purpose = match key_purpose {
+ KeyPurpose::ENCRYPT => MetricsPurpose::ENCRYPT,
+ KeyPurpose::DECRYPT => MetricsPurpose::DECRYPT,
+ KeyPurpose::SIGN => MetricsPurpose::SIGN,
+ KeyPurpose::VERIFY => MetricsPurpose::VERIFY,
+ KeyPurpose::WRAP_KEY => MetricsPurpose::WRAP_KEY,
+ KeyPurpose::AGREE_KEY => MetricsPurpose::AGREE_KEY,
+ KeyPurpose::ATTEST_KEY => MetricsPurpose::ATTEST_KEY,
+ _ => MetricsPurpose::KEY_PURPOSE_UNSPECIFIED,
+ };
+
+ key_operation_with_general_info.outcome = match op_outcome {
+ Outcome::Unknown | Outcome::Dropped => MetricsOutcome::DROPPED,
+ Outcome::Success => MetricsOutcome::SUCCESS,
+ Outcome::Abort => MetricsOutcome::ABORT,
+ Outcome::Pruned => MetricsOutcome::PRUNED,
+ Outcome::ErrorCode(e) => {
+ key_operation_with_general_info.error_code = e.0;
+ MetricsOutcome::ERROR
+ }
+ };
+
+ for key_param in op_params.iter().map(KsKeyParamValue::from) {
+ match key_param {
+ KsKeyParamValue::PaddingMode(p) => {
+ compute_padding_mode_bitmap(
+ &mut key_operation_with_purpose_and_modes_info.padding_mode_bitmap,
+ p,
+ );
+ }
+ KsKeyParamValue::Digest(d) => {
+ compute_digest_bitmap(
+ &mut key_operation_with_purpose_and_modes_info.digest_bitmap,
+ d,
+ );
+ }
+ KsKeyParamValue::BlockMode(b) => {
+ compute_block_mode_bitmap(
+ &mut key_operation_with_purpose_and_modes_info.block_mode_bitmap,
+ b,
+ );
+ }
+ _ => {}
+ }
+ }
+
+ (
+ KeystoreAtomPayload::KeyOperationWithGeneralInfo(key_operation_with_general_info),
+ KeystoreAtomPayload::KeyOperationWithPurposeAndModesInfo(
+ key_operation_with_purpose_and_modes_info,
+ ),
+ )
+}
+
+fn process_security_level(sec_level: SecurityLevel) -> MetricsSecurityLevel {
+ match sec_level {
+ SecurityLevel::SOFTWARE => MetricsSecurityLevel::SECURITY_LEVEL_SOFTWARE,
+ SecurityLevel::TRUSTED_ENVIRONMENT => {
+ MetricsSecurityLevel::SECURITY_LEVEL_TRUSTED_ENVIRONMENT
+ }
+ SecurityLevel::STRONGBOX => MetricsSecurityLevel::SECURITY_LEVEL_STRONGBOX,
+ SecurityLevel::KEYSTORE => MetricsSecurityLevel::SECURITY_LEVEL_KEYSTORE,
+ _ => MetricsSecurityLevel::SECURITY_LEVEL_UNSPECIFIED,
+ }
+}
+
+fn compute_padding_mode_bitmap(padding_mode_bitmap: &mut i32, padding_mode: PaddingMode) {
+ match padding_mode {
+ PaddingMode::NONE => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::NONE_BIT_POSITION as i32;
+ }
+ PaddingMode::RSA_OAEP => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::RSA_OAEP_BIT_POS as i32;
+ }
+ PaddingMode::RSA_PSS => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::RSA_PSS_BIT_POS as i32;
+ }
+ PaddingMode::RSA_PKCS1_1_5_ENCRYPT => {
+ *padding_mode_bitmap |=
+ 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_ENCRYPT_BIT_POS as i32;
+ }
+ PaddingMode::RSA_PKCS1_1_5_SIGN => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::RSA_PKCS1_1_5_SIGN_BIT_POS as i32;
+ }
+ PaddingMode::PKCS7 => {
+ *padding_mode_bitmap |= 1 << PaddingModeBitPosition::PKCS7_BIT_POS as i32;
+ }
+ _ => {}
+ }
+}
+
+fn compute_digest_bitmap(digest_bitmap: &mut i32, digest: Digest) {
+ match digest {
+ Digest::NONE => {
+ *digest_bitmap |= 1 << DigestBitPosition::NONE_BIT_POSITION as i32;
+ }
+ Digest::MD5 => {
+ *digest_bitmap |= 1 << DigestBitPosition::MD5_BIT_POS as i32;
+ }
+ Digest::SHA1 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_1_BIT_POS as i32;
+ }
+ Digest::SHA_2_224 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_2_224_BIT_POS as i32;
+ }
+ Digest::SHA_2_256 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_2_256_BIT_POS as i32;
+ }
+ Digest::SHA_2_384 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_2_384_BIT_POS as i32;
+ }
+ Digest::SHA_2_512 => {
+ *digest_bitmap |= 1 << DigestBitPosition::SHA_2_512_BIT_POS as i32;
+ }
+ _ => {}
+ }
+}
+
+fn compute_block_mode_bitmap(block_mode_bitmap: &mut i32, block_mode: BlockMode) {
+ match block_mode {
+ BlockMode::ECB => {
+ *block_mode_bitmap |= 1 << BlockModeBitPosition::ECB_BIT_POS as i32;
+ }
+ BlockMode::CBC => {
+ *block_mode_bitmap |= 1 << BlockModeBitPosition::CBC_BIT_POS as i32;
+ }
+ BlockMode::CTR => {
+ *block_mode_bitmap |= 1 << BlockModeBitPosition::CTR_BIT_POS as i32;
+ }
+ BlockMode::GCM => {
+ *block_mode_bitmap |= 1 << BlockModeBitPosition::GCM_BIT_POS as i32;
+ }
+ _ => {}
+ }
+}
+
+fn compute_purpose_bitmap(purpose_bitmap: &mut i32, purpose: KeyPurpose) {
+ match purpose {
+ KeyPurpose::ENCRYPT => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::ENCRYPT_BIT_POS as i32;
+ }
+ KeyPurpose::DECRYPT => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::DECRYPT_BIT_POS as i32;
+ }
+ KeyPurpose::SIGN => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::SIGN_BIT_POS as i32;
+ }
+ KeyPurpose::VERIFY => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::VERIFY_BIT_POS as i32;
+ }
+ KeyPurpose::WRAP_KEY => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::WRAP_KEY_BIT_POS as i32;
+ }
+ KeyPurpose::AGREE_KEY => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::AGREE_KEY_BIT_POS as i32;
+ }
+ KeyPurpose::ATTEST_KEY => {
+ *purpose_bitmap |= 1 << KeyPurposeBitPosition::ATTEST_KEY_BIT_POS as i32;
+ }
+ _ => {}
+ }
+}
+
+fn pull_storage_stats() -> Result<Vec<KeystoreAtom>> {
+ let mut atom_vec: Vec<KeystoreAtom> = Vec::new();
+ let mut append = |stat| {
+ match stat {
+ Ok(s) => atom_vec.push(KeystoreAtom {
+ payload: KeystoreAtomPayload::StorageStats(s),
+ ..Default::default()
+ }),
+ Err(error) => {
+ log::error!("pull_metrics_callback: Error getting storage stat: {}", error)
+ }
+ };
+ };
+ DB.with(|db| {
+ let mut db = db.borrow_mut();
+ append(db.get_storage_stat(MetricsStorage::DATABASE));
+ append(db.get_storage_stat(MetricsStorage::KEY_ENTRY));
+ append(db.get_storage_stat(MetricsStorage::KEY_ENTRY_ID_INDEX));
+ append(db.get_storage_stat(MetricsStorage::KEY_ENTRY_DOMAIN_NAMESPACE_INDEX));
+ append(db.get_storage_stat(MetricsStorage::BLOB_ENTRY));
+ append(db.get_storage_stat(MetricsStorage::BLOB_ENTRY_KEY_ENTRY_ID_INDEX));
+ append(db.get_storage_stat(MetricsStorage::KEY_PARAMETER));
+ append(db.get_storage_stat(MetricsStorage::KEY_PARAMETER_KEY_ENTRY_ID_INDEX));
+ append(db.get_storage_stat(MetricsStorage::KEY_METADATA));
+ append(db.get_storage_stat(MetricsStorage::KEY_METADATA_KEY_ENTRY_ID_INDEX));
+ append(db.get_storage_stat(MetricsStorage::GRANT));
+ append(db.get_storage_stat(MetricsStorage::AUTH_TOKEN));
+ append(db.get_storage_stat(MetricsStorage::BLOB_METADATA));
+ append(db.get_storage_stat(MetricsStorage::BLOB_METADATA_BLOB_ENTRY_ID_INDEX));
+ });
+ 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] {
+ let expired_by = SystemTime::now()
+ .duration_since(UNIX_EPOCH)
+ .unwrap_or_else(|_| Duration::new(0, 0))
+ .as_secs() 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) {
+ let rkp_error_stats = KeystoreAtomPayload::RkpErrorStats(RkpErrorStats { rkpError: rkp_error });
+ METRICS_STORE.insert_atom(AtomID::RKP_ERROR_STATS, rkp_error_stats);
+}
+
+/// This function tries to read and update the system property: keystore.crash_count.
+/// 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,
+ 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
+ }
+ };
+
+ if let Err(e) = write(KEYSTORE_CRASH_COUNT_PROPERTY, &new_count.to_string()) {
+ log::error!(
+ concat!(
+ "In update_keystore_crash_sysprop:: ",
+ "Failed to write the system property due to error: {:?}"
+ ),
+ e
+ );
+ }
+}
+
+/// Read the system property: keystore.crash_count.
+pub fn read_keystore_crash_count() -> Result<i32> {
+ let mut prop_reader = PropertyWatcher::new("keystore.crash_count").context(concat!(
+ "In read_keystore_crash_count: Failed to create reader a PropertyWatcher."
+ ))?;
+ prop_reader
+ .read(|_n, v| v.parse::<i32>().map_err(std::convert::Into::into))
+ .context("In read_keystore_crash_count: Failed to read the existing system property.")
+}
+
+/// Enum defining the bit position for each padding mode. Since padding mode can be repeatable, it
+/// is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum PaddingModeBitPosition {
+ ///Bit position in the PaddingMode bitmap for NONE.
+ NONE_BIT_POSITION = 0,
+ ///Bit position in the PaddingMode bitmap for RSA_OAEP.
+ RSA_OAEP_BIT_POS = 1,
+ ///Bit position in the PaddingMode bitmap for RSA_PSS.
+ RSA_PSS_BIT_POS = 2,
+ ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_ENCRYPT.
+ RSA_PKCS1_1_5_ENCRYPT_BIT_POS = 3,
+ ///Bit position in the PaddingMode bitmap for RSA_PKCS1_1_5_SIGN.
+ RSA_PKCS1_1_5_SIGN_BIT_POS = 4,
+ ///Bit position in the PaddingMode bitmap for RSA_PKCS7.
+ PKCS7_BIT_POS = 5,
+}
+
+/// Enum defining the bit position for each digest type. Since digest can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum DigestBitPosition {
+ ///Bit position in the Digest bitmap for NONE.
+ NONE_BIT_POSITION = 0,
+ ///Bit position in the Digest bitmap for MD5.
+ MD5_BIT_POS = 1,
+ ///Bit position in the Digest bitmap for SHA1.
+ SHA_1_BIT_POS = 2,
+ ///Bit position in the Digest bitmap for SHA_2_224.
+ SHA_2_224_BIT_POS = 3,
+ ///Bit position in the Digest bitmap for SHA_2_256.
+ SHA_2_256_BIT_POS = 4,
+ ///Bit position in the Digest bitmap for SHA_2_384.
+ SHA_2_384_BIT_POS = 5,
+ ///Bit position in the Digest bitmap for SHA_2_512.
+ SHA_2_512_BIT_POS = 6,
+}
+
+/// Enum defining the bit position for each block mode type. Since block mode can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum BlockModeBitPosition {
+ ///Bit position in the BlockMode bitmap for ECB.
+ ECB_BIT_POS = 1,
+ ///Bit position in the BlockMode bitmap for CBC.
+ CBC_BIT_POS = 2,
+ ///Bit position in the BlockMode bitmap for CTR.
+ CTR_BIT_POS = 3,
+ ///Bit position in the BlockMode bitmap for GCM.
+ GCM_BIT_POS = 4,
+}
+
+/// Enum defining the bit position for each key purpose. Since key purpose can be repeatable in
+/// key parameters, it is represented using a bitmap.
+#[allow(non_camel_case_types)]
+#[repr(i32)]
+enum KeyPurposeBitPosition {
+ ///Bit position in the KeyPurpose bitmap for Encrypt.
+ ENCRYPT_BIT_POS = 1,
+ ///Bit position in the KeyPurpose bitmap for Decrypt.
+ DECRYPT_BIT_POS = 2,
+ ///Bit position in the KeyPurpose bitmap for Sign.
+ SIGN_BIT_POS = 3,
+ ///Bit position in the KeyPurpose bitmap for Verify.
+ VERIFY_BIT_POS = 4,
+ ///Bit position in the KeyPurpose bitmap for Wrap Key.
+ WRAP_KEY_BIT_POS = 5,
+ ///Bit position in the KeyPurpose bitmap for Agree Key.
+ AGREE_KEY_BIT_POS = 6,
+ ///Bit position in the KeyPurpose bitmap for Attest Key.
+ ATTEST_KEY_BIT_POS = 7,
+}
diff --git a/keystore2/src/operation.rs b/keystore2/src/operation.rs
index 1d595b3..7e08f4e 100644
--- a/keystore2/src/operation.rs
+++ b/keystore2/src/operation.rs
@@ -127,7 +127,7 @@
use crate::enforcements::AuthInfo;
use crate::error::{map_err_with, map_km_error, map_or_log_err, Error, ErrorCode, ResponseCode};
-use crate::metrics::log_key_operation_event_stats;
+use crate::metrics_store::log_key_operation_event_stats;
use crate::utils::watchdog as wd;
use android_hardware_security_keymint::aidl::android::hardware::security::keymint::{
IKeyMintOperation::IKeyMintOperation, KeyParameter::KeyParameter, KeyPurpose::KeyPurpose,
diff --git a/keystore2/src/permission.rs b/keystore2/src/permission.rs
index e7999bc..8343a29 100644
--- a/keystore2/src/permission.rs
+++ b/keystore2/src/permission.rs
@@ -315,6 +315,8 @@
EarlyBootEnded = 0x800, selinux name: early_boot_ended;
/// Checked when IKeystoreMaintenance::onDeviceOffBody is called.
ReportOffBody = 0x1000, selinux name: report_off_body;
+ /// Checked when IkeystoreMetrics::pullMetris is called.
+ PullMetrics = 0x2000, selinux name: pull_metrics;
}
);
diff --git a/keystore2/src/remote_provisioning.rs b/keystore2/src/remote_provisioning.rs
index da581d0..ead24da 100644
--- a/keystore2/src/remote_provisioning.rs
+++ b/keystore2/src/remote_provisioning.rs
@@ -258,26 +258,6 @@
Ok(BnRemoteProvisioning::new_binder(result, BinderFeatures::default()))
}
- /// 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(
- &self,
- 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)
- })
- }
-
/// 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,
@@ -417,6 +397,22 @@
}
}
+/// 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)
+ })
+}
+
impl binder::Interface for RemoteProvisioningService {}
// Implementation of IRemoteProvisioning. See AIDL spec at
@@ -428,7 +424,7 @@
sec_level: SecurityLevel,
) -> binder::public_api::Result<AttestationPoolStatus> {
let _wp = wd::watch_millis("IRemoteProvisioning::getPoolStatus", 500);
- map_or_log_err(self.get_pool_status(expired_by, sec_level), Ok)
+ map_or_log_err(get_pool_status(expired_by, sec_level), Ok)
}
fn generateCsr(
diff --git a/keystore2/src/security_level.rs b/keystore2/src/security_level.rs
index 2fddc18..b66c778 100644
--- a/keystore2/src/security_level.rs
+++ b/keystore2/src/security_level.rs
@@ -23,7 +23,7 @@
use crate::globals::{DB, ENFORCEMENTS, LEGACY_MIGRATOR, SUPER_KEY};
use crate::key_parameter::KeyParameter as KsKeyParam;
use crate::key_parameter::KeyParameterValue as KsKeyParamValue;
-use crate::metrics::log_key_creation_event_stats;
+use crate::metrics_store::log_key_creation_event_stats;
use crate::remote_provisioning::RemProvState;
use crate::super_key::{KeyBlob, SuperKeyManager};
use crate::utils::{
diff --git a/keystore2/system_property/Android.bp b/keystore2/system_property/Android.bp
index 9e7b056..773804d 100644
--- a/keystore2/system_property/Android.bp
+++ b/keystore2/system_property/Android.bp
@@ -31,6 +31,7 @@
"--size_t-is-usize",
"--allowlist-function=__system_property_find",
"--allowlist-function=__system_property_read_callback",
+ "--allowlist-function=__system_property_set",
"--allowlist-function=__system_property_wait",
],
}
diff --git a/keystore2/system_property/lib.rs b/keystore2/system_property/lib.rs
index be13c88..b993c87 100644
--- a/keystore2/system_property/lib.rs
+++ b/keystore2/system_property/lib.rs
@@ -15,6 +15,7 @@
//! This crate provides the PropertyWatcher type, which watches for changes
//! in Android system properties.
+use anyhow::{anyhow, Context, Result as AnyhowResult};
use keystore2_system_property_bindgen::prop_info as PropInfo;
use std::os::raw::c_char;
use std::ptr::null;
@@ -48,6 +49,9 @@
/// read callback returned an error
#[error("Callback failed")]
CallbackError(#[from] anyhow::Error),
+ /// Failure in setting the system property
+ #[error("__system_property_set failed.")]
+ SetPropertyFailed,
}
/// Result type specific for this crate.
@@ -189,3 +193,25 @@
Ok(())
}
}
+
+/// Writes a system property.
+pub fn write(name: &str, value: &str) -> AnyhowResult<()> {
+ if
+ // Unsafe required for FFI call. Input and output are both const and valid strings.
+ unsafe {
+ // If successful, __system_property_set returns 0, otherwise, returns -1.
+ keystore2_system_property_bindgen::__system_property_set(
+ CString::new(name)
+ .context("In keystore2::system_property::write: Construction CString from name.")?
+ .as_ptr(),
+ CString::new(value)
+ .context("In keystore2::system_property::write: Constructing CString from value.")?
+ .as_ptr(),
+ )
+ } == 0
+ {
+ Ok(())
+ } else {
+ Err(anyhow!(PropertyWatcherError::SetPropertyFailed))
+ }
+}
diff --git a/ondevice-signing/Android.bp b/ondevice-signing/Android.bp
index 9085d81..5219240 100644
--- a/ondevice-signing/Android.bp
+++ b/ondevice-signing/Android.bp
@@ -11,8 +11,6 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
-// List of clang-tidy checks that are reported as errors.
-// Please keep this list ordered lexicographically.
package {
// See: http://go/android-license-faq
@@ -23,6 +21,8 @@
default_applicable_licenses: ["system_security_license"],
}
+// List of clang-tidy checks that are reported as errors.
+// Please keep this list ordered lexicographically.
tidy_errors = [
"cert-err34-c",
"google-default-arguments",
@@ -95,6 +95,7 @@
static_libs: [
"libc++fs",
"lib_odsign_proto",
+ "lib_compos_proto",
],
shared_libs: [
@@ -106,7 +107,7 @@
"libcrypto_utils",
"libfsverity",
"liblogwrap",
- "libprotobuf-cpp-full",
+ "libprotobuf-cpp-lite",
"libutils",
],
}
diff --git a/ondevice-signing/CertUtils.cpp b/ondevice-signing/CertUtils.cpp
index ce2b0fd..8d4f273 100644
--- a/ondevice-signing/CertUtils.cpp
+++ b/ondevice-signing/CertUtils.cpp
@@ -31,8 +31,10 @@
#include "KeyConstants.h"
-const char kRootCommonName[] = "ODS";
+// Common properties for all of our certificates.
constexpr int kCertLifetimeSeconds = 10 * 365 * 24 * 60 * 60;
+const char* const kIssuerCountry = "US";
+const char* const kIssuerOrg = "Android";
using android::base::ErrnoError;
using android::base::Error;
@@ -75,7 +77,7 @@
reinterpret_cast<const unsigned char*>(value), -1, -1, 0);
}
-Result<bssl::UniquePtr<RSA>> getRsa(const std::vector<uint8_t>& publicKey) {
+static Result<bssl::UniquePtr<RSA>> getRsaFromModulus(const std::vector<uint8_t>& publicKey) {
bssl::UniquePtr<BIGNUM> n(BN_new());
bssl::UniquePtr<BIGNUM> e(BN_new());
bssl::UniquePtr<RSA> rsaPubkey(RSA_new());
@@ -91,29 +93,24 @@
return rsaPubkey;
}
-Result<void> verifySignature(const std::string& message, const std::string& signature,
- const std::vector<uint8_t>& publicKey) {
- auto rsaKey = getRsa(publicKey);
- if (!rsaKey.ok()) {
- return rsaKey.error();
+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";
}
- uint8_t hashBuf[SHA256_DIGEST_LENGTH];
- SHA256(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(message.c_str())),
- message.length(), hashBuf);
-
- bool success = RSA_verify(NID_sha256, hashBuf, sizeof(hashBuf),
- (const uint8_t*)signature.c_str(), signature.length(), rsaKey->get());
-
- if (!success) {
- return Error() << "Failed to verify signature.";
+ if (derBytes != rsaPublicKey.data() + rsaPublicKey.size()) {
+ return Error() << "Key has unexpected trailing data";
}
- return {};
+
+ return rsaKey;
}
-static Result<bssl::UniquePtr<EVP_PKEY>> toRsaPkey(const std::vector<uint8_t>& publicKey) {
+static Result<bssl::UniquePtr<EVP_PKEY>> modulusToRsaPkey(const std::vector<uint8_t>& publicKey) {
// "publicKey" corresponds to the raw public key bytes - need to create
// a new RSA key with the correct exponent.
- auto rsaPubkey = getRsa(publicKey);
+ auto rsaPubkey = getRsaFromModulus(publicKey);
if (!rsaPubkey.ok()) {
return rsaPubkey.error();
}
@@ -125,8 +122,61 @@
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);
+ if (!rsaKey.ok()) {
+ return rsaKey.error();
+ }
+ uint8_t hashBuf[SHA256_DIGEST_LENGTH];
+ SHA256(const_cast<uint8_t*>(reinterpret_cast<const uint8_t*>(message.c_str())),
+ message.length(), hashBuf);
+
+ bool success = RSA_verify(NID_sha256, hashBuf, sizeof(hashBuf),
+ (const uint8_t*)signature.c_str(), signature.length(), rsaKey->get());
+
+ if (!success) {
+ return Error() << "Failed to verify signature";
+ }
+ return {};
+}
+
+Result<void> verifyRsaPublicKeySignature(const std::string& message, const std::string& signature,
+ const std::vector<uint8_t>& rsaPublicKey) {
+ auto rsaKey = getRsaFromRsaPublicKey(rsaPublicKey);
+ if (!rsaKey.ok()) {
+ return rsaKey.error();
+ }
+
+ uint8_t hashBuf[SHA256_DIGEST_LENGTH];
+ SHA256(reinterpret_cast<const uint8_t*>(message.data()), message.size(), hashBuf);
+
+ bool success = RSA_verify(NID_sha256, hashBuf, sizeof(hashBuf),
+ reinterpret_cast<const uint8_t*>(signature.data()), signature.size(),
+ rsaKey->get());
+ if (!success) {
+ return Error() << "Failed to verify signature";
+ }
+ return {};
+}
+
static Result<void> createCertificate(
- const char* commonName, const std::vector<uint8_t>& publicKey,
+ 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) {
@@ -141,7 +191,7 @@
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()), selfSigned ? 1 : 2);
+ ASN1_INTEGER_set(X509_get_serialNumber(x509.get()), subject.serialNumber);
bssl::UniquePtr<X509_ALGOR> algor(X509_ALGOR_new());
if (!algor ||
@@ -151,12 +201,7 @@
return Error() << "Unable to set x509 signature algorithm";
}
- auto public_key = toRsaPkey(publicKey);
- if (!public_key.ok()) {
- return public_key.error();
- }
-
- if (!X509_set_pubkey(x509.get(), public_key.value().get())) {
+ if (!X509_set_pubkey(x509.get(), publicKey)) {
return Error() << "Unable to set x509 public key";
}
@@ -164,9 +209,9 @@
if (!subjectName) {
return Error() << "Unable to get x509 subject name";
}
- addNameEntry(subjectName, "C", "US");
- addNameEntry(subjectName, "O", "Android");
- addNameEntry(subjectName, "CN", commonName);
+ addNameEntry(subjectName, "C", kIssuerCountry);
+ addNameEntry(subjectName, "O", kIssuerOrg);
+ addNameEntry(subjectName, "CN", subject.commonName);
if (selfSigned) {
if (!X509_set_issuer_name(x509.get(), subjectName)) {
@@ -177,9 +222,9 @@
if (!issuerName) {
return Error() << "Unable to get x509 issuer name";
}
- addNameEntry(issuerName, "C", "US");
- addNameEntry(issuerName, "O", "Android");
- addNameEntry(issuerName, "CN", kRootCommonName);
+ 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.
@@ -239,14 +284,24 @@
const std::vector<uint8_t>& publicKey,
const std::function<Result<std::string>(const std::string&)>& signFunction,
const std::string& path) {
- return createCertificate(kRootCommonName, publicKey, signFunction, {}, 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 char* commonName, const std::vector<uint8_t>& publicKey,
+ 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) {
- return createCertificate(commonName, publicKey, signFunction, issuerCertPath, 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) {
@@ -297,9 +352,40 @@
return extractPublicKey(X509_get_pubkey(cert.value().get()));
}
+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<std::vector<uint8_t>> extractRsaPublicKeyFromX509(const std::vector<uint8_t>& derCert) {
+ auto derCertBytes = derCert.data();
+ bssl::UniquePtr<X509> decoded_cert(d2i_X509(nullptr, &derCertBytes, derCert.size()));
+ if (decoded_cert.get() == nullptr) {
+ return Error() << "Failed to decode X509 certificate.";
+ }
+ bssl::UniquePtr<EVP_PKEY> decoded_pkey(X509_get_pubkey(decoded_cert.get()));
+ if (decoded_pkey == nullptr) {
+ return Error() << "Failed to extract public key from x509 cert";
+ }
+
+ return extractRsaPublicKey(decoded_pkey.get());
+}
+
Result<CertInfo> verifyAndExtractCertInfoFromX509(const std::string& path,
const std::vector<uint8_t>& publicKey) {
- auto public_key = toRsaPkey(publicKey);
+ auto public_key = modulusToRsaPkey(publicKey);
if (!public_key.ok()) {
return public_key.error();
}
@@ -316,7 +402,7 @@
}
bssl::UniquePtr<EVP_PKEY> pkey(X509_get_pubkey(x509));
- auto subject_key = extractPublicKey(pkey.get());
+ auto subject_key = extractRsaPublicKey(pkey.get());
if (!subject_key.ok()) {
return subject_key.error();
}
@@ -340,7 +426,8 @@
return cert_info;
}
-Result<std::vector<uint8_t>> createPkcs7(const std::vector<uint8_t>& signed_digest) {
+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;
@@ -348,19 +435,20 @@
BIGNUM* serial = BN_new();
int sig_nid = NID_rsaEncryption;
- X509_NAME* name = X509_NAME_new();
- if (!name) {
- return Error() << "Unable to get x509 subject name";
+ X509_NAME* issuer_name = X509_NAME_new();
+ if (!issuer_name) {
+ return Error() << "Unable to create x509 subject name";
}
- X509_NAME_add_entry_by_txt(name, "C", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("US"), -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "O", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("Android"), -1, -1, 0);
- X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC,
- reinterpret_cast<const unsigned char*>("ODS"), -1, -1, 0);
+ 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, 1);
- name_der_len = i2d_X509_NAME(name, &name_der);
+ 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) ||
diff --git a/ondevice-signing/CertUtils.h b/ondevice-signing/CertUtils.h
index b412d21..1ed4c06 100644
--- a/ondevice-signing/CertUtils.h
+++ b/ondevice-signing/CertUtils.h
@@ -22,22 +22,37 @@
#include <android-base/result.h>
+// Information extracted from a certificate.
struct CertInfo {
std::string subjectCn;
- std::vector<uint8_t> subjectKey;
+ std::vector<uint8_t> subjectRsaPublicKey;
};
+// Subjects of certificates we issue.
+struct CertSubject {
+ const char* commonName;
+ unsigned serialNumber;
+};
+
+// These are all the certificates we ever sign (the first one being our
+// self-signed cert). We shouldn't really re-use serial numbers for different
+// certificates for the same subject but we do; only one should be in use at a
+// time though.
+inline const CertSubject kRootSubject{"ODS", 1};
+inline const CertSubject kCompOsSubject{"CompOs", 2};
+
android::base::Result<void> createSelfSignedCertificate(
const std::vector<uint8_t>& publicKey,
const std::function<android::base::Result<std::string>(const std::string&)>& signFunction,
const std::string& path);
android::base::Result<void> createLeafCertificate(
- const char* commonName, const std::vector<uint8_t>& publicKey,
+ 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);
+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);
@@ -45,9 +60,16 @@
extractPublicKeyFromSubjectPublicKeyInfo(const std::vector<uint8_t>& subjectKeyInfo);
android::base::Result<std::vector<uint8_t>> extractPublicKeyFromX509(const std::string& path);
+android::base::Result<std::vector<uint8_t>>
+extractRsaPublicKeyFromX509(const std::vector<uint8_t>& x509);
+
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/FakeCompOs.cpp b/ondevice-signing/FakeCompOs.cpp
index 48eb01a..cd54e28 100644
--- a/ondevice-signing/FakeCompOs.cpp
+++ b/ondevice-signing/FakeCompOs.cpp
@@ -26,7 +26,10 @@
#include <binder/IServiceManager.h>
+#include <openssl/nid.h>
#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
using android::String16;
@@ -225,8 +228,8 @@
return signature.error();
}
- std::string dataStr(data.begin(), data.end());
- std::string signatureStr(signature.value().begin(), signature.value().end());
-
- return verifySignature(dataStr, signatureStr, publicKey);
+ std::string signatureString(reinterpret_cast<char*>(signature.value().data()),
+ signature.value().size());
+ std::string dataString(reinterpret_cast<char*>(data.data()), data.size());
+ return verifyRsaPublicKeySignature(dataString, signatureString, publicKey);
}
diff --git a/ondevice-signing/FakeCompOs.h b/ondevice-signing/FakeCompOs.h
index 7d76938..eb1a8dd 100644
--- a/ondevice-signing/FakeCompOs.h
+++ b/ondevice-signing/FakeCompOs.h
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2020 The Android Open Source Project
+ * 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.
diff --git a/ondevice-signing/KeystoreKey.cpp b/ondevice-signing/KeystoreKey.cpp
index 9780787..03bb6d5 100644
--- a/ondevice-signing/KeystoreKey.cpp
+++ b/ondevice-signing/KeystoreKey.cpp
@@ -122,7 +122,7 @@
return Error() << "Failed to create new key: " << status;
}
- // Exteact the nublir key from the certificate, HMAC it and store the signature
+ // Extract the public key from the certificate, HMAC it and store the signature
auto cert = metadata.certificate;
if (!cert) {
return Error() << "Key did not have a certificate.";
@@ -178,7 +178,7 @@
return false;
}
mPublicKey = *key;
- LOG(ERROR) << "Initialized Keystore key.";
+ LOG(INFO) << "Initialized Keystore key.";
return true;
}
diff --git a/ondevice-signing/VerityUtils.cpp b/ondevice-signing/VerityUtils.cpp
index 243e7df..e58de68 100644
--- a/ondevice-signing/VerityUtils.cpp
+++ b/ondevice-signing/VerityUtils.cpp
@@ -27,11 +27,13 @@
#include <android-base/logging.h>
#include <android-base/unique_fd.h>
+#include <asm/byteorder.h>
#include <libfsverity.h>
#include <linux/fsverity.h>
#include "CertUtils.h"
#include "SigningKey.h"
+#include "compos_signature.pb.h"
#define FS_VERITY_MAX_DIGEST_SIZE 64
@@ -40,17 +42,16 @@
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";
-#if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
-#define cpu_to_le16(v) ((__force __le16)(uint16_t)(v))
-#define le16_to_cpu(v) ((__force uint16_t)(__le16)(v))
-#else
-#define cpu_to_le16(v) ((__force __le16)__builtin_bswap16(v))
-#define le16_to_cpu(v) (__builtin_bswap16((__force uint16_t)(v)))
-#endif
+static bool isSignatureFile(const std::filesystem::path& path) {
+ return path.extension().native() == kSignatureExtension;
+}
-static std::string toHex(std::span<uint8_t> data) {
+static std::string toHex(std::span<const uint8_t> data) {
std::stringstream ss;
for (auto it = data.begin(); it != data.end(); ++it) {
ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
@@ -64,16 +65,11 @@
return 0;
}
-Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+Result<std::vector<uint8_t>> createDigest(int fd) {
struct stat filestat;
- unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
- return ErrnoError() << "Failed to open " << path;
- }
-
- int ret = stat(path.c_str(), &filestat);
+ int ret = fstat(fd, &filestat);
if (ret < 0) {
- return ErrnoError() << "Failed to stat " << path;
+ return ErrnoError() << "Failed to fstat";
}
struct libfsverity_merkle_tree_params params = {
.version = 1,
@@ -85,13 +81,21 @@
struct libfsverity_digest* digest;
ret = libfsverity_compute_digest(&fd, &read_callback, ¶ms, &digest);
if (ret < 0) {
- return ErrnoError() << "Failed to compute fs-verity digest for " << path;
+ return ErrnoError() << "Failed to compute fs-verity digest";
}
std::vector<uint8_t> digestVector(&digest->digest[0], &digest->digest[32]);
free(digest);
return digestVector;
}
+Result<std::vector<uint8_t>> createDigest(const std::string& path) {
+ unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
+ if (!fd.ok()) {
+ return ErrnoError() << "Unable to open";
+ }
+ return createDigest(fd.get());
+}
+
namespace {
template <typename T> struct DeleteAsPODArray {
void operator()(T* x) {
@@ -117,8 +121,8 @@
auto d = makeUniqueWithTrailingData<fsverity_formatted_digest>(digest.size());
memcpy(d->magic, "FSVerity", 8);
- d->digest_algorithm = cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
- d->digest_size = cpu_to_le16(digest.size());
+ d->digest_algorithm = __cpu_to_le16(FS_VERITY_HASH_ALG_SHA256);
+ d->digest_size = __cpu_to_le16(digest.size());
memcpy(d->digest, digest.data(), digest.size());
auto signed_digest = key.sign(std::string((char*)d.get(), sizeof(*d) + digest.size()));
@@ -129,10 +133,32 @@
return std::vector<uint8_t>(signed_digest->begin(), signed_digest->end());
}
+Result<void> enableFsVerity(int fd, std::span<uint8_t> pkcs7) {
+ struct fsverity_enable_arg arg = {.version = 1};
+
+ arg.sig_ptr = reinterpret_cast<uint64_t>(pkcs7.data());
+ arg.sig_size = pkcs7.size();
+ arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
+ arg.block_size = 4096;
+
+ int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
+
+ if (ret != 0) {
+ return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY";
+ }
+
+ return {};
+}
+
Result<std::string> enableFsVerity(const std::string& path, const SigningKey& key) {
- auto digest = createDigest(path);
+ 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());
if (!digest.ok()) {
- return digest.error();
+ return Error() << digest.error() << ": " << path;
}
auto signed_digest = signDigest(key, digest.value());
@@ -140,20 +166,14 @@
return signed_digest.error();
}
- auto pkcs7_data = createPkcs7(signed_digest.value());
+ auto pkcs7_data = createPkcs7(signed_digest.value(), kRootSubject);
+ if (!pkcs7_data.ok()) {
+ return pkcs7_data.error();
+ }
- struct fsverity_enable_arg arg = {.version = 1};
-
- arg.sig_ptr = (uint64_t)pkcs7_data->data();
- arg.sig_size = pkcs7_data->size();
- arg.hash_algorithm = FS_VERITY_HASH_ALG_SHA256;
- arg.block_size = 4096;
-
- unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- int ret = ioctl(fd, FS_IOC_ENABLE_VERITY, &arg);
-
- if (ret != 0) {
- return ErrnoError() << "Failed to call FS_IOC_ENABLE_VERITY on " << path;
+ auto enabled = enableFsVerity(fd.get(), pkcs7_data.value());
+ if (!enabled.ok()) {
+ return Error() << enabled.error() << ": " << path;
}
// Return the root hash as a hex string
@@ -163,12 +183,10 @@
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);
- auto end = std::filesystem::recursive_directory_iterator();
-
- while (!ec && it != end) {
+ 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);
@@ -177,38 +195,40 @@
}
digests[it->path()] = *result;
}
- ++it;
}
if (ec) {
- return Error() << "Failed to iterate " << path << ": " << ec;
+ return Error() << "Failed to iterate " << path << ": " << ec.message();
}
return digests;
}
-Result<std::string> isFileInVerity(const std::string& path) {
- unsigned int flags;
+Result<std::string> isFileInVerity(int fd) {
+ auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
+ d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
+ auto ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
+ if (ret < 0) {
+ if (errno == ENODATA) {
+ return Error() << "File is not in fs-verity";
+ } else {
+ return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY";
+ }
+ }
+ return toHex({&d->digest[0], &d->digest[d->digest_size]});
+}
+Result<std::string> isFileInVerity(const std::string& path) {
unique_fd fd(TEMP_FAILURE_RETRY(open(path.c_str(), O_RDONLY | O_CLOEXEC)));
- if (fd < 0) {
+ if (!fd.ok()) {
return ErrnoError() << "Failed to open " << path;
}
- int ret = ioctl(fd, FS_IOC_GETFLAGS, &flags);
- if (ret < 0) {
- return ErrnoError() << "Failed to FS_IOC_GETFLAGS for " << path;
- }
- if (!(flags & FS_VERITY_FL)) {
- return Error() << "File is not in fs-verity: " << path;
+ auto digest = isFileInVerity(fd);
+ if (!digest.ok()) {
+ return Error() << digest.error() << ": " << path;
}
- auto d = makeUniqueWithTrailingData<fsverity_digest>(FS_VERITY_MAX_DIGEST_SIZE);
- d->digest_size = FS_VERITY_MAX_DIGEST_SIZE;
- ret = ioctl(fd, FS_IOC_MEASURE_VERITY, d.get());
- if (ret < 0) {
- return ErrnoError() << "Failed to FS_IOC_MEASURE_VERITY for " << path;
- }
- return toHex({&d->digest[0], &d->digest[d->digest_size]});
+ return digest;
}
Result<std::map<std::string, std::string>> verifyAllFilesInVerity(const std::string& path) {
@@ -242,6 +262,124 @@
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;
+
+ std::error_code ec;
+ auto it = std::filesystem::recursive_directory_iterator(directory_path, ec);
+ for (auto end = std::filesystem::recursive_directory_iterator(); it != end; it.increment(ec)) {
+ auto& path = it->path();
+ if (it->is_regular_file()) {
+ if (isSignatureFile(path)) {
+ continue;
+ }
+
+ 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;
+ }
+ } 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;
+ }
+
+ 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;
+ } else if (it->is_directory()) {
+ // These are fine to ignore
+ } else if (it->is_symlink()) {
+ return Error() << "Rejecting artifacts, symlink at " << path;
+ } else {
+ return Error() << "Rejecting artifacts, unexpected file type for " << path;
+ }
+ }
+ if (ec) {
+ return Error() << "Failed to iterate " << directory_path << ": " << ec.message();
+ }
+
+ // 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();
+ }
+ }
+
+ return new_digests;
+}
+
Result<void> addCertToFsVerityKeyring(const std::string& path, const char* keyName) {
const char* const argv[] = {kFsVerityInitPath, "--load-extra-key", keyName};
diff --git a/ondevice-signing/VerityUtils.h b/ondevice-signing/VerityUtils.h
index dca3184..8d8e62c 100644
--- a/ondevice-signing/VerityUtils.h
+++ b/ondevice-signing/VerityUtils.h
@@ -24,5 +24,9 @@
android::base::Result<std::vector<uint8_t>> createDigest(const std::string& path);
android::base::Result<std::map<std::string, std::string>>
verifyAllFilesInVerity(const std::string& path);
+
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);
diff --git a/ondevice-signing/odsign_main.cpp b/ondevice-signing/odsign_main.cpp
index 5fad7fc..ff7a105 100644
--- a/ondevice-signing/odsign_main.cpp
+++ b/ondevice-signing/odsign_main.cpp
@@ -59,10 +59,14 @@
static const bool kUseCompOs = false; // STOPSHIP if true
static const char* kVirtApexPath = "/apex/com.android.virt";
-static const char* kCompOsCommonName = "CompOS";
const std::string kCompOsCert = "/data/misc/odsign/compos_key.cert";
const std::string kCompOsPublicKey = "/data/misc/odsign/compos_key.pubkey";
const std::string kCompOsKeyBlob = "/data/misc/odsign/compos_key.blob";
+const std::string kCompOsPendingPublicKey =
+ "/data/misc/apexdata/com.android.compos/compos_pending_key.pubkey";
+const std::string kCompOsPendingKeyBlob =
+ "/data/misc/apexdata/com.android.compos/compos_pending_key.blob";
+const std::string kCompOsPendingArtifactsDir = "/data/misc/apexdata/com.android.art/compos-pending";
static const char* kOdsignVerificationDoneProp = "odsign.verification.done";
static const char* kOdsignKeyDoneProp = "odsign.key.done";
@@ -82,6 +86,48 @@
return std::vector<uint8_t>(str.begin(), str.end());
}
+static int removeDirectory(const std::string& directory) {
+ std::error_code ec;
+ auto num_removed = std::filesystem::remove_all(directory, ec);
+ if (ec) {
+ LOG(ERROR) << "Can't remove " << directory << ": " << ec.message();
+ return 0;
+ } else {
+ if (num_removed > 0) {
+ LOG(INFO) << "Removed " << num_removed << " entries from " << directory;
+ }
+ return num_removed;
+ }
+}
+
+static bool directoryHasContent(const std::string& directory) {
+ std::error_code ec;
+ return std::filesystem::is_directory(directory, ec) &&
+ !std::filesystem::is_empty(directory, ec);
+}
+
+art::odrefresh::ExitCode compileArtifacts(bool force) {
+ const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
+ const int exit_code =
+ logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+ return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+art::odrefresh::ExitCode checkArtifacts() {
+ const char* const argv[] = {kOdrefreshPath, "--check"};
+ const int exit_code =
+ logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
+ return static_cast<art::odrefresh::ExitCode>(exit_code);
+}
+
+static std::string toHex(const std::vector<uint8_t>& digest) {
+ std::stringstream ss;
+ for (auto it = digest.begin(); it != digest.end(); ++it) {
+ ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
+ }
+ return ss.str();
+}
+
bool compOsPresent() {
return access(kVirtApexPath, F_OK) == 0;
}
@@ -120,11 +166,11 @@
return createSelfSignedCertificate(*publicKey, keySignFunction, outPath);
}
-Result<std::vector<uint8_t>> extractPublicKeyFromLeafCert(const SigningKey& key,
- const std::string& certPath,
- const std::string& expectedCn) {
+Result<std::vector<uint8_t>> extractRsaPublicKeyFromLeafCert(const SigningKey& key,
+ const std::string& certPath,
+ const std::string& expectedCn) {
if (access(certPath.c_str(), F_OK) < 0) {
- return ErrnoError() << "Certificate not found: " << kCompOsCert;
+ return ErrnoError() << "Certificate not found: " << certPath;
}
auto trustedPublicKey = key.getPublicKey();
if (!trustedPublicKey.ok()) {
@@ -143,50 +189,106 @@
<< ", should be " << expectedCn;
}
- return existingCertInfo.value().subjectKey;
+ return existingCertInfo.value().subjectRsaPublicKey;
}
-Result<void> verifyOrGenerateCompOsKey(const SigningKey& signingKey) {
- auto compOsStatus = FakeCompOs::newInstance();
- if (!compOsStatus.ok()) {
- return Error() << "Failed to start CompOs: " << compOsStatus.error();
- }
-
- FakeCompOs* compOs = compOsStatus.value().get();
-
+Result<std::vector<uint8_t>> verifyOrGenerateCompOsKey(const SigningKey& signingKey) {
+ std::unique_ptr<FakeCompOs> compOs;
std::vector<uint8_t> keyBlob;
std::vector<uint8_t> publicKey;
- bool haveKey = false;
+ bool new_key = true;
- if (access(kCompOsPublicKey.c_str(), F_OK) == 0 && access(kCompOsKeyBlob.c_str(), F_OK) == 0) {
- // We have a purported key, but not a valid signature for it.
- // If compOs can verify it, we can sign it now.
- keyBlob = readBytesFromFile(kCompOsKeyBlob);
- publicKey = readBytesFromFile(kCompOsPublicKey);
+ // If a pending key has been generated we don't know if it is the correct
+ // one for the current CompOS VM, so we need to start it and ask it.
+ if (access(kCompOsPendingPublicKey.c_str(), F_OK) == 0 &&
+ access(kCompOsPendingKeyBlob.c_str(), F_OK) == 0) {
+ auto compOsStatus = FakeCompOs::newInstance();
+ if (!compOsStatus.ok()) {
+ return Error() << "Failed to start CompOs: " << compOsStatus.error();
+ }
+ compOs = std::move(compOsStatus.value());
- auto response = compOs->loadAndVerifyKey(keyBlob, publicKey);
+ auto pendingKeyBlob = readBytesFromFile(kCompOsPendingKeyBlob);
+ auto pendingPublicKey = readBytesFromFile(kCompOsPendingPublicKey);
+
+ auto response = compOs->loadAndVerifyKey(pendingKeyBlob, pendingPublicKey);
if (response.ok()) {
- LOG(INFO) << "Verified existing CompOs key";
- haveKey = true;
+ LOG(INFO) << "Verified pending CompOs key";
+ keyBlob = std::move(pendingKeyBlob);
+ publicKey = std::move(pendingPublicKey);
} else {
- LOG(WARNING) << "Failed to verify existing CompOs key: " << response.error();
+ LOG(WARNING) << "Failed to verify pending CompOs key: " << response.error();
+ // And fall through to looking at the current key.
+ }
+ // Whether they're good or bad, we've finished with these files.
+ unlink(kCompOsPendingKeyBlob.c_str());
+ unlink(kCompOsPendingPublicKey.c_str());
+ }
+
+ if (publicKey.empty()) {
+ // Alternatively if we signed a cert for the key on a previous boot, then we
+ // can use that straight away.
+ auto existing_key =
+ extractRsaPublicKeyFromLeafCert(signingKey, kCompOsCert, kCompOsSubject.commonName);
+ if (existing_key.ok()) {
+ LOG(INFO) << "Found and verified existing CompOs public key certificate: "
+ << kCompOsCert;
+ return existing_key.value();
}
}
- if (!haveKey) {
- // If we don't have a key, or it doesn't verify, then we need a new one.
+ if (compOs == nullptr) {
+ auto compOsStatus = FakeCompOs::newInstance();
+ if (!compOsStatus.ok()) {
+ return Error() << "Failed to start CompOs: " << compOsStatus.error();
+ }
+ compOs = std::move(compOsStatus.value());
+ }
+
+ // Otherwise, if there is an existing key that we haven't signed yet, then we can sign it
+ // now if CompOS confirms it's OK.
+ if (publicKey.empty()) {
+ if (access(kCompOsPublicKey.c_str(), F_OK) == 0 &&
+ access(kCompOsKeyBlob.c_str(), F_OK) == 0) {
+ auto currentKeyBlob = readBytesFromFile(kCompOsKeyBlob);
+ auto currentPublicKey = readBytesFromFile(kCompOsPublicKey);
+
+ auto response = compOs->loadAndVerifyKey(currentKeyBlob, currentPublicKey);
+ if (response.ok()) {
+ LOG(INFO) << "Verified existing CompOs key";
+ keyBlob = std::move(currentKeyBlob);
+ publicKey = std::move(currentPublicKey);
+ new_key = false;
+ } else {
+ LOG(WARNING) << "Failed to verify existing CompOs key: " << response.error();
+ }
+ }
+ }
+
+ // If all else has failed we need to ask CompOS to generate a new key.
+ if (publicKey.empty()) {
auto keyData = compOs->generateKey();
if (!keyData.ok()) {
return Error() << "Failed to generate key: " << keyData.error();
}
- auto publicKeyStatus = extractPublicKeyFromX509(keyData.value().cert);
+ auto publicKeyStatus = extractRsaPublicKeyFromX509(keyData.value().cert);
if (!publicKeyStatus.ok()) {
return Error() << "Failed to extract CompOs public key" << publicKeyStatus.error();
}
+ LOG(INFO) << "Generated new CompOs key";
+
keyBlob = std::move(keyData.value().blob);
publicKey = std::move(publicKeyStatus.value());
+ }
+ // We've finished with CompOs now, let it exit.
+ compOs.reset();
+
+ // One way or another we now have a valid key pair. Persist the data for
+ // CompOS, and a cert so we can simplify the checks on subsequent boots.
+
+ if (new_key) {
writeBytesToFile(keyBlob, kCompOsKeyBlob);
writeBytesToFile(publicKey, kCompOsPublicKey);
}
@@ -194,28 +296,13 @@
auto signFunction = [&](const std::string& to_be_signed) {
return signingKey.sign(to_be_signed);
};
- auto certStatus = createLeafCertificate(kCompOsCommonName, publicKey, signFunction,
+ auto certStatus = createLeafCertificate(kCompOsSubject, publicKey, signFunction,
kSigningKeyCert, kCompOsCert);
if (!certStatus.ok()) {
return Error() << "Failed to create CompOs cert: " << certStatus.error();
}
- return {};
-}
-
-art::odrefresh::ExitCode compileArtifacts(bool force) {
- const char* const argv[] = {kOdrefreshPath, force ? "--force-compile" : "--compile"};
- const int exit_code =
- logwrap_fork_execvp(arraysize(argv), argv, nullptr, false, LOG_ALOG, false, nullptr);
- return static_cast<art::odrefresh::ExitCode>(exit_code);
-}
-
-static std::string toHex(const std::vector<uint8_t>& digest) {
- std::stringstream ss;
- for (auto it = digest.begin(); it != digest.end(); ++it) {
- ss << std::setfill('0') << std::setw(2) << std::hex << static_cast<unsigned>(*it);
- }
- return ss.str();
+ return publicKey;
}
Result<std::map<std::string, std::string>> computeDigests(const std::string& path) {
@@ -229,7 +316,8 @@
if (it->is_regular_file()) {
auto digest = createDigest(it->path());
if (!digest.ok()) {
- return Error() << "Failed to compute digest for " << it->path();
+ return Error() << "Failed to compute digest for " << it->path() << ": "
+ << digest.error();
}
digests[it->path()] = toHex(*digest);
}
@@ -341,20 +429,6 @@
return {};
}
-static int removeArtifacts() {
- std::error_code ec;
- auto num_removed = std::filesystem::remove_all(kArtArtifactsDir, ec);
- if (ec) {
- LOG(ERROR) << "Can't remove " << kArtArtifactsDir << ": " << ec.message();
- return 0;
- } else {
- if (num_removed > 0) {
- LOG(INFO) << "Removed " << num_removed << " entries from " << kArtArtifactsDir;
- }
- return num_removed;
- }
-}
-
static Result<void> verifyArtifacts(const SigningKey& key, bool supportsFsVerity) {
auto signInfo = getOdsignInfo(key);
// Tell init we're done with the key; this is a boot time optimization
@@ -386,10 +460,95 @@
return {};
}
+Result<std::vector<uint8_t>> addCompOsCertToFsVerityKeyring(const SigningKey& signingKey) {
+ auto publicKey = verifyOrGenerateCompOsKey(signingKey);
+ if (!publicKey.ok()) {
+ return publicKey.error();
+ }
+
+ auto cert_add_result = addCertToFsVerityKeyring(kCompOsCert, "fsv_compos");
+ 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: "
+ << cert_add_result.error();
+ }
+
+ return publicKey;
+}
+
+art::odrefresh::ExitCode checkCompOsPendingArtifacts(const std::vector<uint8_t>& compos_key,
+ const SigningKey& signingKey,
+ bool* digests_verified) {
+ if (!directoryHasContent(kCompOsPendingArtifactsDir)) {
+ return art::odrefresh::ExitCode::kCompilationRequired;
+ }
+
+ // CompOs has generated some artifacts that may, or may not, match the
+ // current state. But if there are already valid artifacts present the
+ // CompOs ones are redundant.
+ art::odrefresh::ExitCode odrefresh_status = checkArtifacts();
+ if (odrefresh_status != art::odrefresh::ExitCode::kCompilationRequired) {
+ if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
+ LOG(INFO) << "Current artifacts are OK, deleting pending artifacts";
+ removeDirectory(kCompOsPendingArtifactsDir);
+ }
+ return odrefresh_status;
+ }
+
+ // No useful current artifacts, lets see if the CompOs ones are ok
+ LOG(INFO) << "Current artifacts are out of date, switching to pending artifacts";
+ removeDirectory(kArtArtifactsDir);
+ std::error_code ec;
+ std::filesystem::rename(kCompOsPendingArtifactsDir, kArtArtifactsDir, ec);
+ if (ec) {
+ LOG(ERROR) << "Can't rename " << kCompOsPendingArtifactsDir << " to " << kArtArtifactsDir
+ << ": " << ec.message();
+ removeDirectory(kCompOsPendingArtifactsDir);
+ 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";
+ return odrefresh_status;
+ }
+
+ // 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();
+ }
+ } else {
+ LOG(WARNING) << "Pending artifact verification failed: " << digests.error();
+ }
+
+ // We can't use the existing artifacts, so we will need to generate new
+ // ones.
+ removeDirectory(kArtArtifactsDir);
+ return art::odrefresh::ExitCode::kCompilationRequired;
+}
+
int main(int /* argc */, char** /* argv */) {
auto errorScopeGuard = []() {
// In case we hit any error, remove the artifacts and tell Zygote not to use anything
- removeArtifacts();
+ removeDirectory(kArtArtifactsDir);
+ removeDirectory(kCompOsPendingArtifactsDir);
// Tell init we don't need to use our key anymore
SetProperty(kOdsignKeyDoneProp, "1");
// Tell init we're done with verification, and that it was an error
@@ -440,46 +599,37 @@
}
}
- if (supportsCompOs) {
- auto compos_key = extractPublicKeyFromLeafCert(*key, kCompOsCert, kCompOsCommonName);
- if (!compos_key.ok()) {
- LOG(WARNING) << compos_key.error();
+ art::odrefresh::ExitCode odrefresh_status = art::odrefresh::ExitCode::kCompilationRequired;
+ bool digests_verified = false;
- auto status = verifyOrGenerateCompOsKey(*key);
- if (!status.ok()) {
- LOG(ERROR) << status.error();
- } else {
- LOG(INFO) << "Generated new CompOs public key certificate";
- }
+ if (supportsCompOs) {
+ auto compos_key = addCompOsCertToFsVerityKeyring(*key);
+ if (!compos_key.ok()) {
+ LOG(ERROR) << compos_key.error();
} else {
- LOG(INFO) << "Found and verified existing CompOs public key certificate: "
- << kCompOsCert;
- };
- auto cert_add_result = addCertToFsVerityKeyring(kCompOsCert, "fsv_compos");
- if (!cert_add_result.ok()) {
- LOG(ERROR) << "Failed to add CompOs certificate to fs-verity keyring: "
- << cert_add_result.error();
- // Best efforts only - nothing we can do if deletion fails.
- unlink(kCompOsCert.c_str());
- // TODO - what do we do now?
- // return -1;
+ odrefresh_status =
+ checkCompOsPendingArtifacts(compos_key.value(), *key, &digests_verified);
}
}
- art::odrefresh::ExitCode odrefresh_status = compileArtifacts(kForceCompilation);
+ if (odrefresh_status == art::odrefresh::ExitCode::kCompilationRequired) {
+ odrefresh_status = compileArtifacts(kForceCompilation);
+ }
if (odrefresh_status == art::odrefresh::ExitCode::kOkay) {
LOG(INFO) << "odrefresh said artifacts are VALID";
- // A post-condition of validating artifacts is that if the ones on /system
- // are used, kArtArtifactsDir is removed. Conversely, if kArtArtifactsDir
- // exists, those are artifacts that will be used, and we should verify them.
- int err = access(kArtArtifactsDir.c_str(), F_OK);
- // If we receive any error other than ENOENT, be suspicious
- bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
- if (artifactsPresent) {
- auto verificationResult = verifyArtifacts(*key, supportsFsVerity);
- if (!verificationResult.ok()) {
- LOG(ERROR) << verificationResult.error();
- return -1;
+ if (!digests_verified) {
+ // A post-condition of validating artifacts is that if the ones on /system
+ // are used, kArtArtifactsDir is removed. Conversely, if kArtArtifactsDir
+ // exists, those are artifacts that will be used, and we should verify them.
+ int err = access(kArtArtifactsDir.c_str(), F_OK);
+ // If we receive any error other than ENOENT, be suspicious
+ bool artifactsPresent = (err == 0) || (err < 0 && errno != ENOENT);
+ if (artifactsPresent) {
+ auto verificationResult = verifyArtifacts(*key, supportsFsVerity);
+ if (!verificationResult.ok()) {
+ LOG(ERROR) << verificationResult.error();
+ return -1;
+ }
}
}
} else if (odrefresh_status == art::odrefresh::ExitCode::kCompilationSuccess ||
diff --git a/ondevice-signing/proto/Android.bp b/ondevice-signing/proto/Android.bp
index fd48f31..c042b8e 100644
--- a/ondevice-signing/proto/Android.bp
+++ b/ondevice-signing/proto/Android.bp
@@ -23,7 +23,21 @@
host_supported: true,
proto: {
export_proto_headers: true,
- type: "full",
+ type: "lite",
},
srcs: ["odsign_info.proto"],
}
+
+cc_library_static {
+ name: "lib_compos_proto",
+ host_supported: true,
+ proto: {
+ export_proto_headers: true,
+ type: "lite",
+ },
+ srcs: ["compos_signature.proto"],
+ apex_available: [
+ "//apex_available:platform",
+ "com.android.compos",
+ ],
+}
diff --git a/ondevice-signing/proto/compos_signature.proto b/ondevice-signing/proto/compos_signature.proto
new file mode 100644
index 0000000..2f7d09f
--- /dev/null
+++ b/ondevice-signing/proto/compos_signature.proto
@@ -0,0 +1,30 @@
+/*
+ * 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 compos.proto;
+
+// Data provided by CompOS to allow validation of a file it generated.
+message Signature {
+ // The fs-verity digest (which is derived from the root hash of
+ // the Merkle tree) of the file contents.
+ bytes digest = 1;
+
+ // Signature of a fsverity_formatted_digest structure containing
+ // the digest, signed using CompOS's private key.
+ bytes signature = 2;
+}