1
2 #include <app_nugget.h>
3 #include <nos/NuggetClientInterface.h>
4 #include <gtest/gtest.h>
5
6 #include <memory>
7
8 #include "user/faceauth/include/fa_common.h"
9
10 #include "nugget_tools.h"
11 #include "util.h"
12
13 using std::string;
14 using std::vector;
15 using std::unique_ptr;
16
17 namespace {
18
19 class FaceAuthTest: public testing::Test {
20 protected:
21 void SetUp() override;
22
23 static void SetUpTestCase();
24 static void TearDownTestCase();
25
26 static unique_ptr<nos::NuggetClientInterface> client;
27 static unique_ptr<test_harness::TestHarness> uart_printer;
28
29 static const fa_result_t RunTask(const fa_task_t task,
30 const fa_embedding_t* embed = NULL,
31 const fa_token_t* token = NULL);
32 static void Run(const fa_result_t expected, const fa_task_t task,
33 const fa_embedding_t* embed = NULL,
34 const fa_token_t* token = NULL);
35
36 static void LockProfileTest(uint32_t profile1);
37 static void UnlockProfileTest(uint32_t profile1);
38 static bool IsProfileLocked(uint32_t profile1);
39 static void UnockProfileTest(uint32_t profile1);
40 static void FullMatchMismatchTest(uint32_t profile1, uint32_t profile2,
41 uint32_t slot1, uint32_t slot2);
42 static fa_token_t MakeToken(uint32_t profile_id);
43
44 static vector<uint64_t> user_ids;
45 };
46
47 vector<uint64_t> FaceAuthTest::user_ids;
48
49 unique_ptr<nos::NuggetClientInterface> FaceAuthTest::client;
50 unique_ptr<test_harness::TestHarness> FaceAuthTest::uart_printer;
51
SetUpTestCase()52 void FaceAuthTest::SetUpTestCase() {
53 srand(time(NULL));
54 for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
55 user_ids.push_back(rand());
56 }
57 uart_printer = test_harness::TestHarness::MakeUnique();
58
59 client = nugget_tools::MakeNuggetClient();
60 client->Open();
61 EXPECT_TRUE(client->IsOpen()) << "Unable to connect";
62 }
63
TearDownTestCase()64 void FaceAuthTest::TearDownTestCase() {
65 client->Close();
66 client = unique_ptr<nos::NuggetClientInterface>();
67
68 uart_printer = nullptr;
69 }
70
CalcCrc8(const uint8_t * data,int len)71 uint8_t CalcCrc8(const uint8_t *data, int len)
72 {
73 unsigned crc = 0;
74 int i, j;
75
76 for (j = len; j; j--, data++) {
77 crc ^= (*data << 8);
78 for (i = 8; i; i--) {
79 if (crc & 0x8000) {
80 crc ^= (0x1070 << 3);
81 }
82 crc <<= 1;
83 }
84 }
85
86 return (uint8_t)(crc >> 8);
87 }
88
MakeTask(uint64_t session_id,uint32_t profile_id,uint32_t cmd,uint32_t input_data1=0,uint32_t input_data2=0,uint32_t version=FACEAUTH_MIN_ABH_VERSION)89 static fa_task_t MakeTask(uint64_t session_id, uint32_t profile_id,
90 uint32_t cmd, uint32_t input_data1 = 0,
91 uint32_t input_data2 = 0,
92 uint32_t version = FACEAUTH_MIN_ABH_VERSION) {
93 fa_task_t task;
94 task.version = version;
95 task.session_id = session_id;
96 task.profile_id = profile_id;
97 task.cmd = cmd;
98 task.input.data.first = input_data1;
99 task.input.data.second = input_data2;
100 task.crc = CalcCrc8(reinterpret_cast<const uint8_t*>(&task),
101 offsetof(struct fa_task_t, crc));
102 return task;
103 }
104
MakeEmbedding(uint32_t base,uint32_t version=1)105 static fa_embedding_t* MakeEmbedding(uint32_t base, uint32_t version = 1) {
106 static fa_embedding_t embed;
107 memset(&embed, base, sizeof(fa_embedding_t));
108 embed.version = version;
109 embed.valid = 0;
110 embed.crc = CalcCrc8(reinterpret_cast<const uint8_t*>(&embed),
111 offsetof(struct fa_embedding_t, crc));
112 return &embed;
113 }
114
MakeResult(uint64_t session_id,int32_t error,uint32_t output_data1=0,uint32_t output_data2=0,uint32_t lockout_event=FACEAUTH_LOCKOUT_NOP)115 static fa_result_t MakeResult(uint64_t session_id, int32_t error,
116 uint32_t output_data1 = 0,
117 uint32_t output_data2 = 0,
118 uint32_t lockout_event = FACEAUTH_LOCKOUT_NOP) {
119 fa_result_t result;
120 memset(&result, 0, sizeof(fa_result_t));
121 result.version = 1;
122 result.session_id = session_id;
123 result.error = error;
124 result.output.data.first = output_data1;
125 result.output.data.second = output_data2;
126 result.lockout_event = lockout_event;
127 result.complete = 1;
128 result.crc = CalcCrc8(reinterpret_cast<const uint8_t*>(&result),
129 offsetof(struct fa_result_t, crc));
130 return result;
131 }
132
MakeToken(uint32_t profile_id)133 fa_token_t FaceAuthTest::MakeToken(uint32_t profile_id) {
134 fa_token_t token;
135 token.user_id = user_ids[profile_id];
136 return token;
137 }
138
Task2Buffer(const fa_task_t task,const fa_embedding_t * embed,const fa_token_t * token)139 vector<uint8_t> Task2Buffer(const fa_task_t task, const fa_embedding_t* embed,
140 const fa_token_t* token) {
141 vector<uint8_t> buffer;
142 for (size_t i = 0; i < sizeof(fa_task_t); ++i) {
143 buffer.push_back(*(reinterpret_cast<const uint8_t*>(&task) + i));
144 }
145 for (size_t i = 0; i < sizeof(fa_embedding_t); ++i) {
146 if (embed)
147 buffer.push_back(*(reinterpret_cast<const uint8_t*>(embed) + i));
148 else
149 buffer.push_back(0);
150 }
151 for (size_t i = 0; i < sizeof(fa_token_t); ++i) {
152 if (token)
153 buffer.push_back(*(reinterpret_cast<const uint8_t*>(token) + i));
154 else
155 buffer.push_back(0);
156 }
157
158 return buffer;
159 }
160
Buffer2Result(const vector<uint8_t> & buffer)161 static const fa_result_t Buffer2Result(const vector<uint8_t>& buffer)
162 {
163 const fa_result_t result = *(reinterpret_cast<const fa_result_t*>(
164 buffer.data()));
165 return result;
166 }
167
EXPECT_RESULT_EQ(const fa_result_t & r1,const fa_result_t & r2)168 static void EXPECT_RESULT_EQ(const fa_result_t& r1, const fa_result_t& r2)
169 {
170 EXPECT_EQ(r1.version, r2.version);
171 EXPECT_EQ(r1.session_id, r2.session_id);
172 EXPECT_EQ(r1.error, r2.error);
173 EXPECT_EQ(r1.output.data.first, r2.output.data.first);
174 EXPECT_EQ(r1.output.data.second, r2.output.data.second);
175 EXPECT_EQ(r1.lockout_event, r2.lockout_event);
176 EXPECT_EQ(r1.complete, r2.complete);
177 EXPECT_EQ(r1.crc, r2.crc);
178 }
179
RunTask(const fa_task_t task,const fa_embedding_t * embed,const fa_token_t * token)180 const fa_result_t FaceAuthTest::RunTask(const fa_task_t task,
181 const fa_embedding_t* embed,
182 const fa_token_t* token) {
183 vector<uint8_t> buffer_rx;
184 buffer_rx.resize(1024);
185
186 vector<uint8_t> buffer_tx = Task2Buffer(task, embed, token);
187 FaceAuthTest::client->CallApp(APP_ID_FACEAUTH_TEST, 1, buffer_tx, &buffer_rx);
188
189 return Buffer2Result(buffer_rx);
190 }
191
Run(const fa_result_t expected,const fa_task_t task,const fa_embedding_t * embed,const fa_token_t * token)192 void FaceAuthTest::Run(const fa_result_t expected, const fa_task_t task,
193 const fa_embedding_t* embed, const fa_token_t* token) {
194 EXPECT_RESULT_EQ(expected, RunTask(task, embed, token));
195 }
196
SetUp()197 void FaceAuthTest::SetUp() {
198 for (int profiles = 1; profiles <= MAX_NUM_PROFILES; ++profiles) {
199 Run(MakeResult(0x0, FACEAUTH_SUCCESS),
200 MakeTask(0x0, profiles, FACEAUTH_CMD_ERASE));
201 }
202 }
203
TEST_F(FaceAuthTest,SimpleMatchMismatchTest)204 TEST_F(FaceAuthTest, SimpleMatchMismatchTest) {
205 uint64_t session_id = 0xFACE000011110000ull;
206 session_id++;
207
208 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_NOMATCH),
209 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP), MakeEmbedding(0x11));
210 session_id++;
211 Run(MakeResult(session_id, FACEAUTH_SUCCESS, 0x1),
212 MakeTask(session_id, 0x1, FACEAUTH_CMD_ENROLL), MakeEmbedding(0x11));
213 session_id++;
214 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_MATCH),
215 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP), MakeEmbedding(0x11));
216 session_id++;
217 Run(MakeResult(session_id, FACEAUTH_SUCCESS),
218 MakeTask(session_id, 0x1, FACEAUTH_CMD_ERASE));
219 session_id++;
220 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_NOMATCH),
221 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP), MakeEmbedding(0x11));
222 }
223
FullMatchMismatchTest(uint32_t profile1,uint32_t profile2,uint32_t slot1,uint32_t slot2)224 void FaceAuthTest::FullMatchMismatchTest(uint32_t profile1, uint32_t profile2,
225 uint32_t slot1, uint32_t slot2) {
226 uint64_t session_id = 0xFACE000022220000ull;
227 for (uint32_t i = 0; i < 20; ++i) {
228 session_id++;
229 Run(MakeResult(session_id, FACEAUTH_SUCCESS, profile1),
230 MakeTask(session_id, profile1, FACEAUTH_CMD_ENROLL),
231 MakeEmbedding((i == slot1) ? 0x11 : 0x0));
232
233 session_id++;
234 Run(MakeResult(session_id, FACEAUTH_SUCCESS, profile2),
235 MakeTask(session_id, profile2, FACEAUTH_CMD_ENROLL),
236 MakeEmbedding((i == slot2) ? 0xAA : 0x0));
237 }
238
239 session_id++;
240 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_MATCH),
241 MakeTask(session_id, profile1, FACEAUTH_CMD_COMP), MakeEmbedding(0x11));
242 session_id++;
243 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_NOMATCH),
244 MakeTask(session_id, profile1, FACEAUTH_CMD_COMP), MakeEmbedding(0xAA));
245 session_id++;
246 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_NOMATCH),
247 MakeTask(session_id, profile2, FACEAUTH_CMD_COMP), MakeEmbedding(0x11));
248 session_id++;
249 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_MATCH),
250 MakeTask(session_id, profile2, FACEAUTH_CMD_COMP), MakeEmbedding(0xAA));
251 }
252
TEST_F(FaceAuthTest,SFSFullTest)253 TEST_F(FaceAuthTest, SFSFullTest) {
254 uint64_t session_id = 0xFACE000033330000ull;
255 for (int i = 0; i < 20; ++i) {
256 session_id++;
257 Run(MakeResult(session_id, FACEAUTH_SUCCESS, 0x1),
258 MakeTask(session_id, 0x1, FACEAUTH_CMD_ENROLL), MakeEmbedding(0x0));
259 }
260
261 session_id++;
262 Run(MakeResult(session_id, FACEAUTH_ERR_SFS_FULL, 0x1),
263 MakeTask(session_id, 0x1, FACEAUTH_CMD_ENROLL), MakeEmbedding(0x0));
264 }
265
LockProfileTest(uint32_t profile1)266 void FaceAuthTest::LockProfileTest(uint32_t profile1) {
267 uint64_t session_id = 0xFACE000044440000ull;
268
269 for (int i = 0; i < 4; ++i) {
270 session_id++;
271 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_NOMATCH),
272 MakeTask(session_id, profile1, FACEAUTH_CMD_COMP), MakeEmbedding(0x0));
273 }
274
275 session_id++;
276 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_NOMATCH, 0,
277 FACEAUTH_LOCKOUT_ENFORCED),
278 MakeTask(session_id, profile1, FACEAUTH_CMD_COMP), MakeEmbedding(0x0));
279
280 session_id++;
281 Run(MakeResult(session_id, FACEAUTH_ERR_THROTTLE, FACEAUTH_NOMATCH),
282 MakeTask(session_id, profile1, FACEAUTH_CMD_COMP), MakeEmbedding(0x0));
283 }
284
IsProfileLocked(uint32_t profile1)285 bool FaceAuthTest::IsProfileLocked(uint32_t profile1) {
286 uint64_t session_id = 0xFACE000066660000ull;
287
288 const fa_result_t observed =
289 RunTask(MakeTask(session_id, profile1, FACEAUTH_CMD_GET_USER_INFO));
290 const fa_result_t expected =
291 MakeResult(session_id, FACEAUTH_SUCCESS, 0, observed.output.data.second);
292 EXPECT_RESULT_EQ(expected, observed);
293 return observed.output.data.second;
294 }
295
UnlockProfileTest(uint32_t profile1)296 void FaceAuthTest::UnlockProfileTest(uint32_t profile1) {
297 uint64_t session_id = 0xFACE000077770000ull;
298 session_id++;
299 Run(MakeResult(session_id, FACEAUTH_SUCCESS, 0, 0, FACEAUTH_LOCKOUT_REMOVED),
300 MakeTask(session_id, profile1, FACEAUTH_CMD_RESET_LOCKOUT));
301 }
302
TEST_F(FaceAuthTest,ExhaustiveLockoutTest)303 TEST_F(FaceAuthTest, ExhaustiveLockoutTest) {
304 EXPECT_EQ(IsProfileLocked(1), false);
305 EXPECT_EQ(IsProfileLocked(4), false);
306 EXPECT_EQ(IsProfileLocked(5), false);
307 EXPECT_EQ(IsProfileLocked(6), false);
308
309 LockProfileTest(1);
310 LockProfileTest(5);
311 LockProfileTest(6);
312
313 EXPECT_EQ(IsProfileLocked(1), true);
314 EXPECT_EQ(IsProfileLocked(4), false);
315 EXPECT_EQ(IsProfileLocked(5), true);
316 EXPECT_EQ(IsProfileLocked(6), true);
317
318 UnlockProfileTest(1);
319 UnlockProfileTest(6);
320
321 EXPECT_EQ(IsProfileLocked(1), false);
322 EXPECT_EQ(IsProfileLocked(4), false);
323 EXPECT_EQ(IsProfileLocked(5), true);
324 EXPECT_EQ(IsProfileLocked(6), false);
325 }
326
TEST_F(FaceAuthTest,ValidProfileUserIDTest)327 TEST_F(FaceAuthTest, ValidProfileUserIDTest) {
328 fa_token_t token;
329 uint64_t session_id = 0xFACE000088880000ull;
330 session_id++;
331 token = MakeToken(1);
332 Run(MakeResult(session_id, FACEAUTH_SUCCESS, 1),
333 MakeTask(session_id, 0, FACEAUTH_CMD_ENROLL), MakeEmbedding(0x0), &token);
334
335 for (int i = 1; i <= 6; ++i) {
336 session_id++;
337 token = MakeToken(i);
338 Run(MakeResult(session_id, FACEAUTH_SUCCESS, i),
339 MakeTask(session_id, (i % 2) ? i : 0, FACEAUTH_CMD_ENROLL),
340 MakeEmbedding(0x0), &token);
341 }
342
343 session_id++;
344 token = MakeToken(2);
345 Run(MakeResult(session_id, FACEAUTH_ERR_INVALID_TOKEN),
346 MakeTask(session_id, 3, FACEAUTH_CMD_ENROLL), MakeEmbedding(0x0), &token);
347 session_id++;
348 Run(MakeResult(session_id, FACEAUTH_ERR_SFS_FULL),
349 MakeTask(session_id, 0, FACEAUTH_CMD_ENROLL));
350 }
351
TEST_F(FaceAuthTest,InvalidCommandTest)352 TEST_F(FaceAuthTest, InvalidCommandTest) {
353 uint64_t session_id = 0xFACE000099990000ull;
354 session_id++;
355 Run(MakeResult(session_id, FACEAUTH_ERR_INVALID_ARGS),
356 MakeTask(session_id, 0x1, 0x0));
357 }
358
TEST_F(FaceAuthTest,SimpleFeatureTest)359 TEST_F(FaceAuthTest, SimpleFeatureTest) {
360 uint64_t session_id = 0xFACE0000AAAA0000ull;
361 uint32_t index = 0;
362 uint32_t feature_msk[MAX_NUM_PROFILES] = {0};
363
364 for (int k = 0; k < 5; ++k) {
365 for (int i = 1; i <= MAX_NUM_PROFILES; ++i) {
366 session_id++;
367 Run(MakeResult(session_id, FACEAUTH_SUCCESS, feature_msk[i - 1]),
368 MakeTask(session_id, i, FACEAUTH_CMD_GET_USER_INFO));
369 }
370
371 for (int i = 1; i <= MAX_NUM_PROFILES; ++i) {
372 session_id++;
373 Run(MakeResult(session_id, FACEAUTH_SUCCESS),
374 MakeTask(session_id, i, FACEAUTH_CMD_SET_FEATURE, (1 << index)));
375 feature_msk[i - 1] |= (1 << index);
376 index++;
377 }
378 }
379
380 index = 0;
381
382 for (int k = 0; k < 5; ++k) {
383 for (int i = 1; i <= MAX_NUM_PROFILES; ++i) {
384 session_id++;
385 Run(MakeResult(session_id, FACEAUTH_SUCCESS, feature_msk[i - 1]),
386 MakeTask(session_id, i, FACEAUTH_CMD_GET_USER_INFO));
387 }
388
389 for (int i = 1; i <= MAX_NUM_PROFILES; ++i) {
390 session_id++;
391 Run(MakeResult(session_id, FACEAUTH_SUCCESS),
392 MakeTask(session_id, i, FACEAUTH_CMD_CLR_FEATURE, (1 << index)));
393 feature_msk[i - 1] &= ~(1 << index);
394 index++;
395 }
396 }
397 }
398
TEST_F(FaceAuthTest,EmbeddingVersionTest)399 TEST_F(FaceAuthTest, EmbeddingVersionTest) {
400 uint64_t session_id = 0xFACE0000BBBB0000ull;
401 session_id++;
402 Run(MakeResult(session_id, FACEAUTH_SUCCESS, 1),
403 MakeTask(session_id, 0x1, FACEAUTH_CMD_ENROLL), MakeEmbedding(0x11));
404 session_id++;
405 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_MATCH),
406 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP), MakeEmbedding(0x11));
407 session_id++;
408 Run(MakeResult(session_id, FACEAUTH_ERR_RECALIBRATE, FACEAUTH_NOMATCH),
409 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP), MakeEmbedding(0x11, 0x2));
410 }
411
TEST_F(FaceAuthTest,FirmwareVersionTest)412 TEST_F(FaceAuthTest, FirmwareVersionTest) {
413 uint64_t session_id = 0xFACE0000CCCC0000ull;
414 session_id++;
415 Run(MakeResult(session_id, FACEAUTH_SUCCESS, 1),
416 MakeTask(session_id, 0x1, FACEAUTH_CMD_ENROLL), MakeEmbedding(0x11));
417 session_id++;
418 Run(MakeResult(session_id, FACEAUTH_ERR_VERSION, FACEAUTH_NOMATCH),
419 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP, 0, 0, 0x1),
420 MakeEmbedding(0x11));
421 session_id++;
422 Run(MakeResult(session_id, FACEAUTH_ERR_VERSION, FACEAUTH_NOMATCH),
423 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP, 0, 0,
424 FACEAUTH_MIN_ABH_VERSION - 0x100),
425 MakeEmbedding(0x11));
426 session_id++;
427 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_MATCH),
428 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP, 0, 0,
429 FACEAUTH_MIN_ABH_VERSION),
430 MakeEmbedding(0x11));
431 session_id++;
432 Run(MakeResult(session_id, FACEAUTH_SUCCESS, FACEAUTH_MATCH),
433 MakeTask(session_id, 0x1, FACEAUTH_CMD_COMP, 0, 0,
434 FACEAUTH_MIN_ABH_VERSION + 0x100),
435 MakeEmbedding(0x11));
436 }
437
TEST_F(FaceAuthTest,ExhaustiveMatchMismatchTest)438 TEST_F(FaceAuthTest, ExhaustiveMatchMismatchTest) {
439 FullMatchMismatchTest(1, 6, 0, 19);
440 FullMatchMismatchTest(2, 5, 1, 18);
441 FullMatchMismatchTest(3, 4, 2, 17);
442 SetUp();
443 FullMatchMismatchTest(2, 4, 3, 16);
444 FullMatchMismatchTest(1, 5, 4, 15);
445 FullMatchMismatchTest(3, 6, 5, 14);
446 SetUp();
447 FullMatchMismatchTest(3, 5, 6, 13);
448 FullMatchMismatchTest(1, 4, 7, 12);
449 FullMatchMismatchTest(2, 6, 8, 11);
450 SetUp();
451 FullMatchMismatchTest(3, 6, 9, 10);
452 }
453 }
454
455