diff --git a/confirmationui/1.0/Android.bp b/confirmationui/1.0/Android.bp new file mode 100644 index 0000000000..21acecba7e --- /dev/null +++ b/confirmationui/1.0/Android.bp @@ -0,0 +1,27 @@ +// This file is autogenerated by hidl-gen -Landroidbp. + +hidl_interface { + name: "android.hardware.confirmationui@1.0", + root: "android.hardware", + vndk: { + enabled: true, + }, + srcs: [ + "types.hal", + "IConfirmationResultCallback.hal", + "IConfirmationUI.hal", + ], + interfaces: [ + "android.hardware.keymaster@4.0", + "android.hidl.base@1.0", + ], + types: [ + "MessageSize", + "ResponseCode", + "TestKeyBits", + "TestModeCommands", + "UIOption", + ], + gen_java: false, +} + diff --git a/confirmationui/1.0/IConfirmationResultCallback.hal b/confirmationui/1.0/IConfirmationResultCallback.hal new file mode 100644 index 0000000000..03a10cfe6c --- /dev/null +++ b/confirmationui/1.0/IConfirmationResultCallback.hal @@ -0,0 +1,61 @@ +/* + * 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. + */ + +package android.hardware.confirmationui@1.0; + +/** + * Callback interface passed to IConfirmationUI::promptUserConfirmation(). + * Informs the caller about the result of the prompt operation. + */ +interface IConfirmationResultCallback { + /** + * This callback is called by the confirmation provider when it stops prompting the user. + * Iff the user has confirmed the prompted text, error is ErrorCode::OK and the + * parameters formattedMessage and confirmationToken hold the values needed to request + * a signature from keymaster. + * In all other cases formattedMessage and confirmationToken must be of length 0. + * + * @param error - OK: IFF the user has confirmed the prompt. + * - Canceled: If the user has pressed the cancel button. + * - Aborted: If IConfirmationUI::abort() was called. + * - SystemError: If an unexpected System error occurred that prevented the TUI + * from being shut down gracefully. + * @param formattedMessage holds the prompt text and extra data. + * The message is CBOR (RFC 7049) encoded and has the following format: + * CBOR_MAP{ "prompt", , "extra", } + * The message is a CBOR encoded map (type 5) with the keys + * "prompt" and "extra". The keys are encoded as CBOR text string + * (type 3). The value is encoded as CBOR text string + * (type 3), and the value is encoded as CBOR byte string + * (type 2). The map must have exactly one key value pair for each of + * the keys "prompt" and "extra". Other keys are not allowed. + * The value of "prompt" is given by the proptText argument to + * IConfirmationUI::promptUserConfirmation and must not be modified + * by the implementation. + * The value of "extra" is given by the extraData argument to + * IConfirmationUI::promptUserConfirmation and must not be modified + * or interpreted by the implementation. + * + * @param confirmationToken a 32-byte HMAC-SHA256 value, computed over + * "confirmation token" || + * i.e. the literal UTF-8 encoded string "confirmation token", without + * the "", concatenated with the formatted message as returned in the + * formattedMessage argument. The HMAC is keyed with a 256-bit secret + * which is shared with Keymaster. In test mode the test key MUST be + * used (see types.hal TestModeCommands and TestKeyBits). + */ + result(ResponseCode error, vec formattedMessage, vec confirmationToken); +}; diff --git a/confirmationui/1.0/IConfirmationUI.hal b/confirmationui/1.0/IConfirmationUI.hal new file mode 100644 index 0000000000..db8055d16c --- /dev/null +++ b/confirmationui/1.0/IConfirmationUI.hal @@ -0,0 +1,81 @@ +/* + * 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. + */ + +package android.hardware.confirmationui@1.0; + +import android.hardware.keymaster@4.0::HardwareAuthToken; +import IConfirmationResultCallback; + +interface IConfirmationUI { + /** + * Asynchronously initiates a confirmation UI dialog prompting the user to confirm a given text. + * The TUI prompt must be implemented in such a way that a positive response indicates with + * high confidence that a user has seen the given prompt text even if the Android framework + * including the kernel was compromised. + * + * @param resultCB Implementation of IResultCallback. Used by the implementation to report + * the result of the current pending user prompt. + * + * @param promptText UTF-8 encoded string which is to be presented to the user. + * + * @param extraData A binary blob that must be included in the formatted output message as is. + * It is opaque to the implementation. Implementations must neither interpret + * nor modify the content. + * + * @param locale String specifying the locale that must be used by the TUI dialog. The string + * is an IETF BCP 47 tag. + * + * @param uiOptions A set of uiOptions manipulating how the confirmation prompt is displayed. + * Refer to UIOption in types.hal for possible options. + * + * @return error - OK: IFF the dialog was successfully started. In this case, and only in this + * case, the implementation must, eventually, call the callback to + * indicate completion. + * - OperationPending: Is returned when the confirmation provider is currently + * in use. + * - SystemError: An error occurred trying to communicate with the confirmation + * provider (e.g. trusted app). + * - UIError: The confirmation provider encountered an issue with displaying + * the prompt text to the user. + */ + promptUserConfirmation(IConfirmationResultCallback resultCB, string promptText, + vec extraData, string locale, vec uiOptions) + generates(ResponseCode error); + + /** + * DeliverSecureInput is used by the framework to deliver a secure input event to the + * confirmation provider. + * + * VTS test mode: + * This function can be used to test certain code paths non-interactively. See TestModeCommands + * in types.hal for details. + * + * @param secureInputToken An authentication token as generated by Android authentication + * providers. + * + * @return error - Ignored: Unless used for testing (See TestModeCommands). + */ + deliverSecureInputEvent(HardwareAuthToken secureInputToken) + generates(ResponseCode error); + + /** + * Aborts a pending user prompt. This allows the framework to gracefully end a TUI dialog. + * If a TUI operation was pending the corresponding call back is informed with + * ErrorCode::Aborted. + */ + abort(); +}; + diff --git a/confirmationui/1.0/default/Android.bp b/confirmationui/1.0/default/Android.bp new file mode 100644 index 0000000000..10018e8720 --- /dev/null +++ b/confirmationui/1.0/default/Android.bp @@ -0,0 +1,43 @@ +// +// 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. +// + +cc_binary { + name: "android.hardware.confirmationui@1.0-service", + init_rc: ["android.hardware.confirmationui@1.0-service.rc"], + vendor: true, + relative_install_path: "hw", + cflags: [ + "-Wall", + "-Wextra", + "-Werror", + ], + srcs: [ + "service.cpp", + "ConfirmationUI.cpp", + "PlatformSpecifics.cpp", + ], + shared_libs: [ + "android.hardware.confirmationui@1.0", + "android.hardware.confirmationui-support-lib", + "android.hardware.keymaster@4.0", + "libcrypto", + "libbase", + "libhidlbase", + "libhidltransport", + "liblog", + "libutils", + ], +} \ No newline at end of file diff --git a/confirmationui/1.0/default/ConfirmationUI.cpp b/confirmationui/1.0/default/ConfirmationUI.cpp new file mode 100644 index 0000000000..f241a76096 --- /dev/null +++ b/confirmationui/1.0/default/ConfirmationUI.cpp @@ -0,0 +1,66 @@ +/* +** +** Copyright 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. +*/ + +#include "ConfirmationUI.h" + +#include "PlatformSpecifics.h" + +#include +#include + +#include + +#include + +namespace android { +namespace hardware { +namespace confirmationui { +namespace V1_0 { +namespace implementation { + +using ::android::hardware::confirmationui::V1_0::generic::Operation; +using ::android::hardware::keymaster::V4_0::HardwareAuthToken; + +uint8_t hmacKey[32]; + +// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI follow. +Return ConfirmationUI::promptUserConfirmation( + const sp& resultCB, const hidl_string& promptText, + const hidl_vec& extraData, const hidl_string& locale, + const hidl_vec& uiOptions) { + auto& operation = MyOperation::get(); + return operation.init(resultCB, promptText, extraData, locale, uiOptions); +} + +Return ConfirmationUI::deliverSecureInputEvent( + const HardwareAuthToken& secureInputToken) { + auto& operation = MyOperation::get(); + return operation.deliverSecureInputEvent(secureInputToken); +} + +Return ConfirmationUI::abort() { + auto& operation = MyOperation::get(); + operation.abort(); + operation.finalize(hmacKey); + return Void(); +} + +} // namespace implementation +} // namespace V1_0 +} // namespace confirmationui +} // namespace hardware +} // namespace android diff --git a/confirmationui/1.0/default/ConfirmationUI.h b/confirmationui/1.0/default/ConfirmationUI.h new file mode 100644 index 0000000000..e9e7f993b8 --- /dev/null +++ b/confirmationui/1.0/default/ConfirmationUI.h @@ -0,0 +1,57 @@ +/* +** +** Copyright 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 ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H +#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H + +#include +#include +#include + +namespace android { +namespace hardware { +namespace confirmationui { +namespace V1_0 { +namespace implementation { + +using ::android::hardware::hidl_array; +using ::android::hardware::hidl_memory; +using ::android::hardware::hidl_string; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; +using ::android::hardware::Void; +using ::android::sp; + +struct ConfirmationUI : public IConfirmationUI { + // Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI follow. + Return promptUserConfirmation(const sp& resultCB, + const hidl_string& promptText, + const hidl_vec& extraData, + const hidl_string& locale, + const hidl_vec& uiOptions) override; + Return deliverSecureInputEvent( + const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override; + Return abort() override; +}; + +} // namespace implementation +} // namespace V1_0 +} // namespace confirmationui +} // namespace hardware +} // namespace android + +#endif // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_CONFIRMATIONUI_H diff --git a/confirmationui/1.0/default/OWNERS b/confirmationui/1.0/default/OWNERS new file mode 100644 index 0000000000..335660da3b --- /dev/null +++ b/confirmationui/1.0/default/OWNERS @@ -0,0 +1,2 @@ +jdanis@google.com +swillden@google.com diff --git a/confirmationui/1.0/default/PlatformSpecifics.cpp b/confirmationui/1.0/default/PlatformSpecifics.cpp new file mode 100644 index 0000000000..dd039e22ba --- /dev/null +++ b/confirmationui/1.0/default/PlatformSpecifics.cpp @@ -0,0 +1,62 @@ +/* +** +** Copyright 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. +*/ + +#include "PlatformSpecifics.h" + +#include +#include +#include + +namespace android { +namespace hardware { +namespace confirmationui { +namespace V1_0 { +namespace implementation { + +MonotonicClockTimeStamper::TimeStamp MonotonicClockTimeStamper::now() { + timespec ts; + if (!clock_gettime(CLOCK_BOOTTIME, &ts)) { + return TimeStamp(ts.tv_sec * UINT64_C(1000) + ts.tv_nsec / UINT64_C(1000000)); + } else { + return {}; + } +} + +support::NullOr> HMacImplementation::hmac256( + const uint8_t key[32], std::initializer_list buffers) { + HMAC_CTX hmacCtx; + HMAC_CTX_init(&hmacCtx); + if (!HMAC_Init_ex(&hmacCtx, key, 32, EVP_sha256(), nullptr)) { + return {}; + } + for (auto& buffer : buffers) { + if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) { + return {}; + } + } + support::array result; + if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) { + return {}; + } + return result; +} + +} // namespace implementation +} // namespace V1_0 +} // namespace confirmationui +} // namespace hardware +} // namespace android diff --git a/confirmationui/1.0/default/PlatformSpecifics.h b/confirmationui/1.0/default/PlatformSpecifics.h new file mode 100644 index 0000000000..18b88c8fcc --- /dev/null +++ b/confirmationui/1.0/default/PlatformSpecifics.h @@ -0,0 +1,64 @@ +/* +** +** Copyright 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 CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_ +#define CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_ + +#include +#include + +#include +#include +#include + +namespace android { +namespace hardware { +namespace confirmationui { +namespace V1_0 { +namespace implementation { + +struct MonotonicClockTimeStamper { + class TimeStamp { + public: + explicit TimeStamp(uint64_t ts) : timestamp_(ts), ok_(true) {} + TimeStamp() : timestamp_(0), ok_(false) {} + bool isOk() const { return ok_; } + operator const uint64_t() const { return timestamp_; } + + private: + uint64_t timestamp_; + bool ok_; + }; + static TimeStamp now(); +}; + +class HMacImplementation { + public: + static support::NullOr> hmac256( + const uint8_t key[32], std::initializer_list buffers); +}; + +using MyOperation = generic::Operation, MonotonicClockTimeStamper, + HMacImplementation>; + +} // namespace implementation +} // namespace V1_0 +} // namespace confirmationui +} // namespace hardware +} // namespace android + +#endif // CONFIRMATIONUI_1_0_DEFAULT_PLATFORMSPECIFICS_H_ diff --git a/confirmationui/1.0/default/android.hardware.confirmationui@1.0-service.rc b/confirmationui/1.0/default/android.hardware.confirmationui@1.0-service.rc new file mode 100644 index 0000000000..a278028348 --- /dev/null +++ b/confirmationui/1.0/default/android.hardware.confirmationui@1.0-service.rc @@ -0,0 +1,4 @@ +service vendor.confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service + class hal + user system + group system drmrpc diff --git a/confirmationui/1.0/default/service.cpp b/confirmationui/1.0/default/service.cpp new file mode 100644 index 0000000000..58ec66aba5 --- /dev/null +++ b/confirmationui/1.0/default/service.cpp @@ -0,0 +1,38 @@ +/* +** +** Copyright 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 "android.hardware.confirmationui@1.0-service" + +#include +#include + +#include "ConfirmationUI.h" + +using android::hardware::joinRpcThreadpool; + +using android::hardware::confirmationui::V1_0::implementation::ConfirmationUI; + +int main() { + auto confirmationui = new ConfirmationUI(); + auto status = confirmationui->registerAsService(); + if (status != android::OK) { + LOG(FATAL) << "Could not register service for ConfirmationIU 1.0 (" << status << ")"; + } + + joinRpcThreadpool(); + return -1; // Should never get here. +} diff --git a/confirmationui/1.0/types.hal b/confirmationui/1.0/types.hal new file mode 100644 index 0000000000..fd7ae6afdd --- /dev/null +++ b/confirmationui/1.0/types.hal @@ -0,0 +1,104 @@ +/* + * 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. + */ + +package android.hardware.confirmationui@1.0; + +/** + * UI modification options. + */ +enum UIOption : uint32_t { + /** Accessibility: Requests color inverted style. */ + AccessibilityInverted = 0, + /** Accessibility: Requests magnified style. */ + AccessibilityMagnified = 1, +}; + +/** + * Codes returned by ConfirmationUI API calls. + */ +enum ResponseCode : uint32_t { + /** API call succeeded or the user gave approval (result callback). */ + OK = 0, + /** The user canceled the TUI (result callback). */ + Canceled = 1, + /** IConfirmationUI::abort() was called. (result callback). */ + Aborted = 2, + /** Cannot start another prompt. */ + OperationPending = 3, + /** IConfirmationUI::deliverSecureInputEvent call was ingored. */ + Ignored = 4, + /** An unexpected system error occured. */ + SystemError = 5, + /** Returned by an unimplemented API call. */ + Unimplemented = 6, + /** + * This is returned when an error is diagnosed that should have been + * caught by earlier input sanitization. Should never be seen in production. + */ + Unexpected = 7, + /** General UI error. */ + UIError = 0x10000, + UIErrorMissingGlyph, + /** + * The implementation must return this error code on promptUserConfirmation if the + * resulting formatted message does not fit into MessageSize::MAX bytes. It is + * advised that the implementation formats the message upon receiving this API call to + * be able to diagnose this syndrome. + */ + UIErrorMessageTooLong, + UIErrorMalformedUTF8Encoding, +}; + +/** + * This defines the maximum message size. This indirectly limits the size of the prompt text + * and the extra data that can be passed to the confirmation UI. The prompt text and extra data + * must fit in to this size including CBOR header information. + */ +enum MessageSize : uint32_t { MAX = 0x1800 }; + +/** + * The test key is 32byte word with all bytes set to TestKeyBits::BYTE. + */ +enum TestKeyBits: uint8_t { BYTE = 0xA5 }; + +/** + * Test mode commands. + * + * IConfirmationUI::deliverSecureInputEvent can be used to test certain code paths. + * To that end, the caller passes an auth token that has an HMAC keyed with the test key + * (see TestKeyBits in types.hal). Implementations first check the HMAC against test key. + * If the test key produces a matching HMAC, the implementation evaluates the challenge field + * of the auth token against the values defined in TestModeCommand. + * If the command indicates that a confirmation token is to be generated the test key MUST be used + * to generate this confirmation token. + * + * See command code for individual test command descriptions. + */ +enum TestModeCommands: uint64_t { + /** + * Simulates the user pressing the OK button on the UI. If no operation is pending + * ResponseCode::Ignored must be returned. A pending operation is finalized successfully + * see IConfirmationResultCallback::result, however, the test key (see TestKeyBits) MUST be + * used to generate the confirmation token. + */ + OK_EVENT = 0, + /** + * Simulates the user pressing the CANCEL button on the UI. If no operation is pending + * Result::Ignored must be returned. A pending operation is finalized as specified in + * IConfirmationResultCallback.hal. + */ + CANCEL_EVENT = 1, +}; diff --git a/confirmationui/support/Android.bp b/confirmationui/support/Android.bp new file mode 100644 index 0000000000..62156b3419 --- /dev/null +++ b/confirmationui/support/Android.bp @@ -0,0 +1,51 @@ +// +// 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. +// + +cc_library { + name: "android.hardware.confirmationui-support-lib", + vendor_available: true, + host_supported: true, + vndk: { + enabled: true, + }, + srcs: [ + "src/cbor.cpp", + "src/confirmationui_utils.cpp", + ], + export_include_dirs: [ + "include", + ] +} + +cc_test { + name: "android.hardware.confirmationui-support-lib-tests", + srcs: [ + "test/gtest_main.cpp", + "test/android_cbor_test.cpp", + "test/msg_formatting_test.cpp", + ], + static_libs: [ + "libgtest", + "android.hardware.confirmationui-support-lib", + ], + shared_libs: [ + "android.hardware.confirmationui@1.0", + "android.hardware.keymaster@4.0", + "libhidlbase", + ], + clang: true, + cflags: [ "-O0" ], +} diff --git a/confirmationui/support/OWNERS b/confirmationui/support/OWNERS new file mode 100644 index 0000000000..335660da3b --- /dev/null +++ b/confirmationui/support/OWNERS @@ -0,0 +1,2 @@ +jdanis@google.com +swillden@google.com diff --git a/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h b/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h new file mode 100644 index 0000000000..a88cd40e32 --- /dev/null +++ b/confirmationui/support/include/android/hardware/confirmationui/1.0/generic/GenericOperation.h @@ -0,0 +1,188 @@ +/* +** +** Copyright 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 CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_ +#define CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_ + +#include +#include +#include +#include + +namespace android { +namespace hardware { +namespace confirmationui { +namespace V1_0 { +namespace generic { + +namespace { +using namespace ::android::hardware::confirmationui::support; +using ::android::hardware::keymaster::V4_0::HardwareAuthToken; +using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType; + +inline bool hasOption(UIOption option, const hidl_vec& uiOptions) { + for (auto& o : uiOptions) { + if (o == option) return true; + } + return false; +} + +template +class Operation { + using HMacer = support::HMac; + + public: + Operation() : error_(ResponseCode::Ignored), formattedMessageLength_(0) {} + + ResponseCode init(const Callback& resultCB, const hidl_string& promptText, + const hidl_vec& extraData, const hidl_string& locale, + const hidl_vec& uiOptions) { + (void)locale; + (void)uiOptions; + resultCB_ = resultCB; + if (error_ != ResponseCode::Ignored) return ResponseCode::OperationPending; + // TODO make copy of promptText before using it may reside in shared buffer + auto state = write( + WriteState(formattedMessageBuffer_), + map(pair(text("prompt"), text(promptText)), pair(text("extra"), bytes(extraData)))); + switch (state.error_) { + case Error::OK: + break; + case Error::OUT_OF_DATA: + return ResponseCode::UIErrorMessageTooLong; + case Error::MALFORMED_UTF8: + return ResponseCode::UIErrorMalformedUTF8Encoding; + case Error::MALFORMED: + default: + return ResponseCode::Unexpected; + } + formattedMessageLength_ = state.data_ - formattedMessageBuffer_; + // setup TUI and diagnose more UI errors here. + // on success record the start time + startTime_ = TimeStamper::now(); + if (!startTime_.isOk()) { + return ResponseCode::SystemError; + } + error_ = ResponseCode::OK; + return ResponseCode::OK; + } + + void setHmacKey(const uint8_t (&key)[32]) { hmacKey_ = {key}; } + + void abort() { + // tear down TUI here + if (isPending()) { + resultCB_->result(ResponseCode::Aborted, {}, {}); + error_ = ResponseCode::Ignored; + } + } + + void userCancel() { + // tear down TUI here + if (isPending()) error_ = ResponseCode::Canceled; + } + + void finalize(const uint8_t key[32]) { + if (error_ == ResponseCode::Ignored) return; + resultCB_->result(error_, getMessage(), userConfirm(key)); + error_ = ResponseCode::Ignored; + resultCB_ = {}; + } + + bool isPending() const { return error_ != ResponseCode::Ignored; } + + static Operation& get() { + static Operation operation; + return operation; + } + + ResponseCode deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) { + constexpr uint8_t testKeyByte = static_cast(TestKeyBits::BYTE); + constexpr uint8_t testKey[32] = {testKeyByte, testKeyByte, testKeyByte, testKeyByte, + testKeyByte, testKeyByte, testKeyByte, testKeyByte, + testKeyByte, testKeyByte, testKeyByte, testKeyByte, + testKeyByte, testKeyByte, testKeyByte, testKeyByte}; + + auto hmac = HMacer::hmac256(testKey, "\0", bytes_cast(secureInputToken.challenge), + bytes_cast(secureInputToken.userId), + bytes_cast(secureInputToken.authenticatorId), + bytes_cast(hton(secureInputToken.authenticatorType)), + bytes_cast(hton(secureInputToken.timestamp))); + if (!hmac.isOk()) return ResponseCode::Unexpected; + if (hmac.value() == secureInputToken.mac) { + // okay so this is a test token + switch (static_cast(secureInputToken.challenge)) { + case TestModeCommands::OK_EVENT: { + if (isPending()) { + finalize(testKey); + return ResponseCode::OK; + } else { + return ResponseCode::Ignored; + } + } + case TestModeCommands::CANCEL_EVENT: { + bool ignored = !isPending(); + userCancel(); + finalize(testKey); + return ignored ? ResponseCode::Ignored : ResponseCode::OK; + } + default: + return ResponseCode::Ignored; + } + } + return ResponseCode::Ignored; + } + + private: + bool acceptAuthToken(const HardwareAuthToken&) { return false; } + hidl_vec getMessage() { + hidl_vec result; + if (error_ != ResponseCode::OK) return {}; + result.setToExternal(formattedMessageBuffer_, formattedMessageLength_); + return result; + } + hidl_vec userConfirm(const uint8_t key[32]) { + // tear down TUI here + if (error_ != ResponseCode::OK) return {}; + confirmationTokenScratchpad_ = HMacer::hmac256(key, "confirmation token", getMessage()); + if (!confirmationTokenScratchpad_.isOk()) { + error_ = ResponseCode::Unexpected; + return {}; + } + hidl_vec result; + result.setToExternal(confirmationTokenScratchpad_->data(), + confirmationTokenScratchpad_->size()); + return result; + } + + ResponseCode error_; + uint8_t formattedMessageBuffer_[uint32_t(MessageSize::MAX)]; + size_t formattedMessageLength_; + NullOr> confirmationTokenScratchpad_; + Callback resultCB_; + typename TimeStamper::TimeStamp startTime_; + NullOr> hmacKey_; +}; + +} // namespace +} // namespace generic +} // namespace V1_0 +} // namespace confirmationui +} // namespace hardware +} // namespace android + +#endif // CONFIRMATIONUI_1_0_DEFAULT_GENERICOPERATION_H_ diff --git a/confirmationui/support/include/android/hardware/confirmationui/support/cbor.h b/confirmationui/support/include/android/hardware/confirmationui/support/cbor.h new file mode 100644 index 0000000000..f5814d4f05 --- /dev/null +++ b/confirmationui/support/include/android/hardware/confirmationui/support/cbor.h @@ -0,0 +1,335 @@ +/* +** +** Copyright 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 CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_ +#define CONFIRMATIONUI_1_0_DEFAULT_CBOR_H_ + +#include +#include +#include + +namespace android { +namespace hardware { +namespace confirmationui { +namespace support { + +template +Out copy(In begin, In end, Out out) { + while (begin != end) { + *out++ = *begin++; + } + return out; +} + +enum class Type : uint8_t { + NUMBER = 0, + NEGATIVE = 1, + BYTE_STRING = 2, + TEXT_STRING = 3, + ARRAY = 4, + MAP = 5, + TAG = 6, + FLOAT = 7, +}; + +enum class Error : uint32_t { + OK = 0, + OUT_OF_DATA = 1, + MALFORMED = 2, + MALFORMED_UTF8 = 3, +}; + +template +struct MapElement { + const Key& key_; + const Value& value_; + MapElement(const Key& key, const Value& value) : key_(key), value_(value) {} +}; + +template +struct Array; + +template +struct Array { + const Head& head_; + Array tail_; + Array(const Head& head, const Tail&... tail) : head_(head), tail_(tail...) {} + constexpr size_t size() const { return sizeof...(Tail) + 1; }; +}; + +template <> +struct Array<> {}; + +struct TextStr {}; +struct ByteStr {}; + +template +struct StringBuffer { + const T* data_; + size_t size_; + StringBuffer(const T* data, size_t size) : data_(data), size_(size) { + static_assert(sizeof(T) == 1, "elements too large"); + } + const T* data() const { return data_; } + size_t size() const { return size_; } +}; + +/** + * Takes a char array turns it into a StringBuffer of TextStr type. The length of the resulting + * StringBuffer is size - 1, effectively stripping the 0 character from the region being considered. + * If the terminating 0 shall not be stripped use text_keep_last. + */ +template +StringBuffer text(const char (&str)[size]) { + if (size > 0) return StringBuffer(str, size - 1); + return StringBuffer(str, size); +} + +/** + * As opposed to text(const char (&str)[size] this function does not strips the last character. + */ +template +StringBuffer text_keep_last(const char (&str)[size]) { + return StringBuffer(str, size); +} + +template +auto getData(const T& v) -> decltype(v.data()) { + return v.data(); +} + +template +auto getData(const T& v) -> decltype(v.c_str()) { + return v.c_str(); +} + +template +auto text(const T& str) -> StringBuffer, TextStr> { + return StringBuffer, TextStr>(getData(str), str.size()); +} + +inline StringBuffer text(const char* str, size_t size) { + return StringBuffer(str, size); +} + +template +StringBuffer bytes(const T (&str)[size]) { + return StringBuffer(str, size); +} + +template +StringBuffer bytes(const T* str, size_t size) { + return StringBuffer(str, size); +} + +template +auto bytes(const T& str) -> StringBuffer, ByteStr> { + return StringBuffer, ByteStr>(getData(str), str.size()); +} + +template +struct Map; + +template +struct Map, Tail...> { + const MapElement& head_; + Map tail_; + Map(const MapElement& head, const Tail&... tail) + : head_(head), tail_(tail...) {} + constexpr size_t size() const { return sizeof...(Tail) + 1; }; +}; + +template <> +struct Map<> {}; + +template +Map...> map(const MapElement&... elements) { + return Map...>(elements...); +} + +template +Array arr(const Elements&... elements) { + return Array(elements...); +} + +template +MapElement pair(const Key& k, const Value& v) { + return MapElement(k, v); +} + +template +struct getUnsignedType; + +template <> +struct getUnsignedType { + typedef uint8_t type; +}; +template <> +struct getUnsignedType { + typedef uint16_t type; +}; +template <> +struct getUnsignedType { + typedef uint32_t type; +}; +template <> +struct getUnsignedType { + typedef uint64_t type; +}; + +template +using Unsigned = typename getUnsignedType::type; + +class WriteState { + public: + WriteState() : data_(nullptr), size_(0), error_(Error::OK) {} + WriteState(uint8_t* buffer, size_t size) : data_(buffer), size_(size), error_(Error::OK) {} + WriteState(uint8_t* buffer, size_t size, Error error) + : data_(buffer), size_(size), error_(error) {} + template + WriteState(uint8_t (&buffer)[size]) : data_(buffer), size_(size), error_(Error::OK) {} + + WriteState& operator++() { + if (size_) { + ++data_; + --size_; + } else { + error_ = Error::OUT_OF_DATA; + } + return *this; + } + WriteState& operator+=(size_t offset) { + if (offset > size_) { + error_ = Error::OUT_OF_DATA; + } else { + data_ += offset; + size_ -= offset; + } + return *this; + } + operator bool() const { return error_ == Error::OK; } + + uint8_t* data_; + size_t size_; + Error error_; +}; + +WriteState writeHeader(WriteState wState, Type type, const uint64_t value); +bool checkUTF8Copy(const char* begin, const char* const end, uint8_t* out); + +template +WriteState writeNumber(WriteState wState, const T& v) { + if (!wState) return wState; + if (v >= 0) { + return writeHeader(wState, Type::NUMBER, v); + } else { + return writeHeader(wState, Type::NEGATIVE, UINT64_C(-1) - v); + } +} + +inline WriteState write(const WriteState& wState, const uint8_t& v) { + return writeNumber(wState, v); +} +inline WriteState write(const WriteState& wState, const int8_t& v) { + return writeNumber(wState, v); +} +inline WriteState write(const WriteState& wState, const uint16_t& v) { + return writeNumber(wState, v); +} +inline WriteState write(const WriteState& wState, const int16_t& v) { + return writeNumber(wState, v); +} +inline WriteState write(const WriteState& wState, const uint32_t& v) { + return writeNumber(wState, v); +} +inline WriteState write(const WriteState& wState, const int32_t& v) { + return writeNumber(wState, v); +} +inline WriteState write(const WriteState& wState, const uint64_t& v) { + return writeNumber(wState, v); +} +inline WriteState write(const WriteState& wState, const int64_t& v) { + return writeNumber(wState, v); +} + +template +WriteState write(WriteState wState, const StringBuffer& v) { + wState = writeHeader(wState, Type::TEXT_STRING, v.size()); + uint8_t* buffer = wState.data_; + wState += v.size(); + if (!wState) return wState; + if (!checkUTF8Copy(v.data(), v.data() + v.size(), buffer)) { + wState.error_ = Error::MALFORMED_UTF8; + } + return wState; +} + +template +WriteState write(WriteState wState, const StringBuffer& v) { + wState = writeHeader(wState, Type::BYTE_STRING, v.size()); + uint8_t* buffer = wState.data_; + wState += v.size(); + if (!wState) return wState; + static_assert(sizeof(*v.data()) == 1, "elements too large"); + copy(v.data(), v.data() + v.size(), buffer); + return wState; +} + +template