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 |