1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
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::confirmationui::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[] = CONFIRMATIONUI_PORT;
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
|