Added listUidsOfCredentialBoundKeys method to AIDL

Bug: b/112321280
Test: add auth bound keys from an app
Test: adb shell "su 1000 keystore_cli_v2 list-apps-with-keys"
Test: see uid of app listed
Test: $ ./keystore/tests/list_auth_bound_keys_test.sh
Change-Id: Id6739ea17e566f9aa51f1bd2d5a0715f1020b644
diff --git a/keystore/binder/android/security/keystore/IKeystoreService.aidl b/keystore/binder/android/security/keystore/IKeystoreService.aidl
index a760138..ea1e0f4 100644
--- a/keystore/binder/android/security/keystore/IKeystoreService.aidl
+++ b/keystore/binder/android/security/keystore/IKeystoreService.aidl
@@ -75,4 +75,5 @@
     int cancelConfirmationPrompt(IBinder listener);
     boolean isConfirmationPromptSupported();
     int onKeyguardVisibilityChanged(in boolean isShowing, in int userId);
+    int listUidsOfAuthBoundKeys(out int[] uids);
 }
diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp
index 2f07fbf..24bf331 100644
--- a/keystore/key_store_service.cpp
+++ b/keystore/key_store_service.cpp
@@ -276,6 +276,79 @@
     return Status::ok();
 }
 
+/*
+ * This method will return the uids of all auth bound keys for the calling user.
+ * This is intended to be used for alerting the user about which apps will be affected
+ * if the password/pin is removed. Only allowed to be called by system.
+ * The output is bound by the initial size of uidsOut to be compatible with Java.
+ */
+Status KeyStoreService::listUidsOfAuthBoundKeys(::std::vector<int32_t>* uidsOut,
+                                                int32_t* aidl_return) {
+    const int32_t callingUid = IPCThreadState::self()->getCallingUid();
+    const int32_t userId = get_user_id(callingUid);
+    const int32_t appId = get_app_id(callingUid);
+    if (appId != AID_SYSTEM) {
+        ALOGE("Permission listUidsOfAuthBoundKeys denied for aid %d", appId);
+        *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
+        return Status::ok();
+    }
+
+    const String8 prefix8("");
+    auto userState = mKeyStore->getUserStateDB().getUserState(userId);
+    const std::string userDirName = userState->getUserDirName();
+    auto encryptionKey = userState->getEncryptionKey();
+    auto state = userState->getState();
+    // unlock the user state
+    userState = {};
+
+    ResponseCode rc;
+    std::list<LockedKeyBlobEntry> internal_matches;
+    std::tie(rc, internal_matches) =
+        LockedKeyBlobEntry::list(userDirName, [&](uid_t, const std::string&) {
+            // Need to filter on auth bound state, so just return true.
+            return true;
+        });
+    if (rc != ResponseCode::NO_ERROR) {
+        ALOGE("Error listing blob entries for user %d", userId);
+        return Status::fromServiceSpecificError(static_cast<int32_t>(rc));
+    }
+
+    auto it = uidsOut->begin();
+    for (LockedKeyBlobEntry& entry : internal_matches) {
+        if (it == uidsOut->end()) {
+            ALOGW("Maximum number (%d) of auth bound uids found, truncating remainder",
+                  static_cast<int32_t>(uidsOut->capacity()));
+            break;
+        }
+        if (std::find(uidsOut->begin(), it, entry->uid()) != it) {
+            // uid already in list, skip
+            continue;
+        }
+
+        auto [rc, blob, charBlob] = entry.readBlobs(encryptionKey, state);
+        if (rc != ResponseCode::NO_ERROR && rc != ResponseCode::LOCKED) {
+            ALOGE("Error reading blob for key %s", entry->alias().c_str());
+            continue;
+        }
+
+        if (blob && blob.isEncrypted()) {
+            *it++ = entry->uid();
+        } else if (charBlob) {
+            auto [success, hwEnforced, swEnforced] = charBlob.getKeyCharacteristics();
+            if (!success) {
+                ALOGE("Error reading blob characteristics for key %s", entry->alias().c_str());
+                continue;
+            }
+            if (hwEnforced.Contains(TAG_USER_SECURE_ID) ||
+                swEnforced.Contains(TAG_USER_SECURE_ID)) {
+                *it++ = entry->uid();
+            }
+        }
+    }
+    *aidl_return = static_cast<int32_t>(ResponseCode::NO_ERROR);
+    return Status::ok();
+}
+
 Status KeyStoreService::reset(int32_t* aidl_return) {
     if (!checkBinderPermission(P_RESET)) {
         *aidl_return = static_cast<int32_t>(ResponseCode::PERMISSION_DENIED);
diff --git a/keystore/key_store_service.h b/keystore/key_store_service.h
index 601ed21..5a3586f 100644
--- a/keystore/key_store_service.h
+++ b/keystore/key_store_service.h
@@ -61,6 +61,9 @@
                                     int32_t* _aidl_return) override;
     ::android::binder::Status list(const ::android::String16& namePrefix, int32_t uid,
                                    ::std::vector<::android::String16>* _aidl_return) override;
+    ::android::binder::Status listUidsOfAuthBoundKeys(::std::vector<int32_t>* uids,
+                                                      int32_t* _aidl_return) override;
+
     ::android::binder::Status reset(int32_t* _aidl_return) override;
     ::android::binder::Status onUserPasswordChanged(int32_t userId,
                                                     const ::android::String16& newPassword,
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index 0981f1e..e1fb2d9 100644
--- a/keystore/keystore_cli_v2.cpp
+++ b/keystore/keystore_cli_v2.cpp
@@ -66,6 +66,7 @@
            "          delete-all\n"
            "          exists --name=<key_name>\n"
            "          list [--prefix=<key_name_prefix>]\n"
+           "          list-apps-with-keys\n"
            "          sign-verify --name=<key_name>\n"
            "          [en|de]crypt --name=<key_name> --in=<file> --out=<file>\n"
            "                       [--seclevel=software|strongbox|tee(default)]\n"
@@ -285,7 +286,8 @@
     return result;
 }
 
-int GenerateKey(const std::string& name, int32_t flags) {
+// Note: auth_bound keys created with this tool will not be usable.
+int GenerateKey(const std::string& name, int32_t flags, bool auth_bound) {
     std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
     AuthorizationSetBuilder params;
     params.RsaSigningKey(2048, 65537)
@@ -294,8 +296,14 @@
         .Digest(Digest::SHA_2_384)
         .Digest(Digest::SHA_2_512)
         .Padding(PaddingMode::RSA_PKCS1_1_5_SIGN)
-        .Padding(PaddingMode::RSA_PSS)
-        .Authorization(TAG_NO_AUTH_REQUIRED);
+        .Padding(PaddingMode::RSA_PSS);
+    if (auth_bound) {
+        // Gatekeeper normally generates the secure user id.
+        // Using zero allows the key to be created, but it will not be usuable.
+        params.Authorization(TAG_USER_SECURE_ID, 0);
+    } else {
+        params.Authorization(TAG_NO_AUTH_REQUIRED);
+    }
     AuthorizationSet hardware_enforced_characteristics;
     AuthorizationSet software_enforced_characteristics;
     auto result = keystore->generateKey(name, params, flags, &hardware_enforced_characteristics,
@@ -364,6 +372,35 @@
     return 0;
 }
 
+int ListAppsWithKeys() {
+
+    sp<android::IServiceManager> sm = android::defaultServiceManager();
+    sp<android::IBinder> binder = sm->getService(String16("android.security.keystore"));
+    sp<IKeystoreService> service = android::interface_cast<IKeystoreService>(binder);
+    if (service == nullptr) {
+        fprintf(stderr, "Error connecting to keystore service.\n");
+        return 1;
+    }
+    int32_t aidl_return;
+    ::std::vector<int32_t> uids(100);
+    android::binder::Status status = service->listUidsOfAuthBoundKeys(&uids, &aidl_return);
+    if (!status.isOk()) {
+        fprintf(stderr, "Requesting uids of auth bound keys failed with error %s.\n",
+                status.toString8().c_str());
+        return 1;
+    }
+    if (!KeyStoreNativeReturnCode(aidl_return).isOk()) {
+        fprintf(stderr, "Requesting uids of auth bound keys failed with code %d.\n", aidl_return);
+        return 1;
+    }
+    printf("Apps with auth bound keys:\n");
+    for (auto i = uids.begin(); i != uids.end(); ++i) {
+        if (*i == 0) break;
+        printf("%d\n", *i);
+    }
+    return 0;
+}
+
 int SignAndVerify(const std::string& name) {
     std::unique_ptr<KeystoreClient> keystore = CreateKeystoreInstance();
     AuthorizationSetBuilder sign_params;
@@ -591,7 +628,8 @@
                           securityLevelOption2Flags(*command_line));
     } else if (args[0] == "generate") {
         return GenerateKey(command_line->GetSwitchValueASCII("name"),
-                           securityLevelOption2Flags(*command_line));
+                           securityLevelOption2Flags(*command_line),
+                           command_line->HasSwitch("auth_bound"));
     } else if (args[0] == "get-chars") {
         return GetCharacteristics(command_line->GetSwitchValueASCII("name"));
     } else if (args[0] == "export") {
@@ -604,6 +642,8 @@
         return DoesKeyExist(command_line->GetSwitchValueASCII("name"));
     } else if (args[0] == "list") {
         return List(command_line->GetSwitchValueASCII("prefix"));
+    } else if (args[0] == "list-apps-with-keys") {
+        return ListAppsWithKeys();
     } else if (args[0] == "sign-verify") {
         return SignAndVerify(command_line->GetSwitchValueASCII("name"));
     } else if (args[0] == "encrypt") {
diff --git a/keystore/tests/list_auth_bound_keys_test.sh b/keystore/tests/list_auth_bound_keys_test.sh
new file mode 100755
index 0000000..f609b34
--- /dev/null
+++ b/keystore/tests/list_auth_bound_keys_test.sh
@@ -0,0 +1,77 @@
+#!/usr/bin/env bash
+
+#
+# Copyright (C) 2018 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+#
+# Simple adb based test for keystore method list_auth_bound_keys
+# Depends on keystore_cli_v2 tool and root
+#
+
+set -e
+
+ROOT_ID=0
+USER1_ID=10901
+USER2_ID=10902
+SYSTEM_ID=1000
+
+function cli {
+	adb shell "su $1 keystore_cli_v2 $2"
+}
+
+#start as root
+adb root
+
+# generate keys as user
+echo "generating keys"
+cli $USER1_ID "delete --name=no_auth_key" || true
+cli $USER1_ID "generate --name=no_auth_key"
+cli $USER2_ID "delete --name=auth_key" || true
+if ! cli $USER2_ID "generate --name=auth_key --auth_bound"; then
+	echo "Unable to generate auth bound key, make sure device/emulator has a pin/password set."
+	echo "$ adb shell locksettings set-pin 1234"
+	exit 1
+fi
+
+# try to list keys as user
+if cli $USER2_ID list-apps-with-keys; then
+	echo "Error: list-apps-with-keys succeeded as user, this is not expected!"
+	exit 1
+fi
+
+# try to list keys as root
+if cli $ROOT_ID "list-apps-with-keys"; then
+	echo "Error: list-apps-with-keys succeeded as root, this is not expected!"
+	exit 1
+fi
+
+# try to list keys as system
+success=false
+while read -r line; do
+	echo $line
+    if [ "$line" == "$USER2_ID" ]; then
+    	success=true
+    fi
+    if [ "$line" == "$USER1_ID" ]; then
+    	echo "Error: User1 id not expected in list"
+    	exit 1
+    fi
+done <<< $(cli $SYSTEM_ID "list-apps-with-keys")
+if [ $success = true ]; then
+	echo "Success!"
+else
+	echo "Error: User2 id not in list"
+	exit 1
+fi
\ No newline at end of file