GCC Code Coverage Report


Directory: ./
File: libfprint/fpi-print.c
Date: 2024-09-16 14:36:32
Exec Total Coverage
Lines: 107 119 89.9%
Functions: 8 8 100.0%
Branches: 48 78 61.5%

Line Branch Exec Source
1 /*
2 * FPrint Print handling - Private APIs
3 * Copyright (C) 2007 Daniel Drake <dsd@gentoo.org>
4 * Copyright (C) 2019 Benjamin Berg <bberg@redhat.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #define FP_COMPONENT "print"
22 #include "fpi-log.h"
23
24 #include "fp-print-private.h"
25 #include "fpi-device.h"
26 #include "fpi-compat.h"
27
28 /**
29 * SECTION: fpi-print
30 * @title: Internal FpPrint
31 * @short_description: Internal fingerprint handling routines
32 *
33 * Interaction with prints and their storage. See also the public
34 * #FpPrint routines.
35 */
36
37 /**
38 * fpi_print_add_print:
39 * @print: A #FpPrint
40 * @add: Print to append to @print
41 *
42 * Appends the single #FPI_PRINT_NBIS print from @add to the collection of
43 * prints in @print. Both print objects need to be of type #FPI_PRINT_NBIS
44 * for this to work.
45 */
46 void
47 25 fpi_print_add_print (FpPrint *print, FpPrint *add)
48 {
49
1/2
✓ Branch 0 taken 25 times.
✗ Branch 1 not taken.
25 g_return_if_fail (print->type == FPI_PRINT_NBIS);
50
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 g_return_if_fail (add->type == FPI_PRINT_NBIS);
51
52
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 25 times.
25 g_assert (add->prints->len == 1);
53 25 g_ptr_array_add (print->prints, g_memdup2 (add->prints->pdata[0], sizeof (struct xyt_struct)));
54 }
55
56 /**
57 * fpi_print_set_type:
58 * @print: A #FpPrint
59 * @type: The newly type of the print data
60 *
61 * This function can only be called exactly once. Drivers should
62 * call it after creating a new print, or to initialize the template
63 * print passed during enrollment.
64 */
65 void
66 5719 fpi_print_set_type (FpPrint *print,
67 FpiPrintType type)
68 {
69
1/2
✓ Branch 1 taken 5719 times.
✗ Branch 2 not taken.
5719 g_return_if_fail (FP_IS_PRINT (print));
70 /* We only allow setting this once! */
71
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 5719 times.
5719 g_return_if_fail (print->type == FPI_PRINT_UNDEFINED);
72
73 5719 print->type = type;
74
2/2
✓ Branch 0 taken 43 times.
✓ Branch 1 taken 5676 times.
5719 if (print->type == FPI_PRINT_NBIS)
75 {
76
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 43 times.
43 g_assert_null (print->prints);
77 43 print->prints = g_ptr_array_new_with_free_func (g_free);
78 }
79 5719 g_object_notify (G_OBJECT (print), "fpi-type");
80 }
81
82 /**
83 * fpi_print_set_device_stored:
84 * @print: A #FpPrint
85 * @device_stored: Whether the print is stored on the device or not
86 *
87 * Drivers must set this to %TRUE for any print that is really a handle
88 * for data that is stored on the device itself.
89 */
90 void
91 105 fpi_print_set_device_stored (FpPrint *print,
92 gboolean device_stored)
93 {
94
1/2
✓ Branch 1 taken 105 times.
✗ Branch 2 not taken.
105 g_return_if_fail (FP_IS_PRINT (print));
95
96 105 print->device_stored = device_stored;
97 105 g_object_notify (G_OBJECT (print), "device-stored");
98 }
99
100 /* XXX: This is the old version, but wouldn't it be smarter to instead
101 * use the highest quality mintutiae? Possibly just using bz_prune from
102 * upstream? */
103 static void
104 33 minutiae_to_xyt (struct fp_minutiae *minutiae,
105 int bwidth,
106 int bheight,
107 struct xyt_struct *xyt)
108 {
109 33 int i;
110 33 struct fp_minutia *minutia;
111 33 struct minutiae_struct c[MAX_FILE_MINUTIAE];
112
113 /* struct xyt_struct uses arrays of MAX_BOZORTH_MINUTIAE (200) */
114 33 int nmin = min (minutiae->num, MAX_BOZORTH_MINUTIAE);
115
116
2/2
✓ Branch 0 taken 2923 times.
✓ Branch 1 taken 33 times.
2956 for (i = 0; i < nmin; i++)
117 {
118 2923 minutia = minutiae->list[i];
119
120 2923 lfs2nist_minutia_XYT (&c[i].col[0], &c[i].col[1], &c[i].col[2],
121 minutia, bwidth, bheight);
122
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2923 times.
2923 c[i].col[3] = sround (minutia->reliability * 100.0);
123
124
2/2
✓ Branch 0 taken 1592 times.
✓ Branch 1 taken 1331 times.
2923 if (c[i].col[2] > 180)
125 1592 c[i].col[2] -= 360;
126 }
127
128 33 qsort ((void *) &c, (size_t) nmin, sizeof (struct minutiae_struct),
129 sort_x_y);
130
131
2/2
✓ Branch 1 taken 2923 times.
✓ Branch 2 taken 33 times.
2989 for (i = 0; i < nmin; i++)
132 {
133 2923 xyt->xcol[i] = c[i].col[0];
134 2923 xyt->ycol[i] = c[i].col[1];
135 2923 xyt->thetacol[i] = c[i].col[2];
136 }
137 33 xyt->nrows = nmin;
138 33 }
139
140 /**
141 * fpi_print_add_from_image:
142 * @print: A #FpPrint
143 * @image: A #FpImage
144 * @error: Return location for error
145 *
146 * Extracts the minutiae from the given image and adds it to @print of
147 * type #FPI_PRINT_NBIS.
148 *
149 * The @image will be kept so that API users can get retrieve it e.g.
150 * for debugging purposes.
151 *
152 * Returns: %TRUE on success
153 */
154 gboolean
155 33 fpi_print_add_from_image (FpPrint *print,
156 FpImage *image,
157 GError **error)
158 {
159 33 GPtrArray *minutiae;
160 33 struct fp_minutiae _minutiae;
161 33 struct xyt_struct *xyt;
162
163
2/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
33 if (print->type != FPI_PRINT_NBIS || !image)
164 {
165 g_set_error (error,
166 G_IO_ERROR,
167 G_IO_ERROR_INVALID_DATA,
168 "Cannot add print data from image!");
169 return FALSE;
170 }
171
172 33 minutiae = fp_image_get_minutiae (image);
173
2/4
✓ Branch 0 taken 33 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 33 times.
33 if (!minutiae || minutiae->len == 0)
174 {
175 g_set_error (error,
176 G_IO_ERROR,
177 G_IO_ERROR_INVALID_DATA,
178 "No minutiae found in image or not yet detected!");
179 return FALSE;
180 }
181
182 33 _minutiae.num = minutiae->len;
183 33 _minutiae.list = (struct fp_minutia **) minutiae->pdata;
184 33 _minutiae.alloc = minutiae->len;
185
186 33 xyt = g_new0 (struct xyt_struct, 1);
187 33 minutiae_to_xyt (&_minutiae, image->width, image->height, xyt);
188 33 g_ptr_array_add (print->prints, xyt);
189
190
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 33 times.
33 g_clear_object (&print->image);
191 33 print->image = g_object_ref (image);
192 33 g_object_notify (G_OBJECT (print), "image");
193
194 33 return TRUE;
195 }
196
197 /**
198 * fpi_print_bz3_match:
199 * @template: A #FpPrint containing one or more prints
200 * @print: A newly scanned #FpPrint to test
201 * @bz3_threshold: The BZ3 match threshold
202 * @error: Return location for error
203 *
204 * Match the newly scanned @print (containing exactly one print) against the
205 * prints contained in @template which will have been stored during enrollment.
206 *
207 * Both @template and @print need to be of type #FPI_PRINT_NBIS for this to
208 * work.
209 *
210 * Returns: Whether the prints match, @error will be set if #FPI_MATCH_ERROR is returned
211 */
212 FpiMatchResult
213 9 fpi_print_bz3_match (FpPrint *template, FpPrint *print, gint bz3_threshold, GError **error)
214 {
215 9 struct xyt_struct *pstruct;
216 9 gint probe_len;
217 9 gint i;
218
219 /* XXX: Use a different error type? */
220
2/4
✓ Branch 0 taken 9 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 9 times.
9 if (template->type != FPI_PRINT_NBIS || print->type != FPI_PRINT_NBIS)
221 {
222 *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
223 "It is only possible to match NBIS type print data");
224 return FPI_MATCH_ERROR;
225 }
226
227
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 9 times.
9 if (print->prints->len != 1)
228 {
229 *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
230 "New print contains more than one print!");
231 return FPI_MATCH_ERROR;
232 }
233
234 9 pstruct = g_ptr_array_index (print->prints, 0);
235 9 probe_len = bozorth_probe_init (pstruct);
236
237
2/2
✓ Branch 1 taken 26 times.
✓ Branch 2 taken 3 times.
38 for (i = 0; i < template->prints->len; i++)
238 {
239 26 struct xyt_struct *gstruct;
240 26 gint score;
241 26 gstruct = g_ptr_array_index (template->prints, i);
242 26 score = bozorth_to_gallery (probe_len, pstruct, gstruct);
243 26 fp_dbg ("score %d/%d", score, bz3_threshold);
244
245
2/2
✓ Branch 0 taken 20 times.
✓ Branch 1 taken 6 times.
26 if (score >= bz3_threshold)
246 return FPI_MATCH_SUCCESS;
247 }
248
249 return FPI_MATCH_FAIL;
250 }
251
252 /**
253 * fpi_print_generate_user_id:
254 * @print: #FpPrint to generate the ID for
255 *
256 * Generates a string identifier for the represented print. This identifier
257 * encodes some metadata about the print. It also includes a random string
258 * and may be assumed to be unique.
259 *
260 * This is useful if devices are able to store a string identifier, but more
261 * storing more metadata may be desirable. In effect, this means the driver
262 * can provide somewhat more meaningful data to fp_device_list_prints().
263 *
264 * The generated ID may be truncated after 23 characters. However, more space
265 * is required to include the username, and it is recommended to store at
266 * at least 31 bytes.
267 *
268 * The generated format may change in the future. It is versioned though and
269 * decoding should remain functional.
270 *
271 * Returns: A unique string of 23 + strlen(username) characters
272 */
273 gchar *
274 15 fpi_print_generate_user_id (FpPrint *print)
275 {
276 15 const gchar *username = NULL;
277 15 gchar *user_id = NULL;
278 15 const GDate *date;
279 15 gint y = 0, m = 0, d = 0;
280 15 gint32 rand_id = 0;
281
282
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 g_assert (print);
283 15 date = fp_print_get_enroll_date (print);
284
1/4
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
15 if (date && g_date_valid (date))
285 {
286 y = g_date_get_year (date);
287 m = g_date_get_month (date);
288 d = g_date_get_day (date);
289 }
290
291 15 username = fp_print_get_username (print);
292
1/2
✓ Branch 0 taken 15 times.
✗ Branch 1 not taken.
15 if (!username)
293 15 username = "nobody";
294
295
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 15 times.
15 if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
296 rand_id = 0;
297 else
298 rand_id = g_random_int ();
299
300 15 user_id = g_strdup_printf ("FP1-%04d%02d%02d-%X-%08X-%s",
301 y, m, d,
302 15 fp_print_get_finger (print),
303 rand_id,
304 username);
305
306 15 return user_id;
307
308 }
309
310 /**
311 * fpi_print_fill_from_user_id:
312 * @print: #FpPrint to fill metadata into
313 * @user_id: An ID that was likely encoded using fpi_print_generate_user_id()
314 *
315 * This is the reverse operation of fpi_print_generate_user_id(), allowing
316 * the driver to encode some print metadata in a string.
317 *
318 * Returns: Whether a valid ID was found
319 */
320 gboolean
321 82 fpi_print_fill_from_user_id (FpPrint *print, const char *user_id)
322 {
323
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 82 times.
82 g_return_val_if_fail (user_id, FALSE);
324
325 /* The format has 24 bytes at the start and some dashes in the right places */
326
5/6
✓ Branch 0 taken 4 times.
✓ Branch 1 taken 78 times.
✓ Branch 2 taken 15 times.
✓ Branch 3 taken 63 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 63 times.
82 if (g_str_has_prefix (user_id, "FP1-") && strlen (user_id) >= 24 &&
327
3/6
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 63 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 63 times.
63 user_id[12] == '-' && user_id[14] == '-' && user_id[23] == '-')
328 {
329
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
126 g_autofree gchar *copy = g_strdup (user_id);
330 63 g_autoptr(GDate) date = NULL;
331 63 gint32 date_ymd;
332 63 gint32 finger;
333 63 gchar *username;
334 /* Try to parse information from the string. */
335
336 63 copy[12] = '\0';
337 63 date_ymd = g_ascii_strtod (copy + 4, NULL);
338
2/2
✓ Branch 0 taken 9 times.
✓ Branch 1 taken 54 times.
63 if (date_ymd > 0)
339 9 date = g_date_new_dmy (date_ymd % 100,
340 9 (date_ymd / 100) % 100,
341 9 date_ymd / 10000);
342 else
343 54 date = g_date_new ();
344
345 63 fp_print_set_enroll_date (print, date);
346
347 63 copy[14] = '\0';
348 63 finger = g_ascii_strtoll (copy + 13, NULL, 16);
349 63 fp_print_set_finger (print, finger);
350
351 /* We ignore the next chunk, it is just random data.
352 * Then comes the username; nobody is the default if the metadata
353 * is unknown */
354 63 username = copy + 24;
355
3/4
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 15 times.
✓ Branch 4 taken 48 times.
63 if (strlen (username) > 0 && g_strcmp0 (username, "nobody") != 0)
356 15 fp_print_set_username (print, username);
357
358
1/2
✓ Branch 0 taken 63 times.
✗ Branch 1 not taken.
63 return TRUE;
359 }
360
361 return FALSE;
362 }
363