First working version of the confirmationui HAL service
This implementation does not provide any security guaranties.
* The input method (NotSoSecureInput) runs a crypto protocols that is
sufficiently secure IFF the end point is implemented on a trustworthy
secure input device. But since the endpoint is currently in the HAL
service itself this implementation is not secure.
* This implementation provides most of the functionality, but not the
secure UI infrastructure required to run Android Protected
Confirmation.
Bug: 146078942
Test: VtsHalConfirmationUIV1_0TargetTest
Change-Id: I14717b5fa4ef15db960cdd506b8c6fe5369aec8d
diff --git a/trusty/confirmationui/NotSoSecureInput.cpp b/trusty/confirmationui/NotSoSecureInput.cpp
new file mode 100644
index 0000000..3d9a2d6
--- /dev/null
+++ b/trusty/confirmationui/NotSoSecureInput.cpp
@@ -0,0 +1,207 @@
+/*
+ * Copyright 2020, 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 <android-base/logging.h>
+#include <endian.h>
+#include <memory>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+#include <secure_input/evdev.h>
+#include <secure_input/secure_input_device.h>
+#include <teeui/utils.h>
+
+#include <initializer_list>
+
+using namespace secure_input;
+
+using teeui::AuthTokenKey;
+using teeui::ByteBufferProxy;
+using teeui::Hmac;
+using teeui::optional;
+using teeui::ResponseCode;
+using teeui::TestKeyBits;
+
+constexpr const auto kTestKey = AuthTokenKey::fill(static_cast<uint8_t>(TestKeyBits::BYTE));
+
+class SecureInputHMacer {
+ public:
+ static optional<Hmac> hmac256(const AuthTokenKey& key,
+ std::initializer_list<ByteBufferProxy> buffers) {
+ HMAC_CTX hmacCtx;
+ HMAC_CTX_init(&hmacCtx);
+ if (!HMAC_Init_ex(&hmacCtx, key.data(), key.size(), EVP_sha256(), nullptr)) {
+ return {};
+ }
+ for (auto& buffer : buffers) {
+ if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
+ return {};
+ }
+ }
+ Hmac result;
+ if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
+ return {};
+ }
+ return result;
+ }
+};
+
+using HMac = teeui::HMac<SecureInputHMacer>;
+
+Nonce generateNonce() {
+ /*
+ * Completely random nonce.
+ * Running the secure input protocol from the HAL service is not secure
+ * because we don't trust the non-secure world (i.e., HLOS/Android/Linux). So
+ * using a constant "nonce" here does not weaken security. If this code runs
+ * on a truly trustworthy source of input events this function needs to return
+ * hight entropy nonces.
+ * As of this writing the call to RAND_bytes is commented, because the
+ * emulator this HAL service runs on does not have a good source of entropy.
+ * It would block the call to RAND_bytes indefinitely.
+ */
+ Nonce result{0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04,
+ 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04};
+ // RAND_bytes(result.data(), result.size());
+ return result;
+}
+
+/**
+ * This is an implementation of the SecureInput protocol in unserspace. This is
+ * just an example and should not be used as is. The protocol implemented her
+ * should be used by a trusted input device that can assert user events with
+ * high assurance even if the HLOS kernel is compromised. A confirmationui HAL
+ * that links directly against this implementation is not secure and shal not be
+ * used on a production device.
+ */
+class NotSoSecureInput : public SecureInput {
+ public:
+ NotSoSecureInput(HsBeginCb hsBeginCb, HsFinalizeCb hsFinalizeCb, DeliverEventCb deliverEventCb,
+ InputResultCb inputResultCb)
+ : hsBeginCb_{hsBeginCb}, hsFinalizeCb_{hsFinalizeCb}, deliverEventCb_{deliverEventCb},
+ inputResultCb_{inputResultCb}, discardEvents_{true} {}
+
+ operator bool() const override { return true; }
+
+ void handleEvent(const EventDev& evdev) override {
+ bool gotEvent;
+ input_event evt;
+ std::tie(gotEvent, evt) = evdev.readEvent();
+ while (gotEvent) {
+ if (!(discardEvents_) && evt.type == EV_KEY &&
+ (evt.code == KEY_POWER || evt.code == KEY_VOLUMEDOWN || evt.code == KEY_VOLUMEUP) &&
+ evt.value == 1) {
+ DTupKeyEvent event = DTupKeyEvent::RESERVED;
+
+ // Translate the event code into DTupKeyEvent which the TA understands.
+ switch (evt.code) {
+ case KEY_POWER:
+ event = DTupKeyEvent::PWR;
+ break;
+ case KEY_VOLUMEDOWN:
+ event = DTupKeyEvent::VOL_DOWN;
+ break;
+ case KEY_VOLUMEUP:
+ event = DTupKeyEvent::VOL_UP;
+ break;
+ }
+
+ // The event goes into the HMAC in network byte order.
+ uint32_t keyEventBE = htobe32(static_cast<uint32_t>(event));
+ auto signature = HMac::hmac256(kTestKey, kConfirmationUIEventLabel,
+ teeui::bytesCast(keyEventBE), nCi_);
+
+ teeui::ResponseCode rc;
+ InputResponse ir;
+ auto response = std::tie(rc, ir);
+ if (event != DTupKeyEvent::RESERVED) {
+ response = deliverEventCb_(event, *signature);
+ if (rc != ResponseCode::OK) {
+ LOG(ERROR) << "DeliverInputEvent returned with " << uint32_t(rc);
+ inputResultCb_(rc);
+ } else {
+ switch (ir) {
+ case InputResponse::OK:
+ inputResultCb_(rc);
+ break;
+ case InputResponse::PENDING_MORE:
+ rc = performDTUPHandshake();
+ if (rc != ResponseCode::OK) {
+ inputResultCb_(rc);
+ }
+ break;
+ case InputResponse::TIMED_OUT:
+ inputResultCb_(rc);
+ break;
+ }
+ }
+ }
+ }
+ std::tie(gotEvent, evt) = evdev.readEvent();
+ }
+ }
+
+ void start() override {
+ auto rc = performDTUPHandshake();
+ if (rc != ResponseCode::OK) {
+ inputResultCb_(rc);
+ }
+ discardEvents_ = false;
+ };
+
+ private:
+ teeui::ResponseCode performDTUPHandshake() {
+ ResponseCode rc;
+ LOG(INFO) << "Start handshake";
+ Nonce nCo;
+ std::tie(rc, nCo) = hsBeginCb_();
+ if (rc != ResponseCode::OK) {
+ LOG(ERROR) << "Failed to begin secure input handshake (" << uint32_t(rc) << ")";
+ return rc;
+ }
+
+ nCi_ = generateNonce();
+ rc =
+ hsFinalizeCb_(*HMac::hmac256(kTestKey, kConfirmationUIHandshakeLabel, nCo, nCi_), nCi_);
+
+ if (rc != ResponseCode::OK) {
+ LOG(ERROR) << "Failed to finalize secure input handshake (" << uint32_t(rc) << ")";
+ return rc;
+ }
+ return ResponseCode::OK;
+ }
+
+ HsBeginCb hsBeginCb_;
+ HsFinalizeCb hsFinalizeCb_;
+ DeliverEventCb deliverEventCb_;
+ InputResultCb inputResultCb_;
+
+ std::atomic_bool discardEvents_;
+ Nonce nCi_;
+};
+
+namespace secure_input {
+
+std::shared_ptr<SecureInput> createSecureInput(SecureInput::HsBeginCb hsBeginCb,
+ SecureInput::HsFinalizeCb hsFinalizeCb,
+ SecureInput::DeliverEventCb deliverEventCb,
+ SecureInput::InputResultCb inputResultCb) {
+ return std::make_shared<NotSoSecureInput>(hsBeginCb, hsFinalizeCb, deliverEventCb,
+ inputResultCb);
+}
+
+} // namespace secure_input