Compare commits

..

2 Commits

Author SHA1 Message Date
Himura Kazuto
b97914a454 Merge branch 'master' into 'master'
egismoc: Support ETU905A88-E device (1c7a:0584)

See merge request libfprint/libfprint!549
2025-10-29 07:31:54 +00:00
Himura Kazuto
7517793367 egismoc: support ETU905A88-E device (1c7a:0584) 2025-10-29 11:31:05 +04:00
33 changed files with 465 additions and 4523 deletions

View File

@@ -84,7 +84,6 @@ usb:v1C7Ap0584*
usb:v1C7Ap0586*
usb:v1C7Ap0587*
usb:v1C7Ap05A1*
usb:v1C7Ap05A5*
ID_AUTOSUSPEND=1
ID_PERSIST=0

View File

@@ -100,12 +100,6 @@ FP_TYPE_IMAGE_DEVICE
FpImageDevice
</SECTION>
<SECTION>
<FILE>fp-sdcp-device</FILE>
FP_TYPE_SDCP_DEVICE
FpSdcpDevice
</SECTION>
<SECTION>
<FILE>fp-print</FILE>
FP_TYPE_PRINT
@@ -221,31 +215,6 @@ fpi_image_device_retry_scan
fpi_image_device_set_bz3_threshold
</SECTION>
<SECTION>
<FILE>fpi-sdcp-device</FILE>
<TITLE>Internal FpSdcpDevice</TITLE>
FpiSdcpClaim
FpSdcpDeviceClass
fpi_sdcp_claim_get_type
fpi_sdcp_claim_new
fpi_sdcp_claim_copy
fpi_sdcp_claim_free
fpi_sdcp_device_open_complete
fpi_sdcp_device_get_connect_data
fpi_sdcp_device_connect_complete
fpi_sdcp_device_get_reconnect_data
fpi_sdcp_device_reconnect_complete
fpi_sdcp_device_list_complete
fpi_sdcp_device_enroll_commit
fpi_sdcp_device_enroll_commit_complete
fpi_sdcp_device_get_identify_data
fpi_sdcp_device_set_identify_data
fpi_sdcp_device_identify_retry
fpi_sdcp_device_identify_complete
fpi_sdcp_device_get_print_id
fpi_sdcp_device_set_print_id
</SECTION>
<SECTION>
<FILE>fpi-log</FILE>
fp_dbg
@@ -254,8 +223,6 @@ fp_warn
fp_err
BUG_ON
BUG
fp_dbg_hex_dump_bytes
fp_dbg_hex_dump_gbytes
</SECTION>
<SECTION>

View File

@@ -28,7 +28,6 @@
<xi:include href="xml/fp-context.xml"/>
<xi:include href="xml/fp-device.xml"/>
<xi:include href="xml/fp-image-device.xml"/>
<xi:include href="xml/fp-sdcp-device.xml"/>
<xi:include href="xml/fp-print.xml"/>
<xi:include href="xml/fp-image.xml"/>
</part>
@@ -39,7 +38,6 @@
<title>Device methods for drivers</title>
<xi:include href="xml/fpi-device.xml"/>
<xi:include href="xml/fpi-image-device.xml"/>
<xi:include href="xml/fpi-sdcp-device.xml"/>
</chapter>
<chapter id="driver-helpers">

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,15 @@
/*
* Driver for Egis Technology (LighTuning) Match-On-Chip sensors
* Copyright (C) 2023-2025 Joshua Grisham <josh@joshuagrisham.com>
* Originally authored 2023 by Joshua Grisham <josh@joshuagrisham.com>
*
* Portions of code and logic inspired from the elanmoc libfprint driver
* which is copyright (C) 2021 Elan Microelectronics Inc (see elanmoc.c)
*
* Based on original reverse-engineering work by Joshua Grisham. The protocol has
* been reverse-engineered from captures of the official Windows driver, and by
* testing commands on the sensor with a multiplatform Python prototype driver:
* https://github.com/joshuagrisham/galaxy-book2-pro-linux/tree/main/fingerprint/
*
* 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
@@ -22,18 +27,16 @@
#pragma once
#include "fpi-device.h"
#include "fpi-ssm.h"
#include "fpi-sdcp-device.h"
G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC, FpSdcpDevice)
G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC, FpDevice)
#define EGISMOC_DRIVER_FULLNAME "Egis Technology (LighTuning) Match-on-Chip"
#define EGISMOC_DRIVER_CHECK_PREFIX_TYPE1 (1 << 0)
#define EGISMOC_DRIVER_CHECK_PREFIX_TYPE2 (1 << 1)
#define EGISMOC_DRIVER_MAX_ENROLL_STAGES_20 (1 << 2)
#define EGISMOC_DRIVER_MAX_ENROLL_STAGES_15 (1 << 3)
#define EGISMOC_EP_CMD_OUT (0x02 | FPI_USB_ENDPOINT_OUT)
#define EGISMOC_EP_CMD_IN (0x81 | FPI_USB_ENDPOINT_IN)
@@ -49,11 +52,7 @@ G_DECLARE_FINAL_TYPE (FpiDeviceEgisMoc, fpi_device_egismoc, FPI, DEVICE_EGISMOC,
#define EGISMOC_MAX_ENROLL_STAGES_DEFAULT 10
#define EGISMOC_MAX_ENROLL_NUM 10
#define EGISMOC_FINGER_ON_SENSOR_TIMEOUT_USEC (10 * G_USEC_PER_SEC)
#define EGISMOC_CONNECT_RESPONSE_PREFIX_SIZE 15
#define EGISMOC_IDENTIFY_RESPONSE_PREFIX_SIZE 14
#define EGISMOC_ENROLL_STARTING_RESPONSE_PREFIX_SIZE 14
#define EGISMOC_FINGERPRINT_DATA_SIZE 32
#define EGISMOC_LIST_RESPONSE_PREFIX_SIZE 14
#define EGISMOC_LIST_RESPONSE_SUFFIX_SIZE 2
@@ -73,9 +72,6 @@ static gsize cmd_fw_version_len = sizeof (cmd_fw_version) / sizeof (cmd_fw_versi
static guchar rsp_fw_version_suffix[] = {0x90, 0x00};
static gsize rsp_fw_version_suffix_len = sizeof (rsp_fw_version_suffix) / sizeof (rsp_fw_version_suffix[0]);
static guchar rsp_sensor_has_finger_suffix[] = {0x90, 0x00, 0x90, 0x00};
static gsize rsp_sensor_has_finger_suffix_len = sizeof (rsp_sensor_has_finger_suffix) / sizeof (rsp_sensor_has_finger_suffix[0]);
static guchar cmd_list[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x19, 0x04, 0x00, 0x00, 0x01, 0x40};
static gsize cmd_list_len = sizeof (cmd_list) / sizeof (cmd_list[0]);
@@ -97,19 +93,18 @@ static gsize cmd_sensor_enroll_len = sizeof (cmd_sensor_enroll) / sizeof (cmd_se
static guchar cmd_enroll_starting[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x01, 0x00, 0x00, 0x00, 0x20};
static gsize cmd_enroll_starting_len = sizeof (cmd_enroll_starting) / sizeof (cmd_enroll_starting[0]);
static guchar rsp_enroll_starting_suffix[] = {0x90, 0x00};
static gsize rsp_enroll_starting_suffix_len = sizeof (rsp_enroll_starting_suffix) / sizeof (rsp_enroll_starting_suffix[0]);
static guchar cmd_sensor_start_capture[] = {0x00, 0x00, 0x00, 0x04, 0x50, 0x16, 0x02, 0x01};
static gsize cmd_sensor_start_capture_len = sizeof (cmd_sensor_start_capture) / sizeof (cmd_sensor_start_capture[0]);
static guchar cmd_capture_post_wait_finger[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x7a, 0x00, 0x00, 0x00, 0x00, 0x80};
static gsize cmd_capture_post_wait_finger_len = sizeof (cmd_capture_post_wait_finger) / sizeof (cmd_capture_post_wait_finger[0]);
static guchar cmd_read_capture[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x02, 0x02, 0x00, 0x00, 0x02};
static gsize cmd_read_capture_len = sizeof (cmd_read_capture) / sizeof (cmd_read_capture[0]);
static guchar rsp_read_success_prefix[] = {0x00, 0x00, 0x00, 0x04};
static gsize rsp_read_success_prefix_len = sizeof (rsp_read_success_prefix) / sizeof (rsp_read_success_prefix[0]);
static guchar rsp_read_success_suffix[] = {0x90, 0x00};
static gsize rsp_read_success_suffix_len = sizeof (rsp_read_success_suffix) / sizeof (rsp_read_success_suffix[0]);
static guchar rsp_read_offcenter_prefix[] = {0x00, 0x00, 0x00, 0x04};
static gsize rsp_read_offcenter_prefix_len = sizeof (rsp_read_offcenter_prefix) / sizeof (rsp_read_offcenter_prefix[0]);
static guchar rsp_read_offcenter_suffix[] = {0x64, 0x91};
static gsize rsp_read_offcenter_suffix_len = sizeof (rsp_read_offcenter_suffix) / sizeof (rsp_read_offcenter_suffix[0]);
static guchar rsp_read_dirty_prefix[] = {0x00, 0x00, 0x00, 0x02, 0x64};
@@ -117,8 +112,6 @@ static gsize rsp_read_dirty_prefix_len = sizeof (rsp_read_dirty_prefix) / sizeof
static guchar cmd_commit_starting[] = {0x00, 0x00, 0x00, 0x07, 0x50, 0x16, 0x05, 0x00, 0x00, 0x00, 0x20};
static gsize cmd_commit_starting_len = sizeof (cmd_commit_starting) / sizeof (cmd_commit_starting[0]);
static guchar rsp_commit_success_suffix[] = {0x90, 0x00};
static gsize rsp_commit_success_suffix_len = sizeof (rsp_commit_success_suffix) / sizeof (rsp_commit_success_suffix[0]);
/* commands which exist on the device but are currently not used */
@@ -137,13 +130,8 @@ static gsize rsp_commit_success_suffix_len = sizeof (rsp_commit_success_suffix)
/* prefixes/suffixes and other things for dynamically created command payloads */
#define EGISMOC_CHECK_BYTES_LENGTH 2
static guchar cmd_sdcp_connect_prefix[] = {0x00, 0x00, 0x00, 0x6b, 0x50, 0x57, 0x01, 0x00, 0x00, 0x00, 0x62, 0x20};
static gsize cmd_sdcp_connect_prefix_len = sizeof (cmd_sdcp_connect_prefix) / sizeof (cmd_sdcp_connect_prefix[0]);
static guchar cmd_sdcp_connect_suffix[] = {0x00, 0x00};
static gsize cmd_sdcp_connect_suffix_len = sizeof (cmd_sdcp_connect_suffix) / sizeof (cmd_sdcp_connect_suffix[0]);
static guchar rsp_sdcp_connect_success_suffix[] = {0x90, 0x00};
static gsize rsp_sdcp_connect_success_suffix_len = sizeof (rsp_sdcp_connect_success_suffix) / sizeof (rsp_sdcp_connect_success_suffix[0]);
#define EGISMOC_IDENTIFY_RESPONSE_PRINT_ID_OFFSET 46
#define EGISMOC_CMD_CHECK_SEPARATOR_LENGTH 32
static guchar cmd_new_print_prefix[] = {0x00, 0x00, 0x00, 0x27, 0x50, 0x16, 0x03, 0x00, 0x00, 0x00, 0x20};
static gsize cmd_new_print_prefix_len = sizeof (cmd_new_print_prefix) / sizeof (cmd_new_print_prefix[0]);
@@ -181,18 +169,6 @@ typedef enum {
DEV_INIT_STATES,
} DeviceInitStates;
typedef enum {
CONNECT,
CONNECT_RESPONSE,
CONNECT_STATES,
} ConnectStates;
typedef enum {
WAIT_FINGER_NOT_ON_SENSOR,
WAIT_FINGER_ON_SENSOR,
WAIT_FINGER_STATES,
} WaitFingerStates;
typedef enum {
IDENTIFY_GET_ENROLLED_IDS,
IDENTIFY_CHECK_ENROLLED_NUM,
@@ -201,6 +177,8 @@ typedef enum {
IDENTIFY_WAIT_FINGER,
IDENTIFY_SENSOR_CHECK,
IDENTIFY_CHECK,
IDENTIFY_COMPLETE_SENSOR_RESET,
IDENTIFY_COMPLETE,
IDENTIFY_STATES,
} IdentifyStates;
@@ -216,10 +194,11 @@ typedef enum {
ENROLL_CAPTURE_SENSOR_RESET,
ENROLL_CAPTURE_SENSOR_START_CAPTURE,
ENROLL_CAPTURE_WAIT_FINGER,
ENROLL_CAPTURE_POST_WAIT_FINGER,
ENROLL_CAPTURE_READ_RESPONSE,
ENROLL_COMMIT_START,
ENROLL_COMMIT,
ENROLL_COMMIT_SENSOR_RESET,
ENROLL_COMPLETE,
ENROLL_STATES,
} EnrollStates;

View File

@@ -1,331 +0,0 @@
/*
* Virtual driver for SDCP device debugging
*
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
/*
* This is a virtual driver to debug the SDCP-based drivers.
* This virtual driver does not use a socket listener but instead
* embeds the simulated logic of the fake "device" directly within
* the logic of each function. This driver also allows prints to be
* registered programmatically, making it possible to test libfprint
* and fprintd.
*
* This virtual driver will override FpSdcpDevice's dynamically
* generated cryptography values and instead replace them with
* pre-generated values taken from from Microsoft's sample client
* implementation. See:
* https://github.com/Microsoft/SecureDeviceConnectionProtocol
*/
#define FP_COMPONENT "virtual_sdcp"
#include "fpi-log.h"
#include "../fpi-sdcp.h"
#include "virtual-sdcp.h"
struct _FpDeviceVirtualSdcp
{
FpSdcpDevice parent;
GPtrArray *print_ids;
};
G_DECLARE_FINAL_TYPE (FpDeviceVirtualSdcp, fpi_device_virtual_sdcp, FPI, DEVICE_VIRTUAL_SDCP, FpSdcpDevice)
G_DEFINE_TYPE (FpDeviceVirtualSdcp, fpi_device_virtual_sdcp, FP_TYPE_SDCP_DEVICE)
/******************************************************************************/
static const guint8 from_hex_map[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // @abcdef
};
static GBytes *
g_bytes_from_hex (const gchar *hex)
{
g_autoptr(GBytes) res = NULL;
guint8 b0, b1;
gsize bytes_len = strlen (hex) / 2;
guint8 *bytes = g_malloc0 (bytes_len);
for (int i = 0; i < strlen (hex) - 1; i += 2)
{
b0 = ((guint8) hex[i + 0] & 0x1F) ^ 0x10;
b1 = ((guint8) hex[i + 1] & 0x1F) ^ 0x10;
bytes[i / 2] = (guint8) (from_hex_map[b0] << 4) | from_hex_map[b1];
}
res = g_bytes_new_take (bytes, bytes_len);
return g_steal_pointer (&res);
}
static FpiSdcpClaim *
get_fake_sdcp_claim (void)
{
FpiSdcpClaim *claim = g_new0 (FpiSdcpClaim, 1);
claim->model_certificate = g_bytes_from_hex (model_certificate_hex);
claim->device_public_key = g_bytes_from_hex (device_public_key_hex);
claim->firmware_public_key = g_bytes_from_hex (firmware_public_key_hex);
claim->firmware_hash = g_bytes_from_hex (firmware_hash_hex);
claim->model_signature = g_bytes_from_hex (model_signature_hex);
claim->device_signature = g_bytes_from_hex (device_signature_hex);
return g_steal_pointer (&claim);
}
/******************************************************************************/
static void
dev_identify (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (sdcp_device);
g_autoptr(GBytes) enrollment_id = NULL;
g_autoptr(GBytes) identify_mac = NULL;
GBytes *identify_nonce = NULL;
if (self->print_ids->len > 0)
{
/*
* Pretend that the virtual device identified the first print.
* Since we used a pre-generated enrollment_id for it, we can also use the
* matching pre-generated test identify data for its identification.
*/
identify_nonce = g_bytes_from_hex (identify_nonce_hex);
enrollment_id = g_bytes_from_hex (enrollment_id_hex);
fpi_sdcp_device_set_identify_data (sdcp_device, identify_nonce);
identify_mac = g_bytes_from_hex (identify_mac_hex);
fpi_sdcp_device_identify_complete (sdcp_device, enrollment_id, identify_mac, NULL);
}
else
{
fpi_sdcp_device_identify_complete (sdcp_device, NULL, NULL,
fpi_device_error_new (FP_DEVICE_ERROR_DATA_NOT_FOUND));
}
}
static void
dev_enroll_commit (FpSdcpDevice *sdcp_device, GBytes *id)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (sdcp_device);
g_autoptr(GBytes) expected_first_print_id = NULL;
GBytes *print_id;
print_id = g_bytes_new (g_bytes_get_data (id, NULL), g_bytes_get_size (id));
/*
* If this is the first print, it is probably good if we make sure the
* internal API assigned it the expected pre-known ID
*/
if (self->print_ids->len == 0)
{
expected_first_print_id = g_bytes_from_hex (enrollment_id_hex);
if (!g_bytes_equal (print_id, expected_first_print_id))
{
fpi_sdcp_device_enroll_commit_complete (sdcp_device,
fpi_device_error_new_msg (FP_DEVICE_ERROR_UNTRUSTED,
"First enrolled print ID does not match expected value"));
return;
}
}
g_ptr_array_add (self->print_ids, g_steal_pointer (&print_id));
fpi_sdcp_device_enroll_commit_complete (sdcp_device, NULL);
}
static void
dev_enroll (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (sdcp_device);
FpDevice *device = FP_DEVICE (sdcp_device);
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) nonce = NULL;
FpPrint *print;
fpi_device_get_enroll_data (device, &print);
if (self->print_ids->len == 0)
{
/* Use the hard-coded nonce for the first enrollment */
nonce = g_bytes_from_hex (enrollment_nonce_hex);
}
else
{
/* Generate a new nonce for all other enrollments */
nonce = fpi_sdcp_generate_random (&error);
if (error)
fpi_device_enroll_progress (device, 0, print, error);
}
fpi_device_enroll_progress (device, 1, print, NULL);
fpi_sdcp_device_enroll_commit (sdcp_device, nonce, error);
}
static void
dev_list (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (sdcp_device);
GPtrArray *print_ids = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
for (gint i = 0; i < self->print_ids->len; i++)
{
GBytes *print_id = g_ptr_array_index (self->print_ids, i);
fp_dbg ("print %d:", i);
fp_dbg_hex_dump_gbytes (print_id);
g_ptr_array_add (print_ids, g_bytes_new (g_bytes_get_data (print_id, NULL),
g_bytes_get_size (print_id)));
}
fpi_sdcp_device_list_complete (sdcp_device, g_steal_pointer (&print_ids), NULL);
}
static void
dev_reconnect (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) random = NULL;
g_autoptr(GBytes) reconnect_mac = g_bytes_from_hex (reconnect_mac_hex);
/*
* Normally, a driver would fetch the reconnect data and then send it to the
* device's Reconnect command. In this fake device, we will just fetch and
* verify the random was generated but do nothing with it
*/
fpi_sdcp_device_get_reconnect_data (sdcp_device, &random);
g_assert (random);
g_assert (g_bytes_get_size (random) == SDCP_RANDOM_SIZE);
/*
* In emulation mode (FP_DEVICE_EMULATION=1), a different hard-coded random is
* set in fpi-sdcp-device, which was the same random used to generate the
* reconnect_mac value provided here
*/
fpi_sdcp_device_reconnect_complete (sdcp_device, reconnect_mac, error);
}
static void
dev_connect (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) host_random = NULL;
g_autoptr(GBytes) host_public_key = NULL;
g_autoptr(GBytes) device_random = g_bytes_from_hex (device_random_hex);
g_autoptr(GBytes) connect_mac = g_bytes_from_hex (connect_mac_hex);
g_autoptr(FpiSdcpClaim) claim = get_fake_sdcp_claim ();
fpi_sdcp_device_get_connect_data (sdcp_device, &host_random, &host_public_key);
g_assert (host_random);
g_assert (g_bytes_get_size (host_random) == SDCP_RANDOM_SIZE);
g_assert (host_public_key);
g_assert (g_bytes_get_size (host_public_key) == SDCP_PUBLIC_KEY_SIZE);
fpi_sdcp_device_connect_complete (sdcp_device, device_random, claim, connect_mac, error);
}
static void
dev_close (FpDevice *device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
fpi_device_close_complete (device, NULL);
}
static void
dev_open (FpSdcpDevice *sdcp_device)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
fpi_sdcp_device_open_complete (sdcp_device, NULL);
}
static void
fpi_device_virtual_sdcp_finalize (GObject *object)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceVirtualSdcp *self = FPI_DEVICE_VIRTUAL_SDCP (object);
g_clear_pointer (&self->print_ids, g_ptr_array_unref);
G_OBJECT_CLASS (fpi_device_virtual_sdcp_parent_class)->finalize (object);
}
static void
fpi_device_virtual_sdcp_init (FpDeviceVirtualSdcp *self)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
/* Force FP_DEVICE_EMULATION=1 when using FpDeviceVirtualSdcp */
g_setenv ("FP_DEVICE_EMULATION", "1", TRUE);
self->print_ids = g_ptr_array_new_with_free_func ((GDestroyNotify) g_bytes_unref);
}
static const FpIdEntry driver_ids[] = {
{ .virtual_envvar = "FP_VIRTUAL_SDCP" },
{ .virtual_envvar = NULL }
};
static void
fpi_device_virtual_sdcp_class_init (FpDeviceVirtualSdcpClass *klass)
{
fp_dbg ("Virtual SDCP device: %s()", G_STRFUNC);
FpDeviceClass *dev_class = FP_DEVICE_CLASS (klass);
FpSdcpDeviceClass *sdcp_dev_class = FP_SDCP_DEVICE_CLASS (klass);
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = fpi_device_virtual_sdcp_finalize;
dev_class->id = FP_COMPONENT;
dev_class->full_name = "Virtual SDCP device for debugging";
dev_class->type = FP_DEVICE_TYPE_VIRTUAL;
dev_class->id_table = driver_ids;
dev_class->nr_enroll_stages = 1;
dev_class->scan_type = FP_SCAN_TYPE_PRESS;
sdcp_dev_class->ignore_device_certificate = FALSE;
sdcp_dev_class->ignore_device_signatures = FALSE;
sdcp_dev_class->open = dev_open;
sdcp_dev_class->connect = dev_connect;
if (!g_getenv ("FP_VIRTUAL_SDCP_NO_RECONNECT"))
sdcp_dev_class->reconnect = dev_reconnect;
sdcp_dev_class->list = dev_list;
sdcp_dev_class->enroll = dev_enroll;
sdcp_dev_class->enroll_commit = dev_enroll_commit;
sdcp_dev_class->identify = dev_identify;
dev_class->close = dev_close;
fpi_device_class_auto_initialize_features (dev_class);
dev_class->features |= FP_DEVICE_FEATURE_STORAGE;
}

