blob: 3d9a2d6e50bc564055e465a9b3669ab146d67c3a [file] [log] [blame]
/*
* 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