| /* |
| * 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 here |
| * 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 |