You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
208 lines
7.6 KiB
208 lines
7.6 KiB
/*
|
|
* 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
|