blob: 41aefccfa45a3af2815f2116657e34a60831744c [file] [log] [blame]
#include <app_nugget.h>
#include <nos/NuggetClientInterface.h>
#include <gtest/gtest.h>
#include <memory>
#include "user/faceauth/include/fa_common.h"
#include "nugget_tools.h"
#include "util.h"
using std::string;
using std::vector;
using std::unique_ptr;
namespace {
vector<uint8_t> EMBEDDING_VECTOR_NULL(128, 0);
const vector<uint8_t> EMBEDDING_VECTOR_1(64, 16);
const vector<uint8_t> EMBEDDING_VECTOR_2(64, (uint8_t)-16);
const uint32_t PROFILE_1 = 1;
const uint32_t PROFILE_2 = 2;
const uint32_t PROFILE_3 = 3;
const uint32_t PROFILE_4 = 4;
const uint32_t PROFILE_5 = 5;
const uint32_t PROFILE_6 = 6;
class FaceAuthTest: public testing::Test {
public:
static unique_ptr<nos::NuggetClientInterface> client;
static unique_ptr<test_harness::TestHarness> uart_printer;
protected:
void SetUp() override;
static void SetUpTestCase();
static void TearDownTestCase();
static void DisengageGlobalLockout();
};
unique_ptr<nos::NuggetClientInterface> FaceAuthTest::client;
unique_ptr<test_harness::TestHarness> FaceAuthTest::uart_printer;
void FaceAuthTest::SetUpTestCase() {
srand(time(NULL));
uart_printer = test_harness::TestHarness::MakeUnique();
client = nugget_tools::MakeNuggetClient();
client->Open();
EXPECT_TRUE(client->IsOpen()) << "Unable to connect";
/* We need any embedding vector to have magnitude of 128 */
EMBEDDING_VECTOR_NULL[127] = 128;
DisengageGlobalLockout();
}
void FaceAuthTest::TearDownTestCase() {
client->Close();
client = unique_ptr<nos::NuggetClientInterface>();
uart_printer = nullptr;
}
uint8_t CalcCrc8(const uint8_t *data, int len)
{
unsigned crc = 0;
int i, j;
for (j = len; j; j--, data++) {
crc ^= (*data << 8);
for (i = 8; i; i--) {
if (crc & 0x8000) {
crc ^= (0x1070 << 3);
}
crc <<= 1;
}
}
return (uint8_t)(crc >> 8);
}
class Task {
public:
Task(uint32_t profile_id, uint32_t cmd,
uint32_t version = FACEAUTH_MIN_ABH_VERSION) {
memset(&task, 0, sizeof(task));
task.version = version;
task.session_id = 0xFACEBEEFBEEFFACEull;
task.profile_id = profile_id;
task.cmd = cmd;
}
Task& SetFirst(uint64_t first) {
task.input.data.first = first;
return *this;
}
Task& SetChallenge(uint64_t challenge) {
memcpy(task.input.challenge, &challenge, sizeof(challenge));
return *this;
}
Task& Finalize() {
task.crc = CalcCrc8(reinterpret_cast<const uint8_t*>(&task),
offsetof(struct fa_task_t, crc));
return *this;
}
vector<uint8_t> ToBuffer() {
vector<uint8_t> buffer;
for (size_t i = 0; i < sizeof(fa_task_t); ++i) {
buffer.push_back(*(reinterpret_cast<const uint8_t*>(&task) + i));
}
return buffer;
}
private:
fa_task_t task;
};
class Embedding {
public:
Embedding() { memset(&embed, 0, sizeof(embed)); }
Embedding(vector<uint8_t> base, uint32_t version = 1) {
memset(&embed, 0, sizeof(embed));
embed.version = version;
embed.valid = 0;
std::copy(base.begin(), base.end(), &embed.face_id[0]);
std::copy(base.begin(), base.end(), &embed.depth_id[0]);
}
void Finalize() {
embed.crc = CalcCrc8(reinterpret_cast<const uint8_t*>(&embed),
offsetof(struct fa_embedding_t, crc));
}
vector<uint8_t> ToBuffer() {
vector<uint8_t> buffer;
for (size_t i = 0; i < sizeof(fa_embedding_t); ++i) {
buffer.push_back(*(reinterpret_cast<const uint8_t*>(&embed) + i));
}
return buffer;
}
private:
fa_embedding_t embed;
};
class Token {
public:
Token() { memset(&token, 0, sizeof(token)); }
Token(fa_token_t token) : token(token) {}
Token(uint64_t challenge, uint64_t user_id, uint64_t auth_id) {
memset(&token, 0, sizeof(token));
token.challenge = challenge;
token.user_id = user_id;
token.authenticator_id = auth_id;
}
vector<uint8_t> ToBuffer() {
vector<uint8_t> buffer;
for (size_t i = 0; i < sizeof(fa_token_t); ++i) {
buffer.push_back(*(reinterpret_cast<const uint8_t*>(&token) + i));
}
return buffer;
}
fa_token_t GetRaw() { return token; }
private:
fa_token_t token;
};
class Result {
public:
Result() { memset(&result, 0, sizeof(result)); }
Result(fa_result_t result) : result(result) {}
Result(int32_t error) {
memset(&result, 0, sizeof(result));
SetError(error);
}
fa_result_t GetRaw() {
Finalize();
return result;
}
Result& SetError(int32_t error) {
result.error = error;
return *this;
}
Result& SetChallenge(uint64_t challenge) {
memcpy(result.output.challenge, &challenge, sizeof(challenge));
return *this;
}
Result& SetSecond(uint32_t second) {
result.output.data.second = second;
return *this;
}
Result& SetThird(uint32_t third) {
result.output.data.third = third;
return *this;
}
uint64_t GetChallenge() {
uint64_t challenge;
memcpy(&challenge, result.output.challenge, sizeof(challenge));
return challenge;
}
uint32_t GetFirst() { return result.output.data.first; }
uint32_t GetSecond() { return result.output.data.second; }
Result& Finalize() {
result.version = 1;
result.session_id = 0xFACEBEEFBEEFFACEull;
result.complete = 1;
result.crc = CalcCrc8(reinterpret_cast<const uint8_t*>(&result),
offsetof(struct fa_result_t, crc));
return *this;
}
protected:
fa_result_t result;
};
static void EXPECT_REQ(Result r1, Result r2) {
fa_result_t observed = r1.GetRaw();
fa_result_t expected = r2.GetRaw();
EXPECT_EQ(observed.version, expected.version);
EXPECT_EQ(observed.session_id, expected.session_id);
EXPECT_EQ(observed.error, expected.error);
EXPECT_EQ(observed.output.data.first, expected.output.data.first);
EXPECT_EQ(observed.output.data.second, expected.output.data.second);
EXPECT_EQ(observed.output.data.third, expected.output.data.third);
EXPECT_EQ(observed.complete, expected.complete);
EXPECT_EQ(observed.crc, expected.crc);
}
class ResetLockoutResult : public Result {
public:
ResetLockoutResult(Result result) : Result(result) {}
ResetLockoutResult(int32_t error) : Result(error) {}
ResetLockoutResult& SetLockoutEvent(uint32_t lockout_event) {
result.output.data.third = lockout_event;
return *this;
}
};
class MigrateResult : public Result {
public:
MigrateResult(Result result) : Result(result) {}
MigrateResult(int32_t error) : Result(error) {}
MigrateResult& SetEmbeddingNum(uint32_t num) {
result.output.data.second = num;
return *this;
}
MigrateResult& SetMatch(bool m) {
result.output.data.third = m;
return *this;
}
};
class AuthenticateResult : public Result {
public:
AuthenticateResult(Result result, Token token)
: Result(result), token(token) {}
AuthenticateResult(int32_t error) : Result(error) {}
AuthenticateResult& SetMatch(bool m) {
result.output.data.first = m;
return *this;
}
AuthenticateResult& SetLockoutEvent(uint32_t lockout_event) {
result.output.data.third = lockout_event;
return *this;
}
Token GetToken() { return token; }
protected:
Token token;
};
static void EXPECT_AEQ(AuthenticateResult r1, AuthenticateResult r2) {
EXPECT_REQ(r1, r2);
fa_token_t observed = r1.GetToken().GetRaw();
fa_token_t expected = r2.GetToken().GetRaw();
EXPECT_EQ(observed.challenge, expected.challenge);
EXPECT_EQ(observed.user_id, expected.user_id);
EXPECT_EQ(observed.authenticator_id, expected.authenticator_id);
}
class Transaction {
public:
Transaction(Task task, Embedding embed, Token token)
: input_task(task), input_embed(embed), input_token(token) {}
Transaction& Finalize() {
input_task.Finalize();
input_embed.Finalize();
return *this;
}
Transaction& Run() {
vector<uint8_t> task = input_task.ToBuffer();
vector<uint8_t> embed = input_embed.ToBuffer();
vector<uint8_t> token = input_token.ToBuffer();
vector<uint8_t> buffer_rx;
buffer_rx.resize(512);
vector<uint8_t> buffer_tx;
buffer_tx.insert(buffer_tx.end(), task.begin(), task.end());
buffer_tx.insert(buffer_tx.end(), embed.begin(), embed.end());
buffer_tx.insert(buffer_tx.end(), token.begin(), token.end());
FaceAuthTest::client->CallApp(APP_ID_FACEAUTH_TEST, 1, buffer_tx,
&buffer_rx);
struct fa_output_t {
fa_result_t result;
fa_token_t token;
} __attribute__((packed));
fa_output_t output =
*(reinterpret_cast<struct fa_output_t*>(buffer_rx.data()));
output_result = output.result;
output_token = output.token;
return *this;
}
Result GetResult() { return Result(output_result); }
Token GetToken() { return Token(output_token); }
private:
Task input_task;
Embedding input_embed;
Token input_token;
fa_result_t output_result;
fa_token_t output_token;
};
class Device {
public:
static Result Erase(uint32_t profile_id) {
return Transaction(Task(profile_id, FACEAUTH_CMD_ERASE), Embedding(),
Token())
.Finalize()
.Run()
.GetResult();
}
static Result GenerateChallenge() {
Result ret =
Transaction(Task(0, FACEAUTH_CMD_GET_CHALLENGE), Embedding(), Token())
.Finalize()
.Run()
.GetResult();
memcpy(&challenge, ret.GetRaw().output.challenge, sizeof(challenge));
return ret;
}
static Result RevokeChallenge() {
return Transaction(Task(0, FACEAUTH_CMD_REVOKE_CHALLENGE), Embedding(),
Token())
.Finalize()
.Run()
.GetResult();
}
static uint64_t GetChallenge() { return challenge; }
private:
static uint64_t challenge;
};
uint64_t Device::challenge;
void FaceAuthTest::SetUp() {
for (int i = 0; i < MAX_NUM_PROFILES; ++i)
EXPECT_REQ(Device::Erase(i + 1), Result(FACEAUTH_SUCCESS));
}
void FaceAuthTest::DisengageGlobalLockout() {
/* Send Auth Token */
Result generate_result = Device::GenerateChallenge();
EXPECT_REQ(generate_result,
Result(FACEAUTH_SUCCESS).SetChallenge(Device::GetChallenge()));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_ENROLL).Finalize(),
Embedding(EMBEDDING_VECTOR_1, 1),
Token(Device::GetChallenge(), 0, 0))
.Run()
.GetResult(),
Result(FACEAUTH_ERR_CRC));
}
TEST_F(FaceAuthTest, OldFirmwareVersionShouldError) {
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_ERASE,
FACEAUTH_MIN_ABH_VERSION - 0x100),
Embedding(), Token())
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_VERSION));
}
TEST_F(FaceAuthTest, NewFirmwareVersionShouldNotError) {
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_ERASE,
FACEAUTH_MIN_ABH_VERSION + 0x100),
Embedding(), Token())
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_SUCCESS));
}
TEST_F(FaceAuthTest, TaskCRCErrorShouldBeDetected) {
EXPECT_REQ(
Transaction(Task(PROFILE_1, FACEAUTH_CMD_ERASE), Embedding(), Token())
.Run()
.GetResult(),
Result(FACEAUTH_ERR_CRC));
}
TEST_F(FaceAuthTest, ZeroChallengeShouldError) {
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_ENROLL),
Embedding(EMBEDDING_VECTOR_1, 1), Token())
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_CHALLENGE));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_SET_FEATURE),
Embedding(EMBEDDING_VECTOR_1, 1), Token())
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_CHALLENGE));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_CLR_FEATURE),
Embedding(EMBEDDING_VECTOR_1, 1), Token())
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_CHALLENGE));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_RESET_LOCKOUT),
Embedding(EMBEDDING_VECTOR_1, 1), Token())
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_CHALLENGE));
}
TEST_F(FaceAuthTest, InvalidChallengeShouldError) {
Result generate_result = Device::GenerateChallenge();
EXPECT_REQ(generate_result,
Result(FACEAUTH_SUCCESS).SetChallenge(Device::GetChallenge()));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_ENROLL),
Embedding(EMBEDDING_VECTOR_1, 1), Token(rand(), 0, 0))
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_CHALLENGE));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_SET_FEATURE),
Embedding(EMBEDDING_VECTOR_1, 1), Token(rand(), 0, 0))
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_CHALLENGE));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_CLR_FEATURE),
Embedding(EMBEDDING_VECTOR_1, 1), Token(rand(), 0, 0))
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_CHALLENGE));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_RESET_LOCKOUT),
Embedding(EMBEDDING_VECTOR_1, 1), Token(rand(), 0, 0))
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_CHALLENGE));
}
TEST_F(FaceAuthTest, EmbeddingCRCErrorShouldBeDetected) {
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_COMP).Finalize(),
Embedding(EMBEDDING_VECTOR_1, 1), Token())
.Run()
.GetResult(),
Result(FACEAUTH_ERR_CRC));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_MIGRATE).Finalize(),
Embedding(EMBEDDING_VECTOR_1, 1), Token())
.Run()
.GetResult(),
Result(FACEAUTH_ERR_CRC));
Result generate_result = Device::GenerateChallenge();
EXPECT_REQ(generate_result,
Result(FACEAUTH_SUCCESS).SetChallenge(Device::GetChallenge()));
EXPECT_REQ(Transaction(Task(PROFILE_1, FACEAUTH_CMD_ENROLL).Finalize(),
Embedding(EMBEDDING_VECTOR_1, 1),
Token(Device::GetChallenge(), 0, 0))
.Run()
.GetResult(),
Result(FACEAUTH_ERR_CRC));
}
TEST_F(FaceAuthTest, InvalidCommandShouldError) {
Task task(PROFILE_1, 0);
Embedding embed;
Token token;
EXPECT_REQ(Transaction(task, embed, token).Finalize().Run().GetResult(),
Result(FACEAUTH_ERR_INVALID_ARGS));
}
TEST_F(FaceAuthTest, ValidProfileIDTest) {
Embedding embed;
Token token;
EXPECT_REQ(Transaction(Task(0, FACEAUTH_CMD_GET_USER_INFO), embed, token)
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_ARGS));
for (int i = 1; i <= MAX_NUM_PROFILES; ++i) {
EXPECT_REQ(Transaction(Task(i, FACEAUTH_CMD_GET_USER_INFO), embed, token)
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_SUCCESS));
}
EXPECT_REQ(Transaction(Task(MAX_NUM_PROFILES + 1, FACEAUTH_CMD_GET_USER_INFO),
embed, token)
.Finalize()
.Run()
.GetResult(),
Result(FACEAUTH_ERR_INVALID_ARGS));
}
class User {
public:
User(vector<uint8_t> embed_base) : embed_base(embed_base) {
user_id = rand();
user_id <<= 32;
user_id += rand();
}
User& SetEmbeddingVersion(uint8_t version) {
embed_version = version;
return *this;
}
User& SetEmbeddingBase(vector<uint8_t> base) {
embed_base = base;
return *this;
}
User& SetUserID(uint64_t sid) {
user_id = sid;
return *this;
}
Result GetProfileInfo() {
return Transaction(Task(0, FACEAUTH_CMD_GET_PROFILE_INFO), Embedding(),
Token(0, user_id, 0))
.Finalize()
.Run()
.GetResult();
}
Result GetUserInfo(uint32_t profile_id) {
return Transaction(Task(profile_id, FACEAUTH_CMD_GET_USER_INFO),
Embedding(), Token())
.Finalize()
.Run()
.GetResult();
}
Result Enroll(uint32_t profile_id) {
Result generate_result = Device::GenerateChallenge();
EXPECT_REQ(generate_result,
Result(FACEAUTH_SUCCESS).SetChallenge(Device::GetChallenge()));
Result ret = Transaction(Task(profile_id, FACEAUTH_CMD_ENROLL),
Embedding(embed_base, embed_version),
Token(Device::GetChallenge(), user_id, 0))
.Finalize()
.Run()
.GetResult();
uint64_t auth_id_temp = ret.GetChallenge();
memcpy(&auth_id, &auth_id_temp, sizeof(auth_id));
EXPECT_REQ(Device::RevokeChallenge(), Result(FACEAUTH_SUCCESS));
return ret;
}
MigrateResult Migrate(uint32_t profile_id, bool should_migrate) {
return Transaction(
Task(profile_id, FACEAUTH_CMD_MIGRATE).SetFirst(should_migrate),
Embedding(embed_base, embed_version), Token(0, user_id, 0))
.Finalize()
.Run()
.GetResult();
}
AuthenticateResult Authenticate(uint32_t profile_id) {
operation_id = rand();
operation_id <<= 32;
operation_id += rand();
Transaction t =
Transaction(
Task(profile_id, FACEAUTH_CMD_COMP).SetChallenge(operation_id),
Embedding(embed_base, embed_version), Token())
.Finalize()
.Run();
return AuthenticateResult(t.GetResult(), t.GetToken());
}
ResetLockoutResult ResetLockout(uint32_t profile_id) {
Result generate_result = Device::GenerateChallenge();
EXPECT_REQ(generate_result,
Result(FACEAUTH_SUCCESS).SetChallenge(Device::GetChallenge()));
Result ret =
Transaction(Task(profile_id, FACEAUTH_CMD_RESET_LOCKOUT), Embedding(),
Token(Device::GetChallenge(), user_id, 0))
.Finalize()
.Run()
.GetResult();
EXPECT_REQ(Device::RevokeChallenge(), Result(FACEAUTH_SUCCESS));
return ret;
}
bool IsProfileLocked(uint32_t profile_id) {
return (GetUserInfo(profile_id).GetSecond() > 0);
}
void LockProfile(uint32_t profile_id) {
vector<uint8_t> original_embed_base = embed_base;
embed_base = EMBEDDING_VECTOR_NULL;
/* Fail Authentication 4 times */
for (int i = 0; i < 4; ++i) {
EXPECT_REQ(Authenticate(profile_id),
AuthenticateResult(FACEAUTH_SUCCESS).SetMatch(false));
}
/* Fifth Authentication failure should trigger lockout event */
EXPECT_REQ(Authenticate(profile_id),
AuthenticateResult(FACEAUTH_SUCCESS)
.SetMatch(false)
.SetLockoutEvent(FACEAUTH_LOCKOUT_ENFORCED));
embed_base = original_embed_base;
}
uint32_t GetFeature(uint32_t profile_id) {
return GetUserInfo(profile_id).GetFirst();
}
Result SetFeature(uint32_t profile_id, uint32_t feature) {
Result generate_result = Device::GenerateChallenge();
EXPECT_REQ(generate_result,
Result(FACEAUTH_SUCCESS).SetChallenge(Device::GetChallenge()));
Result ret =
Transaction(
Task(profile_id, FACEAUTH_CMD_SET_FEATURE).SetFirst(feature),
Embedding(), Token(Device::GetChallenge(), user_id, 0))
.Finalize()
.Run()
.GetResult();
EXPECT_REQ(Device::RevokeChallenge(), Result(FACEAUTH_SUCCESS));
return ret;
}
Result ClrFeature(uint32_t profile_id, uint32_t feature) {
Result generate_result = Device::GenerateChallenge();
EXPECT_REQ(generate_result,
Result(FACEAUTH_SUCCESS).SetChallenge(Device::GetChallenge()));
Result ret =
Transaction(
Task(profile_id, FACEAUTH_CMD_CLR_FEATURE).SetFirst(feature),
Embedding(), Token(Device::GetChallenge(), user_id, 0))
.Finalize()
.Run()
.GetResult();
EXPECT_REQ(Device::RevokeChallenge(), Result(FACEAUTH_SUCCESS));
return ret;
}
uint64_t GetOperationID() { return operation_id; }
uint64_t GetAuthID() { return auth_id; }
uint64_t GetUserID() { return user_id; }
private:
uint64_t operation_id;
uint64_t auth_id = 0;
uint64_t user_id;
vector<uint8_t> embed_base;
uint8_t embed_version;
};
class SuccessfulAuthenticateResult : public AuthenticateResult {
public:
SuccessfulAuthenticateResult(User u) : AuthenticateResult(FACEAUTH_SUCCESS) {
token = Token(u.GetOperationID(), u.GetUserID(), u.GetAuthID());
SetMatch(true);
}
};
TEST_F(FaceAuthTest, GetProfileInfoTest) {
vector<User> users;
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
users.push_back(User(EMBEDDING_VECTOR_1));
users[i].SetEmbeddingVersion(1);
}
users[0].SetUserID(0x1122334455667788);
users[1].SetUserID(0x1122334455667788);
users[3].SetUserID(0x1122334455667788);
for (int i = 0; i < MAX_NUM_PROFILES; ++i) users[i].Enroll(i + 1);
union {
uint8_t map[8] = {0};
uint64_t info;
};
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
for (int j = 0; j < MAX_NUM_PROFILES; ++j) {
map[i] |= ((users[i].GetUserID() == users[j].GetUserID()) << j);
}
}
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
map[MAX_NUM_PROFILES] = map[i];
EXPECT_REQ(users[i].GetProfileInfo(),
Result(FACEAUTH_SUCCESS).SetChallenge(info));
EXPECT_REQ(users[i].GetUserInfo(i + 1),
Result(FACEAUTH_SUCCESS).SetThird(map[i]));
}
}
TEST_F(FaceAuthTest, EnrollShouldOnlyAcceptSameUser) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
User user2(EMBEDDING_VECTOR_NULL);
user2.SetEmbeddingVersion(1);
/* User 1 Enroll to Profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* User 2 Enroll to Profile 1 should fail */
enroll_result = user2.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result, Result(FACEAUTH_ERR_INVALID_USER_ID));
/* User 1 Enroll to Profile 1 should be successful */
enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
}
TEST_F(FaceAuthTest, EnrollAfterWipeShouldBeSuccessful) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
User user2(EMBEDDING_VECTOR_1);
user2.SetEmbeddingVersion(1);
/* Enroll to Profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* Profile 1 Authentication should now pass */
AuthenticateResult auth_result = user1.Authenticate(PROFILE_1);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user1));
ASSERT_TRUE(nugget_tools::WipeUserData(client.get()));
/* Enroll to Profile 1 */
enroll_result = user2.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user2.GetAuthID()));
/* Profile 1 Authentication should now pass */
auth_result = user2.Authenticate(PROFILE_1);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user2));
}
TEST_F(FaceAuthTest, ValidUserIDCheck) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
User user2(EMBEDDING_VECTOR_1);
user2.SetEmbeddingVersion(1);
/* User 1 Enroll to Profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* Reset Lockout User ID Check */
EXPECT_REQ(user1.ResetLockout(PROFILE_1), Result(FACEAUTH_SUCCESS));
EXPECT_REQ(user2.ResetLockout(PROFILE_1),
Result(FACEAUTH_ERR_INVALID_USER_ID));
/* Set Feature User ID Check */
EXPECT_REQ(user1.SetFeature(PROFILE_1, 0), Result(FACEAUTH_SUCCESS));
EXPECT_REQ(user2.SetFeature(PROFILE_1, 0),
Result(FACEAUTH_ERR_INVALID_USER_ID));
/* Clear Feature User ID Check */
EXPECT_REQ(user1.ClrFeature(PROFILE_1, 0), Result(FACEAUTH_SUCCESS));
EXPECT_REQ(user2.ClrFeature(PROFILE_1, 0),
Result(FACEAUTH_ERR_INVALID_USER_ID));
}
TEST_F(FaceAuthTest, EnrollMismatchVersionShouldFail) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
/* Enroll using version 1 to profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* Enroll using version 2 to the same profile slot should fail */
EXPECT_REQ(user1.SetEmbeddingVersion(2).Enroll(PROFILE_1),
Result(FACEAUTH_ERR_EMBEDDING_VERSION));
}
TEST_F(FaceAuthTest, SFSFullTest) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
/* Enroll to Profile1 20 times */
for (int i = 0; i < 20; ++i) {
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
}
/* Enrolling one more time should fail */
EXPECT_REQ(user1.Enroll(PROFILE_1), Result(FACEAUTH_ERR_SFS_FULL));
}
TEST_F(FaceAuthTest, SimpleMatchMismatchTest) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
/* Profile 1 is empty: authentication will fail */
EXPECT_REQ(user1.Authenticate(PROFILE_1),
AuthenticateResult(FACEAUTH_SUCCESS).SetMatch(false));
/* Enroll to Profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* Profile 1 Authentication should now pass */
AuthenticateResult auth_result = user1.Authenticate(PROFILE_1);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user1));
/* Erase Profile 1*/
EXPECT_REQ(Device::Erase(PROFILE_1), Result(FACEAUTH_SUCCESS));
/* Profile 1 is now empty again: authentication should fail */
EXPECT_REQ(user1.Authenticate(PROFILE_1),
AuthenticateResult(FACEAUTH_SUCCESS).SetMatch(false));
}
TEST_F(FaceAuthTest, EmbeddingMismatchVersionComparisonShouldError) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
/* Enroll to Profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* Upgrade Embedding Version for User 1 */
user1.SetEmbeddingVersion(2);
/* Authenticate should now return recalibration error */
EXPECT_REQ(user1.Authenticate(PROFILE_1), Result(FACEAUTH_ERR_RECALIBRATE));
}
TEST_F(FaceAuthTest, MigrateShouldPreventDowngrade) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(5);
/* User1: enroll to profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* User1: authenticate successfully to profile 1 */
AuthenticateResult auth_result = user1.Authenticate(PROFILE_1);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user1));
/* User1: downgrade and attempt to migrate to profile 3 */
EXPECT_REQ(user1.SetEmbeddingVersion(4).Migrate(PROFILE_3, 0),
Result(FACEAUTH_ERR_EMBEDDING_DOWNGRADE));
}
TEST_F(FaceAuthTest, MigrateShouldPreventMismatchVersion) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(5);
/* User1: enroll to profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* User1: authenticate successfully to profile 1 */
AuthenticateResult auth_result = user1.Authenticate(PROFILE_1);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user1));
/* User1: upgrade and migrate to profile 3 */
EXPECT_REQ(user1.SetEmbeddingVersion(6).Migrate(PROFILE_3, 1),
MigrateResult(FACEAUTH_SUCCESS).SetEmbeddingNum(1).SetMatch(true));
EXPECT_REQ(user1.SetEmbeddingVersion(7).Migrate(PROFILE_3, 0),
Result(FACEAUTH_ERR_EMBEDDING_VERSION));
}
TEST_F(FaceAuthTest, MigrateShouldCopyUserInfo) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(5);
/* User1: enroll to profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* User1: set feature */
EXPECT_REQ(user1.SetFeature(PROFILE_1, 0xFACEDEAD), Result(FACEAUTH_SUCCESS));
/* User1: authenticate successfully to profile 1 */
AuthenticateResult auth_result = user1.Authenticate(PROFILE_1);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user1));
/* User1: migrate to profile 3 */
EXPECT_REQ(user1.SetEmbeddingVersion(8).Migrate(PROFILE_3, 1),
MigrateResult(FACEAUTH_SUCCESS).SetEmbeddingNum(1).SetMatch(true));
EXPECT_EQ(0xFACEDEAD, user1.GetFeature(PROFILE_3));
/* User1: authenticate successfully using profile 3 */
auth_result = user1.Authenticate(PROFILE_3);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user1));
}
TEST_F(FaceAuthTest, SimpleAuthenticateMigrateFlow) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
User user2(EMBEDDING_VECTOR_1);
user2.SetEmbeddingVersion(1);
/* User1: enroll to profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* User2: enroll to profile 2 */
enroll_result = user2.Enroll(PROFILE_2);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user2.GetAuthID()));
/* User1: authenticate successfully to profile 1 */
AuthenticateResult auth_result = user1.Authenticate(PROFILE_1);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user1));
/* User1: migrate to profile 3 */
EXPECT_REQ(user1.Migrate(PROFILE_3, 0), MigrateResult(FACEAUTH_SUCCESS));
/* User2: authenticate successfully to profile 2 */
auth_result = user2.Authenticate(PROFILE_2);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user2));
/* User2: migrate to profile 3 should fail */
EXPECT_REQ(user2.Migrate(PROFILE_3, 0),
MigrateResult(FACEAUTH_ERR_INVALID_USER_ID));
/* User2: migrate to profile 4 */
EXPECT_REQ(user2.Migrate(PROFILE_4, 1),
MigrateResult(FACEAUTH_SUCCESS).SetEmbeddingNum(1).SetMatch(true));
EXPECT_REQ(user2.Migrate(PROFILE_4, 1),
MigrateResult(FACEAUTH_SUCCESS).SetEmbeddingNum(2).SetMatch(true));
}
TEST_F(FaceAuthTest, FiveAuthenticationFailureSequenceTest) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
User user2(EMBEDDING_VECTOR_NULL);
user2.SetEmbeddingVersion(1);
/* Enroll to Profile 1 */
Result enroll_result = user1.Enroll(PROFILE_1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
/* Fail Authentication 4 times */
for (int i = 0; i < 4; ++i) {
EXPECT_REQ(user2.Authenticate(PROFILE_1),
AuthenticateResult(FACEAUTH_SUCCESS).SetMatch(false));
}
/* Fifth Authentication failure should trigger lockout event */
EXPECT_REQ(user2.Authenticate(PROFILE_1),
AuthenticateResult(FACEAUTH_SUCCESS)
.SetMatch(false)
.SetLockoutEvent(FACEAUTH_LOCKOUT_ENFORCED));
/* User will be throttled for 30 seconds */
Result result = user2.GetUserInfo(PROFILE_1);
EXPECT_GE(result.GetSecond(), 25);
EXPECT_REQ(
result,
Result(FACEAUTH_SUCCESS).SetSecond(result.GetSecond()).SetThird(1));
/* Following Authentication attempt will be throttled */
EXPECT_REQ(user2.Authenticate(PROFILE_1),
AuthenticateResult(FACEAUTH_ERR_THROTTLE));
/* Reset Lockout should trigger lockout event */
EXPECT_REQ(user1.ResetLockout(PROFILE_1),
ResetLockoutResult(FACEAUTH_SUCCESS)
.SetLockoutEvent(FACEAUTH_LOCKOUT_REMOVED));
}
TEST_F(FaceAuthTest, ExhaustiveLockoutTest) {
vector<User> users;
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
users.push_back(User(EMBEDDING_VECTOR_1).SetEmbeddingVersion(1));
Result enroll_result = users[i].Enroll(i + 1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(users[i].GetAuthID()));
EXPECT_EQ(users[i].IsProfileLocked(i + 1), false);
}
bool lock_test_vectors[6] = {true, false, false, false, true, true};
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
if (lock_test_vectors[i]) {
users[i].LockProfile(i + 1);
}
}
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
EXPECT_EQ(users[i].IsProfileLocked(i + 1), lock_test_vectors[i]);
}
bool unlock_test_vectors[6] = {true, false, false, false, true, false};
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
if (unlock_test_vectors[i]) {
users[i].ResetLockout(i + 1);
}
}
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
EXPECT_EQ(users[i].IsProfileLocked(i + 1),
lock_test_vectors[i] && !unlock_test_vectors[i]);
}
}
TEST_F(FaceAuthTest, ExhaustiveFeatureTest) {
vector<User> users;
uint32_t feature_msk[MAX_NUM_PROFILES] = {0};
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
users.push_back(User(EMBEDDING_VECTOR_NULL).SetEmbeddingVersion(1));
Result enroll_result = users[i].Enroll(i + 1);
EXPECT_REQ(enroll_result,
Result(FACEAUTH_SUCCESS).SetChallenge(users[i].GetAuthID()));
}
uint32_t index = 0;
for (int k = 0; k < 5; ++k) {
for (int i = 0; i < MAX_NUM_PROFILES; ++i)
EXPECT_EQ(users[i].GetFeature(i + 1), feature_msk[i]);
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
EXPECT_REQ(users[i].SetFeature(i + 1, 1 << index),
Result(FACEAUTH_SUCCESS));
feature_msk[i] |= (1 << index);
index++;
}
}
index = 0;
for (int k = 0; k < 5; ++k) {
for (int i = 0; i < MAX_NUM_PROFILES; ++i)
EXPECT_EQ(users[i].GetFeature(i + 1), feature_msk[i]);
for (int i = 0; i < MAX_NUM_PROFILES; ++i) {
EXPECT_REQ(users[i].ClrFeature(i + 1, 1 << index),
Result(FACEAUTH_SUCCESS));
feature_msk[i] &= ~(1 << index);
index++;
}
}
}
static void FullMatchMismatchTest(uint32_t profile_1, uint32_t profile_2) {
User user1(EMBEDDING_VECTOR_1);
user1.SetEmbeddingVersion(1);
User user2(EMBEDDING_VECTOR_NULL);
user2.SetEmbeddingVersion(1);
for (uint32_t i = 0; i < 20; ++i) {
Result result;
result = user1.SetEmbeddingBase(EMBEDDING_VECTOR_1).Enroll(profile_1);
EXPECT_REQ(result,
Result(FACEAUTH_SUCCESS).SetChallenge(user1.GetAuthID()));
result = user2.SetEmbeddingBase(EMBEDDING_VECTOR_2).Enroll(profile_2);
EXPECT_REQ(result,
Result(FACEAUTH_SUCCESS).SetChallenge(user2.GetAuthID()));
}
user1.SetEmbeddingBase(EMBEDDING_VECTOR_1);
user2.SetEmbeddingBase(EMBEDDING_VECTOR_2);
AuthenticateResult auth_result = user1.Authenticate(profile_1);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user1));
auth_result = user2.Authenticate(profile_1);
EXPECT_REQ(auth_result, AuthenticateResult(FACEAUTH_SUCCESS).SetMatch(false));
auth_result = user1.Authenticate(profile_2);
EXPECT_REQ(auth_result, AuthenticateResult(FACEAUTH_SUCCESS).SetMatch(false));
auth_result = user2.Authenticate(profile_2);
EXPECT_AEQ(auth_result, SuccessfulAuthenticateResult(user2));
}
TEST_F(FaceAuthTest, ExhaustiveMatchMismatchTest) {
FullMatchMismatchTest(PROFILE_1, PROFILE_6);
FullMatchMismatchTest(PROFILE_2, PROFILE_5);
FullMatchMismatchTest(PROFILE_3, PROFILE_4);
}
}