libfprint/tests/test-validity-hal.c
Leonardo Francisco 52606ccebc validity: Iteration 7 — Device Pairing & Hardware Abstraction Layer
Add HAL (validity_hal.h/c) with per-device lookup table for 4 PIDs
(0090, 0097, 009a, 009d). Each entry holds init_hardcoded, clean_slate,
reset_blob, db_write_enable blobs and a flash layout with partition
table + RSA signature.

Add device pairing SSM (validity_pair.h/c) — a 30-state machine that
runs as a child of the open SSM when the sensor has no TLS partitions.
Phases: raw USB keygen + partition flash, TLS handshake, erase 5
partitions, write 4096-byte TLS flash image, reboot.

Integration:
- OPEN_PAIR state in open SSM (between FWEXT and TLS_READ_FLASH)
- Skipped in emulation, no-fwext, or already-paired cases
- Post-reboot returns FP_DEVICE_ERROR_REMOVED for fprintd retry

Migration:
- validity_db.c and validity_fwext.c now use HAL lookups
- Removed hardcoded validity_blob_dbe_009a.inc

Tests: 24 new test cases (10 HAL + 14 pairing), 0 regressions.
Result: 40 OK, 0 Fail, 2 Skipped.
2026-04-22 03:06:34 +00:00

251 lines
8.7 KiB
C

