/* * Copyright 2019, 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 "secure_input_tracker.h" #include "trusty_operation.h" #include #include #include #include #include #include #include #define TLOG_TAG "confirmationui" using namespace secure_input; using teeui::Array; using teeui::AuthTokenKey; using teeui::bytesCast; using teeui::HMac; using teeui::optional; using teeui::ResponseCode; inline auto mtsNow() { return monotonic_time_stamper::now(); } static optional getNonce() { Nonce result; if (trusty_rng_secure_rand(result.data(), result.size()) == NO_ERROR) { return result; } else { return {}; } } ResponseCode InputTracker::newSession() { state_ = InputState::Fresh; event_ = InputEvent::None; auto now = mtsNow(); // Initialize all timestamps to something sane. for (auto& t : timestamps_) { t = now; } return ResponseCode::OK; } // input Handshake std::tuple InputTracker::beginHandshake() { ResponseCode rc; auto now = mtsNow(); if ((state_ == InputState::Fresh && (now - timestamps_[uint32_t(InputState::Fresh)]) >= kUserPreInputGracePeriodMillis) || state_ == InputState::InputDeliveredMorePending) { auto nonce = getNonce(); if (nonce) { input_nonce_ = *nonce; TLOGD("%u", uint32_t(state_)); state_ = InputState::HandshakeOutstanding; timestamps_[uint32_t(state_)] = now; return {ResponseCode::OK, input_nonce_}; } else { rc = ResponseCode::SystemError; } } rc = ResponseCode::Unexpected; state_ = InputState::None; return {rc, {}}; } // input Handshake finalize ResponseCode InputTracker::finalizeHandshake(const Nonce& nCi, const Signature& signature, const AuthTokenKey& key) { ResponseCode rc; if (state_ == InputState::HandshakeOutstanding) { using HMacer = HMac; auto hmac = HMacer::hmac256(key, kConfirmationUIHandshakeLabel, input_nonce_, nCi); if (hmac) { if (*hmac == signature) { // we can forget the nCo and input_nonce now becomes nCi input_nonce_ = nCi; state_ = InputState::HandshakeComplete; timestamps_[uint32_t(state_)] = mtsNow(); TLOGD("%u", uint32_t(state_)); return ResponseCode::OK; } else { rc = ResponseCode::Aborted; } } else { rc = ResponseCode::SystemError; } } else { rc = ResponseCode::Unexpected; } state_ = InputState::None; return rc; } // process input event std::tuple InputTracker::processInputEvent( DTupKeyEvent keyEvent, const Signature& signature, const AuthTokenKey& key) { std::tuple result = {ResponseCode::OK, InputResponse::TIMED_OUT}; ResponseCode& rc = std::get<0>(result); InputResponse& ir = std::get<1>(result); using HMacer = HMac; auto now = mtsNow(); if (state_ != InputState::HandshakeComplete) { state_ = InputState::None; rc = ResponseCode::Unexpected; return result; } uint32_t keyEventBE = htobe32(static_cast(keyEvent)); auto hmac = HMacer::hmac256(key, kConfirmationUIEventLabel, bytesCast(keyEventBE), input_nonce_); if (!hmac) { state_ = InputState::None; rc = ResponseCode::SystemError; return result; } if (!(*hmac == signature)) { state_ = InputState::None; rc = ResponseCode::Aborted; TLOGE("signature on input event did not check out"); return result; } switch (keyEvent) { // fall through intended case DTupKeyEvent::VOL_DOWN: case DTupKeyEvent::VOL_UP: event_ = InputEvent::UserCancel; state_ = InputState::InputDeliveredFinal; ir = InputResponse::OK; break; case DTupKeyEvent::PWR: if (state_ == InputState::HandshakeComplete && now - timestamps_[uint32_t( InputState::InputDeliveredMorePending)] <= kUserDoupleClickTimeoutMillis) { state_ = InputState::InputDeliveredFinal; ir = InputResponse::OK; event_ = InputEvent::UserConfirm; } else { state_ = InputState::InputDeliveredMorePending; ir = InputResponse::PENDING_MORE; } break; case DTupKeyEvent::RESERVED: default: TLOGW("got RESERVED event"); rc = ResponseCode::Aborted; state_ = InputState::None; return result; } timestamps_[uint32_t(state_)] = now; TLOGD("%u", uint32_t(state_)); return result; } ResponseCode InputTracker::fetchInputEvent() { if (state_ == InputState::InputDeliveredFinal) { state_ = InputState::InputFetched; if (event_ == InputEvent::UserConfirm) return ResponseCode::OK; else return ResponseCode::Canceled; } else { TLOGD("%u", uint32_t(state_)); state_ = InputState::None; return ResponseCode::Unexpected; } } ResponseCode InputTracker::reportVerifiedInput(InputEvent event) { auto now = mtsNow(); if (state_ == InputState::Fresh && (now - timestamps_[uint32_t(InputState::Fresh)]) >= kUserPreInputGracePeriodMillis) { state_ = InputState::InputDeliveredFinal; event_ = event; } return ResponseCode::OK; }