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
diff --git a/keystore/keystore_cli_v2.cpp b/keystore/keystore_cli_v2.cpp
index 6e995e0..870a548 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 @@
" 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 @@
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 @@
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();
}