diff options
author | David Zeuthen <zeuthen@google.com> | 2017-11-27 11:33:55 -0500 |
---|---|---|
committer | David Zeuthen <zeuthen@google.com> | 2018-01-17 15:38:44 -0500 |
commit | c6eb7cd999397ff52251beaf8351e7497854746c (patch) | |
tree | 8beddf269a19f977ac1fa49d11fe9fd2ec51bfa4 | |
parent | c8cdf0ec734ec181bf705b1d4f1a6a787bcd2054 (diff) | |
download | security-c6eb7cd999397ff52251beaf8351e7497854746c.tar.gz |
Add support for confirmation APIs.
This code implements new keystore APIs for confirmations.
Also add new 'confirmation' verb to the keystore_cli_v2 command to be
used for testing confirmations. It will block until there's a
callback. Example invocations:
phone:/ # keystore_cli_v2 confirmation --prompt_text="Hello World" --extra_data=010203 --ui_options=1,2,3
Waiting for prompt to complete - use Ctrl+C to abort...
Confirmation prompt completed
responseCode = 0
dataThatWasConfirmed[30] = {0xa2, 0x66, 0x70, 0x72, 0x6f, 0x6d, 0x70, 0x74, 0x6b, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0x20, 0x57, 0x6f, 0x72, 0x6c, 0x64, 0x65, 0x65, 0x78, 0x74, 0x72, 0x61, 0x43, 0x01, 0x02, 0x03}
phone:/ #
If a prompt is already being shown, the |OperationPending| return code
(code 3) is returned:
phone:/ # keystore_cli_v2 confirmation --prompt_text="Hello World" --extra_data=010203 --ui_options=1,2,3
Presenting confirmation prompt failed with return code 3.
Canceling a prompt:
phone:/# keystore_cli_v2 confirmation --prompt_text="Hello World" --extra_data=010203 --cancel_after=1.5
Sleeping 1.5 seconds before canceling prompt...
Waiting for prompt to complete - use Ctrl+C to abort...
Confirmation prompt completed
responseCode = 2
dataThatWasConfirmed[0] = {}
Bug: 63928580
Test: Manually tested.
Change-Id: Ida14706ad066d5350b9081eb7821c7b1a1472dd2
-rw-r--r-- | keystore/Android.bp | 7 | ||||
-rw-r--r-- | keystore/confirmation_manager.cpp | 195 | ||||
-rw-r--r-- | keystore/confirmation_manager.h | 98 | ||||
-rw-r--r-- | keystore/key_store_service.cpp | 45 | ||||
-rw-r--r-- | keystore/key_store_service.h | 21 | ||||
-rw-r--r-- | keystore/keymaster_enforcement.cpp | 2 | ||||
-rw-r--r-- | keystore/keystore_cli_v2.cpp | 142 |
7 files changed, 505 insertions, 5 deletions
diff --git a/keystore/Android.bp b/keystore/Android.bp index de11ec63..cedbfa90 100644 --- a/keystore/Android.bp +++ b/keystore/Android.bp @@ -26,6 +26,7 @@ cc_binary { "Keymaster4.cpp", "auth_token_table.cpp", "blob.cpp", + "confirmation_manager.cpp", "entropy.cpp", "grant_store.cpp", "key_store_service.cpp", @@ -40,6 +41,7 @@ cc_binary { "user_state.cpp", ], shared_libs: [ + "android.hardware.confirmationui@1.0", "android.hardware.keymaster@3.0", "android.hardware.keymaster@4.0", "android.system.wifi.keystore@1.0", @@ -108,12 +110,17 @@ cc_binary { ], srcs: ["keystore_cli_v2.cpp"], shared_libs: [ + "android.hardware.confirmationui@1.0", "android.hardware.keymaster@3.0", + "libbinder", "libchrome", + "libutils", "libhidlbase", "libhwbinder", "libkeymaster4support", + "libkeystore_aidl", "libkeystore_binder", + "libkeystore_parcelables", ], local_include_dirs: ["include"], diff --git a/keystore/confirmation_manager.cpp b/keystore/confirmation_manager.cpp new file mode 100644 index 00000000..d8c53784 --- /dev/null +++ b/keystore/confirmation_manager.cpp @@ -0,0 +1,195 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#define LOG_TAG "ConfirmationManager" + +#include "confirmation_manager.h" + +#include <android/hardware/confirmationui/1.0/IConfirmationResultCallback.h> +#include <android/hardware/confirmationui/1.0/IConfirmationUI.h> +#include <android/hardware/confirmationui/1.0/types.h> +#include <android/security/BpConfirmationPromptCallback.h> +#include <binder/BpBinder.h> +#include <binder/Parcel.h> + +#include "keystore_aidl_hidl_marshalling_utils.h" + +namespace keystore { + +using android::IBinder; +using android::sp; +using android::String16; +using android::String8; +using android::wp; +using android::binder::Status; +using android::hardware::hidl_vec; +using android::hardware::Return; +using android::hardware::confirmationui::V1_0::IConfirmationResultCallback; +using android::hardware::confirmationui::V1_0::IConfirmationUI; +using android::hardware::confirmationui::V1_0::UIOption; + +using android::security::BpConfirmationPromptCallback; +using std::lock_guard; +using std::mutex; +using std::vector; + +ConfirmationManager::ConfirmationManager(IBinder::DeathRecipient* deathRecipient) + : IConfirmationResultCallback(), mDeathRecipient(deathRecipient) {} + +// Called by keystore main thread. +Status ConfirmationManager::presentConfirmationPrompt(const sp<IBinder>& listener, + const String16& promptText, + const hidl_vec<uint8_t>& extraData, + const String16& locale, int uiOptionsAsFlags, + int32_t* aidl_return) { + lock_guard<mutex> lock(mMutex); + + if (mCurrentListener != nullptr) { + *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending); + return Status::ok(); + } + + sp<IConfirmationUI> confirmationUI = IConfirmationUI::tryGetService(); + if (confirmationUI == nullptr) { + ALOGW("Error getting confirmationUI service\n"); + *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::Unimplemented); + return Status::ok(); + } + + String8 promptText8(promptText); + String8 locale8(locale); + vector<UIOption> uiOptionsVector; + for (int n = 0; n < 32; n++) { + if (uiOptionsAsFlags & (1 << n)) { + uiOptionsVector.push_back(UIOption(n)); + } + } + ConfirmationResponseCode responseCode; + responseCode = confirmationUI->promptUserConfirmation(sp<IConfirmationResultCallback>(this), + promptText8.string(), extraData, + locale8.string(), uiOptionsVector); + if (responseCode != ConfirmationResponseCode::OK) { + ALOGW("Unexpecxted responseCode %d from promptUserConfirmation\n", responseCode); + *aidl_return = static_cast<int32_t>(responseCode); + return Status::ok(); + } + + listener->linkToDeath(mDeathRecipient); + confirmationUI->linkToDeath(this, 0); + mCurrentListener = listener; + mCurrentConfirmationUI = confirmationUI; + + *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK); + return Status::ok(); +} + +// Called by keystore main thread. +Status ConfirmationManager::cancelConfirmationPrompt(const sp<IBinder>& listener, + int32_t* aidl_return) { + mMutex.lock(); + if (mCurrentListener != listener) { + // If the prompt was displayed by another application, return + // OperationPending. + mMutex.unlock(); + *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OperationPending); + return Status::ok(); + } + mMutex.unlock(); + + finalizeTransaction(ConfirmationResponseCode::Aborted, {}, true); + + *aidl_return = static_cast<int32_t>(ConfirmationResponseCode::OK); + return Status::ok(); +} + +void ConfirmationManager::finalizeTransaction(ConfirmationResponseCode responseCode, + hidl_vec<uint8_t> dataThatWasConfirmed, + bool callAbortOnHal) { + // Note that confirmationUI->abort() may make the remote HAL process do an IPC call back + // into our process resulting in confirmationResultCallback() to be called... this in turn + // calls finalizeTransaction(). So we have to be careful a) not holding any locks; + // and b) ensure state has been cleared; before doing this... + + mMutex.lock(); + sp<IBinder> listener = mCurrentListener; + if (mCurrentListener != nullptr) { + mCurrentListener->unlinkToDeath(mDeathRecipient); + mCurrentListener = nullptr; + } + sp<IConfirmationUI> confirmationUI = mCurrentConfirmationUI; + if (mCurrentConfirmationUI != nullptr) { + mCurrentConfirmationUI->unlinkToDeath(this); + mCurrentConfirmationUI = nullptr; + } + mMutex.unlock(); + + // Tell the HAL to shut down the confirmation dialog, if requested. + if (confirmationUI != nullptr && callAbortOnHal) { + confirmationUI->abort(); + } + + // Deliver result to the application that started the operation. + if (listener != nullptr) { + sp<BpConfirmationPromptCallback> obj = new BpConfirmationPromptCallback(listener); + Status status = obj->onConfirmationPromptCompleted(static_cast<int32_t>(responseCode), + dataThatWasConfirmed); + if (!status.isOk()) { + ALOGW("Error sending onConfirmationPromptCompleted - status: %s\n", + status.toString8().c_str()); + } + } +} + +// Called by hwbinder thread (not keystore main thread). +Return<void> ConfirmationManager::result(ConfirmationResponseCode responseCode, + const hidl_vec<uint8_t>& dataThatWasConfirmed, + const hidl_vec<uint8_t>& confirmationToken) { + finalizeTransaction(responseCode, dataThatWasConfirmed, false); + lock_guard<mutex> lock(mMutex); + mLatestConfirmationToken = confirmationToken; + return Return<void>(); +} + +// Called by keystore main thread. +hidl_vec<uint8_t> ConfirmationManager::getLatestConfirmationToken() { + lock_guard<mutex> lock(mMutex); + return mLatestConfirmationToken; +} + +void ConfirmationManager::binderDied(const wp<IBinder>& who) { + // This is also called for other binders so need to check it's for + // us before acting on it. + mMutex.lock(); + if (who == mCurrentListener) { + // Clear this so we don't call back into the already dead + // binder in finalizeTransaction(). + mCurrentListener->unlinkToDeath(mDeathRecipient); + mCurrentListener = nullptr; + mMutex.unlock(); + ALOGW("The process which requested the confirmation dialog died.\n"); + finalizeTransaction(ConfirmationResponseCode::SystemError, {}, true); + } else { + mMutex.unlock(); + } +} + +void ConfirmationManager::serviceDied(uint64_t /* cookie */, + const wp<android::hidl::base::V1_0::IBase>& /* who */) { + ALOGW("The ConfirmationUI HAL died.\n"); + finalizeTransaction(ConfirmationResponseCode::SystemError, {}, false); +} + +} // namespace keystore diff --git a/keystore/confirmation_manager.h b/keystore/confirmation_manager.h new file mode 100644 index 00000000..4bf4b8d6 --- /dev/null +++ b/keystore/confirmation_manager.h @@ -0,0 +1,98 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef KEYSTORE_CONFIRMATION_MANAGER_H_ +#define KEYSTORE_CONFIRMATION_MANAGER_H_ + +#include <android/hardware/confirmationui/1.0/IConfirmationUI.h> +#include <android/hardware/confirmationui/1.0/types.h> +#include <binder/Binder.h> +#include <binder/IBinder.h> +#include <binder/Status.h> +#include <keystore/keymaster_types.h> +#include <map> +#include <mutex> +#include <utils/LruCache.h> +#include <utils/StrongPointer.h> +#include <vector> + +namespace keystore { + +using android::binder::Status; +using android::hardware::confirmationui::V1_0::IConfirmationResultCallback; +using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode; + +class ConfirmationManager; + +class ConfirmationManager : public android::hardware::hidl_death_recipient, + public IConfirmationResultCallback { + public: + explicit ConfirmationManager(android::IBinder::DeathRecipient* deathRecipient); + + // Calls into the confirmationui HAL to start a new prompt. + // + // Returns OperationPending if another application is already + // showing a confirmation. Otherwise returns the return code from + // the HAL. + Status presentConfirmationPrompt(const android::sp<android::IBinder>& listener, + const android::String16& promptText, + const hidl_vec<uint8_t>& extraData, + const android::String16& locale, int uiOptionsAsFlags, + int32_t* aidl_return); + + // Calls into the confirmationui HAL to cancel displaying a + // prompt. + // + // Returns OperatingPending if another application is showing a + // confirmation. Otherwise returns the return code from the HAL. + Status cancelConfirmationPrompt(const android::sp<android::IBinder>& listener, + int32_t* aidl_return); + + // Gets the latest confirmation token received from the ConfirmationUI HAL. + hidl_vec<uint8_t> getLatestConfirmationToken(); + + // Called by KeyStoreService when a client binder has died. + void binderDied(const android::wp<android::IBinder>& who); + + // hidl_death_recipient overrides: + virtual void serviceDied(uint64_t cookie, + const android::wp<android::hidl::base::V1_0::IBase>& who) override; + + // IConfirmationResultCallback overrides: + android::hardware::Return<void> result(ConfirmationResponseCode responseCode, + const hidl_vec<uint8_t>& dataThatWasConfirmed, + const hidl_vec<uint8_t>& confirmationToken) override; + + private: + friend class ConfirmationResultCallback; + + void finalizeTransaction(ConfirmationResponseCode responseCode, + hidl_vec<uint8_t> dataThatWasConfirmed, bool callAbortOnHal); + + // This mutex protects all data below it. + std::mutex mMutex; + + // The mCurrentListener and mCurrentConfirmationUI fields are set + // if and only if a prompt is currently showing. + android::sp<android::IBinder> mCurrentListener; + android::sp<android::hardware::confirmationui::V1_0::IConfirmationUI> mCurrentConfirmationUI; + android::IBinder::DeathRecipient* mDeathRecipient; + hidl_vec<uint8_t> mLatestConfirmationToken; +}; + +} // namespace keystore + +#endif // KEYSTORE_CONFIRMATION_MANAGER_H_ diff --git a/keystore/key_store_service.cpp b/keystore/key_store_service.cpp index b13441b6..d00a04bf 100644 --- a/keystore/key_store_service.cpp +++ b/keystore/key_store_service.cpp @@ -55,6 +55,7 @@ using android::security::keymaster::KeymasterArguments; using android::security::keymaster::KeymasterBlob; using android::security::keymaster::KeymasterCertificateChain; using android::security::keymaster::OperationResult; +using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode; constexpr size_t kMaxOperations = 15; constexpr double kIdRotationPeriod = 30 * 24 * 60 * 60; /* Thirty days, in seconds */ @@ -138,6 +139,7 @@ void KeyStoreService::binderDied(const wp<IBinder>& who) { int32_t unused_result; abort(token, &unused_result); } + mConfirmationManager->binderDied(who); } Status KeyStoreService::getState(int32_t userId, int32_t* aidl_return) { @@ -1359,6 +1361,23 @@ Status KeyStoreService::begin(const sp<IBinder>& appToken, const String16& name, return Status::ok(); } +void KeyStoreService::appendConfirmationTokenIfNeeded(const KeyCharacteristics& keyCharacteristics, + std::vector<KeyParameter>* params) { + if (!(containsTag(keyCharacteristics.softwareEnforced, Tag::TRUSTED_CONFIRMATION_REQUIRED) || + containsTag(keyCharacteristics.hardwareEnforced, Tag::TRUSTED_CONFIRMATION_REQUIRED))) { + return; + } + + hidl_vec<uint8_t> confirmationToken = mConfirmationManager->getLatestConfirmationToken(); + if (confirmationToken.size() == 0) { + return; + } + + params->push_back( + Authorization(keymaster::TAG_CONFIRMATION_TOKEN, std::move(confirmationToken))); + ALOGD("Appending confirmation token\n"); +} + Status KeyStoreService::update(const sp<IBinder>& token, const KeymasterArguments& params, const ::std::vector<uint8_t>& data, OperationResult* result) { if (!checkAllowedOperationParams(params.getParameters())) { @@ -1387,6 +1406,9 @@ Status KeyStoreService::update(const sp<IBinder>& token, const KeymasterArgument false /* is_begin_operation */); if (!result->resultCode.isOk()) return Status::ok(); + std::vector<KeyParameter> inParams = params.getParameters(); + appendConfirmationTokenIfNeeded(op.characteristics, &inParams); + auto hidlCb = [&](ErrorCode ret, uint32_t inputConsumed, const hidl_vec<KeyParameter>& outParams, const ::std::vector<uint8_t>& output) { @@ -1398,8 +1420,8 @@ Status KeyStoreService::update(const sp<IBinder>& token, const KeymasterArgument } }; - KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR(op.device->update( - op.handle, params.getParameters(), data, authToken, VerificationToken(), hidlCb)); + KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR( + op.device->update(op.handle, inParams, data, authToken, VerificationToken(), hidlCb)); // just a reminder: on success result->resultCode was set in the callback. So we only overwrite // it if there was a communication error indicated by the ErrorCode. @@ -1438,6 +1460,9 @@ Status KeyStoreService::finish(const sp<IBinder>& token, const KeymasterArgument key_auths.append(op.characteristics.softwareEnforced.begin(), op.characteristics.softwareEnforced.end()); + std::vector<KeyParameter> inParams = params.getParameters(); + appendConfirmationTokenIfNeeded(op.characteristics, &inParams); + result->resultCode = enforcement_policy.AuthorizeOperation( op.purpose, op.keyid, key_auths, params.getParameters(), authToken, op.handle, false /* is_begin_operation */); @@ -1453,7 +1478,7 @@ Status KeyStoreService::finish(const sp<IBinder>& token, const KeymasterArgument }; KeyStoreServiceReturnCode rc = KS_HANDLE_HIDL_ERROR( - op.device->finish(op.handle, params.getParameters(), + op.device->finish(op.handle, inParams, ::std::vector<uint8_t>() /* TODO(swillden): wire up input to finish() */, signature, authToken, VerificationToken(), hidlCb)); mOperationMap.removeOperation(token); @@ -1796,6 +1821,20 @@ Status KeyStoreService::importWrappedKey( return AIDL_RETURN(mKeyStore->put(cFilename.string(), &charBlob, get_user_id(callingUid))); } +Status KeyStoreService::presentConfirmationPrompt(const sp<IBinder>& listener, + const String16& promptText, + const ::std::vector<uint8_t>& extraData, + const String16& locale, int32_t uiOptionsAsFlags, + int32_t* aidl_return) { + return mConfirmationManager->presentConfirmationPrompt(listener, promptText, extraData, locale, + uiOptionsAsFlags, aidl_return); +} + +Status KeyStoreService::cancelConfirmationPrompt(const sp<IBinder>& listener, + int32_t* aidl_return) { + return mConfirmationManager->cancelConfirmationPrompt(listener, aidl_return); +} + /** * Prune the oldest pruneable operation. */ diff --git a/keystore/key_store_service.h b/keystore/key_store_service.h index fec44ec6..958b0dcb 100644 --- a/keystore/key_store_service.h +++ b/keystore/key_store_service.h @@ -20,6 +20,7 @@ #include <android/security/BnKeystoreService.h> #include "auth_token_table.h" +#include "confirmation_manager.h" #include "KeyStore.h" #include "keystore_keymaster_enforcement.h" @@ -36,7 +37,9 @@ namespace keystore { class KeyStoreService : public android::security::BnKeystoreService, android::IBinder::DeathRecipient { public: - explicit KeyStoreService(KeyStore* keyStore) : mKeyStore(keyStore), mOperationMap(this) {} + explicit KeyStoreService(KeyStore* keyStore) + : mKeyStore(keyStore), mOperationMap(this), + mConfirmationManager(new ConfirmationManager(this)) {} virtual ~KeyStoreService() = default; void binderDied(const android::wp<android::IBinder>& who); @@ -160,6 +163,7 @@ class KeyStoreService : public android::security::BnKeystoreService, ::android::security::keymaster::KeymasterCertificateChain* chain, int32_t* _aidl_return) override; ::android::binder::Status onDeviceOffBody(int32_t* _aidl_return) override; + ::android::binder::Status importWrappedKey( const ::android::String16& wrappedKeyAlias, const ::std::vector<uint8_t>& wrappedKey, const ::android::String16& wrappingKeyAlias, const ::std::vector<uint8_t>& maskingKey, @@ -167,6 +171,14 @@ class KeyStoreService : public android::security::BnKeystoreService, int64_t fingerprintSid, ::android::security::keymaster::KeyCharacteristics* characteristics, int32_t* _aidl_return) override; + ::android::binder::Status presentConfirmationPrompt( + const ::android::sp<::android::IBinder>& listener, const ::android::String16& promptText, + const ::std::vector<uint8_t>& extraData, const ::android::String16& locale, + int32_t uiOptionsAsFlags, int32_t* _aidl_return) override; + ::android::binder::Status + cancelConfirmationPrompt(const ::android::sp<::android::IBinder>& listener, + int32_t* _aidl_return) override; + private: static const int32_t UID_SELF = -1; @@ -276,8 +288,15 @@ class KeyStoreService : public android::security::BnKeystoreService, KeyStoreServiceReturnCode upgradeKeyBlob(const android::String16& name, uid_t targetUid, const AuthorizationSet& params, Blob* blob); + /** + * Adds a Confirmation Token to the key parameters if needed. + */ + void appendConfirmationTokenIfNeeded(const KeyCharacteristics& keyCharacteristics, + std::vector<KeyParameter>* params); + KeyStore* mKeyStore; OperationMap mOperationMap; + android::sp<ConfirmationManager> mConfirmationManager; keystore::AuthTokenTable mAuthTokenTable; KeystoreKeymasterEnforcement enforcement_policy; }; diff --git a/keystore/keymaster_enforcement.cpp b/keystore/keymaster_enforcement.cpp index c6d9a55c..18500320 100644 --- a/keystore/keymaster_enforcement.cpp +++ b/keystore/keymaster_enforcement.cpp @@ -345,6 +345,8 @@ ErrorCode KeymasterEnforcement::AuthorizeBegin(const KeyPurpose purpose, const k case Tag::RESET_SINCE_ID_ROTATION: case Tag::ALLOW_WHILE_ON_BODY: case Tag::HARDWARE_TYPE: + case Tag::TRUSTED_CONFIRMATION_REQUIRED: + case Tag::CONFIRMATION_TOKEN: break; case Tag::BOOTLOADER_ONLY: diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp index 6e995e07..870a548e 100644 --- a/keystore/keystore_cli_v2.cpp +++ b/keystore/keystore_cli_v2.cpp @@ -19,13 +19,32 @@ #include <base/command_line.h> #include <base/files/file_util.h> +#include <base/strings/string_number_conversions.h> +#include <base/strings/string_split.h> #include <base/strings/string_util.h> +#include <base/strings/utf_string_conversions.h> +#include <base/threading/platform_thread.h> #include <keystore/keymaster_types.h> #include <keystore/keystore_client_impl.h> +#include <android/hardware/confirmationui/1.0/types.h> +#include <android/security/BnConfirmationPromptCallback.h> +#include <android/security/IKeystoreService.h> + +#include <binder/IPCThreadState.h> +#include <binder/IServiceManager.h> + +//#include <keystore/keystore.h> + using base::CommandLine; using keystore::KeystoreClient; +using android::sp; +using android::String16; +using android::security::IKeystoreService; +using base::CommandLine; +using ConfirmationResponseCode = android::hardware::confirmationui::V1_0::ResponseCode; + namespace { using namespace keystore; @@ -49,7 +68,10 @@ void PrintUsageAndExit() { " list [--prefix=<key_name_prefix>]\n" " sign-verify --name=<key_name>\n" " [en|de]crypt --name=<key_name> --in=<file> --out=<file>\n" - " [--seclevel=software|strongbox|tee(default)]\n"); + " [--seclevel=software|strongbox|tee(default)]\n" + " confirmation --prompt_text=<PromptText> --extra_data=<hex>\n" + " --locale=<locale> [--ui_options=<list_of_ints>]\n" + " --cancel_after=<seconds>\n"); exit(1); } @@ -438,6 +460,118 @@ uint32_t securityLevelOption2Flags(const CommandLine& cmd) { return KEYSTORE_FLAG_NONE; } +class ConfirmationListener : public android::security::BnConfirmationPromptCallback { + public: + ConfirmationListener() {} + + virtual ::android::binder::Status + onConfirmationPromptCompleted(int32_t result, + const ::std::vector<uint8_t>& dataThatWasConfirmed) override { + ConfirmationResponseCode responseCode = static_cast<ConfirmationResponseCode>(result); + printf("Confirmation prompt completed\n" + "responseCode = %d\n", + responseCode); + printf("dataThatWasConfirmed[%zd] = {", dataThatWasConfirmed.size()); + size_t newLineCountDown = 16; + bool hasPrinted = false; + for (uint8_t element : dataThatWasConfirmed) { + if (hasPrinted) { + printf(", "); + } + if (newLineCountDown == 0) { + printf("\n "); + newLineCountDown = 32; + } + printf("0x%02x", element); + hasPrinted = true; + } + printf("}\n"); + exit(0); + } +}; + +int Confirmation(const std::string& promptText, const std::string& extraDataHex, + const std::string& locale, const std::string& uiOptionsStr, + const std::string& cancelAfter) { + 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 == NULL) { + printf("error: could not connect to keystore service.\n"); + return 1; + } + + if (promptText.size() == 0) { + printf("The --promptText parameter cannot be empty.\n"); + return 1; + } + + std::vector<uint8_t> extraData; + if (!base::HexStringToBytes(extraDataHex, &extraData)) { + printf("The --extra_data parameter does not appear to be valid hexadecimal.\n"); + return 1; + } + + std::vector<std::string> pieces = + base::SplitString(uiOptionsStr, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); + int uiOptionsAsFlags = 0; + for (auto& p : pieces) { + int value; + if (!base::StringToInt(p, &value)) { + printf("Error parsing %s in --ui_options parameter as a number.\n", p.c_str()); + return 1; + } + uiOptionsAsFlags |= (1 << value); + } + + double cancelAfterValue = 0.0; + + if (cancelAfter.size() > 0 && !base::StringToDouble(cancelAfter, &cancelAfterValue)) { + printf("Error parsing %s in --cancel_after parameter as a double.\n", cancelAfter.c_str()); + return 1; + } + + String16 promptText16(promptText.data(), promptText.size()); + String16 locale16(locale.data(), locale.size()); + + sp<ConfirmationListener> listener = new ConfirmationListener(); + + int32_t aidl_return; + android::binder::Status status = service->presentConfirmationPrompt( + listener, promptText16, extraData, locale16, uiOptionsAsFlags, &aidl_return); + if (!status.isOk()) { + printf("Presenting confirmation prompt failed with binder status '%s'.\n", + status.toString8().c_str()); + return 1; + } + ConfirmationResponseCode responseCode = static_cast<ConfirmationResponseCode>(aidl_return); + if (responseCode != ConfirmationResponseCode::OK) { + printf("Presenting confirmation prompt failed with response code %d.\n", responseCode); + return 1; + } + + if (cancelAfterValue > 0.0) { + printf("Sleeping %.1f seconds before canceling prompt...\n", cancelAfterValue); + base::PlatformThread::Sleep(base::TimeDelta::FromSecondsD(cancelAfterValue)); + status = service->cancelConfirmationPrompt(listener, &aidl_return); + if (!status.isOk()) { + printf("Canceling confirmation prompt failed with binder status '%s'.\n", + status.toString8().c_str()); + return 1; + } + responseCode = static_cast<ConfirmationResponseCode>(aidl_return); + if (responseCode != ConfirmationResponseCode::OK) { + printf("Canceling confirmation prompt failed with response code %d.\n", responseCode); + return 1; + } + } + + printf("Waiting for prompt to complete - use Ctrl+C to abort...\n"); + // Use the main thread to process Binder transactions. + android::IPCThreadState::self()->joinThreadPool(); + return 0; +} + } // namespace int main(int argc, char** argv) { @@ -480,6 +614,12 @@ int main(int argc, char** argv) { return Decrypt(command_line->GetSwitchValueASCII("name"), command_line->GetSwitchValueASCII("in"), command_line->GetSwitchValueASCII("out")); + } else if (args[0] == "confirmation") { + return Confirmation(command_line->GetSwitchValueASCII("prompt_text"), + command_line->GetSwitchValueASCII("extra_data"), + command_line->GetSwitchValueASCII("locale"), + command_line->GetSwitchValueASCII("ui_options"), + command_line->GetSwitchValueASCII("cancel_after")); } else { PrintUsageAndExit(); } |