GCC Code Coverage Report


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 75.0% high: ≥ 90.0%
Coverage Exec / Excl / Total
Lines: 89.9% 107 / 0 / 119
Functions: 100.0% 8 / 0 / 8
Branches: 61.5% 48 / 0 / 78

libfprint/fpi-print.c
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 2 → 3 not taken.
✓ Branch 2 → 4 taken 25 times.
25 g_return_if_fail (print->type == FPI_PRINT_NBIS);
50
1/2
✓ Branch 4 → 5 taken 25 times.
✗ Branch 4 → 6 not taken.
25 g_return_if_fail (add->type == FPI_PRINT_NBIS);
51
52
1/2
✓ Branch 5 → 7 taken 25 times.
✗ Branch 5 → 9 not taken.
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 5724 fpi_print_set_type (FpPrint *print,
67 FpiPrintType type)
68 {
69
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 5724 times.
5724 g_return_if_fail (FP_IS_PRINT (print));
70 /* We only allow setting this once! */
71
1/2
✓ Branch 5 → 6 taken 5724 times.
✗ Branch 5 → 7 not taken.
5724 g_return_if_fail (print->type == FPI_PRINT_UNDEFINED);
72
73 5724 print->type = type;
74
2/2
✓ Branch 6 → 8 taken 43 times.
✓ Branch 6 → 12 taken 5681 times.
5724 if (print->type == FPI_PRINT_NBIS)
75 {
76
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 43 times.
43 g_assert_null (print->prints);
77 43 print->prints = g_ptr_array_new_with_free_func (g_free);
78 }
79 5724 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 109 fpi_print_set_device_stored (FpPrint *print,
92 gboolean device_stored)
93 {
94
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 109 times.
109 g_return_if_fail (FP_IS_PRINT (print));
95
96 109 print->device_stored = device_stored;
97 109 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 10 → 3 taken 2923 times.
✓ Branch 10 → 11 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 4 → 5 not taken.
✓ Branch 4 → 6 taken 2923 times.
2923 c[i].col[3] = sround (minutia->reliability * 100.0);
123
124
2/2
✓ Branch 7 → 8 taken 1592 times.
✓ Branch 7 → 9 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 14 → 13 taken 2923 times.
✓ Branch 14 → 15 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 2 → 3 taken 33 times.
✗ Branch 2 → 4 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 7 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 8 → 9 taken 33 times.
✗ Branch 8 → 10 not taken.
✗ Branch 9 → 10 not taken.
✓ Branch 9 → 13 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 16 → 17 not taken.
✓ Branch 16 → 18 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 * @print_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 *print_template,
214 FpPrint *print,
215 gint bz3_threshold,
216 GError **error)
217 {
218 9 struct xyt_struct *pstruct;
219 9 gint probe_len;
220 9 gint i;
221
222 /* XXX: Use a different error type? */
223
2/4
✓ Branch 2 → 3 taken 9 times.
✗ Branch 2 → 4 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 9 times.
9 if (print_template->type != FPI_PRINT_NBIS || print->type != FPI_PRINT_NBIS)
224 {
225 *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_NOT_SUPPORTED,
226 "It is only possible to match NBIS type print data");
227 return FPI_MATCH_ERROR;
228 }
229
230
1/2
✗ Branch 6 → 7 not taken.
✓ Branch 6 → 9 taken 9 times.
9 if (print->prints->len != 1)
231 {
232 *error = fpi_device_error_new_msg (FP_DEVICE_ERROR_GENERAL,
233 "New print contains more than one print!");
234 return FPI_MATCH_ERROR;
235 }
236
237 9 pstruct = g_ptr_array_index (print->prints, 0);
238 9 probe_len = bozorth_probe_init (pstruct);
239
240
2/2
✓ Branch 15 → 11 taken 26 times.
✓ Branch 15 → 16 taken 3 times.
38 for (i = 0; i < print_template->prints->len; i++)
241 {
242 26 struct xyt_struct *gstruct;
243 26 gint score;
244 26 gstruct = g_ptr_array_index (print_template->prints, i);
245 26 score = bozorth_to_gallery (probe_len, pstruct, gstruct);
246 26 fp_dbg ("score %d/%d", score, bz3_threshold);
247
248
2/2
✓ Branch 13 → 14 taken 20 times.
✓ Branch 13 → 16 taken 6 times.
26 if (score >= bz3_threshold)
249 return FPI_MATCH_SUCCESS;
250 }
251
252 return FPI_MATCH_FAIL;
253 }
254
255 /**
256 * fpi_print_generate_user_id:
257 * @print: #FpPrint to generate the ID for
258 *
259 * Generates a string identifier for the represented print. This identifier
260 * encodes some metadata about the print. It also includes a random string
261 * and may be assumed to be unique.
262 *
263 * This is useful if devices are able to store a string identifier, but more
264 * storing more metadata may be desirable. In effect, this means the driver
265 * can provide somewhat more meaningful data to fp_device_list_prints().
266 *
267 * The generated ID may be truncated after 23 characters. However, more space
268 * is required to include the username, and it is recommended to store at
269 * at least 31 bytes.
270 *
271 * The generated format may change in the future. It is versioned though and
272 * decoding should remain functional.
273 *
274 * Returns: A unique string of 23 + strlen(username) characters
275 */
276 gchar *
277 16 fpi_print_generate_user_id (FpPrint *print)
278 {
279 16 const gchar *username = NULL;
280 16 gchar *user_id = NULL;
281 16 const GDate *date;
282 16 gint y = 0, m = 0, d = 0;
283 16 gint32 rand_id = 0;
284
285
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 16 times.
16 g_assert (print);
286 16 date = fp_print_get_enroll_date (print);
287
1/4
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 12 taken 16 times.
✗ Branch 7 → 8 not taken.
✗ Branch 7 → 12 not taken.
16 if (date && g_date_valid (date))
288 {
289 y = g_date_get_year (date);
290 m = g_date_get_month (date);
291 d = g_date_get_day (date);
292 }
293
294 16 username = fp_print_get_username (print);
295
1/2
✓ Branch 13 → 14 taken 16 times.
✗ Branch 13 → 15 not taken.
16 if (!username)
296 16 username = "nobody";
297
298
1/2
✗ Branch 17 → 18 not taken.
✓ Branch 17 → 20 taken 16 times.
16 if (g_strcmp0 (g_getenv ("FP_DEVICE_EMULATION"), "1") == 0)
299 rand_id = 0;
300 else
301 rand_id = g_random_int ();
302
303 16 user_id = g_strdup_printf ("FP1-%04d%02d%02d-%X-%08X-%s",
304 y, m, d,
305 16 fp_print_get_finger (print),
306 rand_id,
307 username);
308
309 16 return user_id;
310
311 }
312
313 /**
314 * fpi_print_fill_from_user_id:
315 * @print: #FpPrint to fill metadata into
316 * @user_id: An ID that was likely encoded using fpi_print_generate_user_id()
317 *
318 * This is the reverse operation of fpi_print_generate_user_id(), allowing
319 * the driver to encode some print metadata in a string.
320 *
321 * Returns: Whether a valid ID was found
322 */
323 gboolean
324 86 fpi_print_fill_from_user_id (FpPrint *print, const char *user_id)
325 {
326
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 86 times.
86 g_return_val_if_fail (user_id, FALSE);
327
328 /* The format has 24 bytes at the start and some dashes in the right places */
329
5/6
✓ Branch 4 → 5 taken 82 times.
✓ Branch 4 → 28 taken 4 times.
✓ Branch 5 → 6 taken 67 times.
✓ Branch 5 → 28 taken 15 times.
✓ Branch 6 → 7 taken 67 times.
✗ Branch 6 → 28 not taken.
86 if (g_str_has_prefix (user_id, "FP1-") && strlen (user_id) >= 24 &&
330
3/6
✓ Branch 7 → 8 taken 67 times.
✗ Branch 7 → 28 not taken.
✓ Branch 8 → 9 taken 67 times.
✗ Branch 8 → 28 not taken.
✓ Branch 9 → 10 taken 67 times.
✗ Branch 9 → 28 not taken.
67 user_id[12] == '-' && user_id[14] == '-' && user_id[23] == '-')
331 {
332
1/2
✗ Branch 10 → 11 not taken.
✓ Branch 10 → 13 taken 67 times.
134 g_autofree gchar *copy = g_strdup (user_id);
333 67 g_autoptr(GDate) date = NULL;
334 67 gint32 date_ymd;
335 67 gint32 finger;
336 67 gchar *username;
337 /* Try to parse information from the string. */
338
339 67 copy[12] = '\0';
340 67 date_ymd = g_ascii_strtod (copy + 4, NULL);
341
2/2
✓ Branch 15 → 16 taken 9 times.
✓ Branch 15 → 17 taken 58 times.
67 if (date_ymd > 0)
342 9 date = g_date_new_dmy (date_ymd % 100,
343 9 (date_ymd / 100) % 100,
344 9 date_ymd / 10000);
345 else
346 58 date = g_date_new ();
347
348 67 fp_print_set_enroll_date (print, date);
349
350 67 copy[14] = '\0';
351 67 finger = g_ascii_strtoll (copy + 13, NULL, 16);
352 67 fp_print_set_finger (print, finger);
353
354 /* We ignore the next chunk, it is just random data.
355 * Then comes the username; nobody is the default if the metadata
356 * is unknown */
357 67 username = copy + 24;
358
3/4
✓ Branch 21 → 22 taken 67 times.
✗ Branch 21 → 25 not taken.
✓ Branch 23 → 24 taken 15 times.
✓ Branch 23 → 25 taken 52 times.
67 if (strlen (username) > 0 && g_strcmp0 (username, "nobody") != 0)
359 15 fp_print_set_username (print, username);
360
361
1/2
✓ Branch 25 → 26 taken 67 times.
✗ Branch 25 → 27 not taken.
67 return TRUE;
362 }
363
364 return FALSE;
365 }
366