blob: 6676793a09fdc5fdb94f0b9dc33ebc3e0d8afb07 [file] [log] [blame]
/*
* 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 <secure_input/secure_input_proto.h>
#include <lib/rng/trusty_rng.h>
#include <inttypes.h>
#include <stdio.h>
#include <teeui/utils.h>
#include <trusty_log.h>
#include <uapi/err.h>
#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<Nonce> 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<ResponseCode, Nonce> 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<TrustyOperation>;
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<ResponseCode, InputResponse> InputTracker::processInputEvent(
DTupKeyEvent keyEvent,
const Signature& signature,
const AuthTokenKey& key) {
std::tuple<ResponseCode, InputResponse> result = {ResponseCode::OK,
InputResponse::TIMED_OUT};
ResponseCode& rc = std::get<0>(result);
InputResponse& ir = std::get<1>(result);
using HMacer = HMac<TrustyOperation>;
auto now = mtsNow();
if (state_ != InputState::HandshakeComplete) {
state_ = InputState::None;
rc = ResponseCode::Unexpected;
return result;
}
uint32_t keyEventBE = htobe32(static_cast<uint32_t>(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;
}