diff --git a/TODO b/TODO index f435606..519ee0e 100644 --- a/TODO +++ b/TODO @@ -1,6 +1,5 @@ LIBRARY ======= -fingerprint data classifcation by device or device type identification external API documentation test suite against NFIQ compliance set @@ -8,7 +7,9 @@ test suite against NFIQ compliance set DRIVERS ======= Sunplus 895 driver -AES3501 driver +AES1610 driver +AES2501 driver +AES3400/3500 driver ID Mouse driver Support for 2nd generation MS devices Support for 2nd generation UPEK devices @@ -17,10 +18,12 @@ IMAGING ======= aes4000 doesn't work very well, maybe due to small minutia count? PPMM parameter to get_minutiae seems to have no effect +nbis minutiae should be stored in endian-independent format +return images with standard enroll/verify calls MISC ==== upekts/thinkfinger relicensing (GPL --> LGPL) make library optionally asynchronous and maybe thread-safe pkg-config file - +nbis cleanups diff --git a/libfprint/core.c b/libfprint/core.c index d6c3632..40d956f 100644 --- a/libfprint/core.c +++ b/libfprint/core.c @@ -69,6 +69,10 @@ void fpi_log(enum fpi_log_level level, const char *component, static void register_driver(struct fp_driver *drv) { + if (drv->id == 0) { + fp_err("not registering driver %s: driver ID is 0"); + return; + } registered_drivers = g_list_prepend(registered_drivers, (gpointer) drv); fp_dbg("registered driver %s", drv->name); } @@ -333,7 +337,7 @@ API_EXPORTED int fp_verify_finger(struct fp_dev *dev, return -EINVAL; } - if (!fpi_print_data_compatible(dev, enrolled_print)) { + if (!fpi_print_data_compatible(enrolled_print, dev)) { fp_err("print is not compatible with device"); return -EINVAL; } diff --git a/libfprint/data.c b/libfprint/data.c index 6eba48c..4bd6e91 100644 --- a/libfprint/data.c +++ b/libfprint/data.c @@ -67,16 +67,114 @@ static const char *finger_code_to_str(enum fp_finger finger) return names[finger]; } +static enum fp_print_data_type get_data_type_for_dev(struct fp_dev *dev) +{ + switch (dev->drv->type) { + case DRIVER_PRIMITIVE: + return PRINT_DATA_RAW; + case DRIVER_IMAGING: + return PRINT_DATA_NBIS_MINUTIAE; + default: + fp_err("unrecognised drv type %d", dev->drv->type); + return PRINT_DATA_RAW; + } +} + +static struct fp_print_data *print_data_new(uint16_t driver_id, + uint32_t devtype, enum fp_print_data_type type, size_t length) +{ + struct fp_print_data *data = g_malloc(sizeof(*data) + length); + fp_dbg("length=%zd driver=%02x devtype=%04x", length, driver_id, devtype); + memset(data, 0, sizeof(*data)); + data->driver_id = driver_id; + data->devtype = devtype; + data->type = type; + data->length = length; + return data; +} + struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length) { struct fp_print_data *data = g_malloc(sizeof(*data) + length); memset(data, 0, sizeof(*data)); - fp_dbg("length=%zd", length); - data->driver_name = dev->drv->name; - data->length = length; + return print_data_new(dev->drv->id, dev->devtype, + get_data_type_for_dev(dev), length); +} + +API_EXPORTED size_t fp_print_data_get_data(struct fp_print_data *data, + unsigned char **ret) +{ + struct fpi_print_data_fp1 *buf; + size_t buflen; + + fp_dbg(""); + + buflen = sizeof(*buf) + data->length; + buf = malloc(buflen); + if (!buf) + return 0; + + *ret = (unsigned char *) buf; + buf->prefix[0] = 'F'; + buf->prefix[1] = 'P'; + buf->prefix[2] = '1'; + buf->driver_id = GUINT16_TO_LE(data->driver_id); + buf->devtype = GUINT32_TO_LE(data->devtype); + buf->data_type = data->type; + memcpy(buf->data, data->data, data->length); + return buflen; +} + +API_EXPORTED struct fp_print_data *fp_print_data_from_data(unsigned char *buf, + size_t buflen) +{ + struct fpi_print_data_fp1 *raw = (struct fpi_print_data_fp1 *) buf; + size_t print_data_len; + struct fp_print_data *data; + + fp_dbg("buffer size %zd", buflen); + if (buflen < sizeof(*raw)) + return NULL; + + if (strncmp(raw->prefix, "FP1", 3) != 0) { + fp_dbg("bad header prefix"); + return NULL; + } + + print_data_len = buflen - sizeof(*raw); + data = print_data_new(GUINT16_FROM_LE(raw->driver_id), + GUINT32_FROM_LE(raw->devtype), raw->data_type, print_data_len); + memcpy(data->data, raw->data, print_data_len); return data; } +static char *__get_path_to_storedir(uint16_t driver_id, uint32_t devtype) +{ + char idstr[5]; + char devtypestr[9]; + + g_snprintf(idstr, sizeof(idstr), "%04x", driver_id); + g_snprintf(devtypestr, sizeof(devtypestr), "%08x", devtype); + + return g_build_filename(base_store, idstr, devtypestr, NULL); +} + +static char *get_path_to_storedir(struct fp_dev *dev) +{ + return __get_path_to_storedir(dev->drv->id, dev->devtype); +} + +static char *get_path_to_print(struct fp_dev *dev, const char *fingerstr) +{ + char *dirpath; + char *path; + + dirpath = get_path_to_storedir(dev); + path = g_build_filename(dirpath, fingerstr, NULL); + g_free(dirpath); + return path; +} + API_EXPORTED int fp_print_data_save(struct fp_print_data *data, enum fp_finger finger) { @@ -84,6 +182,8 @@ API_EXPORTED int fp_print_data_save(struct fp_print_data *data, char *path; char *dirpath; const char *fingerstr = finger_code_to_str(finger); + unsigned char *buf; + size_t len; int r; if (!fingerstr) @@ -92,8 +192,12 @@ API_EXPORTED int fp_print_data_save(struct fp_print_data *data, if (!base_store) storage_setup(); - fp_dbg("save %s print from %s", fingerstr, data->driver_name); - dirpath = g_build_filename(base_store, data->driver_name, NULL); + fp_dbg("save %s print from driver %04x", fingerstr, data->driver_id); + len = fp_print_data_get_data(data, &buf); + if (!len) + return -ENOMEM; + + dirpath = __get_path_to_storedir(data->driver_id, data->devtype); r = g_mkdir_with_parents(dirpath, DIR_PERMS); if (r < 0) { fp_err("couldn't create storage directory"); @@ -103,19 +207,57 @@ API_EXPORTED int fp_print_data_save(struct fp_print_data *data, path = g_build_filename(dirpath, fingerstr, NULL); fp_dbg("saving to %s", path); - g_file_set_contents(path, data->buffer, data->length, &err); + g_file_set_contents(path, buf, len, &err); + free(buf); g_free(dirpath); g_free(path); if (err) { r = err->code; fp_err("%s save failed: %s", fingerstr, err->message); g_error_free(err); + /* FIXME interpret error codes */ return r; } return 0; } +gboolean fpi_print_data_compatible(struct fp_print_data *data, + struct fp_dev *dev) +{ + struct fp_driver *drv = dev->drv; + + if (drv->id != data->driver_id) { + fp_dbg("driver name mismatch: %02x vs %02x", drv->id, data->driver_id); + return FALSE; + } + + if (dev->devtype != data->devtype) { + fp_dbg("devtype mismatch: %04x vs %04x", dev->devtype, data->devtype); + return FALSE; + } + + switch (data->type) { + case PRINT_DATA_RAW: + if (drv->type != DRIVER_PRIMITIVE) { + fp_dbg("raw data vs primitive driver mismatch"); + return FALSE; + } + break; + case PRINT_DATA_NBIS_MINUTIAE: + if (drv->type != DRIVER_IMAGING) { + fp_dbg("minutiae data vs imaging driver mismatch"); + return FALSE; + } + break; + default: + fp_err("unrecognised data type %d", data->type); + return FALSE; + } + + return TRUE; +} + API_EXPORTED int fp_print_data_load(struct fp_dev *dev, enum fp_finger finger, struct fp_print_data **data) { @@ -132,7 +274,7 @@ API_EXPORTED int fp_print_data_load(struct fp_dev *dev, if (!base_store) storage_setup(); - path = g_build_filename(base_store, dev->drv->name, fingerstr, NULL); + path = get_path_to_print(dev, fingerstr); fp_dbg("from %s", path); g_file_get_contents(path, &contents, &length, &err); g_free(path); @@ -147,9 +289,14 @@ API_EXPORTED int fp_print_data_load(struct fp_dev *dev, return r; } - fdata = fpi_print_data_new(dev, length); - memcpy(fdata->buffer, contents, length); + fdata = fp_print_data_from_data(contents, length); g_free(contents); + + if (!fpi_print_data_compatible(fdata, dev)) { + fp_err("print data is not compatible!"); + return -EINVAL; + } + *data = fdata; return 0; } @@ -159,7 +306,3 @@ API_EXPORTED void fp_print_data_free(struct fp_print_data *data) g_free(data); } -int fpi_print_data_compatible(struct fp_dev *dev, struct fp_print_data *data) -{ - return strcmp(dev->drv->name, data->driver_name) == 0; -} diff --git a/libfprint/drivers/aes4000.c b/libfprint/drivers/aes4000.c index cbbcfbf..5ad08d2 100644 --- a/libfprint/drivers/aes4000.c +++ b/libfprint/drivers/aes4000.c @@ -210,6 +210,7 @@ static const struct usb_id id_table[] = { struct fp_img_driver aes4000_driver = { .driver = { + .id = 3, .name = FP_COMPONENT, .full_name = "AuthenTec AES4000", .id_table = id_table, diff --git a/libfprint/drivers/upekts.c b/libfprint/drivers/upekts.c index be958e3..8287d5c 100644 --- a/libfprint/drivers/upekts.c +++ b/libfprint/drivers/upekts.c @@ -653,7 +653,7 @@ static int enroll(struct fp_dev *dev, gboolean initial, } fdata = fpi_print_data_new(dev, data_len - sizeof(scan_comp)); - memcpy(fdata->buffer, data + sizeof(scan_comp), data_len - sizeof(scan_comp)); + memcpy(fdata->data, data + sizeof(scan_comp), data_len - sizeof(scan_comp)); *_data = fdata; comp_out: g_free(data); @@ -679,7 +679,7 @@ static int verify(struct fp_dev *dev, struct fp_print_data *print) data = g_malloc(data_len); memcpy(data, verify_hdr, sizeof(verify_hdr)); - memcpy(data + sizeof(verify_hdr), print->buffer, print->length); + memcpy(data + sizeof(verify_hdr), print->data, print->length); r = send_cmd28(dev, 0x03, data, data_len); if (r < 0) @@ -781,6 +781,7 @@ static const struct usb_id id_table[] = { }; struct fp_driver upekts_driver = { + .id = 1, .name = FP_COMPONENT, .full_name = "UPEK TouchStrip", .id_table = id_table, diff --git a/libfprint/drivers/uru4000.c b/libfprint/drivers/uru4000.c index f11aa89..b81da65 100644 --- a/libfprint/drivers/uru4000.c +++ b/libfprint/drivers/uru4000.c @@ -489,6 +489,7 @@ static const struct usb_id id_table[] = { struct fp_img_driver uru4000_driver = { .driver = { + .id = 2, .name = FP_COMPONENT, .full_name = "Digital Persona U.are.U 4000/4000B", .id_table = id_table, diff --git a/libfprint/fp_internal.h b/libfprint/fp_internal.h index 12ec764..cba79ac 100644 --- a/libfprint/fp_internal.h +++ b/libfprint/fp_internal.h @@ -78,6 +78,7 @@ void fpi_log(enum fpi_log_level, const char *component, const char *function, struct fp_dev { struct fp_driver *drv; usb_dev_handle *udev; + uint32_t devtype; void *priv; int nr_enroll_stages; @@ -104,6 +105,7 @@ enum fp_driver_type { }; struct fp_driver { + const uint16_t id; const char *name; const char *full_name; const struct usb_id * const id_table; @@ -152,14 +154,30 @@ struct fp_dscv_dev { unsigned long driver_data; }; -struct fp_print_data { - const char *driver_name; - size_t length; - unsigned char buffer[0]; +enum fp_print_data_type { + PRINT_DATA_RAW = 0, /* memset-imposed default */ + PRINT_DATA_NBIS_MINUTIAE, }; +struct fp_print_data { + uint16_t driver_id; + uint32_t devtype; + enum fp_print_data_type type; + size_t length; + unsigned char data[0]; +}; + +struct fpi_print_data_fp1 { + char prefix[3]; + uint16_t driver_id; + uint32_t devtype; + unsigned char data_type; + unsigned char data[0]; +} __attribute__((__packed__)); + struct fp_print_data *fpi_print_data_new(struct fp_dev *dev, size_t length); -int fpi_print_data_compatible(struct fp_dev *dev, struct fp_print_data *data); +gboolean fpi_print_data_compatible(struct fp_print_data *data, + struct fp_dev *dev); /* bit values for fp_img.flags */ #define FP_IMG_V_FLIPPED (1<<0) diff --git a/libfprint/fprint.h b/libfprint/fprint.h index 89d05c7..15ea365 100644 --- a/libfprint/fprint.h +++ b/libfprint/fprint.h @@ -87,6 +87,9 @@ int fp_print_data_load(struct fp_dev *dev, enum fp_finger finger, struct fp_print_data **data); int fp_print_data_save(struct fp_print_data *data, enum fp_finger finger); void fp_print_data_free(struct fp_print_data *data); +size_t fp_print_data_get_data(struct fp_print_data *data, unsigned char **ret); +struct fp_print_data *fp_print_data_from_data(unsigned char *buf, + size_t buflen); /* Imaging devices */ int fp_imgdev_capture(struct fp_img_dev *imgdev, int unconditional, diff --git a/libfprint/img.c b/libfprint/img.c index b2db8b8..f421c36 100644 --- a/libfprint/img.c +++ b/libfprint/img.c @@ -243,7 +243,8 @@ int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *img, /* FIXME: space is wasted if we dont hit the max minutiae count. would * be good to make this dynamic. */ print = fpi_print_data_new(imgdev->dev, sizeof(struct xyt_struct)); - minutiae_to_xyt(minutiae, bw, bh, print->buffer); + print->type = PRINT_DATA_NBIS_MINUTIAE; + minutiae_to_xyt(minutiae, bw, bh, print->data); /* FIXME: the print buffer at this point is endian-specific, and will * only work when loaded onto machines with identical endianness. not good! * data format should be platform-independant. */ @@ -263,11 +264,17 @@ int fpi_img_detect_minutiae(struct fp_img_dev *imgdev, struct fp_img *img, int fpi_img_compare_print_data(struct fp_print_data *enrolled_print, struct fp_print_data *new_print) { - struct xyt_struct *gstruct = (struct xyt_struct *) enrolled_print->buffer; - struct xyt_struct *pstruct = (struct xyt_struct *) new_print->buffer; + struct xyt_struct *gstruct = (struct xyt_struct *) enrolled_print->data; + struct xyt_struct *pstruct = (struct xyt_struct *) new_print->data; GTimer *timer; int r; + if (enrolled_print->type != PRINT_DATA_NBIS_MINUTIAE || + new_print->type != PRINT_DATA_NBIS_MINUTIAE) { + fp_err("invalid print format"); + return -EINVAL; + } + timer = g_timer_new(); r = bozorth_main(pstruct, gstruct); g_timer_stop(timer); diff --git a/libfprint/imgdev.c b/libfprint/imgdev.c index 9355bb4..b5431dd 100644 --- a/libfprint/imgdev.c +++ b/libfprint/imgdev.c @@ -195,6 +195,8 @@ static int img_dev_verify(struct fp_dev *dev, r = fpi_img_compare_print_data(enrolled_print, print); fp_print_data_free(print); + if (r < 0) + return r; if (r >= 40) return FP_VERIFY_MATCH; else