Merge "First working version of the confirmationui HAL service"
am: a78d0cb735
Change-Id: I3444e33493da7d3a205e2a377a2e1510440e808e
This commit is contained in:
commit
3bfe324c95
12 changed files with 1343 additions and 0 deletions
10
trusty/confirmationui/.clang-format
Normal file
10
trusty/confirmationui/.clang-format
Normal file
|
@ -0,0 +1,10 @@
|
|||
BasedOnStyle: LLVM
|
||||
IndentWidth: 4
|
||||
UseTab: Never
|
||||
BreakBeforeBraces: Attach
|
||||
AllowShortFunctionsOnASingleLine: Inline
|
||||
AllowShortIfStatementsOnASingleLine: true
|
||||
IndentCaseLabels: false
|
||||
ColumnLimit: 100
|
||||
PointerBindsToType: true
|
||||
SpacesBeforeTrailingComments: 2
|
95
trusty/confirmationui/Android.bp
Normal file
95
trusty/confirmationui/Android.bp
Normal file
|
@ -0,0 +1,95 @@
|
|||
// Copyright (C) 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.
|
||||
//
|
||||
|
||||
// WARNING: Everything listed here will be built on ALL platforms,
|
||||
// including x86, the emulator, and the SDK. Modules must be uniquely
|
||||
// named (liblights.panda), and must build everywhere, or limit themselves
|
||||
// to only building on ARM if they include assembly. Individual makefiles
|
||||
// are responsible for having their own logic, for fine-grained control.
|
||||
|
||||
cc_binary {
|
||||
name: "android.hardware.confirmationui@1.0-service.trusty",
|
||||
relative_install_path: "hw",
|
||||
vendor: true,
|
||||
shared_libs: [
|
||||
"android.hardware.confirmationui@1.0",
|
||||
"android.hardware.confirmationui.not-so-secure-input",
|
||||
"android.hardware.confirmationui@1.0-lib.trusty",
|
||||
"libbase",
|
||||
"libhidlbase",
|
||||
"libutils",
|
||||
],
|
||||
|
||||
init_rc: ["android.hardware.confirmationui@1.0-service.trusty.rc"],
|
||||
|
||||
vintf_fragments: ["android.hardware.confirmationui@1.0-service.trusty.xml"],
|
||||
|
||||
srcs: [
|
||||
"service.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-DTEEUI_USE_STD_VECTOR",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "android.hardware.confirmationui@1.0-lib.trusty",
|
||||
vendor: true,
|
||||
shared_libs: [
|
||||
"android.hardware.confirmationui@1.0",
|
||||
"android.hardware.keymaster@4.0",
|
||||
"libbase",
|
||||
"libhidlbase",
|
||||
"libteeui_hal_support",
|
||||
"libtrusty",
|
||||
"libutils",
|
||||
],
|
||||
|
||||
export_include_dirs: ["include"],
|
||||
|
||||
srcs: [
|
||||
"TrustyApp.cpp",
|
||||
"TrustyConfirmationUI.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-DTEEUI_USE_STD_VECTOR",
|
||||
],
|
||||
}
|
||||
|
||||
cc_library {
|
||||
name: "android.hardware.confirmationui.not-so-secure-input",
|
||||
vendor: true,
|
||||
shared_libs: [
|
||||
"libbase",
|
||||
"libcrypto",
|
||||
"libteeui_hal_support",
|
||||
],
|
||||
|
||||
srcs: [
|
||||
"NotSoSecureInput.cpp",
|
||||
],
|
||||
|
||||
cflags: [
|
||||
"-Wall",
|
||||
"-Werror",
|
||||
"-DTEEUI_USE_STD_VECTOR",
|
||||
],
|
||||
}
|
207
trusty/confirmationui/NotSoSecureInput.cpp
Normal file
207
trusty/confirmationui/NotSoSecureInput.cpp
Normal file
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* 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
|
20
trusty/confirmationui/README
Normal file
20
trusty/confirmationui/README
Normal file
|
@ -0,0 +1,20 @@
|
|||
## Secure UI Architecture
|
||||
|
||||
To implement confirmationui a secure UI architecture is required. This entails a way
|
||||
to display the confirmation dialog driven by a reduced trusted computing base, typically
|
||||
a trusted execution environment (TEE), without having to rely on Linux and the Android
|
||||
system for integrity and authenticity of input events. This implementation provides
|
||||
neither. But it provides most of the functionlity required to run a full Android Protected
|
||||
Confirmation feature when integrated into a secure UI architecture.
|
||||
|
||||
## Secure input (NotSoSecureInput)
|
||||
|
||||
This implementation does not provide any security guaranties.
|
||||
The input method (NotSoSecureInput) runs a cryptographic protocols that is
|
||||
sufficiently secure IFF the end point is implemented on a trustworthy
|
||||
secure input device. But since the endpoint is currently in the HAL
|
||||
service itself this implementation is not secure.
|
||||
|
||||
NOTE that a secure input device end point needs a good source of entropy
|
||||
for generating nonces. The current implementation (NotSoSecureInput.cpp#generateNonce)
|
||||
uses a constant nonce.
|
156
trusty/confirmationui/TrustyApp.cpp
Normal file
156
trusty/confirmationui/TrustyApp.cpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
/*
|
||||
* 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 "TrustyApp.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <sys/uio.h>
|
||||
#include <trusty/tipc.h>
|
||||
|
||||
namespace android {
|
||||
namespace trusty {
|
||||
|
||||
// 0x1000 is the message buffer size but we need to leave some space for a protocol header.
|
||||
// This assures that packets can always be read/written in one read/write operation.
|
||||
static constexpr const uint32_t kPacketSize = 0x1000 - 32;
|
||||
|
||||
enum class PacketType : uint32_t {
|
||||
SND,
|
||||
RCV,
|
||||
ACK,
|
||||
};
|
||||
|
||||
struct PacketHeader {
|
||||
PacketType type;
|
||||
uint32_t remaining;
|
||||
};
|
||||
|
||||
const char* toString(PacketType t) {
|
||||
switch (t) {
|
||||
case PacketType::SND:
|
||||
return "SND";
|
||||
case PacketType::RCV:
|
||||
return "RCV";
|
||||
case PacketType::ACK:
|
||||
return "ACK";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
|
||||
static constexpr const uint32_t kHeaderSize = sizeof(PacketHeader);
|
||||
static constexpr const uint32_t kPayloadSize = kPacketSize - kHeaderSize;
|
||||
|
||||
ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
|
||||
uint8_t* iend) {
|
||||
while (obegin != oend) {
|
||||
PacketHeader header = {
|
||||
.type = PacketType::SND,
|
||||
.remaining = uint32_t(oend - obegin),
|
||||
};
|
||||
uint32_t body_size = std::min(kPayloadSize, header.remaining);
|
||||
iovec iov[] = {
|
||||
{
|
||||
.iov_base = &header,
|
||||
.iov_len = kHeaderSize,
|
||||
},
|
||||
{
|
||||
.iov_base = const_cast<uint8_t*>(obegin),
|
||||
.iov_len = body_size,
|
||||
},
|
||||
};
|
||||
int rc = writev(handle, iov, 2);
|
||||
if (!rc) {
|
||||
PLOG(ERROR) << "Error sending SND message. " << rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
obegin += body_size;
|
||||
|
||||
rc = read(handle, &header, kHeaderSize);
|
||||
if (!rc) {
|
||||
PLOG(ERROR) << "Error reading ACK. " << rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (header.type != PacketType::ACK || header.remaining != oend - obegin) {
|
||||
LOG(ERROR) << "malformed ACK";
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
ssize_t remaining = 0;
|
||||
auto begin = ibegin;
|
||||
do {
|
||||
PacketHeader header = {
|
||||
.type = PacketType::RCV,
|
||||
.remaining = 0,
|
||||
};
|
||||
|
||||
iovec iov[] = {
|
||||
{
|
||||
.iov_base = &header,
|
||||
.iov_len = kHeaderSize,
|
||||
},
|
||||
{
|
||||
.iov_base = begin,
|
||||
.iov_len = uint32_t(iend - begin),
|
||||
},
|
||||
};
|
||||
|
||||
ssize_t rc = writev(handle, iov, 1);
|
||||
if (!rc) {
|
||||
PLOG(ERROR) << "Error sending RCV message. " << rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = readv(handle, iov, 2);
|
||||
if (rc < 0) {
|
||||
PLOG(ERROR) << "Error reading response. " << rc;
|
||||
return rc;
|
||||
}
|
||||
|
||||
uint32_t body_size = std::min(kPayloadSize, header.remaining);
|
||||
if (body_size != rc - kHeaderSize) {
|
||||
LOG(ERROR) << "Unexpected amount of data: " << rc;
|
||||
return -1;
|
||||
}
|
||||
|
||||
remaining = header.remaining - body_size;
|
||||
begin += body_size;
|
||||
} while (remaining);
|
||||
|
||||
return begin - ibegin;
|
||||
}
|
||||
|
||||
TrustyApp::TrustyApp(const std::string& path, const std::string& appname)
|
||||
: handle_(kInvalidHandle) {
|
||||
handle_ = tipc_connect(path.c_str(), appname.c_str());
|
||||
if (handle_ == kInvalidHandle) {
|
||||
LOG(ERROR) << AT << "failed to connect to Trusty TA \"" << appname << "\" using dev:"
|
||||
<< "\"" << path << "\"";
|
||||
}
|
||||
LOG(INFO) << AT << "succeeded to connect to Trusty TA \"" << appname << "\"";
|
||||
}
|
||||
TrustyApp::~TrustyApp() {
|
||||
if (handle_ != kInvalidHandle) {
|
||||
tipc_close(handle_);
|
||||
}
|
||||
LOG(INFO) << "Done shutting down TrustyApp";
|
||||
}
|
||||
|
||||
} // namespace trusty
|
||||
} // namespace android
|
155
trusty/confirmationui/TrustyApp.h
Normal file
155
trusty/confirmationui/TrustyApp.h
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <errno.h>
|
||||
#include <poll.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/eventfd.h>
|
||||
#include <sys/stat.h>
|
||||
#include <teeui/msg_formatting.h>
|
||||
#include <trusty/tipc.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <future>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#define AT __FILE__ ":" << __LINE__ << ": "
|
||||
|
||||
namespace android {
|
||||
namespace trusty {
|
||||
|
||||
using ::teeui::Message;
|
||||
using ::teeui::msg2tuple_t;
|
||||
using ::teeui::ReadStream;
|
||||
using ::teeui::WriteStream;
|
||||
|
||||
#ifndef TEEUI_USE_STD_VECTOR
|
||||
/*
|
||||
* TEEUI_USE_STD_VECTOR makes certain wire types like teeui::MsgString and
|
||||
* teeui::MsgVector be aliases for std::vector. This is required for thread safe
|
||||
* message serialization. Always compile this with -DTEEUI_USE_STD_VECTOR set in
|
||||
* CFLAGS of the HAL service.
|
||||
*/
|
||||
#error "Must be compiled with -DTEEUI_USE_STD_VECTOR."
|
||||
#endif
|
||||
|
||||
enum class TrustyAppError : int32_t {
|
||||
OK,
|
||||
ERROR = -1,
|
||||
MSG_TOO_LONG = -2,
|
||||
};
|
||||
|
||||
/*
|
||||
* There is a hard limitation of 0x1800 bytes for the to-be-signed message size. The protocol
|
||||
* overhead is limited, so that 0x2000 is a buffer size that will be sufficient in any benign
|
||||
* mode of operation.
|
||||
*/
|
||||
static constexpr const size_t kSendBufferSize = 0x2000;
|
||||
|
||||
ssize_t TrustyRpc(int handle, const uint8_t* obegin, const uint8_t* oend, uint8_t* ibegin,
|
||||
uint8_t* iend);
|
||||
|
||||
class TrustyApp {
|
||||
private:
|
||||
int handle_;
|
||||
static constexpr const int kInvalidHandle = -1;
|
||||
/*
|
||||
* This mutex serializes communication with the trusted app, not handle_.
|
||||
* Calling issueCmd during construction or deletion is undefined behavior.
|
||||
*/
|
||||
std::mutex mutex_;
|
||||
|
||||
public:
|
||||
TrustyApp(const std::string& path, const std::string& appname);
|
||||
~TrustyApp();
|
||||
|
||||
template <typename Request, typename Response, typename... T>
|
||||
std::tuple<TrustyAppError, msg2tuple_t<Response>> issueCmd(const T&... args) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (handle_ == kInvalidHandle) {
|
||||
LOG(ERROR) << "TrustyApp not connected";
|
||||
return {TrustyAppError::ERROR, {}};
|
||||
}
|
||||
|
||||
uint8_t buffer[kSendBufferSize];
|
||||
WriteStream out(buffer);
|
||||
|
||||
out = write(Request(), out, args...);
|
||||
if (!out) {
|
||||
LOG(ERROR) << AT << "send command failed: message formatting";
|
||||
return {TrustyAppError::MSG_TOO_LONG, {}};
|
||||
}
|
||||
|
||||
auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
|
||||
&buffer[kSendBufferSize]);
|
||||
if (rc < 0) return {TrustyAppError::ERROR, {}};
|
||||
|
||||
ReadStream in(&buffer[0], rc);
|
||||
auto result = read(Response(), in);
|
||||
if (!std::get<0>(result)) {
|
||||
LOG(ERROR) << "send command failed: message parsing";
|
||||
return {TrustyAppError::ERROR, {}};
|
||||
}
|
||||
|
||||
return {std::get<0>(result) ? TrustyAppError::OK : TrustyAppError::ERROR,
|
||||
tuple_tail(std::move(result))};
|
||||
}
|
||||
|
||||
template <typename Request, typename... T> TrustyAppError issueCmd(const T&... args) {
|
||||
std::lock_guard<std::mutex> lock(mutex_);
|
||||
|
||||
if (handle_ == kInvalidHandle) {
|
||||
LOG(ERROR) << "TrustyApp not connected";
|
||||
return TrustyAppError::ERROR;
|
||||
}
|
||||
|
||||
uint8_t buffer[kSendBufferSize];
|
||||
WriteStream out(buffer);
|
||||
|
||||
out = write(Request(), out, args...);
|
||||
if (!out) {
|
||||
LOG(ERROR) << AT << "send command failed: message formatting";
|
||||
return TrustyAppError::MSG_TOO_LONG;
|
||||
}
|
||||
|
||||
auto rc = TrustyRpc(handle_, &buffer[0], const_cast<const uint8_t*>(out.pos()), &buffer[0],
|
||||
&buffer[kSendBufferSize]);
|
||||
if (rc < 0) {
|
||||
LOG(ERROR) << "send command failed: " << strerror(errno) << " (" << errno << ")";
|
||||
return TrustyAppError::ERROR;
|
||||
}
|
||||
|
||||
if (rc > 0) {
|
||||
LOG(ERROR) << "Unexpected non zero length response";
|
||||
return TrustyAppError::ERROR;
|
||||
}
|
||||
return TrustyAppError::OK;
|
||||
}
|
||||
|
||||
operator bool() const { return handle_ != kInvalidHandle; }
|
||||
};
|
||||
|
||||
} // namespace trusty
|
||||
} // namespace android
|
513
trusty/confirmationui/TrustyConfirmationUI.cpp
Normal file
513
trusty/confirmationui/TrustyConfirmationUI.cpp
Normal file
|
@ -0,0 +1,513 @@
|
|||
/*
|
||||
*
|
||||
* 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 "TrustyConfirmationUI.h"
|
||||
|
||||
#include <android-base/logging.h>
|
||||
#include <android/hardware/confirmationui/1.0/types.h>
|
||||
#include <android/hardware/keymaster/4.0/types.h>
|
||||
#include <fcntl.h>
|
||||
#include <linux/input.h>
|
||||
#include <poll.h>
|
||||
#include <pthread.h>
|
||||
#include <secure_input/evdev.h>
|
||||
#include <secure_input/secure_input_device.h>
|
||||
#include <secure_input/secure_input_proto.h>
|
||||
#include <signal.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <teeui/msg_formatting.h>
|
||||
#include <teeui/utils.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <thread>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace confirmationui {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using namespace secure_input;
|
||||
|
||||
using ::android::trusty::TrustyAppError;
|
||||
|
||||
using ::teeui::AbortMsg;
|
||||
using ::teeui::DeliverTestCommandMessage;
|
||||
using ::teeui::DeliverTestCommandResponse;
|
||||
using ::teeui::FetchConfirmationResult;
|
||||
using ::teeui::MsgString;
|
||||
using ::teeui::MsgVector;
|
||||
using ::teeui::PromptUserConfirmationMsg;
|
||||
using ::teeui::PromptUserConfirmationResponse;
|
||||
using ::teeui::ResultMsg;
|
||||
|
||||
using ::secure_input::createSecureInput;
|
||||
|
||||
using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
|
||||
|
||||
using ::std::tie;
|
||||
|
||||
using TeeuiRc = ::teeui::ResponseCode;
|
||||
|
||||
constexpr const char kTrustyDeviceName[] = "/dev/trusty-ipc-dev0";
|
||||
constexpr const char kConfirmationuiAppName[] = "com.android.trusty.confirmationui";
|
||||
|
||||
namespace {
|
||||
|
||||
class Finalize {
|
||||
private:
|
||||
std::function<void()> f_;
|
||||
|
||||
public:
|
||||
Finalize(std::function<void()> f) : f_(f) {}
|
||||
~Finalize() {
|
||||
if (f_) f_();
|
||||
}
|
||||
void release() { f_ = {}; }
|
||||
};
|
||||
|
||||
ResponseCode convertRc(TeeuiRc trc) {
|
||||
static_assert(
|
||||
uint32_t(TeeuiRc::OK) == uint32_t(ResponseCode::OK) &&
|
||||
uint32_t(TeeuiRc::Canceled) == uint32_t(ResponseCode::Canceled) &&
|
||||
uint32_t(TeeuiRc::Aborted) == uint32_t(ResponseCode::Aborted) &&
|
||||
uint32_t(TeeuiRc::OperationPending) == uint32_t(ResponseCode::OperationPending) &&
|
||||
uint32_t(TeeuiRc::Ignored) == uint32_t(ResponseCode::Ignored) &&
|
||||
uint32_t(TeeuiRc::SystemError) == uint32_t(ResponseCode::SystemError) &&
|
||||
uint32_t(TeeuiRc::Unimplemented) == uint32_t(ResponseCode::Unimplemented) &&
|
||||
uint32_t(TeeuiRc::Unexpected) == uint32_t(ResponseCode::Unexpected) &&
|
||||
uint32_t(TeeuiRc::UIError) == uint32_t(ResponseCode::UIError) &&
|
||||
uint32_t(TeeuiRc::UIErrorMissingGlyph) == uint32_t(ResponseCode::UIErrorMissingGlyph) &&
|
||||
uint32_t(TeeuiRc::UIErrorMessageTooLong) ==
|
||||
uint32_t(ResponseCode::UIErrorMessageTooLong) &&
|
||||
uint32_t(TeeuiRc::UIErrorMalformedUTF8Encoding) ==
|
||||
uint32_t(ResponseCode::UIErrorMalformedUTF8Encoding),
|
||||
"teeui::ResponseCode and "
|
||||
"::android::hardware::confirmationui::V1_0::Responsecude are out of "
|
||||
"sync");
|
||||
return ResponseCode(trc);
|
||||
}
|
||||
|
||||
teeui::UIOption convertUIOption(UIOption uio) {
|
||||
static_assert(uint32_t(UIOption::AccessibilityInverted) ==
|
||||
uint32_t(teeui::UIOption::AccessibilityInverted) &&
|
||||
uint32_t(UIOption::AccessibilityMagnified) ==
|
||||
uint32_t(teeui::UIOption::AccessibilityMagnified),
|
||||
"teeui::UIOPtion and ::android::hardware::confirmationui::V1_0::UIOption "
|
||||
"anre out of sync");
|
||||
return teeui::UIOption(uio);
|
||||
}
|
||||
|
||||
inline MsgString hidl2MsgString(const hidl_string& s) {
|
||||
return {s.c_str(), s.c_str() + s.size()};
|
||||
}
|
||||
template <typename T> inline MsgVector<T> hidl2MsgVector(const hidl_vec<T>& v) {
|
||||
return {v};
|
||||
}
|
||||
|
||||
inline MsgVector<teeui::UIOption> hidl2MsgVector(const hidl_vec<UIOption>& v) {
|
||||
MsgVector<teeui::UIOption> result(v.size());
|
||||
for (unsigned int i = 0; i < v.size(); ++i) {
|
||||
result[i] = convertUIOption(v[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
TrustyConfirmationUI::TrustyConfirmationUI()
|
||||
: listener_state_(ListenerState::None), prompt_result_(ResponseCode::Ignored) {}
|
||||
|
||||
TrustyConfirmationUI::~TrustyConfirmationUI() {
|
||||
ListenerState state = listener_state_;
|
||||
if (state == ListenerState::SetupDone || state == ListenerState::Interactive) {
|
||||
abort();
|
||||
}
|
||||
if (state != ListenerState::None) {
|
||||
callback_thread_.join();
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>>
|
||||
TrustyConfirmationUI::promptUserConfirmation_(const MsgString& promptText,
|
||||
const MsgVector<uint8_t>& extraData,
|
||||
const MsgString& locale,
|
||||
const MsgVector<teeui::UIOption>& uiOptions) {
|
||||
std::unique_lock<std::mutex> stateLock(listener_state_lock_);
|
||||
/*
|
||||
* This is the main listener thread function. The listener thread life cycle
|
||||
* is equivalent to the life cycle of a single confirmation request. The life
|
||||
* cycle is devided in four phases.
|
||||
* * The starting phase:
|
||||
* * The Trusted App gets loaded and/or the connection to it gets established.
|
||||
* * A connection to the secure input device is established.
|
||||
* * The prompt is initiated. This sends all information required by the
|
||||
* confirmation dialog to the TA. The dialog is not yet displayed.
|
||||
* * An event loop is created.
|
||||
* * The event loop listens for user input events, fetches them from the
|
||||
* secure input device, and delivers them to the TA.
|
||||
* * All evdev devices are grabbed to give confirmationui exclusive access
|
||||
* to user input.
|
||||
*
|
||||
* Note: During the starting phase the hwbinder service thread is blocked and
|
||||
* waiting for possible Errors. If the setup phase concludes sucessfully, the
|
||||
* hwbinder service thread gets unblocked and returns successfully. Errors
|
||||
* that occur after the first phase are delivered by callback interface.
|
||||
*
|
||||
* * The 2nd phase - non interactive phase
|
||||
* * The event loop thread is started.
|
||||
* * After a grace period:
|
||||
* * A handshake between the secure input device SecureInput and the TA
|
||||
* is performed.
|
||||
* * The input event handler are armed to process user input events.
|
||||
*
|
||||
* * The 3rd phase - interactive phase
|
||||
* * We wait to any external event
|
||||
* * Abort
|
||||
* * Secure user input asserted
|
||||
* * Secure input delivered (for non interactive VTS testing)
|
||||
* * The result is fetched from the TA.
|
||||
*
|
||||
* * The 4th phase - cleanup
|
||||
* The cleanup phase is given by the scope of automatic variables created
|
||||
* in this function. The cleanup commences in reverse order of their creation.
|
||||
* Here is a list of more complex items in the order in which they go out of
|
||||
* scope
|
||||
* * finalizeSecureTouch - signals and joins the secure touch thread.
|
||||
* * eventloop - signals and joins the event loop thread. The event
|
||||
* handlers also own all EventDev instances which ungrab the event devices.
|
||||
* When the eventloop goes out of scope the EventDevs get destroyed
|
||||
* relinquishing the exclusive hold on the event devices.
|
||||
* * finalizeConfirmationPrompt - calls abort on the TA, making sure a
|
||||
* pending operation gets canceled. If the prompt concluded successfully this
|
||||
* is a spurious call but semantically a no op.
|
||||
* * secureInput - shuts down the connection to the secure input device
|
||||
* SecureInput.
|
||||
* * app - disconnects the TA. Since app is a shared pointer this may not
|
||||
* unload the app here. It is possible that more instances of the shared
|
||||
* pointer are held in TrustyConfirmationUI::deliverSecureInputEvent and
|
||||
* TrustyConfirmationUI::abort. But these instances are extremely short lived
|
||||
* and it is safe if they are destroyed by either.
|
||||
* * stateLock - unlocks the listener_state_lock_ if it happens to be held
|
||||
* at the time of return.
|
||||
*/
|
||||
|
||||
std::tuple<TeeuiRc, MsgVector<uint8_t>, MsgVector<uint8_t>> result;
|
||||
TeeuiRc& rc = std::get<TeeuiRc>(result);
|
||||
rc = TeeuiRc::SystemError;
|
||||
|
||||
listener_state_ = ListenerState::Starting;
|
||||
|
||||
auto app = std::make_shared<TrustyApp>(kTrustyDeviceName, kConfirmationuiAppName);
|
||||
if (!app) return result; // TeeuiRc::SystemError
|
||||
|
||||
app_ = app;
|
||||
|
||||
auto hsBegin = [&]() -> std::tuple<TeeuiRc, Nonce> {
|
||||
auto [error, result] =
|
||||
app->issueCmd<secure_input::InputHandshake, secure_input::InputHandshakeResponse>();
|
||||
auto& [rc, nCo] = result;
|
||||
|
||||
if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
|
||||
LOG(ERROR) << "Failed to begin secure input handshake (" << int32_t(error) << "/"
|
||||
<< uint32_t(rc) << ")";
|
||||
rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
auto hsFinalize = [&](const Signature& sig, const Nonce& nCi) -> TeeuiRc {
|
||||
auto [error, finalizeResponse] =
|
||||
app->issueCmd<FinalizeInputSessionHandshake, FinalizeInputSessionHandshakeResponse>(
|
||||
nCi, sig);
|
||||
auto& [rc] = finalizeResponse;
|
||||
if (error != TrustyAppError::OK || rc != TeeuiRc::OK) {
|
||||
LOG(ERROR) << "Failed to finalize secure input handshake (" << int32_t(error) << "/"
|
||||
<< uint32_t(rc) << ")";
|
||||
rc = error != TrustyAppError::OK ? TeeuiRc::SystemError : rc;
|
||||
}
|
||||
return rc;
|
||||
};
|
||||
|
||||
auto deliverInput = [&](DTupKeyEvent event,
|
||||
const Signature& sig) -> std::tuple<TeeuiRc, InputResponse> {
|
||||
auto [error, result] =
|
||||
app->issueCmd<DeliverInputEvent, DeliverInputEventResponse>(event, sig);
|
||||
auto& [rc, ir] = result;
|
||||
if (error != TrustyAppError::OK) {
|
||||
LOG(ERROR) << "Failed to deliver input command";
|
||||
rc = TeeuiRc::SystemError;
|
||||
}
|
||||
return result;
|
||||
};
|
||||
|
||||
std::atomic<TeeuiRc> eventRC = TeeuiRc::OperationPending;
|
||||
auto inputResult = [&](TeeuiRc rc) {
|
||||
TeeuiRc expected = TeeuiRc::OperationPending;
|
||||
if (eventRC.compare_exchange_strong(expected, rc)) {
|
||||
listener_state_condv_.notify_all();
|
||||
}
|
||||
};
|
||||
|
||||
// create Secure Input device.
|
||||
auto secureInput = createSecureInput(hsBegin, hsFinalize, deliverInput, inputResult);
|
||||
if (!secureInput || !(*secureInput)) {
|
||||
LOG(ERROR) << "Failed to open secure input device";
|
||||
return result; // TeeuiRc::SystemError;
|
||||
}
|
||||
|
||||
Finalize finalizeConfirmationPrompt([app] {
|
||||
LOG(INFO) << "Calling abort for cleanup";
|
||||
app->issueCmd<AbortMsg>();
|
||||
});
|
||||
|
||||
// initiate prompt
|
||||
LOG(INFO) << "Initiating prompt";
|
||||
TrustyAppError error;
|
||||
auto initResponse = std::tie(rc);
|
||||
std::tie(error, initResponse) =
|
||||
app->issueCmd<PromptUserConfirmationMsg, PromptUserConfirmationResponse>(
|
||||
promptText, extraData, locale, uiOptions);
|
||||
if (error == TrustyAppError::MSG_TOO_LONG) {
|
||||
LOG(ERROR) << "PromptUserConfirmationMsg failed: message too long";
|
||||
rc = TeeuiRc::UIErrorMessageTooLong;
|
||||
return result;
|
||||
} else if (error != TrustyAppError::OK) {
|
||||
LOG(ERROR) << "PromptUserConfirmationMsg failed: " << int32_t(error);
|
||||
return result; // TeeuiRc::SystemError;
|
||||
}
|
||||
if (rc != TeeuiRc::OK) {
|
||||
LOG(ERROR) << "PromptUserConfirmationMsg failed: " << uint32_t(rc);
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Grabbing event devices";
|
||||
EventLoop eventloop;
|
||||
bool grabbed =
|
||||
grabAllEvDevsAndRegisterCallbacks(&eventloop, [&](short flags, const EventDev& evDev) {
|
||||
if (!(flags & POLLIN)) return;
|
||||
secureInput->handleEvent(evDev);
|
||||
});
|
||||
|
||||
if (!grabbed) {
|
||||
rc = TeeuiRc::SystemError;
|
||||
return result;
|
||||
}
|
||||
|
||||
abort_called_ = false;
|
||||
secureInputDelivered_ = false;
|
||||
|
||||
// ############################## Start 2nd Phase #############################################
|
||||
listener_state_ = ListenerState::SetupDone;
|
||||
stateLock.unlock();
|
||||
listener_state_condv_.notify_all();
|
||||
|
||||
if (!eventloop.start()) {
|
||||
rc = TeeuiRc::SystemError;
|
||||
return result;
|
||||
}
|
||||
|
||||
stateLock.lock();
|
||||
|
||||
LOG(INFO) << "going to sleep for the grace period";
|
||||
auto then = std::chrono::system_clock::now() +
|
||||
std::chrono::milliseconds(kUserPreInputGracePeriodMillis) +
|
||||
std::chrono::microseconds(50);
|
||||
listener_state_condv_.wait_until(stateLock, then, [&]() { return abort_called_; });
|
||||
LOG(INFO) << "waking up";
|
||||
|
||||
if (abort_called_) {
|
||||
LOG(ERROR) << "Abort called";
|
||||
result = {TeeuiRc::Aborted, {}, {}};
|
||||
return result;
|
||||
}
|
||||
|
||||
LOG(INFO) << "Arming event poller";
|
||||
// tell the event poller to act on received input events from now on.
|
||||
secureInput->start();
|
||||
|
||||
// ############################## Start 3rd Phase - interactive phase #########################
|
||||
LOG(INFO) << "Transition to Interactive";
|
||||
listener_state_ = ListenerState::Interactive;
|
||||
stateLock.unlock();
|
||||
listener_state_condv_.notify_all();
|
||||
|
||||
stateLock.lock();
|
||||
listener_state_condv_.wait(stateLock, [&]() {
|
||||
return eventRC != TeeuiRc::OperationPending || abort_called_ || secureInputDelivered_;
|
||||
});
|
||||
LOG(INFO) << "Listener waking up";
|
||||
if (abort_called_) {
|
||||
LOG(ERROR) << "Abort called";
|
||||
result = {TeeuiRc::Aborted, {}, {}};
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!secureInputDelivered_) {
|
||||
if (eventRC != TeeuiRc::OK) {
|
||||
LOG(ERROR) << "Bad input response";
|
||||
result = {eventRC, {}, {}};
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
stateLock.unlock();
|
||||
|
||||
LOG(INFO) << "Fetching Result";
|
||||
std::tie(error, result) = app->issueCmd<FetchConfirmationResult, ResultMsg>();
|
||||
LOG(INFO) << "Result yields " << int32_t(error) << "/" << uint32_t(rc);
|
||||
if (error != TrustyAppError::OK) {
|
||||
result = {TeeuiRc::SystemError, {}, {}};
|
||||
}
|
||||
return result;
|
||||
|
||||
// ############################## Start 4th Phase - cleanup ##################################
|
||||
}
|
||||
|
||||
// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
|
||||
// follow.
|
||||
Return<ResponseCode> TrustyConfirmationUI::promptUserConfirmation(
|
||||
const sp<IConfirmationResultCallback>& resultCB, const hidl_string& promptText,
|
||||
const hidl_vec<uint8_t>& extraData, const hidl_string& locale,
|
||||
const hidl_vec<UIOption>& uiOptions) {
|
||||
std::unique_lock<std::mutex> stateLock(listener_state_lock_, std::defer_lock);
|
||||
if (!stateLock.try_lock()) {
|
||||
return ResponseCode::OperationPending;
|
||||
}
|
||||
switch (listener_state_) {
|
||||
case ListenerState::None:
|
||||
break;
|
||||
case ListenerState::Starting:
|
||||
case ListenerState::SetupDone:
|
||||
case ListenerState::Interactive:
|
||||
return ResponseCode::OperationPending;
|
||||
case ListenerState::Terminating:
|
||||
callback_thread_.join();
|
||||
listener_state_ = ListenerState::None;
|
||||
break;
|
||||
default:
|
||||
return ResponseCode::Unexpected;
|
||||
}
|
||||
|
||||
assert(listener_state_ == ListenerState::None);
|
||||
|
||||
callback_thread_ = std::thread(
|
||||
[this](sp<IConfirmationResultCallback> resultCB, hidl_string promptText,
|
||||
hidl_vec<uint8_t> extraData, hidl_string locale, hidl_vec<UIOption> uiOptions) {
|
||||
auto [trc, msg, token] =
|
||||
promptUserConfirmation_(hidl2MsgString(promptText), hidl2MsgVector(extraData),
|
||||
hidl2MsgString(locale), hidl2MsgVector(uiOptions));
|
||||
bool do_callback = (listener_state_ == ListenerState::Interactive ||
|
||||
listener_state_ == ListenerState::SetupDone) &&
|
||||
resultCB;
|
||||
prompt_result_ = convertRc(trc);
|
||||
listener_state_ = ListenerState::Terminating;
|
||||
if (do_callback) {
|
||||
auto error = resultCB->result(prompt_result_, msg, token);
|
||||
if (!error.isOk()) {
|
||||
LOG(ERROR) << "Result callback failed " << error.description();
|
||||
}
|
||||
} else {
|
||||
listener_state_condv_.notify_all();
|
||||
}
|
||||
},
|
||||
resultCB, promptText, extraData, locale, uiOptions);
|
||||
|
||||
listener_state_condv_.wait(stateLock, [this] {
|
||||
return listener_state_ == ListenerState::SetupDone ||
|
||||
listener_state_ == ListenerState::Interactive ||
|
||||
listener_state_ == ListenerState::Terminating;
|
||||
});
|
||||
if (listener_state_ == ListenerState::Terminating) {
|
||||
callback_thread_.join();
|
||||
listener_state_ = ListenerState::None;
|
||||
return prompt_result_;
|
||||
}
|
||||
return ResponseCode::OK;
|
||||
}
|
||||
|
||||
Return<ResponseCode>
|
||||
TrustyConfirmationUI::deliverSecureInputEvent(const HardwareAuthToken& secureInputToken) {
|
||||
ResponseCode rc = ResponseCode::Ignored;
|
||||
{
|
||||
/*
|
||||
* deliverSecureInputEvent is only used by the VTS test to mock human input. A correct
|
||||
* implementation responds with a mock confirmation token signed with a test key. The
|
||||
* problem is that the non interactive grace period was not formalized in the HAL spec,
|
||||
* so that the VTS test does not account for the grace period. (It probably should.)
|
||||
* This means we can only pass the VTS test if we block until the grace period is over
|
||||
* (SetupDone -> Interactive) before we deliver the input event.
|
||||
*
|
||||
* The true secure input is delivered by a different mechanism and gets ignored -
|
||||
* not queued - until the grace period is over.
|
||||
*
|
||||
*/
|
||||
std::unique_lock<std::mutex> stateLock(listener_state_lock_);
|
||||
listener_state_condv_.wait(stateLock,
|
||||
[this] { return listener_state_ != ListenerState::SetupDone; });
|
||||
|
||||
if (listener_state_ != ListenerState::Interactive) return ResponseCode::Ignored;
|
||||
auto sapp = app_.lock();
|
||||
if (!sapp) return ResponseCode::Ignored;
|
||||
auto [error, response] =
|
||||
sapp->issueCmd<DeliverTestCommandMessage, DeliverTestCommandResponse>(
|
||||
static_cast<teeui::TestModeCommands>(secureInputToken.challenge));
|
||||
if (error != TrustyAppError::OK) return ResponseCode::SystemError;
|
||||
auto& [trc] = response;
|
||||
if (trc != TeeuiRc::Ignored) secureInputDelivered_ = true;
|
||||
rc = convertRc(trc);
|
||||
}
|
||||
if (secureInputDelivered_) listener_state_condv_.notify_all();
|
||||
// VTS test expect an OK response if the event was successfully delivered.
|
||||
// But since the TA returns the callback response now, we have to translate
|
||||
// Canceled into OK. Canceled is only returned if the delivered event canceled
|
||||
// the operation, which means that the event was successfully delivered. Thus
|
||||
// we return OK.
|
||||
if (rc == ResponseCode::Canceled) return ResponseCode::OK;
|
||||
return rc;
|
||||
}
|
||||
|
||||
Return<void> TrustyConfirmationUI::abort() {
|
||||
{
|
||||
std::unique_lock<std::mutex> stateLock(listener_state_lock_);
|
||||
if (listener_state_ == ListenerState::SetupDone ||
|
||||
listener_state_ == ListenerState::Interactive) {
|
||||
auto sapp = app_.lock();
|
||||
if (sapp) sapp->issueCmd<AbortMsg>();
|
||||
abort_called_ = true;
|
||||
}
|
||||
}
|
||||
listener_state_condv_.notify_all();
|
||||
return Void();
|
||||
}
|
||||
|
||||
android::sp<IConfirmationUI> createTrustyConfirmationUI() {
|
||||
return new TrustyConfirmationUI();
|
||||
}
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace confirmationui
|
||||
} // namespace hardware
|
||||
} // namespace android
|
104
trusty/confirmationui/TrustyConfirmationUI.h
Normal file
104
trusty/confirmationui/TrustyConfirmationUI.h
Normal file
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
|
||||
#define ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
|
||||
|
||||
#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
|
||||
#include <android/hardware/keymaster/4.0/types.h>
|
||||
#include <hidl/Status.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <condition_variable>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <teeui/generic_messages.h>
|
||||
#include <thread>
|
||||
|
||||
#include "TrustyApp.h"
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace confirmationui {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
using ::android::sp;
|
||||
using ::android::hardware::hidl_array;
|
||||
using ::android::hardware::hidl_string;
|
||||
using ::android::hardware::hidl_vec;
|
||||
using ::android::hardware::Return;
|
||||
using ::android::hardware::Void;
|
||||
|
||||
using ::android::trusty::TrustyApp;
|
||||
|
||||
class TrustyConfirmationUI : public IConfirmationUI {
|
||||
public:
|
||||
TrustyConfirmationUI();
|
||||
virtual ~TrustyConfirmationUI();
|
||||
// Methods from ::android::hardware::confirmationui::V1_0::IConfirmationUI
|
||||
// follow.
|
||||
Return<ResponseCode> promptUserConfirmation(const sp<IConfirmationResultCallback>& resultCB,
|
||||
const hidl_string& promptText,
|
||||
const hidl_vec<uint8_t>& extraData,
|
||||
const hidl_string& locale,
|
||||
const hidl_vec<UIOption>& uiOptions) override;
|
||||
Return<ResponseCode> deliverSecureInputEvent(
|
||||
const ::android::hardware::keymaster::V4_0::HardwareAuthToken& secureInputToken) override;
|
||||
Return<void> abort() override;
|
||||
|
||||
private:
|
||||
std::weak_ptr<TrustyApp> app_;
|
||||
std::thread callback_thread_;
|
||||
|
||||
enum class ListenerState : uint32_t {
|
||||
None,
|
||||
Starting,
|
||||
SetupDone,
|
||||
Interactive,
|
||||
Terminating,
|
||||
};
|
||||
|
||||
/*
|
||||
* listener_state is protected by listener_state_lock. It makes transitions between phases
|
||||
* of the confirmation operation atomic.
|
||||
* (See TrustyConfirmationUI.cpp#promptUserConfirmation_ for details about operation phases)
|
||||
*/
|
||||
ListenerState listener_state_;
|
||||
/*
|
||||
* abort_called_ is also protected by listener_state_lock_ and indicates that the HAL user
|
||||
* called abort.
|
||||
*/
|
||||
bool abort_called_;
|
||||
std::mutex listener_state_lock_;
|
||||
std::condition_variable listener_state_condv_;
|
||||
ResponseCode prompt_result_;
|
||||
bool secureInputDelivered_;
|
||||
|
||||
std::tuple<teeui::ResponseCode, teeui::MsgVector<uint8_t>, teeui::MsgVector<uint8_t>>
|
||||
promptUserConfirmation_(const teeui::MsgString& promptText,
|
||||
const teeui::MsgVector<uint8_t>& extraData,
|
||||
const teeui::MsgString& locale,
|
||||
const teeui::MsgVector<teeui::UIOption>& uiOptions);
|
||||
};
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace confirmationui
|
||||
} // namespace hardware
|
||||
} // namespace android
|
||||
|
||||
#endif // ANDROID_HARDWARE_CONFIRMATIONUI_V1_0_TRUSTY_CONFIRMATIONUI_H
|
|
@ -0,0 +1,4 @@
|
|||
service confirmationui-1-0 /vendor/bin/hw/android.hardware.confirmationui@1.0-service.trusty
|
||||
class hal
|
||||
user nobody
|
||||
group drmrpc input
|
|
@ -0,0 +1,11 @@
|
|||
<manifest version="1.0" type="device">
|
||||
<hal format="hidl">
|
||||
<name>android.hardware.confirmationui</name>
|
||||
<transport>hwbinder</transport>
|
||||
<version>1.0</version>
|
||||
<interface>
|
||||
<name>IConfirmationUI</name>
|
||||
<instance>default</instance>
|
||||
</interface>
|
||||
</hal>
|
||||
</manifest>
|
33
trusty/confirmationui/include/TrustyConfirmationuiHal.h
Normal file
33
trusty/confirmationui/include/TrustyConfirmationuiHal.h
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
|
||||
|
||||
namespace android {
|
||||
namespace hardware {
|
||||
namespace confirmationui {
|
||||
namespace V1_0 {
|
||||
namespace implementation {
|
||||
|
||||
android::sp<IConfirmationUI> createTrustyConfirmationUI();
|
||||
|
||||
} // namespace implementation
|
||||
} // namespace V1_0
|
||||
} // namespace confirmationui
|
||||
} // namespace hardware
|
||||
} // namespace android
|
35
trusty/confirmationui/service.cpp
Normal file
35
trusty/confirmationui/service.cpp
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* 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 <hidl/HidlTransportSupport.h>
|
||||
|
||||
#include <TrustyConfirmationuiHal.h>
|
||||
|
||||
using android::sp;
|
||||
using android::hardware::confirmationui::V1_0::implementation::createTrustyConfirmationUI;
|
||||
|
||||
int main() {
|
||||
::android::hardware::configureRpcThreadpool(1, true /*willJoinThreadpool*/);
|
||||
auto service = createTrustyConfirmationUI();
|
||||
auto status = service->registerAsService();
|
||||
if (status != android::OK) {
|
||||
LOG(FATAL) << "Could not register service for ConfirmationUI 1.0 (" << status << ")";
|
||||
return -1;
|
||||
}
|
||||
::android::hardware::joinRpcThreadpool();
|
||||
return -1;
|
||||
}
|
Loading…
Reference in a new issue