View File

@@ -1,92 +0,0 @@
/*
* Virtual driver test payloads for SDCP device debugging
*
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-compat.h"
/* host keys */
static const gchar host_private_key_hex[] = "8400ed14579cdf11586477e836e8cb52708441c1c2a447c218c5bbc2d118fbc7";
static const gchar host_public_key_hex[] = "0452f056ffb9c6728654771a3629b770767b19a2106a4916fb81ba06ef6797c4"
"a3df672ade0e9116d1abe278a8223abde4958d62d4ff6882159f0617c6f8ce10"
"bf";
static const gchar host_random_hex[] = "d877403abe82f4d97e1448c5052d83a532a45e56ef049cbbf981137520e713bf";
/* device keys */
static const gchar device_random_hex[] = "6e2f6c1abef2a1973fb1315a17e209fdb0c78520f1fd6a85d294d7aeb40a04a7";
static const gchar model_certificate_hex[] = "30820323308202caa00302010202133300000004c45d661d6eed040d00000000"
"0004300a06082a8648ce3d0403023056310b3009060355040613025553311e30"
"1c060355040a13154d6963726f736f667420436f72706f726174696f6e312730"
"250603550403131e57696e646f77732048656c6c6f2032303936414443432043"
"412032303231301e170d3232303130343231303033355a170d32333034303432"
"31303033355a301c311a3018060355040313115365637572652042494f205365"
"6e736f723059301306072a8648ce3d020106082a8648ce3d0301070342000414"
"cfc287f872a2b7d3339e0b31390e3ca688e61165eaa6687c959270e07666b1fa"
"19e3efaf1750d134a886d494424fe471970c4b06838408a18d1f5d57735dd7a3"
"8201af308201ab30750603551d11046e306ca46a30683132303006082b060104"
"82376402132436423045413344382d383339372d343444332d383043392d3930"
"324130414346344343303132303006082b060104823764011324413944423730"
"32362d433646462d344341462d423134382d393133454641333730423933301d"
"0603551d0e04160414db6d66d642b7236d5e6bb2ec2186decda98067b6301f06"
"03551d23041830168014bf3748e34a632de953a3ba890298c069472a99b9305f"
"0603551d1f045830563054a052a050864e687474703a2f2f7777772e6d696372"
"6f736f66742e636f6d2f706b696f70732f63726c2f57696e646f777325323048"
"656c6c6f25323032303936414443432532304341253230323032312e63726c30"
"6c06082b060105050701010460305e305c06082b060105050730028650687474"
"703a2f2f7777772e6d6963726f736f66742e636f6d2f706b696f70732f636572"
"74732f57696e646f777325323048656c6c6f2532303230393641444343253230"
"4341253230323032312e637274300c0603551d130101ff040230003015060355"
"1d25040e300c060a2b0601040182374c2b01300a06082a8648ce3d0403020347"
"003044022077553ef520f732e03cd740c8cf807e6366e12918bc581f75bfe0f1"
"95b7b1fd4f0220324e25b93b9da7538b797a624272b21b7cc0e96ea487924250"
"4677600450f283";
static const gchar device_public_key_hex[] = "04e2787890a684f95b96b9a2316ca8d3d33d4d79ff4c89dc6f9e888e973990d1"
"d3154133dcc8bd33b99af9dbf0673390d404d092498a3f214cd93f9b9f28fb5f"
"66";
static const gchar firmware_public_key_hex[] = "04f06a84ab51a3a6e8ff46868f91dd720e4cdad21f2e090d11e8f9bfc2ea19ee"
"1b5eac850b4532968a9399f76cd779e7723e8c2ca73b597c0df5f73b94a36f2b"
"6c";
static const gchar firmware_hash_hex[] = "c3bf47ea1f4a4a605470313cacb3a44f4a461f68c6faeab07e737610cb5ac835";
static const gchar model_signature_hex[] = "febe6ba3107813e185f05189e69ae79d9f7a40802582d94324459844c8b97ec6"
"c5daed5462276cb8a193c33e350424b0305d63d79a93a9188dcfc0cb5595f6c1";
static const gchar device_signature_hex[] = "10cc57dd8dafb463510a7327a5fca49b698e999b36448e2023eaf0dff0b0d4a3"
"4f1caf4e872b77364a0a00d7476554d0324c4cc931937e232a0315837d696c06";
static const gchar connect_mac_hex[] = "422bc475a78f972bae842a28e5ad721207457fcbd9a1a3aaf71587c07b84d247";
/* expected application_secret based on above */
static const gchar application_secret_hex[] = "13330ba3135ecf5dc71cede01a886540771efab35c8ba053902b2c1ee7de6efe";
/* test verify_reconnect values */
static const gchar reconnect_random_hex[] = "8a7451c1d3a8dca1c1330ca50d73454b351a49f46c8e9dcee15c964d295c31c9";
static const gchar reconnect_mac_hex[] = "bf3f3bb3bd6ecb2784c160f526f7bc3b3ca8faf5557194c48e0024a0493903c7";
/* test enrollment_id values */
static const gchar enrollment_nonce_hex[] = "c2101c44c9a667bba397e81f48b143398603e2c9335a68b409e1dbe71e005ca2";
static const gchar enrollment_id_hex[] = "67109dc70a216331f1580ddac601915929c1ff6c9bcba6544ba572c660c3d91e";
/* test verify_identify values */
static const gchar identify_nonce_hex[] = "3a1b506f5bec089059acefb9b44dfbdea7a599ee9aa267e5252664d60b798053";
static const gchar identify_mac_hex[] = "53a723eef40713094a90c5ef9996cbd6ba268e30676cd7107705a6c3e3e1eff9";

View File

