uru4000: Add support for Microsoft Fingerprint Reader v2
After lot 713, Microsoft fingerprint readers changed. The new version comes with a new USB product ID and a challenge-response authentication scheme where the device challenges the authenticity of the driver. An independent third party produced documentation on the computations needed to convert a challenge into the correct response, and I then used this documentation to produce a clean-room reimplementation of the authentication scheme.
This commit is contained in:
parent
474da5f2c7
commit
642010643d
3 changed files with 105 additions and 3 deletions
|
@ -21,6 +21,11 @@ PKG_CHECK_MODULES(LIBUSB, "libusb")
|
||||||
AC_SUBST(LIBUSB_CFLAGS)
|
AC_SUBST(LIBUSB_CFLAGS)
|
||||||
AC_SUBST(LIBUSB_LIBS)
|
AC_SUBST(LIBUSB_LIBS)
|
||||||
|
|
||||||
|
# check for OpenSSL's libcrypto
|
||||||
|
PKG_CHECK_MODULES(CRYPTO, "libcrypto")
|
||||||
|
AC_SUBST(CRYPTO_CFLAGS)
|
||||||
|
AC_SUBST(CRYPTO_LIBS)
|
||||||
|
|
||||||
PKG_CHECK_MODULES(GLIB, "glib-2.0")
|
PKG_CHECK_MODULES(GLIB, "glib-2.0")
|
||||||
AC_SUBST(GLIB_CFLAGS)
|
AC_SUBST(GLIB_CFLAGS)
|
||||||
AC_SUBST(GLIB_LIBS)
|
AC_SUBST(GLIB_LIBS)
|
||||||
|
|
|
@ -46,9 +46,9 @@ NBIS_SRC = \
|
||||||
nbis/mindtct/sort.c \
|
nbis/mindtct/sort.c \
|
||||||
nbis/mindtct/util.c
|
nbis/mindtct/util.c
|
||||||
|
|
||||||
libfprint_la_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(AM_CFLAGS)
|
libfprint_la_CFLAGS = -fvisibility=hidden -I$(srcdir)/nbis/include $(LIBUSB_CFLAGS) $(GLIB_CFLAGS) $(IMAGEMAGICK_CFLAGS) $(CRYPTO_CFLAGS) $(AM_CFLAGS)
|
||||||
libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
|
libfprint_la_LDFLAGS = -version-info @lt_major@:@lt_revision@:@lt_age@
|
||||||
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(IMAGEMAGICK_LIBS)
|
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(IMAGEMAGICK_LIBS) $(CRYPTO_LIBS)
|
||||||
|
|
||||||
libfprint_la_SOURCES = \
|
libfprint_la_SOURCES = \
|
||||||
fp_internal.h \
|
fp_internal.h \
|
||||||
|
|
|
@ -23,6 +23,7 @@
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <openssl/aes.h>
|
||||||
#include <usb.h>
|
#include <usb.h>
|
||||||
|
|
||||||
#include <fp_internal.h>
|
#include <fp_internal.h>
|
||||||
|
@ -39,6 +40,7 @@
|
||||||
#define DATABLK2_EXPECT 0xb1c0
|
#define DATABLK2_EXPECT 0xb1c0
|
||||||
#define CAPTURE_HDRLEN 64
|
#define CAPTURE_HDRLEN 64
|
||||||
#define IRQ_LENGTH 64
|
#define IRQ_LENGTH 64
|
||||||
|
#define CR_LENGTH 16
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
IRQDATA_SCANPWR_ON = 0x56aa,
|
IRQDATA_SCANPWR_ON = 0x56aa,
|
||||||
|
@ -51,6 +53,8 @@ enum {
|
||||||
REG_HWSTAT = 0x07,
|
REG_HWSTAT = 0x07,
|
||||||
REG_MODE = 0x4e,
|
REG_MODE = 0x4e,
|
||||||
FIRMWARE_START = 0x100,
|
FIRMWARE_START = 0x100,
|
||||||
|
REG_RESPONSE = 0x2000,
|
||||||
|
REG_CHALLENGE = 0x2010,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
|
@ -75,32 +79,50 @@ static const struct uru4k_dev_profile {
|
||||||
const char *name;
|
const char *name;
|
||||||
uint16_t firmware_start;
|
uint16_t firmware_start;
|
||||||
uint16_t fw_enc_offset;
|
uint16_t fw_enc_offset;
|
||||||
|
gboolean auth_cr;
|
||||||
} uru4k_dev_info[] = {
|
} uru4k_dev_info[] = {
|
||||||
[MS_KBD] = {
|
[MS_KBD] = {
|
||||||
.name = "Microsoft Keyboard with Fingerprint Reader",
|
.name = "Microsoft Keyboard with Fingerprint Reader",
|
||||||
.fw_enc_offset = 0x411,
|
.fw_enc_offset = 0x411,
|
||||||
|
.auth_cr = FALSE,
|
||||||
},
|
},
|
||||||
[MS_INTELLIMOUSE] = {
|
[MS_INTELLIMOUSE] = {
|
||||||
.name = "Microsoft Wireless IntelliMouse with Fingerprint Reader",
|
.name = "Microsoft Wireless IntelliMouse with Fingerprint Reader",
|
||||||
.fw_enc_offset = 0x411,
|
.fw_enc_offset = 0x411,
|
||||||
|
.auth_cr = FALSE,
|
||||||
},
|
},
|
||||||
[MS_STANDALONE] = {
|
[MS_STANDALONE] = {
|
||||||
.name = "Microsoft Fingerprint Reader",
|
.name = "Microsoft Fingerprint Reader",
|
||||||
.fw_enc_offset = 0x411,
|
.fw_enc_offset = 0x411,
|
||||||
|
.auth_cr = FALSE,
|
||||||
|
},
|
||||||
|
[MS_STANDALONE_V2] = {
|
||||||
|
.name = "Microsoft Fingerprint Reader v2",
|
||||||
|
.fw_enc_offset = 0x52e,
|
||||||
|
.auth_cr = TRUE,
|
||||||
},
|
},
|
||||||
[DP_URU4000] = {
|
[DP_URU4000] = {
|
||||||
.name = "Digital Persona U.are.U 4000",
|
.name = "Digital Persona U.are.U 4000",
|
||||||
.fw_enc_offset = 0x693,
|
.fw_enc_offset = 0x693,
|
||||||
|
.auth_cr = FALSE,
|
||||||
},
|
},
|
||||||
[DP_URU4000B] = {
|
[DP_URU4000B] = {
|
||||||
.name = "Digital Persona U.are.U 4000B",
|
.name = "Digital Persona U.are.U 4000B",
|
||||||
.fw_enc_offset = 0x411,
|
.fw_enc_offset = 0x411,
|
||||||
|
.auth_cr = FALSE,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
struct uru4k_dev {
|
struct uru4k_dev {
|
||||||
const struct uru4k_dev_profile *profile;
|
const struct uru4k_dev_profile *profile;
|
||||||
uint8_t interface;
|
uint8_t interface;
|
||||||
|
AES_KEY aeskey;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* For 2nd generation MS devices */
|
||||||
|
static const unsigned char crkey[] = {
|
||||||
|
0x79, 0xac, 0x91, 0x79, 0x5c, 0xa1, 0x47, 0x8e,
|
||||||
|
0x98, 0xe0, 0x0f, 0x3c, 0x59, 0x8f, 0x5f, 0x4b,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -184,6 +206,71 @@ static int set_mode(struct fp_img_dev *dev, unsigned char mode)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int read_challenge(struct fp_img_dev *dev, unsigned char *data)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
/* The windows driver uses a request of 0x0c here. We use 0x04 to be
|
||||||
|
* consistent with every other command we know about. */
|
||||||
|
r = usb_control_msg(dev->udev, CTRL_IN, USB_RQ, REG_CHALLENGE, 0,
|
||||||
|
data, CR_LENGTH, CTRL_TIMEOUT);
|
||||||
|
if (r < 0) {
|
||||||
|
fp_err("error %d", r);
|
||||||
|
return r;
|
||||||
|
} else if (r < CR_LENGTH) {
|
||||||
|
fp_err("read too short (%d)", r);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_response(struct fp_img_dev *dev, unsigned char *data)
|
||||||
|
{
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = usb_control_msg(dev->udev, CTRL_OUT, USB_RQ, REG_RESPONSE, 0, data,
|
||||||
|
CR_LENGTH, CTRL_TIMEOUT);
|
||||||
|
if (r < 0) {
|
||||||
|
fp_err("error %d", r);
|
||||||
|
return r;
|
||||||
|
} else if (r < 1) {
|
||||||
|
fp_err("write too short (%d)", r);
|
||||||
|
return -EIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 2nd generation MS devices added an AES-based challenge/response
|
||||||
|
* authentication scheme, where the device challenges the authenticity of the
|
||||||
|
* driver.
|
||||||
|
*/
|
||||||
|
static int auth_cr(struct fp_img_dev *dev)
|
||||||
|
{
|
||||||
|
struct uru4k_dev *urudev = dev->priv;
|
||||||
|
unsigned char challenge[CR_LENGTH];
|
||||||
|
unsigned char response[CR_LENGTH];
|
||||||
|
int r;
|
||||||
|
|
||||||
|
fp_dbg("");
|
||||||
|
|
||||||
|
r = read_challenge(dev, challenge);
|
||||||
|
if (r < 0) {
|
||||||
|
fp_err("error %d reading challenge", r);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
AES_encrypt(challenge, response, &urudev->aeskey);
|
||||||
|
|
||||||
|
r = write_response(dev, response);
|
||||||
|
if (r < 0)
|
||||||
|
fp_err("error %d writing response", r);
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
static int get_irq(struct fp_img_dev *dev, unsigned char *buf, int timeout)
|
static int get_irq(struct fp_img_dev *dev, unsigned char *buf, int timeout)
|
||||||
{
|
{
|
||||||
uint16_t type;
|
uint16_t type;
|
||||||
|
@ -398,6 +485,8 @@ static int do_init(struct fp_img_dev *dev)
|
||||||
{
|
{
|
||||||
unsigned char status;
|
unsigned char status;
|
||||||
unsigned char tmp;
|
unsigned char tmp;
|
||||||
|
struct uru4k_dev *urudev = dev->priv;
|
||||||
|
gboolean need_auth_cr = urudev->profile->auth_cr;
|
||||||
int timeouts = 0;
|
int timeouts = 0;
|
||||||
int i;
|
int i;
|
||||||
int r;
|
int r;
|
||||||
|
@ -467,7 +556,11 @@ retry:
|
||||||
|
|
||||||
usleep(10000);
|
usleep(10000);
|
||||||
|
|
||||||
/* FIXME do C-R auth for v2 devices */
|
if (need_auth_cr) {
|
||||||
|
r = auth_cr(dev);
|
||||||
|
if (r < 0)
|
||||||
|
return r;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tmp & 0x80) {
|
if (tmp & 0x80) {
|
||||||
|
@ -556,6 +649,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
|
||||||
urudev = g_malloc0(sizeof(*urudev));
|
urudev = g_malloc0(sizeof(*urudev));
|
||||||
urudev->profile = &uru4k_dev_info[driver_data];
|
urudev->profile = &uru4k_dev_info[driver_data];
|
||||||
urudev->interface = iface_desc->bInterfaceNumber;
|
urudev->interface = iface_desc->bInterfaceNumber;
|
||||||
|
AES_set_encrypt_key(crkey, 128, &urudev->aeskey);
|
||||||
dev->priv = urudev;
|
dev->priv = urudev;
|
||||||
|
|
||||||
r = do_init(dev);
|
r = do_init(dev);
|
||||||
|
@ -589,6 +683,9 @@ static const struct usb_id id_table[] = {
|
||||||
/* ms fp rdr (standalone) */
|
/* ms fp rdr (standalone) */
|
||||||
{ .vendor = 0x045e, .product = 0x00bd, .driver_data = MS_STANDALONE },
|
{ .vendor = 0x045e, .product = 0x00bd, .driver_data = MS_STANDALONE },
|
||||||
|
|
||||||
|
/* ms fp rdr (standalone) v2 */
|
||||||
|
{ .vendor = 0x045e, .product = 0x00ca, .driver_data = MS_STANDALONE_V2 },
|
||||||
|
|
||||||
/* dp uru4000 (standalone) */
|
/* dp uru4000 (standalone) */
|
||||||
{ .vendor = 0x05ba, .product = 0x0007, .driver_data = DP_URU4000 },
|
{ .vendor = 0x05ba, .product = 0x0007, .driver_data = DP_URU4000 },
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue