mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2026-05-14 14:38:09 +02:00
Add core fingerprint operations: enrollment, verification, identification, print listing, print deletion, and storage clearing. New files: - validity_db.h/c: On-chip template database operations — command builders for all DB commands (0x45-0x4B, 0x47-0x48, 0x51, 0x5E, 0x60, 0x62, 0x63, 0x64, 0x68, 0x69, 0x6B), response parsers for DB info/user storage/user/ record value/record children/new record ID, identity builder (UUID→VCSFW binary), finger data builder, and db_write_enable blob accessor. - validity_enroll.c: 31-state enrollment SSM with interrupt-driven finger detection (EP 0x83), capture command orchestration via build_cmd_02(), enrollment session management (create/update/commit), DB record creation (user + finger), and LED glow feedback. - validity_verify.c: 17-state verify/identify SSM with match command dispatching (cmd 0x5E for verify, cmd 0x60 for identify), 6-state list SSM for enumerating enrolled prints via GPtrArray, 8-state delete SSM, and clear_storage stub. Modified files: - validity.h: Added DB header include, 5 new state enums (CalibState, EnrollState, VerifyState, ListState, DeleteState), new struct fields for enrollment/verification/list/delete state, function declarations. - validity.c: Replaced all operation stubs with real implementations, added cleanup for new fields in dev_close, wired all FpDevice methods. - meson.build: Added 3 new source files to driver. - tests/meson.build: Added test-validity-db executable. - tests/validity/custom.py: Updated feature assertions (STORAGE, STORAGE_LIST, STORAGE_CLEAR now enabled). Tests: 29 new unit tests in test-validity-db.c covering all command builders, response parsers, identity/finger data builders, and blob accessor. All 37 tests pass (0 fail, 2 skip).
749 lines
27 KiB
C
749 lines
27 KiB
C
/*
|
|
* Unit tests for validity database operations
|
|
*
|
|
* Copyright (C) 2024 libfprint contributors
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*/
|
|
|
|
#include <glib.h>
|
|
#include <string.h>
|
|
|
|
#include "fpi-byte-utils.h"
|
|
|
|
#include "drivers/validity/validity_db.h"
|
|
#include "drivers/validity/vcsfw_protocol.h"
|
|
|
|
/* ================================================================
|
|
* T6.1: test_cmd_db_info
|
|
*
|
|
* Verify cmd 0x45 (DB info) is a single-byte command.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_db_info (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_info (&len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 1);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_DB_INFO);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.2: test_cmd_get_user_storage
|
|
*
|
|
* Verify cmd 0x4B format with a known storage name.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_get_user_storage (void)
|
|
{
|
|
gsize len;
|
|
const gchar *name = "StgWindsor";
|
|
gsize name_len = strlen (name) + 1; /* includes NUL */
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_get_user_storage (name, &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 1 + 2 + 2 + name_len); /* cmd + dbid + name_len + name */
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_GET_USER_STORAGE);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[1]), ==, 0); /* dbid = 0 → lookup by name */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[3]), ==, name_len);
|
|
g_assert_cmpmem (&cmd[5], name_len, name, name_len);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.3: test_cmd_get_user_storage_null_name
|
|
*
|
|
* When name is NULL, should produce a command with zero name_len.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_get_user_storage_null_name (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_get_user_storage (NULL, &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 5); /* cmd(1) + dbid(2) + name_len(2) */
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_GET_USER_STORAGE);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[3]), ==, 0); /* name_len = 0 */
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.4: test_cmd_get_user
|
|
*
|
|
* Verify cmd 0x4A format for get-by-dbid.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_get_user (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_get_user (0x1234, &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 7);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_GET_USER);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[1]), ==, 0x1234);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[3]), ==, 0);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[5]), ==, 0);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.5: test_cmd_lookup_user
|
|
*
|
|
* Verify cmd 0x4A format for lookup-by-identity.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_lookup_user (void)
|
|
{
|
|
gsize len;
|
|
guint8 identity[] = { 0x01, 0x02, 0x03, 0x04 };
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_lookup_user (
|
|
0x0003, identity, sizeof (identity), &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 7 + sizeof (identity));
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_GET_USER);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[1]), ==, 0); /* dbid = 0 */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[3]), ==, 0x0003); /* storage */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[5]), ==, sizeof (identity));
|
|
g_assert_cmpmem (&cmd[7], sizeof (identity), identity, sizeof (identity));
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.6: test_cmd_new_record
|
|
*
|
|
* Verify cmd 0x47 format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_new_record (void)
|
|
{
|
|
gsize len;
|
|
guint8 data[] = { 0xAA, 0xBB };
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_new_record (
|
|
0x0003, 0x0005, 0x0003, data, sizeof (data), &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 9 + sizeof (data));
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_NEW_RECORD);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[1]), ==, 0x0003); /* parent */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[3]), ==, 0x0005); /* type */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[5]), ==, 0x0003); /* storage */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[7]), ==, sizeof (data));
|
|
g_assert_cmpmem (&cmd[9], sizeof (data), data, sizeof (data));
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.7: test_cmd_del_record
|
|
*
|
|
* Verify cmd 0x48 format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_del_record (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_del_record (0xABCD, &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 3);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_DEL_RECORD);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[1]), ==, 0xABCD);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.8: test_cmd_create_enrollment
|
|
*
|
|
* Verify cmd 0x69 start and end variants.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_create_enrollment (void)
|
|
{
|
|
gsize len;
|
|
|
|
/* Start enrollment */
|
|
g_autofree guint8 *cmd_start = validity_db_build_cmd_create_enrollment (TRUE, &len);
|
|
g_assert_nonnull (cmd_start);
|
|
g_assert_cmpuint (len, ==, 5);
|
|
g_assert_cmpuint (cmd_start[0], ==, VCSFW_CMD_CREATE_ENROLLMENT);
|
|
g_assert_cmpuint (FP_READ_UINT32_LE (&cmd_start[1]), ==, 1);
|
|
|
|
/* End enrollment */
|
|
g_autofree guint8 *cmd_end = validity_db_build_cmd_create_enrollment (FALSE, &len);
|
|
g_assert_nonnull (cmd_end);
|
|
g_assert_cmpuint (len, ==, 5);
|
|
g_assert_cmpuint (cmd_end[0], ==, VCSFW_CMD_CREATE_ENROLLMENT);
|
|
g_assert_cmpuint (FP_READ_UINT32_LE (&cmd_end[1]), ==, 0);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.9: test_cmd_enrollment_update_start
|
|
*
|
|
* Verify cmd 0x68 format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_enrollment_update_start (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_enrollment_update_start (0x12345678, &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 9);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_ENROLLMENT_UPDATE_START);
|
|
g_assert_cmpuint (FP_READ_UINT32_LE (&cmd[1]), ==, 0x12345678);
|
|
g_assert_cmpuint (FP_READ_UINT32_LE (&cmd[5]), ==, 0);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.10: test_cmd_match_finger
|
|
*
|
|
* Verify cmd 0x5E format (13 bytes per python-validity).
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_match_finger (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_match_finger (&len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 13);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_MATCH_FINGER);
|
|
g_assert_cmpuint (cmd[1], ==, 0x02);
|
|
g_assert_cmpuint (cmd[2], ==, 0xFF);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[3]), ==, 0);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[5]), ==, 0);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[7]), ==, 1);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[9]), ==, 0);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[11]), ==, 0);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.11: test_cmd_get_match_result
|
|
*
|
|
* Verify cmd 0x60 format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_get_match_result (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_get_match_result (&len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 5);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_GET_MATCH_RESULT);
|
|
/* remaining bytes should be 0 */
|
|
g_assert_cmpuint (cmd[1], ==, 0);
|
|
g_assert_cmpuint (cmd[2], ==, 0);
|
|
g_assert_cmpuint (cmd[3], ==, 0);
|
|
g_assert_cmpuint (cmd[4], ==, 0);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.12: test_cmd_match_cleanup
|
|
*
|
|
* Verify cmd 0x62 format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_match_cleanup (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_match_cleanup (&len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 5);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_MATCH_CLEANUP);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.13: test_cmd_get_prg_status
|
|
*
|
|
* Verify cmd 0x51 both normal and extended variant.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_get_prg_status (void)
|
|
{
|
|
gsize len;
|
|
|
|
g_autofree guint8 *normal = validity_db_build_cmd_get_prg_status (FALSE, &len);
|
|
g_assert_cmpuint (len, ==, 5);
|
|
g_assert_cmpuint (normal[0], ==, VCSFW_CMD_GET_PRG_STATUS);
|
|
/* Normal: 00000000 */
|
|
g_assert_cmpuint (normal[1], ==, 0);
|
|
g_assert_cmpuint (normal[2], ==, 0);
|
|
g_assert_cmpuint (normal[3], ==, 0);
|
|
g_assert_cmpuint (normal[4], ==, 0);
|
|
|
|
g_autofree guint8 *ext = validity_db_build_cmd_get_prg_status (TRUE, &len);
|
|
g_assert_cmpuint (len, ==, 5);
|
|
g_assert_cmpuint (ext[0], ==, VCSFW_CMD_GET_PRG_STATUS);
|
|
/* Extended: 00200000 LE */
|
|
g_assert_cmpuint (ext[1], ==, 0x00);
|
|
g_assert_cmpuint (ext[2], ==, 0x20);
|
|
g_assert_cmpuint (ext[3], ==, 0x00);
|
|
g_assert_cmpuint (ext[4], ==, 0x00);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.14: test_cmd_capture_stop
|
|
*
|
|
* Verify cmd 0x04 format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_capture_stop (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_capture_stop (&len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 1);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_CAPTURE_STOP);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.15: test_cmd_call_cleanups
|
|
*
|
|
* Verify cmd 0x1a format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_call_cleanups (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_call_cleanups (&len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 1);
|
|
g_assert_cmpuint (cmd[0], ==, 0x1a);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.16: test_parse_db_info
|
|
*
|
|
* Construct a known db_info binary response and verify parsing.
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_db_info (void)
|
|
{
|
|
guint8 data[0x1C]; /* 24 bytes header + 4 bytes for 2 roots */
|
|
ValidityDbInfo info;
|
|
|
|
memset (data, 0, sizeof (data));
|
|
|
|
/* Header: unknown1=1, unknown0=0, total=65536, used=1024, free=64512, records=10, n_roots=2 */
|
|
FP_WRITE_UINT32_LE (&data[0], 1); /* unknown1 */
|
|
FP_WRITE_UINT32_LE (&data[4], 0); /* unknown0 */
|
|
FP_WRITE_UINT32_LE (&data[8], 65536); /* total */
|
|
FP_WRITE_UINT32_LE (&data[12], 1024); /* used */
|
|
FP_WRITE_UINT32_LE (&data[16], 64512); /* free */
|
|
FP_WRITE_UINT16_LE (&data[20], 10); /* records */
|
|
FP_WRITE_UINT16_LE (&data[22], 2); /* n_roots */
|
|
FP_WRITE_UINT16_LE (&data[24], 0x0001); /* root[0] */
|
|
FP_WRITE_UINT16_LE (&data[26], 0x0003); /* root[1] */
|
|
|
|
g_assert_true (validity_db_parse_info (data, sizeof (data), &info));
|
|
|
|
g_assert_cmpuint (info.unknown1, ==, 1);
|
|
g_assert_cmpuint (info.unknown0, ==, 0);
|
|
g_assert_cmpuint (info.total, ==, 65536);
|
|
g_assert_cmpuint (info.used, ==, 1024);
|
|
g_assert_cmpuint (info.free_space, ==, 64512);
|
|
g_assert_cmpuint (info.records, ==, 10);
|
|
g_assert_cmpuint (info.n_roots, ==, 2);
|
|
g_assert_nonnull (info.roots);
|
|
g_assert_cmpuint (info.roots[0], ==, 1);
|
|
g_assert_cmpuint (info.roots[1], ==, 3);
|
|
|
|
validity_db_info_clear (&info);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.17: test_parse_db_info_too_short
|
|
*
|
|
* A response shorter than 24 bytes should fail.
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_db_info_too_short (void)
|
|
{
|
|
guint8 data[20] = { 0 };
|
|
ValidityDbInfo info;
|
|
|
|
g_assert_false (validity_db_parse_info (data, sizeof (data), &info));
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.18: test_parse_user_storage
|
|
*
|
|
* Construct a user storage response with 2 users and verify.
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_user_storage (void)
|
|
{
|
|
/* Header: dbid=3, user_count=2, name_sz=11, unknown=0
|
|
* User table: {dbid=10, val_sz=100}, {dbid=11, val_sz=200}
|
|
* Name: "StgWindsor\0" */
|
|
gsize name_len = strlen ("StgWindsor") + 1;
|
|
gsize total = 8 + 2 * 4 + name_len;
|
|
g_autofree guint8 *data = g_new0 (guint8, total);
|
|
|
|
FP_WRITE_UINT16_LE (&data[0], 3); /* dbid */
|
|
FP_WRITE_UINT16_LE (&data[2], 2); /* user_count */
|
|
FP_WRITE_UINT16_LE (&data[4], name_len); /* name_sz */
|
|
FP_WRITE_UINT16_LE (&data[6], 0); /* unknown */
|
|
|
|
FP_WRITE_UINT16_LE (&data[8], 10); /* user[0].dbid */
|
|
FP_WRITE_UINT16_LE (&data[10], 100); /* user[0].val_sz */
|
|
FP_WRITE_UINT16_LE (&data[12], 11); /* user[1].dbid */
|
|
FP_WRITE_UINT16_LE (&data[14], 200); /* user[1].val_sz */
|
|
|
|
memcpy (&data[16], "StgWindsor", name_len);
|
|
|
|
ValidityUserStorage storage;
|
|
g_assert_true (validity_db_parse_user_storage (data, total, &storage));
|
|
|
|
g_assert_cmpuint (storage.dbid, ==, 3);
|
|
g_assert_cmpuint (storage.user_count, ==, 2);
|
|
g_assert_cmpstr (storage.name, ==, "StgWindsor");
|
|
g_assert_nonnull (storage.user_dbids);
|
|
g_assert_cmpuint (storage.user_dbids[0], ==, 10);
|
|
g_assert_cmpuint (storage.user_dbids[1], ==, 11);
|
|
g_assert_cmpuint (storage.user_val_sizes[0], ==, 100);
|
|
g_assert_cmpuint (storage.user_val_sizes[1], ==, 200);
|
|
|
|
validity_user_storage_clear (&storage);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.19: test_parse_user
|
|
*
|
|
* Construct a user response with one finger and verify.
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_user (void)
|
|
{
|
|
guint8 identity_bytes[] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
|
/* Header: dbid=10, finger_count=1, unknown=0, identity_sz=4
|
|
* Finger: dbid=20, subtype=2, storage=3, value_size=500
|
|
* Identity: 4 bytes */
|
|
gsize total = 8 + 8 + sizeof (identity_bytes);
|
|
g_autofree guint8 *data = g_new0 (guint8, total);
|
|
|
|
FP_WRITE_UINT16_LE (&data[0], 10); /* dbid */
|
|
FP_WRITE_UINT16_LE (&data[2], 1); /* finger_count */
|
|
FP_WRITE_UINT16_LE (&data[4], 0); /* unknown */
|
|
FP_WRITE_UINT16_LE (&data[6], sizeof (identity_bytes)); /* identity_sz */
|
|
|
|
/* Finger entry */
|
|
FP_WRITE_UINT16_LE (&data[8], 20); /* finger.dbid */
|
|
FP_WRITE_UINT16_LE (&data[10], 2); /* finger.subtype = right index */
|
|
FP_WRITE_UINT16_LE (&data[12], 3); /* finger.storage */
|
|
FP_WRITE_UINT16_LE (&data[14], 500); /* finger.value_size */
|
|
|
|
memcpy (&data[16], identity_bytes, sizeof (identity_bytes));
|
|
|
|
ValidityUser user;
|
|
g_assert_true (validity_db_parse_user (data, total, &user));
|
|
|
|
g_assert_cmpuint (user.dbid, ==, 10);
|
|
g_assert_cmpuint (user.finger_count, ==, 1);
|
|
g_assert_nonnull (user.fingers);
|
|
g_assert_cmpuint (user.fingers[0].dbid, ==, 20);
|
|
g_assert_cmpuint (user.fingers[0].subtype, ==, 2);
|
|
g_assert_cmpuint (user.fingers[0].storage, ==, 3);
|
|
g_assert_cmpuint (user.fingers[0].value_size, ==, 500);
|
|
g_assert_nonnull (user.identity);
|
|
g_assert_cmpuint (user.identity_len, ==, sizeof (identity_bytes));
|
|
g_assert_cmpmem (user.identity, user.identity_len,
|
|
identity_bytes, sizeof (identity_bytes));
|
|
|
|
validity_user_clear (&user);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.20: test_parse_new_record_id
|
|
*
|
|
* Verify parsing of new_record response (cmd 0x47).
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_new_record_id (void)
|
|
{
|
|
guint16 record_id;
|
|
guint8 data[] = { 0x42, 0x00 };
|
|
|
|
g_assert_true (validity_db_parse_new_record_id (data, sizeof (data), &record_id));
|
|
g_assert_cmpuint (record_id, ==, 0x0042);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.21: test_parse_new_record_id_too_short
|
|
*
|
|
* A 1-byte response should fail.
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_new_record_id_too_short (void)
|
|
{
|
|
guint16 record_id;
|
|
guint8 data[] = { 0x42 };
|
|
|
|
g_assert_false (validity_db_parse_new_record_id (data, sizeof (data), &record_id));
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.22: test_build_identity
|
|
*
|
|
* Build a UUID identity and verify structure.
|
|
* ================================================================ */
|
|
static void
|
|
test_build_identity (void)
|
|
{
|
|
gsize len;
|
|
const gchar *uuid = "550e8400-e29b-41d4-a716-446655440000";
|
|
g_autofree guint8 *id = validity_db_build_identity (uuid, &len);
|
|
|
|
g_assert_nonnull (id);
|
|
/* Minimum size enforced */
|
|
g_assert_cmpuint (len, >=, VALIDITY_IDENTITY_MIN_SIZE);
|
|
|
|
/* type = SID (3) */
|
|
g_assert_cmpuint (FP_READ_UINT32_LE (&id[0]), ==, VALIDITY_IDENTITY_TYPE_SID);
|
|
|
|
/* len field = UUID string length */
|
|
gsize uuid_len = strlen (uuid);
|
|
g_assert_cmpuint (FP_READ_UINT32_LE (&id[4]), ==, uuid_len);
|
|
|
|
/* UUID payload */
|
|
g_assert_cmpmem (&id[8], uuid_len, uuid, uuid_len);
|
|
|
|
/* Remaining bytes should be zero-padded */
|
|
for (gsize i = 8 + uuid_len; i < len; i++)
|
|
g_assert_cmpuint (id[i], ==, 0);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.23: test_build_finger_data
|
|
*
|
|
* Build finger data and verify the tagged format.
|
|
* ================================================================ */
|
|
static void
|
|
test_build_finger_data (void)
|
|
{
|
|
gsize len;
|
|
guint8 template[] = { 0x11, 0x22, 0x33 };
|
|
guint8 tid[] = { 0xAA, 0xBB };
|
|
g_autofree guint8 *fd = validity_db_build_finger_data (
|
|
2, template, sizeof (template), tid, sizeof (tid), &len);
|
|
|
|
g_assert_nonnull (fd);
|
|
|
|
/* Check header: subtype(2) | flags=3(2) | tinfo_len(2) | 0x20(2) */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&fd[0]), ==, 2); /* subtype */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&fd[2]), ==, 3); /* flags */
|
|
|
|
gsize expected_tinfo_len = 4 + sizeof (template) + 4 + sizeof (tid);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&fd[4]), ==, expected_tinfo_len);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&fd[6]), ==, 0x20);
|
|
|
|
/* Tag 1 (template) at offset 8 */
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&fd[8]), ==, 1);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&fd[10]), ==, sizeof (template));
|
|
g_assert_cmpmem (&fd[12], sizeof (template), template, sizeof (template));
|
|
|
|
/* Tag 2 (tid) at offset 12+3 = 15 */
|
|
gsize tid_offset = 12 + sizeof (template);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&fd[tid_offset]), ==, 2);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&fd[tid_offset + 2]), ==, sizeof (tid));
|
|
g_assert_cmpmem (&fd[tid_offset + 4], sizeof (tid), tid, sizeof (tid));
|
|
|
|
/* Total should be header(8) + tinfo + 0x20 padding */
|
|
gsize expected_total = 8 + expected_tinfo_len + 0x20;
|
|
g_assert_cmpuint (len, ==, expected_total);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.24: test_db_write_enable_blob
|
|
*
|
|
* Verify db_write_enable blob accessor returns a valid blob.
|
|
* ================================================================ */
|
|
static void
|
|
test_db_write_enable_blob (void)
|
|
{
|
|
gsize len;
|
|
const guint8 *blob = validity_db_get_write_enable_blob (&len);
|
|
|
|
g_assert_nonnull (blob);
|
|
g_assert_cmpuint (len, >, 0);
|
|
/* The 009a blob is 3621 bytes */
|
|
g_assert_cmpuint (len, ==, 3621);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.25: test_parse_record_value
|
|
*
|
|
* Construct a record value response and verify parsing.
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_record_value (void)
|
|
{
|
|
guint8 value[] = { 0x01, 0x02, 0x03 };
|
|
/* Format: dbid(2) type(2) storage(2) sz(2) pad(2) value */
|
|
gsize total = 10 + sizeof (value);
|
|
g_autofree guint8 *data = g_new0 (guint8, total);
|
|
|
|
FP_WRITE_UINT16_LE (&data[0], 42); /* dbid */
|
|
FP_WRITE_UINT16_LE (&data[2], 8); /* type = DATA */
|
|
FP_WRITE_UINT16_LE (&data[4], 3); /* storage */
|
|
FP_WRITE_UINT16_LE (&data[6], sizeof (value)); /* sz */
|
|
FP_WRITE_UINT16_LE (&data[8], 0); /* pad */
|
|
memcpy (&data[10], value, sizeof (value));
|
|
|
|
ValidityDbRecord record;
|
|
g_assert_true (validity_db_parse_record_value (data, total, &record));
|
|
|
|
g_assert_cmpuint (record.dbid, ==, 42);
|
|
g_assert_cmpuint (record.type, ==, 8);
|
|
g_assert_cmpuint (record.storage, ==, 3);
|
|
g_assert_nonnull (record.value);
|
|
g_assert_cmpuint (record.value_len, ==, sizeof (value));
|
|
g_assert_cmpmem (record.value, record.value_len, value, sizeof (value));
|
|
|
|
validity_db_record_clear (&record);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.26: test_parse_record_children
|
|
*
|
|
* Construct a record children response and verify parsing.
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_record_children (void)
|
|
{
|
|
/* Format: dbid(2) type(2) storage(2) sz(2) cnt(2) pad(2)
|
|
* children[cnt * 4: dbid(2) type(2)] */
|
|
gsize total = 12 + 2 * 4;
|
|
g_autofree guint8 *data = g_new0 (guint8, total);
|
|
|
|
FP_WRITE_UINT16_LE (&data[0], 3); /* dbid */
|
|
FP_WRITE_UINT16_LE (&data[2], 4); /* type = STORAGE */
|
|
FP_WRITE_UINT16_LE (&data[4], 3); /* storage */
|
|
FP_WRITE_UINT16_LE (&data[6], 0); /* sz */
|
|
FP_WRITE_UINT16_LE (&data[8], 2); /* child_count */
|
|
FP_WRITE_UINT16_LE (&data[10], 0); /* pad */
|
|
|
|
/* Children */
|
|
FP_WRITE_UINT16_LE (&data[12], 10); /* child[0].dbid */
|
|
FP_WRITE_UINT16_LE (&data[14], 5); /* child[0].type = USER */
|
|
FP_WRITE_UINT16_LE (&data[16], 11); /* child[1].dbid */
|
|
FP_WRITE_UINT16_LE (&data[18], 5); /* child[1].type = USER */
|
|
|
|
ValidityRecordChildren children;
|
|
g_assert_true (validity_db_parse_record_children (data, total, &children));
|
|
|
|
g_assert_cmpuint (children.dbid, ==, 3);
|
|
g_assert_cmpuint (children.type, ==, 4);
|
|
g_assert_cmpuint (children.storage, ==, 3);
|
|
g_assert_cmpuint (children.child_count, ==, 2);
|
|
g_assert_nonnull (children.children);
|
|
g_assert_cmpuint (children.children[0].dbid, ==, 10);
|
|
g_assert_cmpuint (children.children[0].type, ==, 5);
|
|
g_assert_cmpuint (children.children[1].dbid, ==, 11);
|
|
g_assert_cmpuint (children.children[1].type, ==, 5);
|
|
|
|
validity_record_children_clear (&children);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.27: test_cmd_enrollment_update
|
|
*
|
|
* Verify cmd 0x6B format with template data.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_enrollment_update (void)
|
|
{
|
|
gsize len;
|
|
guint8 prev[] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_enrollment_update (
|
|
prev, sizeof (prev), &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 1 + sizeof (prev));
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_ENROLLMENT_UPDATE);
|
|
g_assert_cmpmem (&cmd[1], sizeof (prev), prev, sizeof (prev));
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.28: test_cmd_get_record_value
|
|
*
|
|
* Verify cmd 0x49 format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_get_record_value (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_get_record_value (0x5678, &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 3);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_GET_RECORD_VALUE);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[1]), ==, 0x5678);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T6.29: test_cmd_get_record_children
|
|
*
|
|
* Verify cmd 0x46 format.
|
|
* ================================================================ */
|
|
static void
|
|
test_cmd_get_record_children (void)
|
|
{
|
|
gsize len;
|
|
g_autofree guint8 *cmd = validity_db_build_cmd_get_record_children (0x1234, &len);
|
|
|
|
g_assert_nonnull (cmd);
|
|
g_assert_cmpuint (len, ==, 3);
|
|
g_assert_cmpuint (cmd[0], ==, VCSFW_CMD_GET_RECORD_CHILDREN);
|
|
g_assert_cmpuint (FP_READ_UINT16_LE (&cmd[1]), ==, 0x1234);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
/* Command builder tests */
|
|
g_test_add_func ("/validity/db/cmd_db_info", test_cmd_db_info);
|
|
g_test_add_func ("/validity/db/cmd_get_user_storage", test_cmd_get_user_storage);
|
|
g_test_add_func ("/validity/db/cmd_get_user_storage_null_name", test_cmd_get_user_storage_null_name);
|
|
g_test_add_func ("/validity/db/cmd_get_user", test_cmd_get_user);
|
|
g_test_add_func ("/validity/db/cmd_lookup_user", test_cmd_lookup_user);
|
|
g_test_add_func ("/validity/db/cmd_new_record", test_cmd_new_record);
|
|
g_test_add_func ("/validity/db/cmd_del_record", test_cmd_del_record);
|
|
g_test_add_func ("/validity/db/cmd_create_enrollment", test_cmd_create_enrollment);
|
|
g_test_add_func ("/validity/db/cmd_enrollment_update_start", test_cmd_enrollment_update_start);
|
|
g_test_add_func ("/validity/db/cmd_enrollment_update", test_cmd_enrollment_update);
|
|
g_test_add_func ("/validity/db/cmd_match_finger", test_cmd_match_finger);
|
|
g_test_add_func ("/validity/db/cmd_get_match_result", test_cmd_get_match_result);
|
|
g_test_add_func ("/validity/db/cmd_match_cleanup", test_cmd_match_cleanup);
|
|
g_test_add_func ("/validity/db/cmd_get_prg_status", test_cmd_get_prg_status);
|
|
g_test_add_func ("/validity/db/cmd_capture_stop", test_cmd_capture_stop);
|
|
g_test_add_func ("/validity/db/cmd_call_cleanups", test_cmd_call_cleanups);
|
|
g_test_add_func ("/validity/db/cmd_get_record_value", test_cmd_get_record_value);
|
|
g_test_add_func ("/validity/db/cmd_get_record_children", test_cmd_get_record_children);
|
|
|
|
/* Response parser tests */
|
|
g_test_add_func ("/validity/db/parse_info", test_parse_db_info);
|
|
g_test_add_func ("/validity/db/parse_info_too_short", test_parse_db_info_too_short);
|
|
g_test_add_func ("/validity/db/parse_user_storage", test_parse_user_storage);
|
|
g_test_add_func ("/validity/db/parse_user", test_parse_user);
|
|
g_test_add_func ("/validity/db/parse_new_record_id", test_parse_new_record_id);
|
|
g_test_add_func ("/validity/db/parse_new_record_id_too_short", test_parse_new_record_id_too_short);
|
|
g_test_add_func ("/validity/db/parse_record_value", test_parse_record_value);
|
|
g_test_add_func ("/validity/db/parse_record_children", test_parse_record_children);
|
|
|
|
/* Identity and finger data tests */
|
|
g_test_add_func ("/validity/db/build_identity", test_build_identity);
|
|
g_test_add_func ("/validity/db/build_finger_data", test_build_finger_data);
|
|
|
|
/* Blob accessor test */
|
|
g_test_add_func ("/validity/db/write_enable_blob", test_db_write_enable_blob);
|
|
|
|
return g_test_run ();
|
|
}
|