@@ -143,7 +143,6 @@ typedef enum {
* @FP_DEVICE_ERROR_DATA_DUPLICATE: Enrolling template duplicates storaged templates
* @FP_DEVICE_ERROR_REMOVED: The device has been removed.
* @FP_DEVICE_ERROR_TOO_HOT: The device might be getting too hot
* @FP_DEVICE_ERROR_UNTRUSTED: Device cannot be trusted
*
* Error codes for device operations. More specific errors from other domains
* such as #G_IO_ERROR or #G_USB_DEVICE_ERROR may also be reported.
@@ -162,7 +161,6 @@ typedef enum {
/* Leave some room to add more DATA related errors */
FP_DEVICE_ERROR_REMOVED = 0x100,
FP_DEVICE_ERROR_TOO_HOT,
FP_DEVICE_ERROR_UNTRUSTED,
} FpDeviceError;
GQuark fp_device_retry_quark (void);

View File

@@ -605,7 +605,7 @@ fp_print_equal (FpPrint *self, FpPrint *other)
if (g_strcmp0 (self->device_id, other->device_id))
return FALSE;
if (self->type == FPI_PRINT_RAW || self->type == FPI_PRINT_SDCP)
if (self->type == FPI_PRINT_RAW)
{
return g_variant_equal (self->data, other->data);
}
@@ -870,7 +870,7 @@ fp_print_deserialize (const guchar *data,
g_ptr_array_add (result->prints, g_steal_pointer (&xyt));
}
}
else if (type == FPI_PRINT_RAW || type == FPI_PRINT_SDCP)
else if (type == FPI_PRINT_RAW)
{
g_autoptr(GVariant) fp_data = g_variant_get_child_value (print_data, 0);

View File

@@ -1,46 +0,0 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-sdcp-device.h"
typedef struct
{
GBytes *host_private_key;
GBytes *host_public_key;
GBytes *host_random;
GBytes *reconnect_random;
GBytes *identify_nonce;
GBytes *data;
} FpSdcpDevicePrivate;
void fpi_sdcp_device_get_application_secret (FpSdcpDevice *self,
GBytes **application_secret);
void fpi_sdcp_device_set_application_secret (FpSdcpDevice *self,
GBytes *application_secret);
void fpi_sdcp_device_open (FpSdcpDevice *self);
void fpi_sdcp_device_connect (FpSdcpDevice *self);
void fpi_sdcp_device_reconnect (FpSdcpDevice *self);
void fpi_sdcp_device_list (FpSdcpDevice *self);
void fpi_sdcp_device_enroll (FpSdcpDevice *self);
void fpi_sdcp_device_identify (FpSdcpDevice *self);

View File

@@ -1,177 +0,0 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "sdcp_device"
#include "fpi-log.h"
#include "fp-sdcp-device-private.h"
/**
* SECTION: fp-sdcp-device
* @title: FpSdcpDevice
* @short_description: SDCP device subclass
*
* This is a base class for devices implementing the SDCP security protocol.
*/
G_DEFINE_ABSTRACT_TYPE_WITH_PRIVATE (FpSdcpDevice, fp_sdcp_device, FP_TYPE_DEVICE)
enum {
PROP_0,
PROP_SDCP_DATA,
N_PROPS
};
static GParamSpec *properties[N_PROPS];
/*******************************************************/
/* Callbacks/vfuncs */
static void
fp_sdcp_device_open (FpDevice *device)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (device);
fpi_sdcp_device_open (self);
}
static void
fp_sdcp_device_list (FpDevice *device)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (device);
fpi_sdcp_device_list (self);
}
static void
fp_sdcp_device_enroll (FpDevice *device)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (device);
fpi_sdcp_device_enroll (self);
}
static void
fp_sdcp_device_identify (FpDevice *device)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (device);
fpi_sdcp_device_identify (self);
}
/*********************************************************/
static void
fp_sdcp_device_finalize (GObject *object)
{
FpSdcpDevice *self = (FpSdcpDevice *) object;
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_clear_pointer (&priv->host_private_key, g_bytes_unref);
g_clear_pointer (&priv->host_public_key, g_bytes_unref);
g_clear_pointer (&priv->host_random, g_bytes_unref);
g_clear_pointer (&priv->reconnect_random, g_bytes_unref);
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
g_clear_pointer (&priv->data, g_bytes_unref);
G_OBJECT_CLASS (fp_sdcp_device_parent_class)->finalize (object);
}
static void
fp_sdcp_device_get_property (GObject *object,
guint prop_id,
GValue *value,
GParamSpec *pspec)
{
FpSdcpDevice *self = (FpSdcpDevice *) object;
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
switch (prop_id)
{
case PROP_SDCP_DATA:
g_value_set_boxed (value, priv->data);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
fp_sdcp_device_set_property (GObject *object,
guint prop_id,
const GValue *value,
GParamSpec *pspec)
{
FpSdcpDevice *self = FP_SDCP_DEVICE (object);
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
switch (prop_id)
{
case PROP_SDCP_DATA:
g_clear_pointer (&priv->data, g_bytes_unref);
priv->data = g_value_dup_boxed (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
}
static void
fp_sdcp_device_constructed (GObject *obj)
{
G_OBJECT_CLASS (fp_sdcp_device_parent_class)->constructed (obj);
}
static void
fp_sdcp_device_class_init (FpSdcpDeviceClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
FpDeviceClass *fp_device_class = FP_DEVICE_CLASS (klass);
object_class->finalize = fp_sdcp_device_finalize;
object_class->get_property = fp_sdcp_device_get_property;
object_class->set_property = fp_sdcp_device_set_property;
object_class->constructed = fp_sdcp_device_constructed;
fp_device_class->open = fp_sdcp_device_open;
fp_device_class->list = fp_sdcp_device_list;
fp_device_class->enroll = fp_sdcp_device_enroll;
fp_device_class->verify = fp_sdcp_device_identify;
fp_device_class->identify = fp_sdcp_device_identify;
properties[PROP_SDCP_DATA] =
g_param_spec_boxed ("sdcp-data",
"SDCP Data",
"SDCP-related device data that should be persisted and used with the "
"device during the current system boot",
G_TYPE_BYTES,
G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE);
g_object_class_install_properties (object_class, N_PROPS, properties);
fpi_device_class_auto_initialize_features (fp_device_class);
}
static void
fp_sdcp_device_init (FpSdcpDevice *self)
{
}

View File

@@ -1,29 +0,0 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include <fp-device.h>
G_BEGIN_DECLS
#define FP_TYPE_SDCP_DEVICE (fp_sdcp_device_get_type ())
G_DECLARE_DERIVABLE_TYPE (FpSdcpDevice, fp_sdcp_device, FP, SDCP_DEVICE, FpDevice)
G_END_DECLS

View File

@@ -190,9 +190,6 @@ fpi_device_error_new (FpDeviceError error)
case FP_DEVICE_ERROR_TOO_HOT:
msg = "Device disabled to prevent overheating.";
case FP_DEVICE_ERROR_UNTRUSTED:
msg = "Could not verify integrity of the device, it cannot be trusted!";
break;
default:

View File

@@ -1,57 +0,0 @@
/*
* FpiLog - Internal logging functions
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "fpi-log.h"
#undef fp_dbg_hex_dump_bytes
#undef fp_dbg_hex_dump_gbytes
void
fp_dbg_hex_dump_bytes (const gchar *log_domain,
const guint8 *buf,
gsize len)
{
g_autoptr(GString) line = NULL;
line = g_string_new ("");
for (gint i = 0; i < len; i++)
{
g_string_append_printf (line, "%02x ", buf[i]);
if ((i + 1) % 16 == 0)
{
g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", line->str);
g_string_set_size (line, 0);
}
}
if (line->len)
g_log (log_domain, G_LOG_LEVEL_DEBUG, "%s", line->str);
}
void
fp_dbg_hex_dump_gbytes (const gchar *log_domain,
GBytes *gbytes)
{
gsize len = 0;
const guint8 *buf = g_bytes_get_data (gbytes, &len);
fp_dbg_hex_dump_bytes (log_domain, buf, len);
}

View File

@@ -96,32 +96,3 @@
* Same as BUG_ON() but is always true.
*/
#define BUG() BUG_ON (1)
/*
* Custom-defined logging functions are wrapped in macros for convenience so
* that the caller does not have to pass G_LOG_DOMAIN every time.
*/
void fp_dbg_hex_dump_bytes (const gchar *log_domain,
const guint8 *buf,
gsize len);
/**
* fp_dbg_hex_dump_bytes:
* @buf: Bytes buffer to dump
* @len: Length of @buf to dump
*
* Prints hex dump of @buf to fp_dbg()
*/
#define fp_dbg_hex_dump_bytes(buf, len) fp_dbg_hex_dump_bytes (G_LOG_DOMAIN, buf, len)
void fp_dbg_hex_dump_gbytes (const gchar *log_domain,
GBytes *gbytes);
/**
* fp_dbg_hex_dump_gbytes:
* @gbytes: #GBytes to dump
*
* Prints hex dump of @gbytes to fp_dbg()
*/
#define fp_dbg_hex_dump_gbytes(gbytes) fp_dbg_hex_dump_gbytes (G_LOG_DOMAIN, gbytes)

View File

@@ -11,13 +11,11 @@ G_BEGIN_DECLS
* @FPI_PRINT_UNDEFINED: Undefined type, this happens prior to enrollment
* @FPI_PRINT_RAW: A raw print where the data is directly compared
* @FPI_PRINT_NBIS: NBIS minutiae comparison
* @FPI_PRINT_SDCP: Print from an SDCP conforming device
*/
typedef enum {
FPI_PRINT_UNDEFINED = 0,
FPI_PRINT_RAW,
FPI_PRINT_NBIS,
FPI_PRINT_SDCP,
} FpiPrintType;
/**

View File

@@ -1,980 +0,0 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "sdcp_device"
#include "fpi-log.h"
#include "fpi-compat.h"
#include "fpi-print.h"
#include "fp-sdcp-device-private.h"
#include "fpi-sdcp.h"
#include "fpi-sdcp-device.h"
/**
* SECTION: fpi-sdcp-device
* @title: Internal FpSdcpDevice
* @short_description: Internal SDCP device routines
*
* Internal SDCP handling routines. See #FpSdcpDevice for public routines.
*/
G_DEFINE_BOXED_TYPE (FpiSdcpClaim, fpi_sdcp_claim, fpi_sdcp_claim_copy, fpi_sdcp_claim_free)
/**
* fpi_sdcp_claim_new:
*
* Create an empty #FpiSdcpClaim to provide to the base class.
*
* Returns: (transfer full): A newly created #FpiSdcpClaim
*/
FpiSdcpClaim *
fpi_sdcp_claim_new (void)
{
FpiSdcpClaim *res = NULL;
res = g_new0 (FpiSdcpClaim, 1);
return res;
}
/**
* fpi_sdcp_claim_free:
* @claim: a #FpiSdcpClaim
*
* Release the memory used by an #FpiSdcpClaim.
*/
void
fpi_sdcp_claim_free (FpiSdcpClaim *claim)
{
g_return_if_fail (claim);
g_clear_pointer (&claim->model_certificate, g_bytes_unref);
g_clear_pointer (&claim->device_public_key, g_bytes_unref);
g_clear_pointer (&claim->firmware_public_key, g_bytes_unref);
g_clear_pointer (&claim->firmware_hash, g_bytes_unref);
g_clear_pointer (&claim->model_signature, g_bytes_unref);
g_clear_pointer (&claim->device_signature, g_bytes_unref);
g_free (claim);
}
/**
* fpi_sdcp_claim_copy:
* @other: The #FpiSdcpClaim to copy
*
* Create a (shallow) copy of a #FpiSdcpClaim.
*
* Returns: (transfer full): A newly created #FpiSdcpClaim
*/
FpiSdcpClaim *
fpi_sdcp_claim_copy (FpiSdcpClaim *other)
{
FpiSdcpClaim *res = NULL;
res = fpi_sdcp_claim_new ();
if (other->model_certificate)
res->model_certificate = g_bytes_ref (other->model_certificate);
if (other->device_public_key)
res->device_public_key = g_bytes_ref (other->device_public_key);
if (other->firmware_public_key)
res->firmware_public_key = g_bytes_ref (other->firmware_public_key);
if (other->firmware_hash)
res->firmware_hash = g_bytes_ref (other->firmware_hash);
if (other->model_signature)
res->model_signature = g_bytes_ref (other->model_signature);
if (other->device_signature)
res->device_signature = g_bytes_ref (other->device_signature);
return res;
}
/* Manually redefine what G_DEFINE_* macro does */
static inline gpointer
fp_sdcp_device_get_instance_private (FpSdcpDevice *self)
{
FpSdcpDeviceClass *sdcp_class = g_type_class_peek_static (FP_TYPE_SDCP_DEVICE);
return G_STRUCT_MEMBER_P (self,
g_type_class_get_instance_private_offset (sdcp_class));
}
/* Example values from Microsoft's SDCP documentation to use when testing (FP_DEVICE_EMULATION=1) */
static const guchar test_host_private_key[] = {
0x84, 0x00, 0xed, 0x14, 0x57, 0x9c, 0xdf, 0x11, 0x58, 0x64, 0x77, 0xe8, 0x36, 0xe8, 0xcb, 0x52,
0x70, 0x84, 0x41, 0xc1, 0xc2, 0xa4, 0x47, 0xc2, 0x18, 0xc5, 0xbb, 0xc2, 0xd1, 0x18, 0xfb, 0xc7
};
static const guchar test_host_public_key[] = {
0x04, 0x52, 0xf0, 0x56, 0xff, 0xb9, 0xc6, 0x72, 0x86, 0x54, 0x77, 0x1a, 0x36, 0x29, 0xb7, 0x70,
0x76, 0x7b, 0x19, 0xa2, 0x10, 0x6a, 0x49, 0x16, 0xfb, 0x81, 0xba, 0x06, 0xef, 0x67, 0x97, 0xc4,
0xa3, 0xdf, 0x67, 0x2a, 0xde, 0x0e, 0x91, 0x16, 0xd1, 0xab, 0xe2, 0x78, 0xa8, 0x22, 0x3a, 0xbd,
0xe4, 0x95, 0x8d, 0x62, 0xd4, 0xff, 0x68, 0x82, 0x15, 0x9f, 0x06, 0x17, 0xc6, 0xf8, 0xce, 0x10,
0xbf
};
static const gchar test_host_random[] = {
0xd8, 0x77, 0x40, 0x3a, 0xbe, 0x82, 0xf4, 0xd9, 0x7e, 0x14, 0x48, 0xc5, 0x05, 0x2d, 0x83, 0xa5,
0x32, 0xa4, 0x5e, 0x56, 0xef, 0x04, 0x9c, 0xbb, 0xf9, 0x81, 0x13, 0x75, 0x20, 0xe7, 0x13, 0xbf
};
static const gchar test_reconnect_random[] = {
0x8a, 0x74, 0x51, 0xc1, 0xd3, 0xa8, 0xdc, 0xa1, 0xc1, 0x33, 0x0c, 0xa5, 0x0d, 0x73, 0x45, 0x4b,
0x35, 0x1a, 0x49, 0xf4, 0x6c, 0x8e, 0x9d, 0xce, 0xe1, 0x5c, 0x96, 0x4d, 0x29, 0x5c, 0x31, 0xc9
};
static const gchar test_identify_nonce[] = {
0x3a, 0x1b, 0x50, 0x6f, 0x5b, 0xec, 0x08, 0x90, 0x59, 0xac, 0xef, 0xb9, 0xb4, 0x4d, 0xfb, 0xde,
0xa7, 0xa5, 0x99, 0xee, 0x9a, 0xa2, 0x67, 0xe5, 0x25, 0x26, 0x64, 0xd6, 0x0b, 0x79, 0x80, 0x53
};
/* FpiSdcpDevice */
/* Internal functions of FpSdcpDevice */
void
fpi_sdcp_device_get_application_secret (FpSdcpDevice *self,
GBytes **application_secret)
{
GBytes *data = NULL;
g_return_if_fail (*application_secret == NULL);
g_object_get (G_OBJECT (self), "sdcp-data", &data, NULL);
if (!data)
return;
*application_secret = g_steal_pointer (&data);
}
void
fpi_sdcp_device_set_application_secret (FpSdcpDevice *self,
GBytes *application_secret)
{
g_return_if_fail (application_secret);
g_object_set (G_OBJECT (self), "sdcp-data", application_secret, NULL);
}
void
fpi_sdcp_device_open (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_OPEN);
cls->open (self);
}
void
fpi_sdcp_device_connect (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
GError *error = NULL;
g_clear_pointer (&priv->host_private_key, g_bytes_unref);
g_clear_pointer (&priv->host_public_key, g_bytes_unref);
g_clear_pointer (&priv->host_random, g_bytes_unref);
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") != 0)
{
/* SDCP Connect: 3.i. Generate host ephemeral ECDH key pair */
fpi_sdcp_generate_host_key (&priv->host_private_key, &priv->host_public_key, &error);
if (error)
{
fpi_sdcp_device_connect_complete (self,
NULL, NULL, NULL,
error);
return;
}
/* SDCP Connect: 3.ii. Generate host random */
priv->host_random = fpi_sdcp_generate_random (&error);
if (error)
{
fpi_sdcp_device_connect_complete (self,
NULL, NULL, NULL,
error);
return;
}
}
else
{
/* Use Microsoft's SDCP documentation example values in emulation mode */
priv->host_private_key = g_bytes_new (test_host_private_key, sizeof (test_host_private_key));
priv->host_public_key = g_bytes_new (test_host_public_key, sizeof (test_host_public_key));
priv->host_random = g_bytes_new (test_host_random, sizeof (test_host_random));
}
/* SDCP Connect: 3.iii. Send the Connect message */
cls->connect (self);
return;
}
void
fpi_sdcp_device_reconnect (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
GError *error = NULL;
g_clear_pointer (&priv->reconnect_random, g_bytes_unref);
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") != 0)
{
/* SDCP Reconnect: 2.i. Generate host random */
priv->reconnect_random = fpi_sdcp_generate_random (&error);
if (error)
{
fpi_sdcp_device_reconnect_complete (self, NULL, error);
return;
}
}
else
{
/* Use Microsoft's SDCP documentation example value in emulation mode */
priv->reconnect_random = g_bytes_new (test_reconnect_random, sizeof (test_reconnect_random));
}
/* SDCP Reconnect: 2.ii. Send the Reconnect message */
if (cls->reconnect)
cls->reconnect (self);
else
fpi_sdcp_device_connect (self);
}
void
fpi_sdcp_device_list (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_LIST);
cls->list (self);
}
void
fpi_sdcp_device_enroll (FpSdcpDevice *self)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
FpPrint *print;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_ENROLL);
fpi_sdcp_device_get_application_secret (self, &application_secret);
g_return_if_fail (application_secret != NULL);
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
fpi_print_set_device_stored (print, FALSE);
g_object_set (print, "fpi-data", NULL, NULL);
cls->enroll (self);
}
void
fpi_sdcp_device_identify (FpSdcpDevice *self)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
FpiDeviceAction action;
GError *error = NULL;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_IDENTIFY || action == FPI_DEVICE_ACTION_VERIFY);
fpi_sdcp_device_get_application_secret (self, &application_secret);
g_return_if_fail (application_secret != NULL);
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") != 0)
{
/* Generate a new nonce. */
priv->identify_nonce = fpi_sdcp_generate_random (&error);
if (error)
{
fpi_device_action_error (FP_DEVICE (self), error);
return;
}
}
else
{
/* Use Microsoft's SDCP documentation example value in emulation mode */
priv->identify_nonce = g_bytes_new (test_identify_nonce, sizeof (test_identify_nonce));
}
cls->identify (self);
}
/*********************************************************/
/* Private API */
/**
* fpi_sdcp_device_open_complete:
* @self: a #FpSdcpDevice fingerprint device
* @error: A #GError or %NULL on success
*
* Reports completion of open operation. Responsible for triggering SDCP connect
* or reconnect as necessary.
*/
void
fpi_sdcp_device_open_complete (FpSdcpDevice *self,
GError *error)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
if (!error)
{
fpi_sdcp_device_get_application_secret (self, &application_secret);
/* Try a reconnect if implemented and we already have an application_secret */
if (cls->reconnect && application_secret)
fpi_sdcp_device_reconnect (self);
/* Connect if we don't already have an application_secret */
else if (!application_secret)
fpi_sdcp_device_connect (self);
/* Complete open if we are already connected */
else
fpi_device_open_complete (FP_DEVICE (self), NULL);
}
else
{
fpi_device_open_complete (FP_DEVICE (self), error);
}
}
/**
* fp_sdcp_device_get_connect_data:
* @self: a #FpSdcpDevice fingerprint device
* @host_random: (out) (transfer full): The host-generated random
* @host_public_key: (out) (transfer full): The host public key
*
* Get data required to connect to (i.e. open) the device securely.
*/
void
fpi_sdcp_device_get_connect_data (FpSdcpDevice *self,
GBytes **host_random,
GBytes **host_public_key)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_return_if_fail (host_random != NULL);
g_return_if_fail (host_public_key != NULL);
g_return_if_fail (priv->host_random);
g_return_if_fail (priv->host_public_key);
*host_random = g_bytes_new_from_bytes (priv->host_random, 0,
g_bytes_get_size (priv->host_random));
*host_public_key = g_bytes_new_from_bytes (priv->host_public_key, 0,
g_bytes_get_size (priv->host_public_key));
}
/**
* fp_sdcp_device_get_reconnect_data:
* @self: a #FpSdcpDevice fingerprint device
* @reconnect_random: (out) (transfer full): The host-generated random
*
* Get data required to reconnect to (i.e. open) to the device securely.
*/
void
fpi_sdcp_device_get_reconnect_data (FpSdcpDevice *self,
GBytes **reconnect_random)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_return_if_fail (reconnect_random != NULL);
g_return_if_fail (priv->reconnect_random);
*reconnect_random = g_bytes_new_from_bytes (priv->reconnect_random, 0,
g_bytes_get_size (priv->reconnect_random));
}
/**
* fp_sdcp_device_get_identify_data:
* @self: a #FpSdcpDevice fingerprint device
* @nonce: (out) (transfer full): A new host-generated nonce
*
* Get data required to identify a new print.
*/
void
fpi_sdcp_device_get_identify_data (FpSdcpDevice *self,
GBytes **nonce)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_return_if_fail (nonce != NULL);
g_return_if_fail (priv->identify_nonce);
*nonce = g_bytes_new_from_bytes (priv->identify_nonce, 0,
g_bytes_get_size (priv->identify_nonce));
}
/**
* fp_sdcp_device_set_identify_data:
* @self: a #FpSdcpDevice fingerprint device
* @nonce: A driver-specified nonce
*
* Sets data required to identify a new print.
*
* Most drivers should not use this function, but instead use the automatically
* generated values retrieved from fpi_sdcp_device_get_identify_data() when
* executing the device-specific Identify command.
*
* In cases where a device's Identify command does not accept a randomly
* generated nonce, this function can be used to override the randomly generated
* nonce to the nonce that was actually sent to the device.
*/
void
fpi_sdcp_device_set_identify_data (FpSdcpDevice *self,
GBytes *nonce)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_return_if_fail (nonce != NULL);
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
priv->identify_nonce = g_steal_pointer (&nonce);
}
/**
* fpi_sdcp_device_connect_complete:
* @self: a #FpSdcpDevice fingerprint device
* @device_random: The device random
* @claim: The device #FpiSdcpClaim
* @mac: The MAC authenticating @claim
* @error: A #GError or %NULL on success
*
* Reports completion of connect operation. Responsible for performing SDCP key
* agreement, deriving secrets necessary for processing all other SDCP-related
* payloads, and verifying the device connection is trusted.
*/
void
fpi_sdcp_device_connect_complete (FpSdcpDevice *self,
GBytes *device_random,
FpiSdcpClaim *claim,
GBytes *mac,
GError *error)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_OPEN);
g_return_if_fail (priv->host_private_key);
g_return_if_fail (priv->host_random);
if (error)
{
if (device_random || claim || mac)
{
g_clear_pointer (&device_random, g_bytes_unref);
g_clear_pointer (&claim, fpi_sdcp_claim_free);
g_clear_pointer (&mac, g_bytes_unref);
fp_warn ("Driver provided SDCP Connect information but also reported error.");
}
fpi_device_open_complete (FP_DEVICE (self), error);
return;
}
if (!device_random || !claim || !mac ||
(!claim->model_certificate || !claim->device_public_key || !claim->firmware_public_key ||
!claim->firmware_hash || !claim->model_signature || !claim->device_signature))
{
fp_dbg ("Driver did not provide all required information to callback; returning error instead.");
g_clear_pointer (&device_random, g_bytes_unref);
g_clear_pointer (&claim, fpi_sdcp_claim_free);
g_clear_pointer (&mac, g_bytes_unref);
fpi_device_open_complete (FP_DEVICE (self),
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Driver called connect complete with "
"incomplete arguments"));
return;
}
/* Verify connect and store the application_secret */
if (!fpi_sdcp_verify_connect (priv->host_private_key,
priv->host_random,
device_random,
claim,
mac,
!cls->ignore_device_certificate,
!cls->ignore_device_signatures,
&application_secret,
&error))
{
fpi_device_open_complete (FP_DEVICE (self),
fpi_device_error_new_msg (FP_DEVICE_ERROR_UNTRUSTED,
"SDCP Connect verification failed: %s",
error->message));
return;
}
fpi_sdcp_device_set_application_secret (self, application_secret);
/* Clear no longer needed private data */
g_clear_pointer (&priv->host_private_key, g_bytes_unref);
g_clear_pointer (&priv->host_public_key, g_bytes_unref);
g_clear_pointer (&priv->host_random, g_bytes_unref);
fpi_device_open_complete (FP_DEVICE (self), NULL);
}
/**
* fpi_sdcp_device_reconnect_complete:
* @self: a #FpSdcpDevice fingerprint device
* @mac: The MAC authenticating @claim
* @error: A #GError or %NULL on success
*
* Reports completion of a reconnect (i.e. open) operation.
*/
void
fpi_sdcp_device_reconnect_complete (FpSdcpDevice *self,
GBytes *mac,
GError *error)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_autoptr(GBytes) application_secret = NULL;
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_OPEN);
g_return_if_fail (priv->reconnect_random);
if (error)
{
if (mac)
{
fp_warn ("Driver provided a reconnect MAC but also reported an error.");
g_clear_pointer (&mac, g_bytes_unref);
}
/* Silently try a normal connect instead. */
fpi_sdcp_device_connect (self);
}
else if (mac)
{
fpi_sdcp_device_get_application_secret (self, &application_secret);
if (fpi_sdcp_verify_reconnect (application_secret, priv->reconnect_random, mac, &error))
{
fp_dbg ("SDCP Reconnect succeeded");
fpi_device_open_complete (FP_DEVICE (self), NULL);
}
else
{
fp_dbg ("SDCP Reconnect failed; doing a full connect.");
fpi_sdcp_device_connect (self);
}
}
else
{
fpi_device_open_complete (FP_DEVICE (self),
fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
"Driver called reconnect complete with wrong arguments"));
}
/* Clear no longer needed private data */
g_clear_pointer (&priv->reconnect_random, g_bytes_unref);
}
/**
* fpi_sdcp_device_list_complete:
* @self: a #FpSdcpDevice fingerprint device
* @ids: A #GPtrArray of #GBytes of each SDCP enrollment ID stored on the device
* @error: A #GError or %NULL on success
*
* Convenience function to create the minimally required #FpPrint list for
* #FpSdcpDevice prints using the provided @ids, then uses that #FpPrint list to
* report completion of the list operation.
*
* If the device provides additional attributes that should be stored on each
* #FpPrint as part of the list operation, a #GPtrArray of #FpPrint can instead
* be created with the additional attributes and fpi_device_list_complete() can
* be used instead of this function.
*
* Please note that the @ids array will be freed using g_ptr_array_unref() and
* the elements are destroyed automatically. As such, you must use
* g_ptr_array_new_with_free_func() with `(GDestroyNotify) g_bytes_unref` as the
* free func when creating the #GPtrArray.
*/
void
fpi_sdcp_device_list_complete (FpSdcpDevice *self,
GPtrArray *ids,
GError *error)
{
g_autoptr(GPtrArray) prints = NULL;
gint prints_len = 0;
FpiDeviceAction action;
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_LIST);
if (error)
{
fpi_device_list_complete (FP_DEVICE (self), NULL, error);
return;
}
prints = g_ptr_array_new_with_free_func (g_object_unref);
/* Allow an empty array (prints_len=0) but if ids has been passed, use it */
if (ids)
prints_len = ids->len;
for (gint i = 0; i < prints_len; i++)
{
FpPrint *print = fp_print_new (FP_DEVICE (self));
fpi_print_set_type (print, FPI_PRINT_SDCP);
fpi_print_set_device_stored (print, FALSE);
fpi_sdcp_device_set_print_id (print, g_ptr_array_index (ids, i));
g_ptr_array_add (prints, g_object_ref_sink (print));
}
fpi_device_list_complete (FP_DEVICE (self), g_steal_pointer (&prints), NULL);
g_clear_pointer (&ids, g_ptr_array_unref);
}
/**
* fpi_sdcp_device_enroll_commit:
* @self: a #FpSdcpDevice fingerprint device
* @nonce: The device generated nonce
* @error: a #GError or %NULL on success
*
* Called when the print is ready to be committed to device memory.
* During enrollment, fpi_device_enroll_progress() must be called for each
* successful stage before the print can be committed.
* The @nonce generated by the device-specific EnrollmentNonce response must be
* provided in order for the enrollment ID to be generated.
* The driver's enroll_commit() vfunc will be triggered upon successfully
* generating the enrollment ID.
*/
void
fpi_sdcp_device_enroll_commit (FpSdcpDevice *self,
GBytes *nonce,
GError *error)
{
FpSdcpDeviceClass *cls = FP_SDCP_DEVICE_GET_CLASS (self);
g_autoptr(GBytes) application_secret = NULL;
GBytes *id = NULL;
FpPrint *print;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_ENROLL);
g_return_if_fail (nonce != NULL);
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
fpi_sdcp_device_get_application_secret (self, &application_secret);
id = fpi_sdcp_generate_enrollment_id (application_secret, nonce, &error);
if (!id || error)
{
fp_warn ("Could not generate SDCP enrollment ID");
fpi_device_enroll_complete (FP_DEVICE (self), NULL, error);
g_object_set (print, "fpi-data", NULL, NULL);
return;
}
/* Set to true once committed */
fpi_print_set_device_stored (print, FALSE);
/* Attach the ID to the print */
fpi_sdcp_device_set_print_id (print, id);
cls->enroll_commit (self, id);
g_clear_pointer (&id, g_bytes_unref);
}
/**
* fpi_sdcp_device_enroll_commit_complete:
* @self: a #FpSdcpDevice fingerprint device
* @error: a #GError or %NULL on success
*
* Called when device has committed the given print to memory.
* This finalizes the enroll operation.
*/
void
fpi_sdcp_device_enroll_commit_complete (FpSdcpDevice *self,
GError *error)
{
g_autoptr(GBytes) id = NULL;
FpPrint *print;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
g_return_if_fail (fpi_device_get_current_action (FP_DEVICE (self)) == FPI_DEVICE_ACTION_ENROLL);
if (error)
{
fpi_device_enroll_complete (FP_DEVICE (self), NULL, error);
return;
}
fpi_device_get_enroll_data (FP_DEVICE (self), &print);
fpi_sdcp_device_get_print_id (print, &id);
if (!id)
{
g_error ("Inconsistent state; the print must have the enrolled ID attached at this point");
return;
}
fpi_print_set_type (print, FPI_PRINT_SDCP);
fpi_print_set_device_stored (print, TRUE);
fpi_device_enroll_complete (FP_DEVICE (self), g_object_ref (print), NULL);
}
/**
* fpi_sdcp_device_identify_retry:
* @self: a #FpSdcpDevice fingerprint device
* @error: a #GError containing the retry condition
*
* Called when the device requires the finger to be presented again.
* This should not be called for a verified no-match, it should only
* be called if e.g. the finger was not centered properly or similar.
*
* Effectively this simply raises the error up. This function exists
* to bridge the difference in semantics that SDPC has from how
* libfprint works internally.
*/
void
fpi_sdcp_device_identify_retry (FpSdcpDevice *self,
GError *error)
{
FpiDeviceAction action;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_IDENTIFY || action == FPI_DEVICE_ACTION_VERIFY);
if (action == FPI_DEVICE_ACTION_VERIFY)
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_ERROR, NULL, error);
else if (action == FPI_DEVICE_ACTION_IDENTIFY)
fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, error);
}
/**
* fpi_sdcp_device_identify_complete:
* @self: a #FpSdcpDevice fingerprint device
* @id: (transfer none): the ID as reported by the device
* @mac: (transfer none): MAC authenticating the message
* @error: (transfer full): #GError if an error occured
*
* Called when device is done with the identification routine. The
* returned ID may be %NULL if none of the in-device templates matched.
*/
void
fpi_sdcp_device_identify_complete (FpSdcpDevice *self,
GBytes *id,
GBytes *mac,
GError *error)
{
FpSdcpDevicePrivate *priv = fp_sdcp_device_get_instance_private (self);
g_autoptr(GBytes) application_secret = NULL;
FpPrint *identified_print;
FpiDeviceAction action;
g_return_if_fail (FP_IS_SDCP_DEVICE (self));
action = fpi_device_get_current_action (FP_DEVICE (self));
g_return_if_fail (action == FPI_DEVICE_ACTION_IDENTIFY || action == FPI_DEVICE_ACTION_VERIFY);
g_return_if_fail (priv->identify_nonce);
if (error)
{
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
fpi_device_action_error (FP_DEVICE (self), error);
return;
}
/* No error and no valid id/mac provided means that there was no match from the device */
if (!id || !mac || g_bytes_get_size (id) != SDCP_ENROLLMENT_ID_SIZE ||
g_bytes_get_size (mac) != SDCP_MAC_SIZE)
{
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
if (action == FPI_DEVICE_ACTION_VERIFY)
{
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_FAIL, NULL, NULL);
fpi_device_verify_complete (FP_DEVICE (self), NULL);
}
else
{
fpi_device_identify_report (FP_DEVICE (self), NULL, NULL, NULL);
fpi_device_identify_complete (FP_DEVICE (self), NULL);
}
return;
}
fpi_sdcp_device_get_application_secret (self, &application_secret);
if (!fpi_sdcp_verify_identify (application_secret, priv->identify_nonce, id, mac, &error))
{
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
fpi_device_action_error (FP_DEVICE (self), error);
return;
}
/* Clear no longer needed private data */
g_clear_pointer (&priv->identify_nonce, g_bytes_unref);
/* Create a new print */
identified_print = fp_print_new (FP_DEVICE (self));
fpi_print_set_type (identified_print, FPI_PRINT_SDCP);
/* Set to true once committed */
fpi_print_set_device_stored (identified_print, FALSE);
/* Attach the ID to the print */
fpi_sdcp_device_set_print_id (identified_print, id);
/* The surrounding API expects a match/no-match against a given set. */
if (action == FPI_DEVICE_ACTION_VERIFY)
{
FpPrint *print;
fpi_device_get_verify_data (FP_DEVICE (self), &print);
if (fp_print_equal (print, identified_print))
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_SUCCESS, identified_print, NULL);
else
fpi_device_verify_report (FP_DEVICE (self), FPI_MATCH_FAIL, identified_print, NULL);
fpi_device_verify_complete (FP_DEVICE (self), NULL);
}
else
{
GPtrArray *prints;
gint i;
fpi_device_get_identify_data (FP_DEVICE (self), &prints);
for (i = 0; i < prints->len; i++)
{
FpPrint *print = g_ptr_array_index (prints, i);
if (fp_print_equal (print, identified_print))
{
fpi_device_identify_report (FP_DEVICE (self), print, identified_print, NULL);
fpi_device_identify_complete (FP_DEVICE (self), NULL);
return;
}
}
/* Print wasn't in database. */
fpi_device_identify_report (FP_DEVICE (self), NULL, identified_print, NULL);
fpi_device_identify_complete (FP_DEVICE (self), NULL);
}
}
/**
* fpi_sdcp_device_get_print_id:
* @print: an SDCP device #FpPrint
* @id: (out) (transfer full): the ID gotten from the @print data
*
* Gets the SDCP enrollment ID from the @print data.
*
* The returned @id may be %NULL if the data was not set or in the wrong format.
*/
void
fpi_sdcp_device_get_print_id (FpPrint *print,
GBytes **id)
{
g_autoptr(GVariant) id_var = NULL;
g_autoptr(GVariant) data = NULL;
const guint8 *id_data;
gsize id_len;
g_return_if_fail (print);
g_return_if_fail (*id == NULL);
g_object_get (G_OBJECT (print), "fpi-data", &data, NULL);
if (!data)
{
fp_warn ("SDCP print data has not been set.");
return;
}
if (!g_variant_check_format_string (data, "(@ay)", FALSE))
{
fp_warn ("SDCP print data is not in expected format.");
return;
}
g_variant_get (data, "(@ay)", &id_var);
id_data = g_variant_get_fixed_array (id_var, &id_len, sizeof (guint8));
*id = g_bytes_new (id_data, id_len);
}
/**
* fpi_sdcp_device_set_print_id:
* @print: an SDCP device #FpPrint
* @id: the ID to set in the @print data
*
* Sets the SDCP enrollment ID in the @print data.
*/
void
fpi_sdcp_device_set_print_id (FpPrint *print,
GBytes *id)
{
GVariant *id_var;
GVariant *data;
g_return_if_fail (print);
g_return_if_fail (id);
id_var = g_variant_new_fixed_array (G_VARIANT_TYPE_BYTE,
g_bytes_get_data (id, NULL),
g_bytes_get_size (id),
1);
data = g_variant_new ("(@ay)", id_var);
g_object_set (G_OBJECT (print), "fpi-data", data, NULL);
}

View File

@@ -1,185 +0,0 @@
/*
* FpSdcpDevice - A base class for SDCP enabled devices
* Copyright (C) 2020 Benjamin Berg <bberg@redhat.com>
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-device.h"
#include "fp-sdcp-device.h"
#define SDCP_PUBLIC_KEY_SIZE 65
#define SDCP_APPLICATION_SECRET_SIZE 32
#define SDCP_RANDOM_SIZE 32
#define SDCP_MAC_SIZE 32
#define SDCP_NONCE_SIZE 32
#define SDCP_ENROLLMENT_ID_SIZE 32
#define SDCP_SIGNATURE_SIZE 64
/**
* FpiSdcpClaim:
* @model_certificate: Microsoft-issued per-model certificate encoded in x509
* ASN.1 DER format (`cert_m`)
* @device_public_key: The per-device ECDSA public key (`pk_d`)
* @firmware_public_key: The ephemeral public key generated by the device
* firmware (`pk_f`)
* @firmware_hash: Hash of the firmware and firmware public key (`h_f`)
* @model_signature: Device public key signed by the model key (`s_m`)
* @device_signature: Firmware hash and public key signed by the device private
* key (`s_d`)
*
* Structure to hold the claim as produced by the device during a secure
* connect. See the SDCP specification for more details.
*
* Note all of these may simply be memory views into a larger #GBytes created
* using g_bytes_new_from_bytes().
*/
struct _FpiSdcpClaim
{
/*< public >*/
GBytes *model_certificate; /* cert_m */
GBytes *device_public_key; /* pk_d */
GBytes *firmware_public_key; /* pk_f */
GBytes *firmware_hash; /* h_f */
GBytes *model_signature; /* s_m */
GBytes *device_signature; /* s_d */
};
typedef struct _FpiSdcpClaim FpiSdcpClaim;
GType fpi_sdcp_claim_get_type (void) G_GNUC_CONST;
FpiSdcpClaim *fpi_sdcp_claim_new (void);
FpiSdcpClaim *fpi_sdcp_claim_copy (FpiSdcpClaim *other);
void fpi_sdcp_claim_free (FpiSdcpClaim *claim);
G_DEFINE_AUTOPTR_CLEANUP_FUNC (FpiSdcpClaim, fpi_sdcp_claim_free)
/**
* FpSdcpDeviceClass:
* @ignore_device_certificate: Set to %TRUE to skip validating the device's
* #FpiSdcpClaim.model_certificate against the SDCP truststore.
* @ignore_device_signatures: Set to %TRUE to skip verifying the device's
* #FpiSdcpClaim.model_signature and #FpiSdcpClaim.device_signature.
* @open: Open the device. Similar to #FpDeviceClass.open except that
* completion with fpi_sdcp_device_open_complete() will also take care of
* executing @connect and @reconnect as necessary.
* @connect: Establish SDCP connection.
* @reconnect: Perform a faster reconnect. Drivers do not need to provide this
* function. If reconnect fails, then a normal connect will be tried.
* @list: List prints stored on the device. The driver must create a #GPtrArray
* of #GBytes with each enrollment ID stored on the device and use it to call
* fpi_sdcp_device_list_complete() in order to complete the operation.
* @enroll: Start the enrollment procedure and capture all samples. The driver
* must report enrollment progress using fpi_device_enroll_progress(). It
* should also store available metadata about the print in device memory. The
* driver must call fpi_sdcp_device_enroll_commit() when all enrollment stages
* are complete and the print is ready to be commited to the device.
* @enroll_commit: Commit the newly-enrolled print to the device memory using
* the passed id. id may be %NULL, in which case the driver must abort the
* enrollment process. id is owned by the base class and remains valid
* throughout the operation. On completion, the driver must call
* fpi_sdcp_device_enroll_commit_complete().
* @identify: Start identification process. On completion, the driver must call
* fpi_sdcp_device_identify_complete(). To request the user to retry the
* fpi_sdcp_device_identify_retry() function is used.
*
* These are the main entry points for drivers implementing SDCP.
*
* Drivers *must* eventually call the corresponding function to finish the
* operation.
*
* The following #FpDeviceClass entry points are also compatible and can be set
* on the #FpDeviceClass if supported for a given device:
* - #FpDeviceClass.probe
* - #FpDeviceClass.close
* - #FpDeviceClass.delete
* - #FpDeviceClass.clear_storage
* - #FpDeviceClass.cancel
* - #FpDeviceClass.suspend
* - #FpDeviceClass.resume
*
* XXX: Is the use of fpi_device_action_error() acceptable?
*
* Drivers *must* also handle cancellation properly for any long running
* operation (i.e. any operation that requires capturing). It is entirely fine
* to ignore cancellation requests for short operations (e.g. open/close).
*
* This API is solely intended for drivers. It is purely internal and neither
* API nor ABI stable.
*/
struct _FpSdcpDeviceClass
{
FpDeviceClass parent_class;
gboolean ignore_device_certificate;
gboolean ignore_device_signatures;
void (*open) (FpSdcpDevice *sdcp_device);
void (*connect) (FpSdcpDevice *sdcp_device);
void (*reconnect) (FpSdcpDevice *sdcp_device);
void (*list) (FpSdcpDevice *sdcp_device);
void (*enroll) (FpSdcpDevice *sdcp_device);
void (*enroll_commit) (FpSdcpDevice *sdcp_device,
GBytes *id);
void (*identify) (FpSdcpDevice *sdcp_device);
};
void fpi_sdcp_device_open_complete (FpSdcpDevice *self,
GError *error);
void fpi_sdcp_device_get_connect_data (FpSdcpDevice *self,
GBytes **host_random,
GBytes **host_public_key);
void fpi_sdcp_device_connect_complete (FpSdcpDevice *self,
GBytes *device_random,
FpiSdcpClaim *claim,
GBytes *mac,
GError *error);
void fpi_sdcp_device_get_reconnect_data (FpSdcpDevice *self,
GBytes **reconnect_random);
void fpi_sdcp_device_reconnect_complete (FpSdcpDevice *self,
GBytes *mac,
GError *error);
void fpi_sdcp_device_list_complete (FpSdcpDevice *self,
GPtrArray *ids,
GError *error);
void fpi_sdcp_device_enroll_commit (FpSdcpDevice *self,
GBytes *nonce,
GError *error);
void fpi_sdcp_device_enroll_commit_complete (FpSdcpDevice *self,
GError *error);
void fpi_sdcp_device_get_identify_data (FpSdcpDevice *self,
GBytes **nonce);
void fpi_sdcp_device_set_identify_data (FpSdcpDevice *self,
GBytes *nonce);
void fpi_sdcp_device_identify_retry (FpSdcpDevice *self,
GError *error);
void fpi_sdcp_device_identify_complete (FpSdcpDevice *self,
GBytes *id,
GBytes *mac,
GError *error);
void fpi_sdcp_device_get_print_id (FpPrint *print,
GBytes **id);
void fpi_sdcp_device_set_print_id (FpPrint *print,
GBytes *id);

