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:
Daniel Drake 2007-11-16 17:25:41 +00:00
parent 474da5f2c7
commit 642010643d
3 changed files with 105 additions and 3 deletions

View file

@ -21,6 +21,11 @@ PKG_CHECK_MODULES(LIBUSB, "libusb")
AC_SUBST(LIBUSB_CFLAGS)
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")
AC_SUBST(GLIB_CFLAGS)
AC_SUBST(GLIB_LIBS)

View file

@ -46,9 +46,9 @@ NBIS_SRC = \
nbis/mindtct/sort.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_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(IMAGEMAGICK_LIBS)
libfprint_la_LIBADD = -lm $(LIBUSB_LIBS) $(GLIB_LIBS) $(IMAGEMAGICK_LIBS) $(CRYPTO_LIBS)
libfprint_la_SOURCES = \
fp_internal.h \

View file

@ -23,6 +23,7 @@
#include <string.h>
#include <unistd.h>
#include <openssl/aes.h>
#include <usb.h>
#include <fp_internal.h>
@ -39,6 +40,7 @@
#define DATABLK2_EXPECT 0xb1c0
#define CAPTURE_HDRLEN 64
#define IRQ_LENGTH 64
#define CR_LENGTH 16
enum {
IRQDATA_SCANPWR_ON = 0x56aa,
@ -51,6 +53,8 @@ enum {
REG_HWSTAT = 0x07,
REG_MODE = 0x4e,
FIRMWARE_START = 0x100,
REG_RESPONSE = 0x2000,
REG_CHALLENGE = 0x2010,
};
enum {
@ -75,32 +79,50 @@ static const struct uru4k_dev_profile {
const char *name;
uint16_t firmware_start;
uint16_t fw_enc_offset;
gboolean auth_cr;
} uru4k_dev_info[] = {
[MS_KBD] = {
.name = "Microsoft Keyboard with Fingerprint Reader",
.fw_enc_offset = 0x411,
.auth_cr = FALSE,
},
[MS_INTELLIMOUSE] = {
.name = "Microsoft Wireless IntelliMouse with Fingerprint Reader",
.fw_enc_offset = 0x411,
.auth_cr = FALSE,
},
[MS_STANDALONE] = {
.name = "Microsoft Fingerprint Reader",
.fw_enc_offset = 0x411,
.auth_cr = FALSE,
},
[MS_STANDALONE_V2] = {
.name = "Microsoft Fingerprint Reader v2",
.fw_enc_offset = 0x52e,
.auth_cr = TRUE,
},
[DP_URU4000] = {
.name = "Digital Persona U.are.U 4000",
.fw_enc_offset = 0x693,
.auth_cr = FALSE,
},
[DP_URU4000B] = {
.name = "Digital Persona U.are.U 4000B",
.fw_enc_offset = 0x411,
.auth_cr = FALSE,
},
};
struct uru4k_dev {
const struct uru4k_dev_profile *profile;
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;
}
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)
{
uint16_t type;
@ -398,6 +485,8 @@ static int do_init(struct fp_img_dev *dev)
{
unsigned char status;
unsigned char tmp;
struct uru4k_dev *urudev = dev->priv;
gboolean need_auth_cr = urudev->profile->auth_cr;
int timeouts = 0;
int i;
int r;
@ -467,7 +556,11 @@ retry:
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) {
@ -556,6 +649,7 @@ static int dev_init(struct fp_img_dev *dev, unsigned long driver_data)
urudev = g_malloc0(sizeof(*urudev));
urudev->profile = &uru4k_dev_info[driver_data];
urudev->interface = iface_desc->bInterfaceNumber;
AES_set_encrypt_key(crkey, 128, &urudev->aeskey);
dev->priv = urudev;
r = do_init(dev);
@ -589,6 +683,9 @@ static const struct usb_id id_table[] = {
/* ms fp rdr (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) */
{ .vendor = 0x05ba, .product = 0x0007, .driver_data = DP_URU4000 },