summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJim Miller <jaggies@google.com>2015-08-19 19:42:27 -0700
committerJim Miller <jaggies@google.com>2015-08-25 20:25:44 -0700
commitc336ddfb0d857df378cf543a0591e8a6501f88c4 (patch)
tree7a54ee268ef87fe95344395787076783e90e03dd
parent4a91c438b37988bf2faaaa1fd8d827d2b3a894dd (diff)
downloadandroid_device_generic_goldfish-c336ddfb0d857df378cf543a0591e8a6501f88c4.tar.gz
android_device_generic_goldfish-c336ddfb0d857df378cf543a0591e8a6501f88c4.tar.bz2
android_device_generic_goldfish-c336ddfb0d857df378cf543a0591e8a6501f88c4.zip
Add some guidance documentation to the emulator
Because fingerprint HAL is vendor-specific due to the Trusted Execution Environment (TEE) requirements, source is not provided in AOSP. As such, this example is a reasonable place to document some of the requirements and expectations. Fixes bug 23045367 Change-Id: Iacf14d6d32b468464215320b411ef7ff1a387ff0
-rw-r--r--fingerprint/fingerprint.c113
1 files changed, 104 insertions, 9 deletions
diff --git a/fingerprint/fingerprint.c b/fingerprint/fingerprint.c
index c3dc787..1b26b8f 100644
--- a/fingerprint/fingerprint.c
+++ b/fingerprint/fingerprint.c
@@ -14,10 +14,24 @@
* limitations under the License.
*/
+/**
+ * This is a very basic implementation of fingerprint to allow testing on the emulator. It
+ * is *not* meant to be the final implementation on real devices. For example, it does *not*
+ * implement all of the required features, such as secure template storage and recognition
+ * inside a Trusted Execution Environment (TEE). However, this file is a reasonable starting
+ * point as developers add fingerprint support to their platform. See inline comments and
+ * recommendations for details.
+ *
+ * Please see the Android Compatibility Definition Document (CDD) for a full list of requirements
+ * and suggestions.
+ */
#define FINGERPRINT_LISTEN_SERVICE_NAME "fingerprintlisten"
#define FINGERPRINT_TXT_FILENAME "/data/fingerprint.txt"
#define LOG_TAG "FingerprintHal"
+
+// Typical devices will allow up to 5 fingerprints per user to maintain performance of
+// t < 500ms for recognition. This is the total number of fingerprints we'll store.
#define MAX_NUM_FINGERS 32
#include <errno.h>
@@ -30,11 +44,19 @@
#include <hardware/fingerprint.h>
#include <hardware/qemud.h>
+/**
+ * Most devices will have an internal state machine resembling this. There are 3 basic states, as
+ * shown below. When device is not authenticating or enrolling, it is expected to be in
+ * the idle state.
+ *
+ * Note that this is completely independent of device wake state. If the hardware device was in
+ * the "scan" state when the device drops into power collapse, it should resume scanning when power
+ * is restored. This is to facilitate rapid touch-to-unlock from keyguard.
+ */
typedef enum worker_state_t {
STATE_ENROLL = 1,
STATE_SCAN = 2,
STATE_IDLE = 3,
- STATE_EXIT = 4
} worker_state_t;
typedef struct worker_thread_t {
@@ -63,6 +85,9 @@ typedef struct emu_fingerprint_hal_device_t {
} emu_fingerprint_hal_device_t;
static uint64_t get_64bit_rand() {
+ // This should use a cryptographically-secure random number generator like arc4random().
+ // It should be generated inside of the TEE where possible. Here we just use something
+ // very simple.
return (((uint64_t) rand()) << 32) | ((uint64_t) rand());
}
@@ -90,6 +115,9 @@ static void save_fingerid(FILE* fp, int fingerid, uint64_t secureid, uint64_t au
fflush(fp);
}
+/**
+ * This is the communication channel from the HAL layer to fingerprintd.
+ */
static void listener_send_notice(emu_fingerprint_hal_device_t* dev)
{
fingerprint_msg_t message = {0};
@@ -103,11 +131,13 @@ static void listener_send_notice(emu_fingerprint_hal_device_t* dev)
dev->authenticator_id = get_64bit_rand();
dev->listener.state = STATE_SCAN;
if (!finger_already_enrolled(dev)) {
- dev->listener.all_fingerids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = dev->listener.fingerid;
- dev->listener.all_secureids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = dev->secure_user_id;
- dev->listener.all_authenids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = dev->authenticator_id;
+ const int n = dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS;
+ dev->listener.all_fingerids[n] = dev->listener.fingerid;
+ dev->listener.all_secureids[n] = dev->secure_user_id;
+ dev->listener.all_authenids[n] = dev->authenticator_id;
++ dev->listener.num_fingers_enrolled;
- save_fingerid(dev->listener.fp_write, dev->listener.fingerid, dev->secure_user_id, dev->authenticator_id);
+ save_fingerid(dev->listener.fp_write, dev->listener.fingerid, dev->secure_user_id,
+ dev->authenticator_id);
is_valid_finger = true;
}
} else {
@@ -141,6 +171,29 @@ static void listener_send_notice(emu_fingerprint_hal_device_t* dev)
pthread_mutex_unlock(&dev->lock);
}
+/**
+ * This a very simple event loop for the fingerprint sensor. For a given state (enroll, scan),
+ * this would receive events from the sensor and forward them to fingerprintd using the
+ * notify() method.
+ *
+ * In this simple example, we open a qemu channel (a pipe) where the developer can inject events to
+ * exercise the API and test application code.
+ *
+ * The scanner should remain in the scanning state until either an error occurs or the operation
+ * completes.
+ *
+ * Recoverable errors such as EINTR should be handled locally; they should not
+ * be propagated unless there's something the user can do about it (e.g. "clean sensor"). Such
+ * messages should go through the onAcquired() interface.
+ *
+ * If an unrecoverable error occurs, an acquired message (e.g. ACQUIRED_PARTIAL) should be sent,
+ * followed by an error message (e.g. FINGERPRINT_ERROR_UNABLE_TO_PROCESS).
+ *
+ * Note that this event loop would typically run in TEE since it must interact with the sensor
+ * hardware and handle raw fingerprint data and encrypted templates. It is expected that
+ * this code monitors the TEE for resulting events, such as enrollment and authentication status.
+ * Here we just have a very simple event loop that monitors a qemu channel for pseudo events.
+ */
static void* listenerFunction(void* data)
{
emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) data;
@@ -171,9 +224,10 @@ static void* listenerFunction(void* data)
uint64_t secureid = 0;
uint64_t authenid = 0;
if(fscanf(fp_stored, "%d %" SCNu64 " %" SCNu64, &fingerid, &secureid, &authenid) == 3) {
- dev->listener.all_fingerids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = fingerid;
- dev->listener.all_secureids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = secureid;
- dev->listener.all_authenids[dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS] = authenid;
+ const int n = dev->listener.num_fingers_enrolled % MAX_NUM_FINGERS;
+ dev->listener.all_fingerids[n] = fingerid;
+ dev->listener.all_secureids[n] = secureid;
+ dev->listener.all_authenids[n] = authenid;
++ dev->listener.num_fingers_enrolled;
} else {
break;
@@ -243,16 +297,28 @@ static void setListenerState(emu_fingerprint_hal_device_t* dev, worker_state_t s
}
static uint64_t fingerprint_get_auth_id(struct fingerprint_device __unused *device) {
+ // This should return the authentication_id generated when the fingerprint template database
+ // was created. Though this isn't expected to be secret, it is reasonable to expect it to be
+ // cryptographically generated to avoid replay attacks.
emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device;
return dev->authenticator_id;
}
static int fingerprint_set_active_group(struct fingerprint_device __unused *device, uint32_t gid,
const char *path) {
- // TODO: implements me
+ // Groups are a future feature. For now, the framework sends the profile owner's id (userid)
+ // as the primary group id for the user. This code should create a tuple (groupId, fingerId)
+ // that represents a single fingerprint entity in the database. For now we just generate
+ // globally unique ids.
return 0;
}
+/**
+ * If fingerprints are enrolled, then this function is expected to put the sensor into a
+ * "scanning" state where it's actively scanning and recognizing fingerprint features.
+ * Actual authentication must happen in TEE and should be monitored in a separate thread
+ * since this function is expected to return immediately.
+ */
static int fingerprint_authenticate(struct fingerprint_device __unused *device,
uint64_t __unused operation_id, __unused uint32_t gid)
{
@@ -266,6 +332,15 @@ static int fingerprint_authenticate(struct fingerprint_device __unused *device,
return 0;
}
+/**
+ * This is expected to put the sensor into an "enroll" state where it's actively scanning and
+ * working towards a finished fingerprint database entry. Authentication must happen in
+ * a separate thread since this function is expected to return immediately.
+ *
+ * Note: This method should always generate a new random authenticator_id.
+ *
+ * Note: As with fingerprint_authenticate(), this would run in TEE on a real device.
+ */
static int fingerprint_enroll(struct fingerprint_device *device,
const hw_auth_token_t *hat,
uint32_t __unused gid,
@@ -273,6 +348,9 @@ static int fingerprint_enroll(struct fingerprint_device *device,
ALOGD("fingerprint_enroll");
emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device;
if (hat && hat->challenge == dev->challenge) {
+ // The secure_user_id retrieved from the auth token should be stored
+ // with the enrolled fingerprint template and returned in the auth result
+ // for a successful authentication with that finger.
dev->secure_user_id = hat->user_id;
} else {
ALOGW("%s: invalid or null auth token", __func__);
@@ -293,13 +371,26 @@ static int fingerprint_enroll(struct fingerprint_device *device,
}
+/**
+ * The pre-enrollment step is simply to get an authentication token that can be wrapped and
+ * verified at a later step. The primary purpose is to return a token that protects against
+ * spoofing and replay attacks. It is passed to password authentication where it is wrapped and
+ * propagated to the enroll step.
+ */
static uint64_t fingerprint_pre_enroll(struct fingerprint_device *device) {
ALOGD("fingerprint_pre_enroll");
emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device;
+ // The challenge will typically be a cryptographically-secure key
+ // coming from the TEE so it can be verified at a later step. For now we just generate a
+ // random value.
dev->challenge = get_64bit_rand();
return dev->challenge;
}
+/**
+ * Cancel is called by the framework to cancel an outstanding event. This should *not* be called
+ * by the driver since it will cause the framework to stop listening for fingerprints.
+ */
static int fingerprint_cancel(struct fingerprint_device __unused *device) {
ALOGD("fingerprint_cancel");
emu_fingerprint_hal_device_t* dev = (emu_fingerprint_hal_device_t*) device;
@@ -357,6 +448,10 @@ static int fingerprint_open(const hw_module_t* module, const char __unused *id,
dev->device.set_notify = set_notify_callback;
dev->device.notify = NULL;
+ // This is typically a cryptographically-secure token generated when the private fingerprint
+ // template database is created. For simplicity of this driver, we store a recognizable value.
+ //
+ // Real devices should *not* use this token!
dev->authenticator_id = 0xdeadbeef;
pthread_mutex_init(&dev->lock, NULL);