File diff suppressed because it is too large Load Diff

View File

@@ -1,132 +0,0 @@
/*
* Secure Device Connection Protocol (SDCP) support implementation
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
#include "fpi-compat.h"
#include "fpi-sdcp-device.h"
/**
* fpi_sdcp_generate_host_key:
* @private_key: (out) (transfer full): The host private key (sk_h)
* @public_key: (out) (transfer full): The host public key (pk_h)
* @error: (out): #GError in case the out values are %NULL
*
* Function to generate a new ephemeral ECDH key pair for use with SDCP.
**/
void fpi_sdcp_generate_host_key (GBytes **private_key,
GBytes **public_key,
GError **error);
/**
* fpi_sdcp_generate_random:
* @error: (out): #GError in case the return value is %NULL
*
* Returns: A new #GBytes with a secure random of length %SDCP_RANDOM_SIZE
**/
GBytes *fpi_sdcp_generate_random (GError **error);
/**
* fpi_sdcp_verify_connect:
* @host_private_key: Private key generated using fpi_sdcp_generate_host_key() (sk_h)
* @host_random: Random generated using fpi_sdcp_generate_random() (r_h)
* @device_random: The random provided in the device's ConnectResponse (r_d)
* @claim: #FpiSdcpClaim provided in the device's ConnectResponse (c)
* @mac: The MAC provided in the device's ConnectResponse (m)
* @validate_certificate: If the model certificate (cert_m) should be parsed and
* its trust chain validated as issued from Microsoft's well-known issuers
* @verify_signatures: If the model signature (s_m) and device signature (s_d)
* should be validated against the certificate and keys provided in the claim
* @application_secret: (out) (transfer full): A new #GBytes with the derived
* application secret (s) of length %SDCP_APPLICATION_SECRET_SIZE
* @error: (out): #GError in case the return value is %NULL
*
* High level function which internally handles the derivation of all necessary
* SDCP-related keys and secrets from the device's ConnectResponse and derives
* the application secret for use with all other SDCP-related functions.
*
* This function will also perform a validation of the ConnectResponse MAC and
* optionally perform additional verifications based on the provided
* @validate_certificate and @verify_signatures booleans. If any of these these
* validations fail then %NULL will be returned, indicating that the SDCP secure
* connection channel could not be established.
*
* Returns: %TRUE if the @application_secret was successfully derived and the
* ConnectResponse has been successfully verified
**/
gboolean fpi_sdcp_verify_connect (GBytes *host_private_key,
GBytes *host_random,
GBytes *device_random,
FpiSdcpClaim *claim,
GBytes *mac,
gboolean validate_certificate,
gboolean verify_signatures,
GBytes **application_secret,
GError **error);
/**
* fpi_sdcp_verify_reconnect:
* @application_secret: The host's derived application secret (s)
* @random: The host-generated random sent to the device's Reconnect command (r)
* @mac: The MAC provided in the device's ReconnectResponse (m)
* @error: (out): #GError in case the return value is %FALSE
*
* Verifies the SDCP ReconnectResponse.
*
* Returns: %TRUE if the ReconnectResponse is verified successfully
**/
gboolean fpi_sdcp_verify_reconnect (GBytes *application_secret,
GBytes *random,
GBytes *mac,
GError **error);
/**
* fpi_sdcp_verify_identify:
* @application_secret: The host's derived application secret (s)
* @nonce: The host-generated nonce sent to the device's Identify command (n)
* @id: The ID provided in the device's AuthorizedIdentity (id)
* @mac: The MAC provided in the device's AuthorizedIdentity (m)
* @error: (out): #GError in case the return value is %FALSE
*
* Verifies the SDCP ReconnectResponse.
*
* Returns: %TRUE if the ReconnectResponse is verified successfully
**/
gboolean fpi_sdcp_verify_identify (GBytes *application_secret,
GBytes *nonce,
GBytes *id,
GBytes *mac,
GError **error);
/**
* fpi_sdcp_generate_enrollment_id:
* @application_secret: The host's derived application secret (s)
* @nonce: The nonce received from the device in response to the EnrollBegin
* command (n)
* @error: (out): #GError in case the return value is %NULL
*
* Generates a new id for use with the device's EnrollCommit command.
*
* Returns: A new #GBytes with the generated enrollment id of length
* %SDCP_ENROLLMENT_ID_SIZE
**/
GBytes *fpi_sdcp_generate_enrollment_id (GBytes *application_secret,
GBytes *nonce,
GError **error);