/*
* Unit tests for validity HAL (device descriptor lookup and flash layout)
*
* 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 "drivers/validity/validity.h"
#include "drivers/validity/validity_hal.h"
/* ================================================================
* T7.1: HAL lookup by device type — all valid types return non-NULL
* ================================================================ */
static void
test_hal_lookup_all_types (void)
{
const guint types[] = { VALIDITY_DEV_90, VALIDITY_DEV_97,
VALIDITY_DEV_9A, VALIDITY_DEV_9D };
for (guint i = 0; i < G_N_ELEMENTS (types); i++)
{
const ValidityDeviceDesc *desc = validity_hal_device_lookup (types[i]);
g_assert_nonnull (desc);
g_assert_cmpuint (desc->vid, >, 0);
g_assert_cmpuint (desc->pid, >, 0);
}
}
/* ================================================================
* T7.2: HAL lookup by PID — all supported VID/PID combos
* ================================================================ */
static void
test_hal_lookup_by_pid (void)
{
/* All 4 supported devices */
struct { guint16 vid; guint16 pid; } devices[] = {
{ 0x138a, 0x0090 },
{ 0x138a, 0x0097 },
{ 0x06cb, 0x009a },
{ 0x138a, 0x009d },
};
for (guint i = 0; i < G_N_ELEMENTS (devices); i++)
{
const ValidityDeviceDesc *desc =
validity_hal_device_lookup_by_pid (devices[i].vid, devices[i].pid);
g_assert_nonnull (desc);
g_assert_cmpuint (desc->vid, ==, devices[i].vid);
g_assert_cmpuint (desc->pid, ==, devices[i].pid);
}
}
/* ================================================================
* T7.3: HAL lookup — invalid type returns NULL
* ================================================================ */
static void
test_hal_lookup_invalid (void)
{
const ValidityDeviceDesc *desc = validity_hal_device_lookup (99);
g_assert_null (desc);
}
/* ================================================================
* T7.4: HAL lookup by PID — unknown PID returns NULL
* ================================================================ */
static void
test_hal_lookup_by_pid_invalid (void)
{
const ValidityDeviceDesc *desc =
validity_hal_device_lookup_by_pid (0x1234, 0x5678);
g_assert_null (desc);
}
/* ================================================================
* T7.5: All devices have non-empty blobs
* ================================================================ */
static void
test_hal_blobs_present (void)
{
const guint types[] = { VALIDITY_DEV_90, VALIDITY_DEV_97,
VALIDITY_DEV_9A, VALIDITY_DEV_9D };
for (guint i = 0; i < G_N_ELEMENTS (types); i++)
{
const ValidityDeviceDesc *desc = validity_hal_device_lookup (types[i]);
g_assert_nonnull (desc);
/* init_hardcoded must be present for all */
g_assert_nonnull (desc->init_hardcoded);
g_assert_cmpuint (desc->init_hardcoded_len, >, 0);
/* reset_blob must be present for all */
g_assert_nonnull (desc->reset_blob);
g_assert_cmpuint (desc->reset_blob_len, >, 0);
/* db_write_enable must be present for all */
g_assert_nonnull (desc->db_write_enable);
g_assert_cmpuint (desc->db_write_enable_len, >, 0);
}
}
/* ================================================================
* T7.6: PID 0090 has smaller db partition and no clean_slate blob
* ================================================================ */
static void
test_hal_pid_0090_specifics (void)
{
const ValidityDeviceDesc *desc = validity_hal_device_lookup (VALIDITY_DEV_90);
g_assert_nonnull (desc);
/* 0090 has no init_hardcoded_clean_slate */
g_assert_null (desc->init_clean_slate);
g_assert_cmpuint (desc->init_clean_slate_len, ==, 0);
/* Flash layout should exist */
g_assert_nonnull (desc->flash_layout);
g_assert_cmpuint (desc->flash_layout->num_partitions, ==,
VALIDITY_FLASH_NUM_PARTITIONS);
}
/* ================================================================
* T7.7: Non-0090 devices have init_hardcoded_clean_slate
* ================================================================ */
static void
test_hal_clean_slate_present (void)
{
const guint types[] = { VALIDITY_DEV_97, VALIDITY_DEV_9A, VALIDITY_DEV_9D };
for (guint i = 0; i < G_N_ELEMENTS (types); i++)
{
const ValidityDeviceDesc *desc = validity_hal_device_lookup (types[i]);
g_assert_nonnull (desc);
g_assert_nonnull (desc->init_clean_slate);
g_assert_cmpuint (desc->init_clean_slate_len, >, 0);
}
}
/* ================================================================
* T7.8: Flash layout has valid partition table
* ================================================================ */
static void
test_hal_flash_layout (void)
{
const guint types[] = { VALIDITY_DEV_90, VALIDITY_DEV_97,
VALIDITY_DEV_9A, VALIDITY_DEV_9D };
for (guint i = 0; i < G_N_ELEMENTS (types); i++)
{
const ValidityDeviceDesc *desc = validity_hal_device_lookup (types[i]);
g_assert_nonnull (desc);
g_assert_nonnull (desc->flash_layout);
const ValidityFlashLayout *layout = desc->flash_layout;
g_assert_cmpuint (layout->num_partitions, ==,
VALIDITY_FLASH_NUM_PARTITIONS);
/* Signature must be 256 bytes */
g_assert_nonnull (layout->partition_sig);
g_assert_cmpuint (layout->partition_sig_len, ==,
VALIDITY_PARTITION_SIG_SIZE);
/* Verify partitions are ordered and non-overlapping */
for (guint p = 0; p < layout->num_partitions; p++)
{
const ValidityPartition *part = &layout->partitions[p];
g_assert_cmpuint (part->size, >, 0);
if (p > 0)
{
const ValidityPartition *prev = &layout->partitions[p - 1];
g_assert_cmpuint (part->offset, >=,
prev->offset + prev->size);
}
}
}
}
/* ================================================================
* T7.9: Blob sizes match expected values from python-validity
* ================================================================ */
static void
test_hal_blob_sizes (void)
{
const ValidityDeviceDesc *desc_9a =
validity_hal_device_lookup (VALIDITY_DEV_9A);
g_assert_nonnull (desc_9a);
/* 009a blobs: init=581, clean_slate=741, reset=12037, dbe=3621 */
g_assert_cmpuint (desc_9a->init_hardcoded_len, ==, 581);
g_assert_cmpuint (desc_9a->init_clean_slate_len, ==, 741);
g_assert_cmpuint (desc_9a->reset_blob_len, ==, 12037);
g_assert_cmpuint (desc_9a->db_write_enable_len, ==, 3621);
const ValidityDeviceDesc *desc_90 =
validity_hal_device_lookup (VALIDITY_DEV_90);
g_assert_nonnull (desc_90);
/* 0090 blobs: init=485, no clean_slate, reset=11493, dbe=1765 */
g_assert_cmpuint (desc_90->init_hardcoded_len, ==, 485);
g_assert_cmpuint (desc_90->reset_blob_len, ==, 11493);
g_assert_cmpuint (desc_90->db_write_enable_len, ==, 1765);
}
/* ================================================================
* T7.10: Lookup consistency — by-type and by-PID return same pointer
* ================================================================ */
static void
test_hal_lookup_consistency (void)
{
const ValidityDeviceDesc *by_type =
validity_hal_device_lookup (VALIDITY_DEV_9A);
const ValidityDeviceDesc *by_pid =
validity_hal_device_lookup_by_pid (0x06cb, 0x009a);
g_assert_true (by_type == by_pid);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/validity/hal/lookup-all-types",
test_hal_lookup_all_types);
g_test_add_func ("/validity/hal/lookup-by-pid",
test_hal_lookup_by_pid);
g_test_add_func ("/validity/hal/lookup-invalid",
test_hal_lookup_invalid);
g_test_add_func ("/validity/hal/lookup-by-pid-invalid",
test_hal_lookup_by_pid_invalid);
g_test_add_func ("/validity/hal/blobs-present",
test_hal_blobs_present);
g_test_add_func ("/validity/hal/pid-0090-specifics",
test_hal_pid_0090_specifics);
g_test_add_func ("/validity/hal/clean-slate-present",
test_hal_clean_slate_present);
g_test_add_func ("/validity/hal/flash-layout",
test_hal_flash_layout);
g_test_add_func ("/validity/hal/blob-sizes",
test_hal_blob_sizes);
g_test_add_func ("/validity/hal/lookup-consistency",
test_hal_lookup_consistency);
return g_test_run ();
}