mirror of
https://gitlab.freedesktop.org/libfprint/libfprint.git
synced 2026-05-15 01:08:08 +02:00
Apply uncrustify formatting to all validity driver and test files to pass the CI test_indent check. Fix two pre-existing test failures: - test-validity-capture: LED command blobs are 125 bytes, not 128 - test-validity-enroll: add 2-byte length prefix to test data to match parser's expected format, fix empty-data assertion (parser returns FALSE for data_len < 2) All 41 tests pass, 0 failures.
328 lines
12 KiB
C
328 lines
12 KiB
C
/*
|
|
* Unit tests for enrollment response parsing
|
|
*
|
|
* 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.h"
|
|
|
|
/* ================================================================
|
|
* Helper: build a tagged block
|
|
* [tag:2LE][len:2LE][padding:MAGIC_LEN][payload:len]
|
|
* Total block size = 4 + MAGIC_LEN + len = MAGIC_LEN + len + 4
|
|
* Wait — re-read the parser:
|
|
* tag(2LE) | len(2LE) => block_size = MAGIC_LEN + len
|
|
* so the full block is [tag:2][len:2] + body[MAGIC_LEN + len]
|
|
* No — looking at the code: pos + 4 reads tag+len, then
|
|
* block_size = MAGIC_LEN + len, and the block starts at data[pos].
|
|
* Template: data[pos .. pos + block_size].
|
|
* Header: data[pos + MAGIC_LEN .. pos + MAGIC_LEN + len].
|
|
* Advance: pos += block_size.
|
|
*
|
|
* Actually re-reading more carefully:
|
|
* tag = data[pos], len = data[pos+2]
|
|
* block_size = MAGIC_LEN + len
|
|
* template = data[pos .. pos + block_size]
|
|
* So the 4 bytes of tag+len are INSIDE the block_size.
|
|
* MAGIC_LEN = 0x38 = 56 which is > 4, so tag+len fit inside.
|
|
*
|
|
* To build test data: write tag(2LE) at offset 0, len(2LE) at
|
|
* offset 2, then (MAGIC_LEN - 4) padding bytes, then len payload bytes.
|
|
* Total = MAGIC_LEN + len.
|
|
* ================================================================ */
|
|
static guint8 *
|
|
build_block (guint16 tag, const guint8 *payload, guint16 payload_len,
|
|
gsize *out_len)
|
|
{
|
|
gsize block_size = ENROLLMENT_MAGIC_LEN + payload_len;
|
|
guint8 *buf = g_malloc0 (block_size);
|
|
|
|
FP_WRITE_UINT16_LE (buf, tag);
|
|
FP_WRITE_UINT16_LE (buf + 2, payload_len);
|
|
|
|
if (payload && payload_len > 0)
|
|
memcpy (buf + ENROLLMENT_MAGIC_LEN, payload, payload_len);
|
|
|
|
*out_len = block_size;
|
|
return buf;
|
|
}
|
|
|
|
/* Wrap raw block data with the 2-byte declared_len prefix the parser expects:
|
|
* [declared_len:2LE][blocks...]
|
|
* declared_len = blocks_len (total size of all concatenated blocks). */
|
|
static guint8 *
|
|
wrap_response (const guint8 *blocks, gsize blocks_len, gsize *out_len)
|
|
{
|
|
*out_len = 2 + blocks_len;
|
|
|
|
guint8 *buf = g_malloc (*out_len);
|
|
|
|
FP_WRITE_UINT16_LE (buf, (guint16) blocks_len);
|
|
if (blocks && blocks_len > 0)
|
|
memcpy (buf + 2, blocks, blocks_len);
|
|
return buf;
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.1: parse empty data — returns TRUE, all fields NULL
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_empty (void)
|
|
{
|
|
EnrollmentUpdateResult result;
|
|
gboolean ok = parse_enrollment_update_response (NULL, 0, &result);
|
|
|
|
/* Empty data (len < 2) → parser returns FALSE */
|
|
g_assert_false (ok);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.2: parse single template block (tag=0)
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_template_block (void)
|
|
{
|
|
guint8 payload[] = { 0xDE, 0xAD, 0xBE, 0xEF };
|
|
gsize block_len;
|
|
g_autofree guint8 *block = build_block (0, payload, sizeof (payload),
|
|
&block_len);
|
|
gsize resp_len;
|
|
g_autofree guint8 *data = wrap_response (block, block_len, &resp_len);
|
|
|
|
EnrollmentUpdateResult result;
|
|
gboolean ok = parse_enrollment_update_response (data, resp_len, &result);
|
|
|
|
g_assert_true (ok);
|
|
g_assert_nonnull (result.template_data);
|
|
g_assert_cmpuint (result.template_len, ==, block_len);
|
|
g_assert_null (result.header);
|
|
g_assert_null (result.tid);
|
|
|
|
enrollment_update_result_clear (&result);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.3: parse header block (tag=1)
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_header_block (void)
|
|
{
|
|
guint8 payload[] = { 0x01, 0x02, 0x03 };
|
|
gsize block_len;
|
|
g_autofree guint8 *block = build_block (1, payload, sizeof (payload),
|
|
&block_len);
|
|
gsize resp_len;
|
|
g_autofree guint8 *data = wrap_response (block, block_len, &resp_len);
|
|
|
|
EnrollmentUpdateResult result;
|
|
gboolean ok = parse_enrollment_update_response (data, resp_len, &result);
|
|
|
|
g_assert_true (ok);
|
|
g_assert_nonnull (result.header);
|
|
g_assert_cmpuint (result.header_len, ==, sizeof (payload));
|
|
g_assert_cmpmem (result.header, result.header_len, payload, sizeof (payload));
|
|
g_assert_null (result.template_data);
|
|
g_assert_null (result.tid);
|
|
|
|
enrollment_update_result_clear (&result);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.4: parse tid block (tag=3) — signals enrollment complete
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_tid_block (void)
|
|
{
|
|
guint8 payload[] = { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF };
|
|
gsize block_len;
|
|
g_autofree guint8 *block = build_block (3, payload, sizeof (payload),
|
|
&block_len);
|
|
gsize resp_len;
|
|
g_autofree guint8 *data = wrap_response (block, block_len, &resp_len);
|
|
|
|
EnrollmentUpdateResult result;
|
|
gboolean ok = parse_enrollment_update_response (data, resp_len, &result);
|
|
|
|
g_assert_true (ok);
|
|
g_assert_nonnull (result.tid);
|
|
g_assert_cmpuint (result.tid_len, ==, sizeof (payload));
|
|
g_assert_cmpmem (result.tid, result.tid_len, payload, sizeof (payload));
|
|
g_assert_null (result.template_data);
|
|
g_assert_null (result.header);
|
|
|
|
enrollment_update_result_clear (&result);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.5: parse multiple blocks — template + header + tid
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_multiple_blocks (void)
|
|
{
|
|
guint8 tmpl_payload[] = { 0x11, 0x22 };
|
|
guint8 hdr_payload[] = { 0x33, 0x44, 0x55 };
|
|
guint8 tid_payload[] = { 0x66 };
|
|
|
|
gsize tmpl_len, hdr_len, tid_len;
|
|
g_autofree guint8 *tmpl = build_block (0, tmpl_payload,
|
|
sizeof (tmpl_payload), &tmpl_len);
|
|
g_autofree guint8 *hdr = build_block (1, hdr_payload,
|
|
sizeof (hdr_payload), &hdr_len);
|
|
g_autofree guint8 *tid = build_block (3, tid_payload,
|
|
sizeof (tid_payload), &tid_len);
|
|
|
|
/* Concatenate all three blocks, then wrap with length prefix */
|
|
gsize blocks_total = tmpl_len + hdr_len + tid_len;
|
|
g_autofree guint8 *blocks = g_malloc (blocks_total);
|
|
|
|
memcpy (blocks, tmpl, tmpl_len);
|
|
memcpy (blocks + tmpl_len, hdr, hdr_len);
|
|
memcpy (blocks + tmpl_len + hdr_len, tid, tid_len);
|
|
|
|
gsize resp_len;
|
|
g_autofree guint8 *data = wrap_response (blocks, blocks_total, &resp_len);
|
|
|
|
EnrollmentUpdateResult result;
|
|
gboolean ok = parse_enrollment_update_response (data, resp_len, &result);
|
|
|
|
g_assert_true (ok);
|
|
g_assert_nonnull (result.template_data);
|
|
g_assert_nonnull (result.header);
|
|
g_assert_nonnull (result.tid);
|
|
g_assert_cmpuint (result.template_len, ==, tmpl_len);
|
|
g_assert_cmpuint (result.header_len, ==, sizeof (hdr_payload));
|
|
g_assert_cmpuint (result.tid_len, ==, sizeof (tid_payload));
|
|
|
|
enrollment_update_result_clear (&result);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.6: parse truncated data — stops before reading past buffer
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_truncated (void)
|
|
{
|
|
/* Build a response where the declared length is consistent with data_len
|
|
* but the block content is too short for a full block to be parsed.
|
|
* declared_len = 6, so data = [06 00][tag:2][len:2][2 more bytes]
|
|
* The block_size = MAGIC_LEN + len will exceed 8 for any len > 0,
|
|
* so the parser's "pos + block_size > data_len" check will skip it. */
|
|
guint8 data[8];
|
|
|
|
FP_WRITE_UINT16_LE (data, 6); /* declared_len = 6 */
|
|
FP_WRITE_UINT16_LE (data + 2, 0); /* tag = 0 (template) */
|
|
FP_WRITE_UINT16_LE (data + 4, 10); /* len = 10 → block_size = MAGIC_LEN + 10 > 8 */
|
|
data[6] = 0;
|
|
data[7] = 0;
|
|
|
|
EnrollmentUpdateResult result;
|
|
gboolean ok = parse_enrollment_update_response (data, sizeof (data), &result);
|
|
|
|
g_assert_true (ok);
|
|
/* No fields should be populated since the block was truncated */
|
|
g_assert_null (result.template_data);
|
|
g_assert_null (result.header);
|
|
g_assert_null (result.tid);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.7: parse unknown tag — silently skipped
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_unknown_tag (void)
|
|
{
|
|
guint8 payload[] = { 0x99 };
|
|
gsize block_len;
|
|
g_autofree guint8 *block = build_block (42, payload, sizeof (payload),
|
|
&block_len);
|
|
gsize resp_len;
|
|
g_autofree guint8 *data = wrap_response (block, block_len, &resp_len);
|
|
|
|
EnrollmentUpdateResult result;
|
|
gboolean ok = parse_enrollment_update_response (data, resp_len, &result);
|
|
|
|
g_assert_true (ok);
|
|
g_assert_null (result.template_data);
|
|
g_assert_null (result.header);
|
|
g_assert_null (result.tid);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.8: result_clear — frees and zeroes
|
|
* ================================================================ */
|
|
static void
|
|
test_result_clear (void)
|
|
{
|
|
EnrollmentUpdateResult result;
|
|
|
|
result.header = g_malloc (10);
|
|
result.header_len = 10;
|
|
result.template_data = g_malloc (20);
|
|
result.template_len = 20;
|
|
result.tid = g_malloc (5);
|
|
result.tid_len = 5;
|
|
|
|
enrollment_update_result_clear (&result);
|
|
|
|
g_assert_null (result.header);
|
|
g_assert_null (result.template_data);
|
|
g_assert_null (result.tid);
|
|
g_assert_cmpuint (result.header_len, ==, 0);
|
|
g_assert_cmpuint (result.template_len, ==, 0);
|
|
g_assert_cmpuint (result.tid_len, ==, 0);
|
|
}
|
|
|
|
/* ================================================================
|
|
* T8.9: parse zero-length payload — tag present but no data
|
|
* ================================================================ */
|
|
static void
|
|
test_parse_zero_length_payload (void)
|
|
{
|
|
gsize block_len;
|
|
g_autofree guint8 *block = build_block (1, NULL, 0, &block_len);
|
|
gsize resp_len;
|
|
g_autofree guint8 *data = wrap_response (block, block_len, &resp_len);
|
|
|
|
EnrollmentUpdateResult result;
|
|
gboolean ok = parse_enrollment_update_response (data, resp_len, &result);
|
|
|
|
g_assert_true (ok);
|
|
/* Tag 1 with len=0: header should be NULL (len > 0 check in parser) */
|
|
g_assert_null (result.header);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
g_test_init (&argc, &argv, NULL);
|
|
|
|
g_test_add_func ("/validity/enroll/parse-empty",
|
|
test_parse_empty);
|
|
g_test_add_func ("/validity/enroll/parse-template-block",
|
|
test_parse_template_block);
|
|
g_test_add_func ("/validity/enroll/parse-header-block",
|
|
test_parse_header_block);
|
|
g_test_add_func ("/validity/enroll/parse-tid-block",
|
|
test_parse_tid_block);
|
|
g_test_add_func ("/validity/enroll/parse-multiple-blocks",
|
|
test_parse_multiple_blocks);
|
|
g_test_add_func ("/validity/enroll/parse-truncated",
|
|
test_parse_truncated);
|
|
g_test_add_func ("/validity/enroll/parse-unknown-tag",
|
|
test_parse_unknown_tag);
|
|
g_test_add_func ("/validity/enroll/result-clear",
|
|
test_result_clear);
|
|
g_test_add_func ("/validity/enroll/parse-zero-length-payload",
|
|
test_parse_zero_length_payload);
|
|
|
|
return g_test_run ();
|
|
}
|