View File

@@ -120,7 +120,6 @@ static const FpIdEntry allowlist_id_table[] = {
{ .vid = 0x1c7a, .pid = 0x0300 },
{ .vid = 0x1c7a, .pid = 0x0575 },
{ .vid = 0x1c7a, .pid = 0x0576 },
{ .vid = 0x1c7a, .pid = 0x0584 },
{ .vid = 0x1c7a, .pid = 0x0577 },
{ .vid = 0x1c7a, .pid = 0x057e },
{ .vid = 0x2541, .pid = 0x0236 },

View File

@@ -21,7 +21,6 @@ libfprint_private_sources = [
'fpi-device.c',
'fpi-image-device.c',
'fpi-image.c',
'fpi-log.c',
'fpi-print.c',
'fpi-ssm.c',
'fpi-usb-transfer.c',
@@ -48,8 +47,8 @@ libfprint_private_headers = [
'fpi-log.h',
'fpi-minutiae.h',
'fpi-print.h',
'fpi-ssm.h',
'fpi-usb-transfer.h',
'fpi-ssm.h',
] + spi_headers
nbis_sources = [
@@ -144,8 +143,6 @@ driver_sources = {
[ 'drivers/virtual-device.c' ],
'virtual_device_storage' :
[ 'drivers/virtual-device-storage.c' ],
'virtual_sdcp' :
[ 'drivers/virtual-sdcp.c' ],
'synaptics' :
[ 'drivers/synaptics/synaptics.c', 'drivers/synaptics/bmkt_message.c' ],
'goodixmoc' :
@@ -167,8 +164,6 @@ helper_sources = {
[ 'drivers/aes3k.c' ],
'openssl' :
[ ],
'sdcp' :
[ ],
'udev' :
[ ],
'virtual' :
@@ -184,24 +179,6 @@ foreach helper : driver_helpers
drivers_sources += helper_sources[helper]
endforeach
subdir('sdcp')
if 'sdcp' in driver_helpers
libfprint_sources += [
'fp-sdcp-device.c',
]
libfprint_private_sources += [
'fpi-sdcp.c',
'fpi-sdcp-device.c',
sdcp_truststore_resource_c,
]
libfprint_public_headers += [
'fp-sdcp-device.h',
]
libfprint_private_headers += [
'fpi-sdcp.h',
'fpi-sdcp-device.h',
]
endif
fp_enums = gnome.mkenums_simple('fp-enums',
sources: libfprint_public_headers,

View File

@@ -1,25 +0,0 @@
#!/usr/bin/python3
import os
import sys
if len(sys.argv) != 3:
print("generate-gresource.py: Generates SDCP Truststore GResource XML file from certificates in ./truststore/*.pem")
print("Usage: generate-gresource.py <libfprint_sdcp_source_dir> <output_xml_file>")
sys.exit(1)
gresource_prefix = "/org/freedesktop/fprint/sdcp"
relative_folder = "truststore"
full_folder = os.path.join(sys.argv[1], relative_folder)
output = sys.argv[2]
files = [f for f in os.listdir(full_folder) if os.path.isfile(os.path.join(full_folder, f)) and f.endswith('.pem')]
with open(output, 'w') as f:
f.write('<?xml version="1.0" encoding="UTF-8"?>\n')
f.write('<gresources>\n')
f.write(f' <gresource prefix="{gresource_prefix}">\n')
for file in files:
f.write(f' <file compressed="true">{relative_folder}/{file}</file>\n')
f.write(' </gresource>\n')
f.write('</gresources>\n')

View File

@@ -1,32 +0,0 @@
sdcp_truststore_gresource_xml = custom_target('sdcp-truststore.gresource',
input : 'generate-gresource.py',
output : 'sdcp-truststore.gresource.xml',
command : [find_program('python3'), '@INPUT@', meson.current_source_dir(), '@OUTPUT@'],
)
sdcp_truststore_resource_h = custom_target('fpi-sdcp-truststore-resource.h',
input : sdcp_truststore_gresource_xml,
output : 'fpi-sdcp-truststore-resource.h',
command : ['glib-compile-resources',
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--internal',
'--generate',
'--c-name', 'fpi_sdcp_truststore',
'--manual-register',
'@INPUT@']
)
sdcp_truststore_resource_c = custom_target('fpi-sdcp-truststore-resource.c',
depends : [sdcp_truststore_resource_h],
input : sdcp_truststore_gresource_xml,
output : 'fpi-sdcp-truststore-resource.c',
command : ['glib-compile-resources',
'--target=@OUTPUT@',
'--sourcedir=' + meson.current_source_dir(),
'--internal',
'--generate',
'--c-name', 'fpi_sdcp_truststore',
'--manual-register',
'@INPUT@']
)

View File

@@ -1,19 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDIDCCAqWgAwIBAgIQKs9yK9kUXqlMVB+fSF1UMjAKBggqhkjOPQQDAzCBlDEL
MAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1v
bmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE+MDwGA1UEAxM1TWlj
cm9zb2Z0IEVDQyBEZXZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIw
MTcwHhcNMTcxMTA5MTk0MDQ4WhcNNDIxMTA5MTk0ODE5WjCBlDELMAkGA1UEBhMC
VVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNV
BAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE+MDwGA1UEAxM1TWljcm9zb2Z0IEVD
QyBEZXZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5IDIwMTcwdjAQBgcq
hkjOPQIBBgUrgQQAIgNiAARiivDX0DS0EXoGlfbd2PwxSC87Cszr6/aAjSx6pMwU
4kzXcId0dhrjSkPSIO5UCz50ggQGQiTwqRzyhM44FlEyzbzl6OHGDwR1vAg3wdmm
WEXWySzyAZKsfkwg0G7bPkijgbkwgbYwCwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQF
MAMBAf8wHQYDVR0OBBYEFBTaW/EOZkfRXRNfW3rr618BCLVJMBAGCSsGAQQBgjcV
AQQDAgEAMGUGA1UdIAReMFwwBgYEVR0gADBSBgwrBgEEAYI3TIN9AQEwQjBABggr
BgEFBQcCARY0aHR0cDovL3d3dy5taWNyb3NvZnQuY29tL3BraW9wcy9Eb2NzL1Jl
cG9zaXRvcnkuaHRtADAKBggqhkjOPQQDAwNpADBmAjEAxxAFFL8juLXiulXvZgBQ
pGGPCcV2Tr3CorZ4p/uO2/rtBemqhL3CjKAm40VlhEz8AjEArE5fhA54SEDjoTwZ
VUosaqXa8ych31qjZI+e1ttbOPebAZTt9ac7+lTzJcLTEQch
-----END CERTIFICATE-----

View File

@@ -1,23 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIDwzCCA0qgAwIBAgITMwAAAAuNaMBkOddK4wAAAAAACzAKBggqhkjOPQQDAjCB
hDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEuMCwGA1UEAxMl
V2luZG93cyBIZWxsbyBTZWN1cmUgRGV2aWNlcyBQQ0EgMjAxODAeFw0yMTEyMDky
MzI1MDFaFw0zMDEyMDkyMzM1MDFaMFYxCzAJBgNVBAYTAlVTMR4wHAYDVQQKExVN
aWNyb3NvZnQgQ29ycG9yYXRpb24xJzAlBgNVBAMTHldpbmRvd3MgSGVsbG8gMjA5
NkFEQ0MgQ0EgMjAyMTBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABJWgPvl44Gei
RrTuA3f1eT60pAlBWM7ym7WSchqz3hge1WS8RUxPVedu0f7MCe/R6O6RVjV7HWq4
c6jo9FwoiAGjggHGMIIBwjAQBgkrBgEEAYI3FQEEAwIBADAdBgNVHQ4EFgQUvzdI
40pjLelTo7qJApjAaUcqmbkwVAYDVR0gBE0wSzBJBgRVHSAAMEEwPwYIKwYBBQUH
AgEWM2h0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMvRG9jcy9SZXBvc2l0
b3J5Lmh0bTAZBgkrBgEEAYI3FAIEDB4KAFMAdQBiAEMAQTALBgNVHQ8EBAMCAYYw
DwYDVR0TAQH/BAUwAwEB/zAfBgNVHSMEGDAWgBTaykvQTFYDJ1+X63WjAsO/RZz4
sTBoBgNVHR8EYTBfMF2gW6BZhldodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtp
b3BzL2NybC9XaW5kb3dzJTIwSGVsbG8lMjBTZWN1cmUlMjBEZXZpY2VzJTIwUENB
JTIwMjAxOC5jcmwwdQYIKwYBBQUHAQEEaTBnMGUGCCsGAQUFBzAChllodHRwOi8v
d3d3Lm1pY3Jvc29mdC5jb20vcGtpb3BzL2NlcnRzL1dpbmRvd3MlMjBIZWxsbyUy
MFNlY3VyZSUyMERldmljZXMlMjBQQ0ElMjAyMDE4LmNydDAKBggqhkjOPQQDAgNn
ADBkAjAeGyYlzf+uBQXI/EW84I5CGFbo/U6dL4k1Y83f2p94d0wrNwjUb/yprb4+
L9+OKfQCME8PgRyJQxsvsne+WI6gr0ZzJilotoiRdvDzlMK4+hx5TGJWTV17AsAo
z2330epHQQ==
-----END CERTIFICATE-----

View File

@@ -1,26 +0,0 @@
-----BEGIN CERTIFICATE-----
MIIEVjCCA9ygAwIBAgITMwAAAANsz+3iRHAZvwAAAAAAAzAKBggqhkjOPQQDAzCB
lDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1Jl
ZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjE+MDwGA1UEAxM1
TWljcm9zb2Z0IEVDQyBEZXZpY2VzIFJvb3QgQ2VydGlmaWNhdGUgQXV0aG9yaXR5
IDIwMTcwHhcNMTgwMTI1MTk0OTM4WhcNMzMwMTI1MTk1OTM4WjCBhDELMAkGA1UE
BhMCVVMxEzARBgNVBAgTCldhc2hpbmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAc
BgNVBAoTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjEuMCwGA1UEAxMlV2luZG93cyBI
ZWxsbyBTZWN1cmUgRGV2aWNlcyBQQ0EgMjAxODB2MBAGByqGSM49AgEGBSuBBAAi
A2IABB3dCAIDJXUg4nGLrSgJgukG7oPFOmxLcZJQTiDpcrT8UyrvXcyatM12uJSX
RLJxDsmxFgOhZSu56F1f8jAu3bErIPy+AIjqH6d/mYSYfHE+TTSDaZsIy3iyS73X
Pr5noKOCAfwwggH4MBAGCSsGAQQBgjcVAQQDAgEAMB0GA1UdDgQWBBTaykvQTFYD
J1+X63WjAsO/RZz4sTBlBgNVHSAEXjBcMAYGBFUdIAAwUgYMKwYBBAGCN0yDfQEB
MEIwQAYIKwYBBQUHAgEWNGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2lvcHMv
RG9jcy9SZXBvc2l0b3J5Lmh0bQAwGQYJKwYBBAGCNxQCBAweCgBTAHUAYgBDAEEw
CwYDVR0PBAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHwYDVR0jBBgwFoAUFNpb8Q5m
R9FdE19beuvrXwEItUkwegYDVR0fBHMwcTBvoG2ga4ZpaHR0cDovL3d3dy5taWNy
b3NvZnQuY29tL3BraW9wcy9jcmwvTWljcm9zb2Z0JTIwRUNDJTIwRGV2aWNlcyUy
MFJvb3QlMjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMTcuY3JsMIGHBggr
BgEFBQcBAQR7MHkwdwYIKwYBBQUHMAKGa2h0dHA6Ly93d3cubWljcm9zb2Z0LmNv
bS9wa2lvcHMvY2VydHMvTWljcm9zb2Z0JTIwRUNDJTIwRGV2aWNlcyUyMFJvb3Ql
MjBDZXJ0aWZpY2F0ZSUyMEF1dGhvcml0eSUyMDIwMTcuY3J0MAoGCCqGSM49BAMD
A2gAMGUCMFYqrXJMuYyzI4D1X/ghlGYPdnfiewPdMF7LkMp45gstEuX3ZzFYcebz
ZMEEs4vp4gIxALkgYbnQXjqkoor+HfwnYQuYFowCnCB/7vPLHwo3YrGOztmanqzm
GtS48agrsbRAmw==
-----END CERTIFICATE-----

View File

@@ -112,8 +112,8 @@ virtual_drivers = [
'virtual_image',
'virtual_device',
'virtual_device_storage',
'virtual_sdcp',
]
default_drivers = [
'upektc_img',
'vfs5011',
@@ -209,13 +209,11 @@ driver_helper_mapping = {
'aes2660' : [ 'aeslib', 'aesx660' ],
'aes3500' : [ 'aeslib', 'aes3k' ],
'aes4000' : [ 'aeslib', 'aes3k' ],
'egismoc' : [ 'sdcp' ],
'elanspi' : [ 'udev' ],
'uru4000' : [ 'openssl' ],
'elanspi' : [ 'udev' ],
'virtual_image' : [ 'virtual' ],
'virtual_device' : [ 'virtual' ],
'virtual_device_storage' : [ 'virtual' ],
'virtual_sdcp' : [ 'virtual', 'sdcp' ],
}
driver_helpers = []
@@ -274,13 +272,6 @@ foreach i : driver_helpers
error('OpenSSL is required for @0@ and possibly others'.format(driver))
endif
optional_deps += openssl_dep
elif i == 'sdcp'
openssl_dep = dependency('openssl', version: '>= 3.0.8', required: false)
if not openssl_dep.found()
error('OpenSSL >= 3.0.8 is required for SDCP support (@0@ and possibly others)'.format(driver))
endif
optional_deps += openssl_dep
elif i == 'udev'
install_udev_rules = true

Binary file not shown.

View File

@@ -1,21 +1,17 @@
P: /devices/pci0000:00/0000:00:14.0/usb3/3-5
N: bus/usb/003/002=12010002FF0000407A1C820581110102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005
N: bus/usb/003/012=12010002FF0000407A1C820581110102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005
E: BUSNUM=003
E: CURRENT_TAGS=:snap_cups_ippeveprinter:snap_android-platform-tools_fastboot:snap_android-platform-tools_adb:snap_cups_cupsd:
E: DEVNAME=/dev/bus/usb/003/002
E: DEVNUM=002
E: CURRENT_TAGS=:snap_cups_ippeveprinter:snap_cups_cupsd:
E: DEVNAME=/dev/bus/usb/003/012
E: DEVNUM=012
E: DEVTYPE=usb_device
E: DRIVER=usb
E: ID_AUTOSUSPEND=1
E: ID_BUS=usb
E: ID_MODEL=ETU905A80-E
E: ID_MODEL_ENC=ETU905A80-E
E: ID_MODEL_ID=0582
E: ID_PATH=pci-0000:00:14.0-usb-0:5
E: ID_PATH_TAG=pci-0000_00_14_0-usb-0_5
E: ID_PATH_WITH_USB_REVISION=pci-0000:00:14.0-usbv2-0:5
E: ID_PERSIST=0
E: ID_PROCESSING=1
E: ID_REVISION=1181
E: ID_SERIAL=EGIS_ETU905A80-E_0E7828PBS393
E: ID_SERIAL_SHORT=0E7828PBS393
@@ -34,10 +30,10 @@ E: ID_VENDOR_ENC=EGIS
E: ID_VENDOR_FROM_DATABASE=LighTuning Technology Inc.
E: ID_VENDOR_ID=1c7a
E: MAJOR=189
E: MINOR=257
E: MINOR=267
E: PRODUCT=1c7a/582/1181
E: SUBSYSTEM=usb
E: TAGS=:snap_cups_ippeveprinter:snap_android-platform-tools_fastboot:snap_cups_cupsd:snap_android-platform-tools_adb:
E: TAGS=:snap_cups_ippeveprinter:snap_cups_cupsd:
E: TYPE=255/0/0
A: authorized=1\n
A: avoid_reset_quirk=0\n
@@ -54,8 +50,8 @@ A: bmAttributes=a0\n
A: busnum=3\n
A: configuration=
H: descriptors=12010002FF0000407A1C820581110102030109022700010100A0320904000003FF000000070581020002000705020200020007058303400005
A: dev=189:257\n
A: devnum=2\n
A: dev=189:267\n
A: devnum=12\n
A: devpath=5\n
L: driver=../../../../../bus/usb/drivers/usb
L: firmware_node=../../../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:51/device:52/device:57
@@ -70,20 +66,20 @@ A: physical_location/lid=no\n
A: physical_location/panel=unknown\n
A: physical_location/vertical_position=center\n
L: port=../3-0:1.0/usb3-port5
A: power/active_duration=2329204\n
A: power/active_duration=1425996\n
A: power/async=enabled\n
A: power/autosuspend=2\n
A: power/autosuspend_delay_ms=2000\n
A: power/connected_duration=96447632\n
A: power/control=auto\n
A: power/level=auto\n
A: power/persist=1\n
A: power/connected_duration=1426656\n
A: power/control=on\n
A: power/level=on\n
A: power/persist=0\n
A: power/runtime_active_kids=0\n
A: power/runtime_active_time=2345067\n
A: power/runtime_enabled=enabled\n
A: power/runtime_active_time=1426124\n
A: power/runtime_enabled=forbidden\n
A: power/runtime_status=active\n
A: power/runtime_suspended_time=94056717\n
A: power/runtime_usage=0\n
A: power/runtime_suspended_time=0\n
A: power/runtime_usage=1\n
A: power/wakeup=disabled\n
A: power/wakeup_abort_count=\n
A: power/wakeup_active=\n
@@ -100,13 +96,13 @@ A: rx_lanes=1\n
A: serial=0E7828PBS393\n
A: speed=480\n
A: tx_lanes=1\n
A: urbnum=10257\n
A: urbnum=2803\n
A: version= 2.00\n
P: /devices/pci0000:00/0000:00:14.0/usb3
N: bus/usb/003/001=12010002090001406B1D020015060302010109021900010100E0000904000001090000000705810304000C
N: bus/usb/003/001=12010002090001406B1D020002060302010109021900010100E0000904000001090000000705810304000C
E: BUSNUM=003
E: CURRENT_TAGS=:snap_cups_ippeveprinter:seat:snap_android-platform-tools_fastboot:snap_cups_cupsd:snap_android-platform-tools_adb:
E: CURRENT_TAGS=:seat:snap_cups_cupsd:snap_cups_ippeveprinter:
E: DEVNAME=/dev/bus/usb/003/001
E: DEVNUM=001
E: DEVTYPE=usb_device
@@ -120,28 +116,28 @@ E: ID_MODEL_FROM_DATABASE=2.0 root hub
E: ID_MODEL_ID=0002
E: ID_PATH=pci-0000:00:14.0
E: ID_PATH_TAG=pci-0000_00_14_0
E: ID_REVISION=0615
E: ID_SERIAL=Linux_6.15.1_xhci-hcd_xHCI_Host_Controller_0000:00:14.0
E: ID_REVISION=0602
E: ID_SERIAL=Linux_6.2.0-34-generic_xhci-hcd_xHCI_Host_Controller_0000:00:14.0
E: ID_SERIAL_SHORT=0000:00:14.0
E: ID_USB_INTERFACES=:090000:
E: ID_USB_MODEL=xHCI_Host_Controller
E: ID_USB_MODEL_ENC=xHCI\x20Host\x20Controller
E: ID_USB_MODEL_ID=0002
E: ID_USB_REVISION=0615
E: ID_USB_SERIAL=Linux_6.15.1_xhci-hcd_xHCI_Host_Controller_0000:00:14.0
E: ID_USB_REVISION=0602
E: ID_USB_SERIAL=Linux_6.2.0-34-generic_xhci-hcd_xHCI_Host_Controller_0000:00:14.0
E: ID_USB_SERIAL_SHORT=0000:00:14.0
E: ID_USB_VENDOR=Linux_6.15.1_xhci-hcd
E: ID_USB_VENDOR_ENC=Linux\x206.15.1\x20xhci-hcd
E: ID_USB_VENDOR=Linux_6.2.0-34-generic_xhci-hcd
E: ID_USB_VENDOR_ENC=Linux\x206.2.0-34-generic\x20xhci-hcd
E: ID_USB_VENDOR_ID=1d6b
E: ID_VENDOR=Linux_6.15.1_xhci-hcd
E: ID_VENDOR_ENC=Linux\x206.15.1\x20xhci-hcd
E: ID_VENDOR=Linux_6.2.0-34-generic_xhci-hcd
E: ID_VENDOR_ENC=Linux\x206.2.0-34-generic\x20xhci-hcd
E: ID_VENDOR_FROM_DATABASE=Linux Foundation
E: ID_VENDOR_ID=1d6b
E: MAJOR=189
E: MINOR=256
E: PRODUCT=1d6b/2/615
E: PRODUCT=1d6b/2/602
E: SUBSYSTEM=usb
E: TAGS=:snap_cups_ippeveprinter:seat:snap_android-platform-tools_fastboot:snap_cups_cupsd:snap_android-platform-tools_adb:
E: TAGS=:snap_cups_cupsd:seat:snap_cups_ippeveprinter:
E: TYPE=9/0/1
A: authorized=1\n
A: authorized_default=1\n
@@ -154,11 +150,11 @@ A: bMaxPacketSize0=64\n
A: bMaxPower=0mA\n
A: bNumConfigurations=1\n
A: bNumInterfaces= 1\n
A: bcdDevice=0615\n
A: bcdDevice=0602\n
A: bmAttributes=e0\n
A: busnum=3\n
A: configuration=
H: descriptors=12010002090001406B1D020015060302010109021900010100E0000904000001090000000705810304000C
H: descriptors=12010002090001406B1D020002060302010109021900010100E0000904000001090000000705810304000C
A: dev=189:256\n
A: devnum=1\n
A: devpath=0\n
@@ -168,20 +164,20 @@ A: idProduct=0002\n
A: idVendor=1d6b\n
A: interface_authorized_default=1\n
A: ltm_capable=no\n
A: manufacturer=Linux 6.15.1 xhci-hcd\n
A: manufacturer=Linux 6.2.0-34-generic xhci-hcd\n
A: maxchild=12\n
A: power/active_duration=2362684\n
A: power/active_duration=337953872\n
A: power/async=enabled\n
A: power/autosuspend=0\n
A: power/autosuspend_delay_ms=0\n
A: power/connected_duration=96447784\n
A: power/connected_duration=337978524\n
A: power/control=auto\n
A: power/level=auto\n
A: power/runtime_active_kids=1\n
A: power/runtime_active_time=2368910\n
A: power/runtime_active_time=337962424\n
A: power/runtime_enabled=enabled\n
A: power/runtime_status=active\n
A: power/runtime_suspended_time=94033284\n
A: power/runtime_suspended_time=616\n
A: power/runtime_usage=0\n
A: power/wakeup=disabled\n
A: power/wakeup_abort_count=\n
@@ -199,15 +195,12 @@ A: rx_lanes=1\n
A: serial=0000:00:14.0\n
A: speed=480\n
A: tx_lanes=1\n
A: urbnum=7078\n
A: urbnum=4969\n
A: version= 2.00\n
P: /devices/pci0000:00/0000:00:14.0
E: DRIVER=xhci_hcd
E: ID_AUTOSUSPEND=1
E: ID_MODEL_FROM_DATABASE=Alder Lake PCH USB 3.2 xHCI Host Controller
E: ID_PATH=pci-0000:00:14.0
E: ID_PATH_TAG=pci-0000_00_14_0
E: ID_PCI_CLASS_FROM_DATABASE=Serial bus controller
E: ID_PCI_INTERFACE_FROM_DATABASE=XHCI
E: ID_PCI_SUBCLASS_FROM_DATABASE=USB controller
@@ -221,15 +214,10 @@ E: SUBSYSTEM=pci
A: ari_enabled=0\n
A: broken_parity_status=0\n
A: class=0x0c0330\n
H: config=8680ED51060490020130030C000080000400161D6000000000000000000000000000000000000000000000004D1470C8000000007000000000000000FF010000FD0134A089C27F8000000000000000003F6DD80F000000000000000000000000316000000000000000000000000000000180C2C10800000000000000000000000590B7001804E0FE000000000000000009B014F01000400100000000C10A080000080E00001800008F50020000010000090000018680C00009001014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B50F010112000000
H: config=8680ED51060490020130030C000080000400161D6000000000000000000000000000000000000000000000004D1470C8000000007000000000000000FF010000FD0134A089C27F8000000000000000003F6DD80F000000000000000000000000316000000000000000000000000000000180C2C1080000000000000000000000059087007805E0FE000000000000000009B014F01000400100000000C10A080000080E00001800008F50020000010000090000018680C00009001014000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000B50F010112000000
A: consistent_dma_mask_bits=64\n
A: d3cold_allowed=1\n
A: dbc=disabled\n
A: dbc_bInterfaceProtocol=01\n
A: dbc_bcdDevice=0010\n
A: dbc_idProduct=0010\n
A: dbc_idVendor=1d6b\n
A: dbc_poll_interval_ms=64\n
A: device=0x51ed\n
A: dma_mask_bits=64\n
L: driver=../../../bus/pci/drivers/xhci_hcd
@@ -239,39 +227,32 @@ L: firmware_node=../../LNXSYSTM:00/LNXSYBUS:00/PNP0A08:00/device:51
A: index=7\n
L: iommu=../../virtual/iommu/dmar1
L: iommu_group=../../../kernel/iommu_groups/8
A: irq=133\n
A: irq=145\n
A: label=Onboard - Other\n
A: local_cpulist=0-15\n
A: local_cpus=ffff\n
A: modalias=pci:v00008086d000051EDsv0000144Dsd0000C870bc0Csc03i30\n
A: msi_bus=1\n
A: msi_irqs/133=msi\n
A: msi_irqs/134=msi\n
A: msi_irqs/135=msi\n
A: msi_irqs/136=msi\n
A: msi_irqs/137=msi\n
A: msi_irqs/138=msi\n
A: msi_irqs/139=msi\n
A: msi_irqs/140=msi\n
A: msi_irqs/145=msi\n
A: numa_node=-1\n
A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 6 7 2112 7\nxHCI ring segments 27 27 4096 27\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\n
A: pools=poolinfo - 0.1\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 0 128 0\nbuffer-32 0 0 32 0\nxHCI 1KB stream ctx arrays 0 0 1024 0\nxHCI 256 byte stream ctx arrays 0 0 256 0\nxHCI input/output contexts 6 9 2112 9\nxHCI ring segments 26 34 4096 34\nbuffer-2048 0 0 2048 0\nbuffer-512 0 0 512 0\nbuffer-128 0 32 128 1\nbuffer-32 0 0 32 0\n
A: power/async=enabled\n
A: power/control=auto\n
A: power/runtime_active_kids=1\n
A: power/runtime_active_time=2376480\n
A: power/runtime_active_time=337964621\n
A: power/runtime_enabled=enabled\n
A: power/runtime_status=active\n
A: power/runtime_suspended_time=94028360\n
A: power/runtime_suspended_time=438\n
A: power/runtime_usage=0\n
A: power/wakeup=enabled\n
A: power/wakeup_abort_count=0\n
A: power/wakeup_active=0\n
A: power/wakeup_active_count=1\n
A: power/wakeup_active_count=7\n
A: power/wakeup_count=0\n
A: power/wakeup_expire_count=1\n
A: power/wakeup_last_time_ms=41666464\n
A: power/wakeup_max_time_ms=101\n
A: power/wakeup_total_time_ms=101\n
A: power/wakeup_expire_count=7\n
A: power/wakeup_last_time_ms=336554844\n
A: power/wakeup_max_time_ms=105\n
A: power/wakeup_total_time_ms=721\n
A: power_state=D0\n
A: resource=0x000000601d160000 0x000000601d16ffff 0x0000000000140204\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n0x0000000000000000 0x0000000000000000 0x0000000000000000\n
A: revision=0x01\n

View File

@@ -19,7 +19,6 @@ envs.set('FP_DRIVERS_ALLOWLIST', ':'.join([
'virtual_image',
'virtual_device',
'virtual_device_storage',
'virtual_sdcp',
]))
envs.set('FP_PRINTS_PATH', meson.project_source_root() / 'examples' / 'prints')
@@ -53,9 +52,9 @@ drivers_tests = [
'nb1010',
'egis0570',
'egismoc',
# 'egismoc-05a1', # commented out until new capture with SDCP support can be provided
# 'egismoc-0586', # commented out until new capture with SDCP support can be provided
# 'egismoc-0587', # commented out until new capture with SDCP support can be provided
'egismoc-05a1',
'egismoc-0586',
'egismoc-0587',
'fpcmoc',
'realtek',
'realtek-5816',
@@ -92,7 +91,6 @@ if get_option('introspection')
virtual_devices_tests = [
'virtual-image',
'virtual-device',
'virtual-sdcp',
]
unittest_inspector = find_program('unittest_inspector.py')
@@ -133,7 +131,6 @@ if get_option('introspection')
suite: ut_suite,
depends: libfprint_typelib,
env: envs,
workdir: meson.current_source_dir(),
)
endforeach
@@ -184,7 +181,6 @@ if get_option('introspection')
meson.current_source_dir() / driver_test,
],
env: driver_envs,
workdir: meson.current_source_dir(),
suite: ['drivers'],
timeout: 15,
depends: libfprint_typelib,
@@ -248,10 +244,13 @@ else
endforeach
endif
test_util_sources = [
'test-utils.c',
'test-device-fake.c',
]
test_utils = static_library('fprint-test-utils',
sources: [
'test-utils.c',
'test-device-fake.c',
],
dependencies: libfprint_private_dep,
install: false)
unit_tests = [
'fpi-device',
@@ -266,17 +265,6 @@ if 'virtual_image' in drivers
]
endif
if 'sdcp' in driver_helpers
unit_tests += [
'fpi-sdcp',
]
endif
test_utils = static_library('fprint-test-utils',
sources: test_util_sources,
dependencies: libfprint_private_dep,
install: false)
unit_tests_deps = { 'fpi-assembling' : [cairo_dep] }
foreach test_name: unit_tests

View File

@@ -1,210 +0,0 @@
/*
* Secure Device Connection Protocol (SDCP) support unit tests
* Copyright (C) 2025 Joshua Grisham <josh@joshuagrisham.com>
*
* 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.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#define FP_COMPONENT "test_fpi_sdcp"
#include "fpi-log.h"
#include "fpi-sdcp.h"
#include "fpi-sdcp-device.h"
/* We can re-use the test payloads from virtual-sdcp */
#include "drivers/virtual-sdcp.h"
/******************************************************************************/
static const guint8 from_hex_map[] = {
0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, // 01234567
0x08, 0x09, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 89:;<=>?
0x00, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, // @abcdef
};
static GBytes *
g_bytes_from_hex (const gchar *hex)
{
g_autoptr(GBytes) res = NULL;
guint8 b0, b1;
gsize bytes_len = strlen (hex) / 2;
guint8 *bytes = g_malloc0 (bytes_len);
for (int i = 0; i < strlen (hex) - 1; i += 2)
{
b0 = ((guint8) hex[i + 0] & 0x1F) ^ 0x10;
b1 = ((guint8) hex[i + 1] & 0x1F) ^ 0x10;
bytes[i / 2] = (guint8) (from_hex_map[b0] << 4) | from_hex_map[b1];
}
res = g_bytes_new_take (bytes, bytes_len);
return g_steal_pointer (&res);
}
static FpiSdcpClaim *
get_fake_sdcp_claim (void)
{
FpiSdcpClaim *claim = g_new0 (FpiSdcpClaim, 1);
claim->model_certificate = g_bytes_from_hex (model_certificate_hex);
claim->device_public_key = g_bytes_from_hex (device_public_key_hex);
claim->firmware_public_key = g_bytes_from_hex (firmware_public_key_hex);
claim->firmware_hash = g_bytes_from_hex (firmware_hash_hex);
claim->model_signature = g_bytes_from_hex (model_signature_hex);
claim->device_signature = g_bytes_from_hex (device_signature_hex);
return g_steal_pointer (&claim);
}
/******************************************************************************/
static void
test_generate_enrollment_id (void)
{
g_autoptr(GBytes) id = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) application_secret = g_bytes_from_hex (application_secret_hex);
g_autoptr(GBytes) nonce = g_bytes_from_hex (enrollment_nonce_hex);
g_autoptr(GBytes) expected_id = g_bytes_from_hex (enrollment_id_hex);
id = fpi_sdcp_generate_enrollment_id (application_secret, nonce, &error);
fp_dbg ("id:");
fp_dbg_hex_dump_gbytes (id);
fp_dbg ("expected:");
fp_dbg_hex_dump_gbytes (expected_id);
g_assert (g_bytes_equal (expected_id, id));
g_assert_null (error);
}
static void
test_verify_identify (void)
{
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) application_secret = g_bytes_from_hex (application_secret_hex);
g_autoptr(GBytes) nonce = g_bytes_from_hex (identify_nonce_hex);
g_autoptr(GBytes) id = g_bytes_from_hex (enrollment_id_hex);
g_autoptr(GBytes) mac = g_bytes_from_hex (identify_mac_hex);
g_assert_true (fpi_sdcp_verify_identify (application_secret, nonce, id, mac, &error));
g_assert_null (error);
}
static void
test_verify_reconnect (void)
{
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) application_secret = g_bytes_from_hex (application_secret_hex);
g_autoptr(GBytes) random = g_bytes_from_hex (reconnect_random_hex);
g_autoptr(GBytes) mac = g_bytes_from_hex (reconnect_mac_hex);
g_assert_true (fpi_sdcp_verify_reconnect (application_secret, random, mac, &error));
g_assert_null (error);
}
static void
test_verify_connect (void)
{
g_autoptr(GBytes) application_secret = NULL;
g_autoptr(GError) error = NULL;
g_autoptr(GBytes) host_private_key = g_bytes_from_hex (host_private_key_hex);
g_autoptr(GBytes) host_random = g_bytes_from_hex (host_random_hex);
g_autoptr(GBytes) device_random = g_bytes_from_hex (device_random_hex);
g_autoptr(GBytes) connect_mac = g_bytes_from_hex (connect_mac_hex);
FpiSdcpClaim *claim = get_fake_sdcp_claim ();
g_autoptr(GBytes) expected_application_secret = g_bytes_from_hex (application_secret_hex);
g_assert_true (fpi_sdcp_verify_connect (host_private_key,
host_random,
device_random,
claim,
connect_mac,
TRUE,
TRUE,
&application_secret,
&error));
g_assert_null (error);
g_assert (g_bytes_get_size (application_secret) == SDCP_APPLICATION_SECRET_SIZE);
fp_dbg ("application_secret:");
fp_dbg_hex_dump_gbytes (application_secret);
fp_dbg ("expected:");
fp_dbg_hex_dump_gbytes (expected_application_secret);
g_assert_true (g_bytes_equal (expected_application_secret, application_secret));
fpi_sdcp_claim_free (claim);
}
static void
test_generate_random (void)
{
g_autoptr(GBytes) random = NULL;
g_autoptr(GError) error = NULL;
random = fpi_sdcp_generate_random (&error);
g_assert_null (error);
g_assert (g_bytes_get_size (random) == SDCP_RANDOM_SIZE);
fp_dbg ("random:");
fp_dbg_hex_dump_gbytes (random);
}
static void
test_generate_host_key (void)
{
g_autoptr(GBytes) private_key = NULL;
g_autoptr(GBytes) public_key = NULL;
g_autoptr(GError) error = NULL;
gsize len = 0;
fpi_sdcp_generate_host_key (&private_key, &public_key, &error);
g_assert_null (error);
g_bytes_get_data (private_key, &len);
g_assert (len == 32);
fp_dbg ("private_key:");
fp_dbg_hex_dump_gbytes (private_key);
g_bytes_get_data (public_key, &len);
g_assert (len == SDCP_PUBLIC_KEY_SIZE);
fp_dbg ("public_key:");
fp_dbg_hex_dump_gbytes (public_key);
}
int
main (int argc, char *argv[])
{
g_test_init (&argc, &argv, NULL);
g_test_add_func ("/sdcp/generate_host_key", test_generate_host_key);
g_test_add_func ("/sdcp/generate_random", test_generate_random);
g_test_add_func ("/sdcp/verify_connect", test_verify_connect);
g_test_add_func ("/sdcp/verify_reconnect", test_verify_reconnect);
g_test_add_func ("/sdcp/verify_identify", test_verify_identify);
g_test_add_func ("/sdcp/generate_enrollment_id", test_generate_enrollment_id);
return g_test_run ();
}

View File

@@ -1,169 +0,0 @@
#!/usr/bin/env python3
import sys
try:
import gi
import os
from gi.repository import GLib
import unittest
except Exception as e:
print("Missing dependencies: %s" % str(e))
sys.exit(77)
FPrint = None
# Only permit loading virtual_sdcp driver for tests in this file
os.environ['FP_DRIVERS_WHITELIST'] = 'virtual_sdcp'
if hasattr(os.environ, 'MESON_SOURCE_ROOT'):
root = os.environ['MESON_SOURCE_ROOT']
else:
root = os.path.join(os.path.dirname(__file__), '..')
ctx = GLib.main_context_default()
class VirtualSDCPBase(unittest.TestCase):
@classmethod
def setUpClass(cls):
os.environ['FP_VIRTUAL_SDCP'] = '1'
cls.ctx = FPrint.Context()
cls.dev = None
for dev in cls.ctx.get_devices():
cls.dev = dev
break
assert cls.dev is not None, "You need to compile with virtual_sdcp for testing"
@classmethod
def tearDownClass(cls):
del cls.dev
del cls.ctx
def setUp(self):
self.ctx = FPrint.Context()
self.assertIsNotNone(self.dev)
self.assertFalse(self.dev.is_open())
self.dev.open_sync()
self.assertTrue(self.dev.is_open())
def tearDown(self):
self.dev.close_sync()
self.assertFalse(self.dev.is_open())
del self.ctx
class VirtualSDCP(VirtualSDCPBase):
def test_connect(self):
# Nothing to do here since setUp and tearDown will open and close the device
pass
def test_reconnect(self):
# Ensure device was opened once before, this may be a reconnect if
# it is the same process as another test.
self.dev.close_sync()
# Check that a reconnect happens on next open. To know about this, we
# need to parse check log messages for that.
success = [False]
def log_func(domain, level, msg):
print("log: '%s', '%s', '%s'" % (str(domain), str(level), msg))
if msg == 'SDCP Reconnect succeeded':
success[0] = True
# Call default handler
GLib.log_default_handler(domain, level, msg)
handler_id = GLib.log_set_handler('libfprint-sdcp_device', GLib.LogLevelFlags.LEVEL_DEBUG, log_func)
self.dev.open_sync()
GLib.log_remove_handler('libfprint-sdcp_device', handler_id)
assert success[0]
def test_list(self):
prints = self.dev.list_prints_sync()
assert len(prints) == 0
def test_enroll_list_verify(self):
# Set up a new print
template = FPrint.Print.new(self.dev)
template.set_finger(FPrint.Finger.LEFT_THUMB)
# Enroll the new print
new_print = self.dev.enroll_sync(template, None, None, None)
self.assertIsInstance(new_print, FPrint.Print)
# Get the print list again and ensure there is exactly 1 print
prints = self.dev.list_prints_sync()
self.assertTrue(len(prints) == 1)
# Ensure the one print from list is the same as new_print
self.assertTrue(prints[0].equal(new_print))
# Verify new_print
verify_res, verify_print = self.dev.verify_sync(prints[0])
self.assertTrue(verify_res)
self.assertTrue(verify_print.equal(prints[0]))
# Set up a second new print
template = FPrint.Print.new(self.dev)
template.set_finger(FPrint.Finger.LEFT_INDEX)
# Enroll the second print
new_print2 = self.dev.enroll_sync(template, None, None, None)
self.assertIsInstance(new_print2, FPrint.Print)
# Get the print list again and ensure there is exactly 2 prints
prints = self.dev.list_prints_sync()
self.assertTrue(len(prints) == 2)
# Ensure the second print from list is the same as new_print2
self.assertTrue(prints[1].equal(new_print2))
class VirtualSDCPNoReconnect(VirtualSDCPBase):
@classmethod
def setUpClass(cls):
os.environ['FP_VIRTUAL_SDCP_NO_RECONNECT'] = '1'
super().setUpClass()
def test_connect(self):
# Nothing to do here since setUp and tearDown will open and close the device
pass
def test_reconnect(self):
# Ensure device was opened once before, this may be a reconnect if
# it is the same process as another test.
self.dev.close_sync()
# Check that a reconnect happens on next open. To know about this, we
# need to parse check log messages for that.
success = [False]
def log_func(domain, level, msg):
print("log: '%s', '%s', '%s'" % (str(domain), str(level), msg))
if msg == 'SDCP Reconnect succeeded':
success[0] = True
# Call default handler
GLib.log_default_handler(domain, level, msg)
handler_id = GLib.log_set_handler('libfprint-sdcp_device', GLib.LogLevelFlags.LEVEL_DEBUG, log_func)
self.dev.open_sync()
GLib.log_remove_handler('libfprint-sdcp_device', handler_id)
# Ensure that we did NOT see "SDCP Reconnect succeeded" in the log
assert success[0] == False
if __name__ == '__main__':
try:
gi.require_version('FPrint', '2.0')
from gi.repository import FPrint
except Exception as e:
print("Missing dependencies: %s" % str(e))
sys.exit(77)
unittest.main(testRunner=unittest.TextTestRunner(stream=sys.stdout, verbosity=2))