blob: 463bb40a1556f73ba33b19f624bf4ef9f3bc357c [file] [log] [blame]
Janis Danisevskise0b19032017-11-09 14:57:39 -08001/*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#define LOG_TAG "ConfirmationIOHidlHalTest"
18#include <cutils/log.h>
19
20#include <algorithm>
21#include <iostream>
22#include <memory>
23
24#include <android/hardware/confirmationui/1.0/IConfirmationResultCallback.h>
25#include <android/hardware/confirmationui/1.0/IConfirmationUI.h>
26#include <android/hardware/confirmationui/1.0/types.h>
27#include <android/hardware/confirmationui/support/confirmationui_utils.h>
28
29#include <VtsHalHidlTargetCallbackBase.h>
30#include <VtsHalHidlTargetTestBase.h>
31
32#include <openssl/hmac.h>
33#include <openssl/sha.h>
34
35#include <cn-cbor/cn-cbor.h>
36
37using ::android::sp;
38
39using ::std::string;
40
41namespace android {
42namespace hardware {
43
44namespace confirmationui {
45namespace V1_0 {
46
47namespace test {
48namespace {
49class HMacImplementation {
50 public:
51 static support::NullOr<support::array<uint8_t, 32>> hmac256(
52 const uint8_t key[32], std::initializer_list<support::ByteBufferProxy> buffers) {
53 HMAC_CTX hmacCtx;
54 HMAC_CTX_init(&hmacCtx);
55 if (!HMAC_Init_ex(&hmacCtx, key, 32, EVP_sha256(), nullptr)) {
56 return {};
57 }
58 for (auto& buffer : buffers) {
59 if (!HMAC_Update(&hmacCtx, buffer.data(), buffer.size())) {
60 return {};
61 }
62 }
63 support::array<uint8_t, 32> result;
64 if (!HMAC_Final(&hmacCtx, result.data(), nullptr)) {
65 return {};
66 }
67 return result;
68 }
69};
70
71using HMacer = support::HMac<HMacImplementation>;
72
73constexpr uint8_t testKeyByte = static_cast<uint8_t>(TestKeyBits::BYTE);
74
75template <typename... Data>
76hidl_vec<uint8_t> testHMAC(const Data&... data) {
77 constexpr uint8_t testKey[32] = {testKeyByte, testKeyByte, testKeyByte, testKeyByte,
78 testKeyByte, testKeyByte, testKeyByte, testKeyByte,
79 testKeyByte, testKeyByte, testKeyByte, testKeyByte,
80 testKeyByte, testKeyByte, testKeyByte, testKeyByte};
81 constexpr uint8_t hmac_size_bytes = sizeof testKey;
82
83 auto hmac = HMacer::hmac256(testKey, data...);
84 if (!hmac.isOk()) {
85 EXPECT_TRUE(false) << "Failed to compute test hmac. This is a self-test error.";
86 return {};
87 }
88 hidl_vec<uint8_t> result(hmac_size_bytes);
89 copy(hmac.value().data(), hmac.value().data() + hmac_size_bytes, result.data());
90 return result;
91}
92
93using ::android::hardware::keymaster::V4_0::HardwareAuthToken;
94using ::android::hardware::keymaster::V4_0::HardwareAuthenticatorType;
95
96template <typename T>
97auto toBytes(const T& v) -> const uint8_t (&)[sizeof(T)] {
98 return *reinterpret_cast<const uint8_t(*)[sizeof(T)]>(&v);
99}
100
101HardwareAuthToken makeTestToken(const TestModeCommands command, uint64_t timestamp = 0) {
102 HardwareAuthToken auth_token;
103 auth_token.challenge = static_cast<uint64_t>(command);
104 auth_token.userId = 0;
105 auth_token.authenticatorId = 0;
106 auth_token.authenticatorType = HardwareAuthenticatorType::NONE;
107 auth_token.timestamp = timestamp;
108
109 // Canonical form of auth-token v0
110 // version (1 byte)
111 // challenge (8 bytes)
112 // user_id (8 bytes)
113 // authenticator_id (8 bytes)
114 // authenticator_type (4 bytes)
115 // timestamp (8 bytes)
116 // total 37 bytes
117 auth_token.mac = testHMAC("\0",
118 toBytes(auth_token.challenge), //
119 toBytes(auth_token.userId), //
120 toBytes(auth_token.authenticatorId), //
121 toBytes(support::hton(auth_token.authenticatorType)), //
122 toBytes(support::hton(auth_token.timestamp))); //
123
124 return auth_token;
125}
126
127#define DEBUG_CONFRIMATIONUI_UTILS_TEST
128
129#ifdef DEBUG_CONFRIMATIONUI_UTILS_TEST
130std::ostream& hexdump(std::ostream& out, const uint8_t* data, size_t size) {
131 for (size_t i = 0; i < size; ++i) {
132 uint8_t byte = data[i];
133 out << std::hex << std::setw(2) << std::setfill('0') << (unsigned)byte;
134 switch (i & 0xf) {
135 case 0xf:
136 out << "\n";
137 break;
138 case 7:
139 out << " ";
140 break;
141 default:
142 out << " ";
143 break;
144 }
145 }
146 return out;
147}
148#endif
149
150constexpr char hex_value[256] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
151 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
152 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
153 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0, // '0'..'9'
154 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'A'..'F'
155 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
156 0, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 'a'..'f'
157 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
158 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
159 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
160 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
161 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
162 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
163 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
164 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, //
165 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
166
167std::string hex2str(std::string a) {
168 std::string b;
169 size_t num = a.size() / 2;
170 b.resize(num);
171 for (size_t i = 0; i < num; i++) {
172 b[i] = (hex_value[a[i * 2] & 0xFF] << 4) + (hex_value[a[i * 2 + 1] & 0xFF]);
173 }
174 return b;
175}
176
177} // namespace
178
179class ConfirmationArgs {
180 public:
181 ResponseCode error_;
182 hidl_vec<uint8_t> formattedMessage_;
183 hidl_vec<uint8_t> confirmationToken_;
184 bool verifyConfirmationToken() {
185 static constexpr char confirmationPrefix[] = "confirmation token";
186 EXPECT_EQ(32U, confirmationToken_.size());
187 return 32U == confirmationToken_.size() &&
188 !memcmp(confirmationToken_.data(),
189 testHMAC(confirmationPrefix, formattedMessage_).data(), 32);
190 }
191};
192
193class ConfirmationTestCallback : public ::testing::VtsHalHidlTargetCallbackBase<ConfirmationArgs>,
194 public IConfirmationResultCallback {
195 public:
196 Return<void> result(ResponseCode error, const hidl_vec<uint8_t>& formattedMessage,
197 const hidl_vec<uint8_t>& confirmationToken) override {
198 ConfirmationArgs args;
199 args.error_ = error;
200 args.formattedMessage_ = formattedMessage;
201 args.confirmationToken_ = confirmationToken;
202 NotifyFromCallback(args);
203 return Void();
204 }
205};
206
207class ConfirmationUIHidlEnvironment : public ::testing::VtsHalHidlTargetTestEnvBase {
208 public:
209 // get the test environment singleton
210 static ConfirmationUIHidlEnvironment* Instance() {
211 static ConfirmationUIHidlEnvironment* instance = new ConfirmationUIHidlEnvironment;
212 return instance;
213 }
214
215 void registerTestServices() override { registerTestService<IConfirmationUI>(); }
216
217 private:
218 ConfirmationUIHidlEnvironment(){};
219
220 GTEST_DISALLOW_COPY_AND_ASSIGN_(ConfirmationUIHidlEnvironment);
221};
222
223class ConfirmationUIHidlTest : public ::testing::VtsHalHidlTargetTestBase {
224 public:
225 void TearDown() override { confirmator().abort(); }
226
227 static void SetUpTestCase() {
228 string service_name =
229 ConfirmationUIHidlEnvironment::Instance()->getServiceName<IConfirmationUI>();
230 confirmator_ = IConfirmationUI::getService(service_name);
231 ASSERT_NE(nullptr, confirmator_.get());
232 }
233
234 static void TearDownTestCase() { confirmator_.clear(); }
235
236 static IConfirmationUI& confirmator() { return *confirmator_; }
237
238 private:
239 static sp<IConfirmationUI> confirmator_;
240};
241
242sp<IConfirmationUI> ConfirmationUIHidlTest::confirmator_;
243
244#define ASSERT_HAL_CALL(expected, call) \
245 { \
246 auto result = call; \
247 ASSERT_TRUE(result.isOk()); \
248 ASSERT_EQ(expected, static_cast<decltype(expected)>(result)); \
249 }
250
251struct CnCborDeleter {
252 void operator()(cn_cbor* ptr) { cn_cbor_free(ptr); }
253};
254
255typedef std::unique_ptr<cn_cbor, CnCborDeleter> CnCborPtr;
256
257// Simulates the User taping Ok
258TEST_F(ConfirmationUIHidlTest, UserOkTest) {
259 static constexpr char test_prompt[] = "Me first, gimme gimme!";
260 static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
261 sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
262 hidl_string prompt_text(test_prompt);
263 hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
264 ASSERT_HAL_CALL(ResponseCode::OK,
265 confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
266
267 ASSERT_HAL_CALL(ResponseCode::OK, confirmator().deliverSecureInputEvent(
268 makeTestToken(TestModeCommands::OK_EVENT)));
269
270 auto result = conf_cb->WaitForCallback();
271 ASSERT_EQ(ResponseCode::OK, result.args->error_);
272
273 ASSERT_TRUE(result.args->verifyConfirmationToken());
274
275 cn_cbor_errback cn_cbor_error;
276 auto parsed_message =
277 CnCborPtr(cn_cbor_decode(result.args->formattedMessage_.data(),
278 result.args->formattedMessage_.size(), &cn_cbor_error));
279 // is parsable CBOR
280 ASSERT_TRUE(parsed_message.get());
281 // is a map
282 ASSERT_EQ(CN_CBOR_MAP, parsed_message->type);
283
284 // the message must have exactly 2 key value pairs.
285 // cn_cbor holds 2*<no_of_pairs> in the length field
286 ASSERT_EQ(4, parsed_message->length);
287 // map has key "prompt"
288 auto prompt = cn_cbor_mapget_string(parsed_message.get(), "prompt");
289 ASSERT_TRUE(prompt);
290 ASSERT_EQ(CN_CBOR_TEXT, prompt->type);
291 ASSERT_EQ(22, prompt->length);
292 ASSERT_EQ(0, memcmp(test_prompt, prompt->v.str, 22));
293 // map has key "extra"
294 auto extra_out = cn_cbor_mapget_string(parsed_message.get(), "extra");
295 ASSERT_TRUE(extra_out);
296 ASSERT_EQ(CN_CBOR_BYTES, extra_out->type);
297 ASSERT_EQ(3, extra_out->length);
298 ASSERT_EQ(0, memcmp(test_extra, extra_out->v.bytes, 3));
299}
300
301// Initiates a confirmation prompt with a message that is too long
302TEST_F(ConfirmationUIHidlTest, MessageTooLongTest) {
303 static constexpr uint8_t test_extra[static_cast<uint32_t>(MessageSize::MAX)] = {};
304 static constexpr char test_prompt[] = "D\'oh!";
305 sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
306 hidl_string prompt_text(test_prompt);
307 hidl_vec<uint8_t> extra(test_extra, test_extra + sizeof(test_extra));
308 ASSERT_HAL_CALL(ResponseCode::UIErrorMessageTooLong,
309 confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
310}
311
312// If the message gets very long some HAL implementations might fail even before the message
313// reaches the trusted app implementation. But the HAL must still diagnose the correct error.
314TEST_F(ConfirmationUIHidlTest, MessageWayTooLongTest) {
315 static constexpr uint8_t test_extra[static_cast<uint32_t>(MessageSize::MAX) * 10] = {};
316 static constexpr char test_prompt[] = "D\'oh!";
317 sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
318 hidl_string prompt_text(test_prompt);
319 hidl_vec<uint8_t> extra(test_extra, test_extra + sizeof(test_extra));
320 ASSERT_HAL_CALL(ResponseCode::UIErrorMessageTooLong,
321 confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
322}
323
324// Simulates the User tapping the Cancel
325TEST_F(ConfirmationUIHidlTest, UserCancelTest) {
326 static constexpr char test_prompt[] = "Me first, gimme gimme!";
327 static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
328 sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
329 hidl_string prompt_text(test_prompt);
330 hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
331 ASSERT_HAL_CALL(ResponseCode::OK,
332 confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
333
334 ASSERT_HAL_CALL(ResponseCode::OK, confirmator().deliverSecureInputEvent(
335 makeTestToken(TestModeCommands::CANCEL_EVENT)));
336
337 auto result = conf_cb->WaitForCallback();
338 ASSERT_EQ(ResponseCode::Canceled, result.args->error_);
339
340 ASSERT_EQ(0U, result.args->confirmationToken_.size());
341 ASSERT_EQ(0U, result.args->formattedMessage_.size());
342}
343
344// Simulates the framework candelling an ongoing prompt
345TEST_F(ConfirmationUIHidlTest, AbortTest) {
346 static constexpr char test_prompt[] = "Me first, gimme gimme!";
347 static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
348 sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
349 hidl_string prompt_text(test_prompt);
350 hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
351 ASSERT_HAL_CALL(ResponseCode::OK,
352 confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
353
354 confirmator().abort();
355
356 auto result = conf_cb->WaitForCallback();
357 ASSERT_EQ(ResponseCode::Aborted, result.args->error_);
358 ASSERT_EQ(0U, result.args->confirmationToken_.size());
359 ASSERT_EQ(0U, result.args->formattedMessage_.size());
360}
361
362// Passing malformed UTF-8 to the confirmation UI
363// This test passes a string that ends in the middle of a multibyte character
364TEST_F(ConfirmationUIHidlTest, MalformedUTF8Test1) {
365 static constexpr char test_prompt[] = {char(0xc0), 0};
366 static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
367 sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
368 hidl_string prompt_text(test_prompt);
369 hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
370 ASSERT_HAL_CALL(ResponseCode::UIErrorMalformedUTF8Encoding,
371 confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
372}
373
374// Passing malformed UTF-8 to the confirmation UI
375// This test passes a string with a 5-byte character.
376TEST_F(ConfirmationUIHidlTest, MalformedUTF8Test2) {
377 static constexpr char test_prompt[] = {char(0xf8), char(0x82), char(0x82),
378 char(0x82), char(0x82), 0};
379 static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
380 sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
381 hidl_string prompt_text(test_prompt);
382 hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
383 ASSERT_HAL_CALL(ResponseCode::UIErrorMalformedUTF8Encoding,
384 confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
385}
386
387// Passing malformed UTF-8 to the confirmation UI
388// This test passes a string with a 2-byte character followed by a stray non UTF-8 character.
389TEST_F(ConfirmationUIHidlTest, MalformedUTF8Test3) {
390 static constexpr char test_prompt[] = {char(0xc0), char(0x82), char(0x83), 0};
391 static constexpr uint8_t test_extra[] = {0x1, 0x2, 0x3};
392 sp<ConfirmationTestCallback> conf_cb = new ConfirmationTestCallback;
393 hidl_string prompt_text(test_prompt);
394 hidl_vec<uint8_t> extra(test_extra, test_extra + 3);
395 ASSERT_HAL_CALL(ResponseCode::UIErrorMalformedUTF8Encoding,
396 confirmator().promptUserConfirmation(conf_cb, prompt_text, extra, "en", {}));
397}
398
399// Test the implementation of HMAC SHA 256 against a golden blob.
400TEST(ConfirmationUITestSelfTest, HMAC256SelfTest) {
401 const char key_str[32] = "keykeykeykeykeykeykeykeykeykeyk";
402 const uint8_t(&key)[32] = *reinterpret_cast<const uint8_t(*)[32]>(key_str);
403 auto expected = hex2str("2377fbcaa7fb3f6c20cfa1d9ebc60e9922cf58c909e25e300f3cb57f7805c886");
404 auto result = HMacer::hmac256(key, "value1", "value2", "value3");
405
406#ifdef DEBUG_CONFRIMATIONUI_UTILS_TEST
407 hexdump(std::cout, reinterpret_cast<const uint8_t*>(expected.data()), 32) << std::endl;
408 hexdump(std::cout, result.value().data(), 32) << std::endl;
409#endif
410
411 support::ByteBufferProxy expected_bytes(expected);
412 ASSERT_TRUE(result.isOk());
413 ASSERT_EQ(expected, result.value());
414}
415
416} // namespace test
417} // namespace V1_0
418} // namespace confirmationui
419} // namespace hardware
420} // namespace android
421
422int main(int argc, char** argv) {
423 ::testing::InitGoogleTest(&argc, argv);
424 std::vector<std::string> positional_args;
425 int status = RUN_ALL_TESTS();
426 ALOGI("Test result = %d", status);
427 return status;
428}