Merge "Include operation handle in OperationResult"
diff --git a/keystore/.clang-format b/keystore/.clang-format
new file mode 100644
index 0000000..5747e19
--- /dev/null
+++ b/keystore/.clang-format
@@ -0,0 +1,10 @@
+BasedOnStyle: LLVM
+IndentWidth: 4
+UseTab: Never
+BreakBeforeBraces: Attach
+AllowShortFunctionsOnASingleLine: Inline
+AllowShortIfStatementsOnASingleLine: false
+IndentCaseLabels: false
+ColumnLimit: 100
+PointerBindsToType: true
+SpacesBeforeTrailingComments: 2
diff --git a/keystore/Android.mk b/keystore/Android.mk
index 8bf2852..3480123 100644
--- a/keystore/Android.mk
+++ b/keystore/Android.mk
@@ -65,3 +65,18 @@
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
 LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
 include $(BUILD_SHARED_LIBRARY)
+
+# Library for unit tests
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_CFLAGS := -Wall -Wextra -Werror
+LOCAL_SRC_FILES := auth_token_table.cpp
+LOCAL_MODULE := libkeystore_test
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libgtest_main
+LOCAL_SHARED_LIBRARIES := libkeymaster_messages
+LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)/include
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_STATIC_LIBRARY)
diff --git a/keystore/IKeystoreService.cpp b/keystore/IKeystoreService.cpp
index adde6b2..d3cec50 100644
--- a/keystore/IKeystoreService.cpp
+++ b/keystore/IKeystoreService.cpp
@@ -312,6 +312,24 @@
     return true;
 }
 
+/**
+ * Read a byte array from in. The data at *data is still owned by the parcel
+ */
+static void readByteArray(const Parcel& in, const uint8_t** data, size_t* length) {
+    ssize_t slength = in.readInt32();
+    if (slength > 0) {
+        *data = reinterpret_cast<const uint8_t*>(in.readInplace(slength));
+        if (*data) {
+            *length = static_cast<size_t>(slength);
+        } else {
+            *length = 0;
+        }
+    } else {
+        *data = NULL;
+        *length = 0;
+    }
+}
+
 // Read a keymaster_key_param_t* from a Parcel for use in a
 // keymaster_key_characteristics_t. This will be free'd by calling
 // keymaster_free_key_characteristics.
@@ -345,12 +363,18 @@
     return NULL;
 }
 
-static void readKeymasterBlob(const Parcel& in, keymaster_blob_t* blob) {
+static std::unique_ptr<keymaster_blob_t> readKeymasterBlob(const Parcel& in) {
+    std::unique_ptr<keymaster_blob_t> blob;
+    if (in.readInt32() != 1) {
+        blob.reset(NULL);
+        return blob;
+    }
     ssize_t length = in.readInt32();
+    blob.reset(new keymaster_blob_t);
     if (length > 0) {
-        blob->data = (uint8_t*) in.readInplace(length);
+        blob->data = reinterpret_cast<const uint8_t*>(in.readInplace(length));
         if (blob->data) {
-            blob->data_length = (size_t) length;
+            blob->data_length = static_cast<size_t>(length);
         } else {
             blob->data_length = 0;
         }
@@ -358,6 +382,7 @@
         blob->data = NULL;
         blob->data_length = 0;
     }
+    return blob;
 }
 
 class BpKeystoreService: public BpInterface<IKeystoreService>
@@ -959,7 +984,6 @@
     {
         Parcel data, reply;
         data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
-        data.writeInt32(bufLength);
         data.writeByteArray(bufLength, buf);
         status_t status = remote()->transact(BnKeystoreService::ADD_RNG_ENTROPY, data, &reply);
         if (status != NO_ERROR) {
@@ -976,13 +1000,15 @@
     };
 
     virtual int32_t generateKey(const String16& name, const KeymasterArguments& params,
-                                int uid, int flags, KeyCharacteristics* outCharacteristics)
+                                const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+                                KeyCharacteristics* outCharacteristics)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
         data.writeString16(name);
         data.writeInt32(1);
         params.writeToParcel(&data);
+        data.writeByteArray(entropyLength, entropy);
         data.writeInt32(uid);
         data.writeInt32(flags);
         status_t status = remote()->transact(BnKeystoreService::GENERATE_KEY, data, &reply);
@@ -1002,15 +1028,23 @@
         return ret;
     }
     virtual int32_t getKeyCharacteristics(const String16& name,
-                                          const keymaster_blob_t& clientId,
-                                          const keymaster_blob_t& appData,
+                                          const keymaster_blob_t* clientId,
+                                          const keymaster_blob_t* appData,
                                           KeyCharacteristics* outCharacteristics)
     {
         Parcel data, reply;
         data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
         data.writeString16(name);
-        data.writeByteArray(clientId.data_length, clientId.data);
-        data.writeByteArray(appData.data_length, appData.data);
+        if (clientId) {
+            data.writeByteArray(clientId->data_length, clientId->data);
+        } else {
+            data.writeInt32(-1);
+        }
+        if (appData) {
+            data.writeByteArray(appData->data_length, appData->data);
+        } else {
+            data.writeInt32(-1);
+        }
         status_t status = remote()->transact(BnKeystoreService::GET_KEY_CHARACTERISTICS,
                                              data, &reply);
         if (status != NO_ERROR) {
@@ -1060,8 +1094,8 @@
     }
 
     virtual void exportKey(const String16& name, keymaster_key_format_t format,
-                           const keymaster_blob_t& clientId,
-                           const keymaster_blob_t& appData, ExportResult* result)
+                           const keymaster_blob_t* clientId,
+                           const keymaster_blob_t* appData, ExportResult* result)
     {
         if (!result) {
             return;
@@ -1071,8 +1105,16 @@
         data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
         data.writeString16(name);
         data.writeInt32(format);
-        data.writeByteArray(clientId.data_length, clientId.data);
-        data.writeByteArray(appData.data_length, appData.data);
+        if (clientId) {
+            data.writeByteArray(clientId->data_length, clientId->data);
+        } else {
+            data.writeInt32(-1);
+        }
+        if (appData) {
+            data.writeByteArray(appData->data_length, appData->data);
+        } else {
+            data.writeInt32(-1);
+        }
         status_t status = remote()->transact(BnKeystoreService::EXPORT_KEY, data, &reply);
         if (status != NO_ERROR) {
             ALOGD("exportKey() could not contact remote: %d\n", status);
@@ -1092,7 +1134,8 @@
 
     virtual void begin(const sp<IBinder>& appToken, const String16& name,
                        keymaster_purpose_t purpose, bool pruneable,
-                       const KeymasterArguments& params, KeymasterArguments* outParams,
+                       const KeymasterArguments& params, const uint8_t* entropy,
+                       size_t entropyLength, KeymasterArguments* outParams,
                        OperationResult* result)
     {
         if (!result || !outParams) {
@@ -1106,6 +1149,7 @@
         data.writeInt32(pruneable ? 1 : 0);
         data.writeInt32(1);
         params.writeToParcel(&data);
+        data.writeByteArray(entropyLength, entropy);
         status_t status = remote()->transact(BnKeystoreService::BEGIN, data, &reply);
         if (status != NO_ERROR) {
             ALOGD("begin() could not contact remote: %d\n", status);
@@ -1189,7 +1233,7 @@
         Parcel data, reply;
         data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
         data.writeStrongBinder(token);
-        status_t status = remote()->transact(BnKeystoreService::FINISH, data, &reply);
+        status_t status = remote()->transact(BnKeystoreService::ABORT, data, &reply);
         if (status != NO_ERROR) {
             ALOGD("abort() could not contact remote: %d\n", status);
             return KM_ERROR_UNKNOWN_ERROR;
@@ -1202,6 +1246,45 @@
         }
         return ret;
     }
+
+    virtual bool isOperationAuthorized(const sp<IBinder>& token)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+        data.writeStrongBinder(token);
+        status_t status = remote()->transact(BnKeystoreService::IS_OPERATION_AUTHORIZED, data,
+                                             &reply);
+        if (status != NO_ERROR) {
+            ALOGD("isOperationAuthorized() could not contact remote: %d\n", status);
+            return false;
+        }
+        int32_t err = reply.readExceptionCode();
+        int32_t ret = reply.readInt32();
+        if (err < 0) {
+            ALOGD("isOperationAuthorized() caught exception %d\n", err);
+            return false;
+        }
+        return ret == 1;
+    }
+
+    virtual int32_t addAuthToken(const uint8_t* token, size_t length)
+    {
+        Parcel data, reply;
+        data.writeInterfaceToken(IKeystoreService::getInterfaceDescriptor());
+        data.writeByteArray(length, token);
+        status_t status = remote()->transact(BnKeystoreService::ADD_AUTH_TOKEN, data, &reply);
+        if (status != NO_ERROR) {
+            ALOGD("addAuthToken() could not contact remote: %d\n", status);
+            return -1;
+        }
+        int32_t err = reply.readExceptionCode();
+        int32_t ret = reply.readInt32();
+        if (err < 0) {
+            ALOGD("addAuthToken() caught exception %d\n", err);
+            return -1;
+        }
+        return ret;
+    };
 };
 
 IMPLEMENT_META_INTERFACE(KeystoreService, "android.security.IKeystoreService");
@@ -1530,13 +1613,9 @@
         }
         case ADD_RNG_ENTROPY: {
             CHECK_INTERFACE(IKeystoreService, data, reply);
-            size_t size = data.readInt32();
-            uint8_t* bytes;
-            if (size > 0) {
-                bytes = (uint8_t*) data.readInplace(size);
-            } else {
-                bytes = NULL;
-            }
+            const uint8_t* bytes = NULL;
+            size_t size = 0;
+            readByteArray(data, &bytes, &size);
             int32_t ret = addRngEntropy(bytes, size);
             reply->writeNoException();
             reply->writeInt32(ret);
@@ -1549,10 +1628,14 @@
             if (data.readInt32() != 0) {
                 args.readFromParcel(data);
             }
+            const uint8_t* entropy = NULL;
+            size_t entropyLength = 0;
+            readByteArray(data, &entropy, &entropyLength);
             int32_t uid = data.readInt32();
             int32_t flags = data.readInt32();
             KeyCharacteristics outCharacteristics;
-            int32_t ret = generateKey(name, args, uid, flags, &outCharacteristics);
+            int32_t ret = generateKey(name, args, entropy, entropyLength, uid, flags,
+                                      &outCharacteristics);
             reply->writeNoException();
             reply->writeInt32(ret);
             reply->writeInt32(1);
@@ -1562,11 +1645,11 @@
         case GET_KEY_CHARACTERISTICS: {
             CHECK_INTERFACE(IKeystoreService, data, reply);
             String16 name = data.readString16();
-            keymaster_blob_t clientId, appData;
-            readKeymasterBlob(data, &clientId);
-            readKeymasterBlob(data, &appData);
+            std::unique_ptr<keymaster_blob_t> clientId = readKeymasterBlob(data);
+            std::unique_ptr<keymaster_blob_t> appData = readKeymasterBlob(data);
             KeyCharacteristics outCharacteristics;
-            int ret = getKeyCharacteristics(name, clientId, appData, &outCharacteristics);
+            int ret = getKeyCharacteristics(name, clientId.get(), appData.get(),
+                                            &outCharacteristics);
             reply->writeNoException();
             reply->writeInt32(ret);
             reply->writeInt32(1);
@@ -1581,18 +1664,13 @@
                 args.readFromParcel(data);
             }
             keymaster_key_format_t format = static_cast<keymaster_key_format_t>(data.readInt32());
-            uint8_t* keyData = NULL;
-            ssize_t keyLength = data.readInt32();
-            size_t ukeyLength = (size_t) keyLength;
-            if (keyLength >= 0) {
-                keyData = (uint8_t*) data.readInplace(keyLength);
-            } else {
-                ukeyLength = 0;
-            }
+            const uint8_t* keyData = NULL;
+            size_t keyLength = 0;
+            readByteArray(data, &keyData, &keyLength);
             int32_t uid = data.readInt32();
             int32_t flags = data.readInt32();
             KeyCharacteristics outCharacteristics;
-            int32_t ret = importKey(name, args, format, keyData, ukeyLength, uid, flags,
+            int32_t ret = importKey(name, args, format, keyData, keyLength, uid, flags,
                                     &outCharacteristics);
             reply->writeNoException();
             reply->writeInt32(ret);
@@ -1605,11 +1683,10 @@
             CHECK_INTERFACE(IKeystoreService, data, reply);
             String16 name = data.readString16();
             keymaster_key_format_t format = static_cast<keymaster_key_format_t>(data.readInt32());
-            keymaster_blob_t clientId, appData;
-            readKeymasterBlob(data, &clientId);
-            readKeymasterBlob(data, &appData);
+            std::unique_ptr<keymaster_blob_t> clientId = readKeymasterBlob(data);
+            std::unique_ptr<keymaster_blob_t> appData = readKeymasterBlob(data);
             ExportResult result;
-            exportKey(name, format, clientId, appData, &result);
+            exportKey(name, format, clientId.get(), appData.get(), &result);
             reply->writeNoException();
             reply->writeInt32(1);
             result.writeToParcel(reply);
@@ -1626,9 +1703,13 @@
             if (data.readInt32() != 0) {
                 args.readFromParcel(data);
             }
+            const uint8_t* entropy = NULL;
+            size_t entropyLength = 0;
+            readByteArray(data, &entropy, &entropyLength);
             KeymasterArguments outArgs;
             OperationResult result;
-            begin(token, name, purpose, pruneable, args, &outArgs, &result);
+            begin(token, name, purpose, pruneable, args, entropy, entropyLength, &outArgs,
+                  &result);
             reply->writeNoException();
             reply->writeInt32(1);
             result.writeToParcel(reply);
@@ -1644,16 +1725,11 @@
             if (data.readInt32() != 0) {
                 args.readFromParcel(data);
             }
-            uint8_t* buf = NULL;
-            ssize_t bufLength = data.readInt32();
-            size_t ubufLength = (size_t) bufLength;
-            if (bufLength > 0) {
-                buf = (uint8_t*) data.readInplace(ubufLength);
-            } else {
-                ubufLength = 0;
-            }
+            const uint8_t* buf = NULL;
+            size_t bufLength = 0;
+            readByteArray(data, &buf, &bufLength);
             OperationResult result;
-            update(token, args, buf, ubufLength, &result);
+            update(token, args, buf, bufLength, &result);
             reply->writeNoException();
             reply->writeInt32(1);
             result.writeToParcel(reply);
@@ -1667,16 +1743,11 @@
             if (data.readInt32() != 0) {
                 args.readFromParcel(data);
             }
-            uint8_t* buf = NULL;
-            ssize_t bufLength = data.readInt32();
-            size_t ubufLength = (size_t) bufLength;
-            if (bufLength > 0) {
-                buf = (uint8_t*) data.readInplace(ubufLength);
-            } else {
-                ubufLength = 0;
-            }
+            const uint8_t* buf = NULL;
+            size_t bufLength = 0;
+            readByteArray(data, &buf, &bufLength);
             OperationResult result;
-            finish(token, args, buf, ubufLength, &result);
+            finish(token, args, buf, bufLength, &result);
             reply->writeNoException();
             reply->writeInt32(1);
             result.writeToParcel(reply);
@@ -1692,6 +1763,27 @@
 
             return NO_ERROR;
         }
+        case IS_OPERATION_AUTHORIZED: {
+            CHECK_INTERFACE(IKeystoreService, data, reply);
+            sp<IBinder> token = data.readStrongBinder();
+            bool result = isOperationAuthorized(token);
+            reply->writeNoException();
+            reply->writeInt32(result ? 1 : 0);
+
+            return NO_ERROR;
+        }
+        case ADD_AUTH_TOKEN: {
+            CHECK_INTERFACE(IKeystoreService, data, reply);
+            sp<IBinder> token = data.readStrongBinder();
+            const uint8_t* token_bytes = NULL;
+            size_t size = 0;
+            readByteArray(data, &token_bytes, &size);
+            int32_t result = addAuthToken(token_bytes, size);
+            reply->writeNoException();
+            reply->writeInt32(result);
+
+            return NO_ERROR;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/keystore/auth_token_table.cpp b/keystore/auth_token_table.cpp
new file mode 100644
index 0000000..2ae10a0
--- /dev/null
+++ b/keystore/auth_token_table.cpp
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "auth_token_table.h"
+
+#include <assert.h>
+#include <time.h>
+
+#include <algorithm>
+
+#include <keymaster/google_keymaster_utils.h>
+#include <keymaster/logger.h>
+
+namespace keymaster {
+
+//
+// Some trivial template wrappers around std algorithms, so they take containers not ranges.
+//
+template <typename Container, typename Predicate>
+typename Container::iterator find_if(Container& container, Predicate pred) {
+    return std::find_if(container.begin(), container.end(), pred);
+}
+
+template <typename Container, typename Predicate>
+typename Container::iterator remove_if(Container& container, Predicate pred) {
+    return std::remove_if(container.begin(), container.end(), pred);
+}
+
+template <typename Container> typename Container::iterator min_element(Container& container) {
+    return std::min_element(container.begin(), container.end());
+}
+
+time_t clock_gettime_raw() {
+    struct timespec time;
+    clock_gettime(CLOCK_MONOTONIC_RAW, &time);
+    return time.tv_sec;
+}
+
+void AuthTokenTable::AddAuthenticationToken(const hw_auth_token_t* auth_token) {
+    Entry new_entry(auth_token, clock_function_());
+    RemoveEntriesSupersededBy(new_entry);
+    if (entries_.size() >= max_entries_) {
+        LOG_W("Auth token table filled up; replacing oldest entry", 0);
+        *min_element(entries_) = std::move(new_entry);
+    } else {
+        entries_.push_back(std::move(new_entry));
+    }
+}
+
+inline bool KeyRequiresAuthentication(const AuthorizationSet& key_info) {
+    return key_info.find(TAG_NO_AUTH_REQUIRED) == -1;
+}
+
+inline bool KeyRequiresAuthPerOperation(const AuthorizationSet& key_info) {
+    return key_info.find(TAG_AUTH_TIMEOUT) == -1;
+}
+
+AuthTokenTable::Error AuthTokenTable::FindAuthorization(const AuthorizationSet& key_info,
+                                                        keymaster_operation_handle_t op_handle,
+                                                        const hw_auth_token_t** found) {
+    if (!KeyRequiresAuthentication(key_info))
+        return AUTH_NOT_REQUIRED;
+
+    hw_authenticator_type_t auth_type = HW_AUTH_NONE;
+    key_info.GetTagValue(TAG_USER_AUTH_TYPE, &auth_type);
+
+    std::vector<uint64_t> key_sids;
+    ExtractSids(key_info, &key_sids);
+
+    if (KeyRequiresAuthPerOperation(key_info))
+        return FindAuthPerOpAuthorization(key_sids, auth_type, op_handle, found);
+    else
+        return FindTimedAuthorization(key_sids, auth_type, key_info, found);
+}
+
+AuthTokenTable::Error AuthTokenTable::FindAuthPerOpAuthorization(
+    const std::vector<uint64_t>& sids, hw_authenticator_type_t auth_type,
+    keymaster_operation_handle_t op_handle, const hw_auth_token_t** found) {
+    if (op_handle == 0)
+        return OP_HANDLE_REQUIRED;
+
+    auto matching_op = find_if(
+        entries_, [&](Entry& e) { return e.token()->challenge == op_handle && !e.completed(); });
+
+    if (matching_op == entries_.end())
+        return AUTH_TOKEN_NOT_FOUND;
+
+    if (!matching_op->SatisfiesAuth(sids, auth_type))
+        return AUTH_TOKEN_WRONG_SID;
+
+    *found = matching_op->token();
+    return OK;
+}
+
+AuthTokenTable::Error AuthTokenTable::FindTimedAuthorization(const std::vector<uint64_t>& sids,
+                                                             hw_authenticator_type_t auth_type,
+                                                             const AuthorizationSet& key_info,
+                                                             const hw_auth_token_t** found) {
+    Entry* newest_match = NULL;
+    for (auto& entry : entries_)
+        if (entry.SatisfiesAuth(sids, auth_type) && entry.is_newer_than(newest_match))
+            newest_match = &entry;
+
+    if (!newest_match)
+        return AUTH_TOKEN_NOT_FOUND;
+
+    uint32_t timeout;
+    key_info.GetTagValue(TAG_AUTH_TIMEOUT, &timeout);
+    time_t now = clock_function_();
+    if (static_cast<int64_t>(newest_match->time_received()) + timeout < static_cast<int64_t>(now))
+        return AUTH_TOKEN_EXPIRED;
+
+    newest_match->UpdateLastUse(now);
+    *found = newest_match->token();
+    return OK;
+}
+
+void AuthTokenTable::ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids) {
+    assert(sids);
+    for (auto& param : key_info)
+        if (param.tag == TAG_USER_SECURE_ID)
+            sids->push_back(param.long_integer);
+}
+
+void AuthTokenTable::RemoveEntriesSupersededBy(const Entry& entry) {
+    entries_.erase(remove_if(entries_, [&](Entry& e) { return entry.Supersedes(e); }),
+                   entries_.end());
+}
+
+bool AuthTokenTable::IsSupersededBySomeEntry(const Entry& entry) {
+    return std::any_of(entries_.begin(), entries_.end(),
+                       [&](Entry& e) { return e.Supersedes(entry); });
+}
+
+void AuthTokenTable::MarkCompleted(const keymaster_operation_handle_t op_handle) {
+    auto found = find_if(entries_, [&](Entry& e) { return e.token()->challenge == op_handle; });
+    if (found == entries_.end())
+        return;
+
+    assert(!IsSupersededBySomeEntry(*found));
+    found->mark_completed();
+
+    if (IsSupersededBySomeEntry(*found))
+        entries_.erase(found);
+}
+
+AuthTokenTable::Entry::Entry(const hw_auth_token_t* token, time_t current_time)
+    : token_(token), time_received_(current_time), last_use_(current_time),
+      operation_completed_(token_->challenge == 0) {
+}
+
+uint32_t AuthTokenTable::Entry::timestamp_host_order() const {
+    return ntoh(token_->timestamp);
+}
+
+hw_authenticator_type_t AuthTokenTable::Entry::authenticator_type() const {
+    hw_authenticator_type_t result = static_cast<hw_authenticator_type_t>(
+        ntoh(static_cast<uint32_t>(token_->authenticator_type)));
+    return result;
+}
+
+bool AuthTokenTable::Entry::SatisfiesAuth(const std::vector<uint64_t>& sids,
+                                          hw_authenticator_type_t auth_type) {
+    for (auto sid : sids)
+        if ((sid == token_->authenticator_id) ||
+            (sid == token_->user_id && (auth_type & authenticator_type()) != 0))
+            return true;
+    return false;
+}
+
+void AuthTokenTable::Entry::UpdateLastUse(time_t time) {
+    this->last_use_ = time;
+}
+
+bool AuthTokenTable::Entry::Supersedes(const Entry& entry) const {
+    if (!entry.completed())
+        return false;
+
+    return (token_->user_id == entry.token_->user_id &&
+            token_->authenticator_type == entry.token_->authenticator_type &&
+            token_->authenticator_type == entry.token_->authenticator_type &&
+            timestamp_host_order() > entry.timestamp_host_order());
+}
+
+}  // namespace keymaster
diff --git a/keystore/auth_token_table.h b/keystore/auth_token_table.h
new file mode 100644
index 0000000..d1184e9
--- /dev/null
+++ b/keystore/auth_token_table.h
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <memory>
+#include <vector>
+
+#include <hardware/hw_auth_token.h>
+#include <keymaster/authorization_set.h>
+
+#ifndef SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H
+#define SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H
+
+namespace keymaster {
+
+namespace test {
+class AuthTokenTableTest;
+}  // namespace test
+
+time_t clock_gettime_raw();
+
+/**
+ * AuthTokenTable manages a set of received authorization tokens and can provide the appropriate
+ * token for authorizing a key operation.
+ *
+ * To keep the table from growing without bound, superseded entries are removed when possible, and
+ * least recently used entries are automatically pruned when when the table exceeds a size limit,
+ * which is expected to be relatively small, since the implementation uses a linear search.
+ */
+class AuthTokenTable {
+  public:
+    AuthTokenTable(size_t max_entries = 32, time_t (*clock_function)() = clock_gettime_raw)
+        : max_entries_(max_entries), clock_function_(clock_function) {}
+
+    enum Error {
+        OK,
+        AUTH_NOT_REQUIRED = -1,
+        AUTH_TOKEN_EXPIRED = -2,    // Found a matching token, but it's too old.
+        AUTH_TOKEN_WRONG_SID = -3,  // Found a token with the right challenge, but wrong SID.  This
+                                    // most likely indicates that the authenticator was updated
+                                    // (e.g. new fingerprint enrolled).
+        OP_HANDLE_REQUIRED = -4,    // The key requires auth per use but op_handle was zero.
+        AUTH_TOKEN_NOT_FOUND = -5,
+    };
+
+    /**
+     * Add an authorization token to the table.  The table takes ownership of the argument.
+     */
+    void AddAuthenticationToken(const hw_auth_token_t* token);
+
+    /**
+     * Find an authorization token that authorizes the operation specified by \p operation_handle on
+     * a key with the characteristics specified in \p key_info.
+     *
+     * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info
+     * and m is the number of entries in the table.  It could be made better, but n and m should
+     * always be small.
+     *
+     * The table retains ownership of the returned object.
+     */
+    Error FindAuthorization(const AuthorizationSet& key_info,
+                            keymaster_operation_handle_t op_handle, const hw_auth_token_t** found);
+
+    /**
+     * Find an authorization token that authorizes the operation specified by \p operation_handle on
+     * a key with the characteristics specified in \p key_info.
+     *
+     * This method is O(n * m), where n is the number of KM_TAG_USER_SECURE_ID entries in key_info
+     * and m is the number of entries in the table.  It could be made better, but n and m should
+     * always be small.
+     *
+     * The table retains ownership of the returned object.
+     */
+    Error FindAuthorization(const keymaster_key_param_t* params, size_t params_count,
+                            keymaster_operation_handle_t op_handle, const hw_auth_token_t** found) {
+        return FindAuthorization(AuthorizationSet(params, params_count), op_handle, found);
+    }
+
+    /**
+     * Mark operation completed.  This allows tokens associated with the specified operation to be
+     * superseded by new tokens.
+     */
+    void MarkCompleted(const keymaster_operation_handle_t op_handle);
+
+    size_t size() { return entries_.size(); }
+
+  private:
+    friend class AuthTokenTableTest;
+
+    class Entry {
+      public:
+        Entry(const hw_auth_token_t* token, time_t current_time);
+        Entry(Entry&& entry) { *this = std::move(entry); }
+
+        void operator=(Entry&& rhs) {
+            token_ = std::move(rhs.token_);
+            time_received_ = rhs.time_received_;
+            last_use_ = rhs.last_use_;
+            operation_completed_ = rhs.operation_completed_;
+        }
+
+        bool operator<(const Entry& rhs) const { return last_use_ < rhs.last_use_; }
+
+        void UpdateLastUse(time_t time);
+
+        bool Supersedes(const Entry& entry) const;
+        bool SatisfiesAuth(const std::vector<uint64_t>& sids, hw_authenticator_type_t auth_type);
+
+        bool is_newer_than(const Entry* entry) {
+            if (!entry)
+                return true;
+            return timestamp_host_order() > entry->timestamp_host_order();
+        }
+
+        void mark_completed() { operation_completed_ = true; }
+
+        const hw_auth_token_t* token() { return token_.get(); }
+        time_t time_received() const { return time_received_; }
+        bool completed() const { return operation_completed_; }
+        uint32_t timestamp_host_order() const;
+        hw_authenticator_type_t authenticator_type() const;
+
+      private:
+        std::unique_ptr<const hw_auth_token_t> token_;
+        time_t time_received_;
+        time_t last_use_;
+        bool operation_completed_;
+    };
+
+    Error FindAuthPerOpAuthorization(const std::vector<uint64_t>& sids,
+                                     hw_authenticator_type_t auth_type,
+                                     keymaster_operation_handle_t op_handle,
+                                     const hw_auth_token_t** found);
+    Error FindTimedAuthorization(const std::vector<uint64_t>& sids,
+                                 hw_authenticator_type_t auth_type,
+                                 const AuthorizationSet& key_info, const hw_auth_token_t** found);
+    void ExtractSids(const AuthorizationSet& key_info, std::vector<uint64_t>* sids);
+    void RemoveEntriesSupersededBy(const Entry& entry);
+    bool IsSupersededBySomeEntry(const Entry& entry);
+
+    std::vector<Entry> entries_;
+    size_t max_entries_;
+    time_t (*clock_function_)();
+};
+
+}  // namespace keymaster
+
+#endif  // SYSTEM_KEYMASTER_AUTH_TOKEN_TABLE_H
diff --git a/keystore/include/keystore/IKeystoreService.h b/keystore/include/keystore/IKeystoreService.h
index cbdab83..8b5b5d3 100644
--- a/keystore/include/keystore/IKeystoreService.h
+++ b/keystore/include/keystore/IKeystoreService.h
@@ -133,6 +133,8 @@
         UPDATE = IBinder::FIRST_CALL_TRANSACTION + 32,
         FINISH = IBinder::FIRST_CALL_TRANSACTION + 33,
         ABORT = IBinder::FIRST_CALL_TRANSACTION + 34,
+        IS_OPERATION_AUTHORIZED = IBinder::FIRST_CALL_TRANSACTION + 35,
+        ADD_AUTH_TOKEN = IBinder::FIRST_CALL_TRANSACTION + 36,
     };
 
     DECLARE_META_INTERFACE(KeystoreService);
@@ -198,23 +200,27 @@
     virtual int32_t addRngEntropy(const uint8_t* data, size_t dataLength) = 0;
 
     virtual int32_t generateKey(const String16& name, const KeymasterArguments& params,
-                                int uid, int flags, KeyCharacteristics* outCharacteristics) = 0;
+                                const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+                                KeyCharacteristics* outCharacteristics) = 0;
+
     virtual int32_t getKeyCharacteristics(const String16& name,
-                                          const keymaster_blob_t& clientId,
-                                          const keymaster_blob_t& appData,
+                                          const keymaster_blob_t* clientId,
+                                          const keymaster_blob_t* appData,
                                           KeyCharacteristics* outCharacteristics) = 0;
+
     virtual int32_t importKey(const String16& name, const KeymasterArguments&  params,
                               keymaster_key_format_t format, const uint8_t *keyData,
                               size_t keyLength, int uid, int flags,
                               KeyCharacteristics* outCharacteristics) = 0;
 
     virtual void exportKey(const String16& name, keymaster_key_format_t format,
-                           const keymaster_blob_t& clientId,
-                           const keymaster_blob_t& appData, ExportResult* result) = 0;
+                           const keymaster_blob_t* clientId,
+                           const keymaster_blob_t* appData, ExportResult* result) = 0;
 
     virtual void begin(const sp<IBinder>& apptoken, const String16& name,
                        keymaster_purpose_t purpose, bool pruneable,
-                       const KeymasterArguments& params, KeymasterArguments* outParams,
+                       const KeymasterArguments& params, const uint8_t* entropy,
+                       size_t entropyLength, KeymasterArguments* outParams,
                        OperationResult* result) = 0;
 
     virtual void update(const sp<IBinder>& token, const KeymasterArguments& params,
@@ -226,6 +232,10 @@
 
     virtual int32_t abort(const sp<IBinder>& handle) = 0;
 
+    virtual bool isOperationAuthorized(const sp<IBinder>& handle) = 0;
+
+    virtual int32_t addAuthToken(const uint8_t* token, size_t length) = 0;
+
 };
 
 // ----------------------------------------------------------------------------
diff --git a/keystore/keystore.cpp b/keystore/keystore.cpp
index 560b261..5997cd5 100644
--- a/keystore/keystore.cpp
+++ b/keystore/keystore.cpp
@@ -2469,7 +2469,8 @@
     }
 
     int32_t generateKey(const String16& name, const KeymasterArguments& params,
-                        int uid, int flags, KeyCharacteristics* outCharacteristics) {
+                        const uint8_t* entropy, size_t entropyLength, int uid, int flags,
+                        KeyCharacteristics* outCharacteristics) {
         uid_t callingUid = IPCThreadState::self()->getCallingUid();
         pid_t callingPid = IPCThreadState::self()->getCallingPid();
         if (!has_permission(callingUid, P_INSERT, callingPid)) {
@@ -2499,19 +2500,37 @@
         if (device == NULL) {
             return ::SYSTEM_ERROR;
         }
-        // TODO: Seed from Linux RNG either before this or periodically
+        // TODO: Seed from Linux RNG before this.
         if (device->common.module->module_api_version >= KEYMASTER_MODULE_API_VERSION_1_0 &&
                 device->generate_key != NULL) {
-            rc = device->generate_key(device, params.params.data(), params.params.size(), &blob,
-                                      &out);
+            if (!entropy) {
+                rc = KM_ERROR_OK;
+            } else if (device->add_rng_entropy) {
+                rc = device->add_rng_entropy(device, entropy, entropyLength);
+            } else {
+                rc = KM_ERROR_UNIMPLEMENTED;
+            }
+            if (rc == KM_ERROR_OK) {
+                rc = device->generate_key(device, params.params.data(), params.params.size(),
+                                          &blob, &out);
+            }
         }
         // If the HW device didn't support generate_key or generate_key failed
         // fall back to the software implementation.
         if (rc && fallback->generate_key != NULL) {
             isFallback = true;
-            rc = fallback->generate_key(fallback, params.params.data(), params.params.size(),
-                                      &blob,
-                                      &out);
+            if (!entropy) {
+                rc = KM_ERROR_OK;
+            } else if (fallback->add_rng_entropy) {
+                rc = fallback->add_rng_entropy(fallback, entropy, entropyLength);
+            } else {
+                rc = KM_ERROR_UNIMPLEMENTED;
+            }
+            if (rc == KM_ERROR_OK) {
+                rc = fallback->generate_key(fallback, params.params.data(), params.params.size(),
+                                            &blob,
+                                            &out);
+            }
         }
 
         if (out) {
@@ -2540,8 +2559,8 @@
     }
 
     int32_t getKeyCharacteristics(const String16& name,
-                                  const keymaster_blob_t& clientId,
-                                  const keymaster_blob_t& appData,
+                                  const keymaster_blob_t* clientId,
+                                  const keymaster_blob_t* appData,
                                   KeyCharacteristics* outCharacteristics) {
 
         if (!outCharacteristics) {
@@ -2568,7 +2587,7 @@
             ALOGW("device does not implement get_key_characteristics");
             return KM_ERROR_UNIMPLEMENTED;
         }
-        rc = dev->get_key_characteristics(dev, &key, &clientId, &appData, &out);
+        rc = dev->get_key_characteristics(dev, &key, clientId, appData, &out);
         if (out) {
             outCharacteristics->characteristics = *out;
             free(out);
@@ -2645,8 +2664,8 @@
     }
 
     void exportKey(const String16& name, keymaster_key_format_t format,
-                           const keymaster_blob_t& clientId,
-                           const keymaster_blob_t& appData, ExportResult* result) {
+                           const keymaster_blob_t* clientId,
+                           const keymaster_blob_t* appData, ExportResult* result) {
 
         uid_t callingUid = IPCThreadState::self()->getCallingUid();
 
@@ -2669,15 +2688,15 @@
             return;
         }
         uint8_t* ptr = NULL;
-        rc = dev->export_key(dev, format, &key, &clientId, &appData,
+        rc = dev->export_key(dev, format, &key, clientId, appData,
                                              &ptr, &result->dataLength);
         result->exportData.reset(ptr);
         result->resultCode = rc ? rc : ::NO_ERROR;
     }
 
     void begin(const sp<IBinder>& appToken, const String16& name, keymaster_purpose_t purpose,
-               bool pruneable, const KeymasterArguments& params,
-               KeymasterArguments* outParams, OperationResult* result) {
+               bool pruneable, const KeymasterArguments& params, const uint8_t* entropy,
+               size_t entropyLength, KeymasterArguments* outParams, OperationResult* result) {
         if (!result || !outParams) {
             ALOGE("Unexpected null arguments to begin()");
             return;
@@ -2703,10 +2722,22 @@
         size_t outSize;
         keymaster_operation_handle_t handle;
         keymaster1_device_t* dev = mKeyStore->getDeviceForBlob(keyBlob);
+        keymaster_error_t err = KM_ERROR_UNIMPLEMENTED;
+        // Add entropy to the device first.
+        if (entropy) {
+            if (dev->add_rng_entropy) {
+                err = dev->add_rng_entropy(dev, entropy, entropyLength);
+            } else {
+                err = KM_ERROR_UNIMPLEMENTED;
+            }
+            if (err) {
+                result->resultCode = err;
+                return;
+            }
+        }
         // TODO: Check authorization.
-        keymaster_error_t err = dev->begin(dev, purpose, &key, params.params.data(),
-                                           params.params.size(), &out, &outSize,
-                                           &handle);
+        err = dev->begin(dev, purpose, &key, params.params.data(), params.params.size(), &out,
+                         &outSize, &handle);
 
         // If there are too many operations abort the oldest operation that was
         // started as pruneable and try again.
@@ -2794,6 +2825,20 @@
         return ::NO_ERROR;
     }
 
+    bool isOperationAuthorized(const sp<IBinder>& token) {
+        const keymaster1_device_t* dev;
+        keymaster_operation_handle_t handle;
+        if(!mOperationMap.getOperation(token, &handle, &dev)) {
+            return false;
+        }
+        // TODO: Check authorization.
+        return true;
+    }
+
+    int32_t addAuthToken(const uint8_t* /*token*/, size_t /*length*/) {
+        return KM_ERROR_UNIMPLEMENTED;
+    }
+
 private:
     inline bool isKeystoreUnlocked(State state) {
         switch (state) {
diff --git a/keystore/tests/Android.mk b/keystore/tests/Android.mk
new file mode 100644
index 0000000..be8c426
--- /dev/null
+++ b/keystore/tests/Android.mk
@@ -0,0 +1,31 @@
+#
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# Unit test for AuthTokenTable
+include $(CLEAR_VARS)
+ifeq ($(USE_32_BIT_KEYSTORE), true)
+LOCAL_MULTILIB := 32
+endif
+LOCAL_CFLAGS := -Wall -Wextra -Werror
+LOCAL_SRC_FILES := auth_token_table_test.cpp
+LOCAL_MODULE := auth_token_table_test
+LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
+LOCAL_STATIC_LIBRARIES := libgtest_main libkeystore_test
+LOCAL_SHARED_LIBRARIES := libkeymaster_messages
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/Android.mk
+include $(BUILD_NATIVE_TEST)
diff --git a/keystore/tests/Makefile b/keystore/tests/Makefile
new file mode 100644
index 0000000..5c1117f
--- /dev/null
+++ b/keystore/tests/Makefile
@@ -0,0 +1,116 @@
+##########
+# This makefile builds local unit tests that run locally on the development machine.  Note
+# that it may be necessary to install some libraries on the dev maching to make the tests
+# build.
+#
+# The same unit tests are also built by Android.mk to run on the target device.  The tests
+# should always build and pass in both places.  The on-device test is what really matters,
+# of course, but debugging is often somewhat easier on the dev platform.
+##########
+
+BASE=../../../..
+SUBS=system/core \
+	system/keymaster\
+	hardware/libhardware \
+	external/gtest
+GTEST=$(BASE)/external/gtest
+KEYMASTER=$(BASE)/system/keymaster
+
+INCLUDES=$(foreach dir,$(SUBS),-I $(BASE)/$(dir)/include) \
+	-I $(BASE)/libnativehelper/include/nativehelper \
+	-I $(GTEST) -Iinclude
+
+# Add USE_CLANG=1 to the make command line to build with clang, which has better error
+# reporting and diagnoses some conditions that GCC doesn't.
+ifdef USE_CLANG
+CC=/usr/bin/clang
+CXX=/usr/bin/clang
+CLANG_TEST_DEFINE=-DKEYMASTER_CLANG_TEST_BUILD
+COMPILER_SPECIFIC_ARGS=-std=c++11 $(CLANG_TEST_DEFINE)
+else
+COMPILER_SPECIFIC_ARGS=-std=c++0x -fprofile-arcs
+endif
+
+CPPFLAGS=$(INCLUDES) -g -O0 -MD
+CXXFLAGS=-Wall -Werror -Wno-unused -Winit-self -Wpointer-arith	-Wunused-parameter \
+        -Werror=sign-compare -Wmissing-declarations -ftest-coverage -fno-permissive \
+	-Wno-deprecated-declarations -fno-exceptions -DKEYMASTER_NAME_TAGS \
+	$(COMPILER_SPECIFIC_ARGS)
+
+# Uncomment to enable debug logging.
+# CXXFLAGS += -DDEBUG
+
+LDLIBS=-lpthread -lstdc++ -lgcov
+
+# This list of sources is used for dependency generation and cleanup.  Add each new source
+# file here (not headers).
+CPPSRCS=\
+	../auth_token_table.cpp \
+	auth_token_table_test.cpp
+
+# This list of binaries determes what gets built and run.  Add each new test binary here.
+BINARIES=\
+	auth_token_table_test
+
+.PHONY: coverage memcheck massif clean run
+
+%.run: %
+	./$<
+	touch $@
+
+run: $(BINARIES:=.run)
+
+auth_token_table_test: auth_token_table_test.o \
+	../auth_token_table.o \
+	$(GTEST)/src/gtest-all.o \
+	$(KEYMASTER)/authorization_set.o \
+	$(KEYMASTER)/logger.o \
+	$(KEYMASTER)/serializable.o
+
+coverage: coverage.info
+	genhtml coverage.info --output-directory coverage
+
+coverage.info: run
+	lcov --capture --directory=. --directory=.. -b . --output-file coverage.info
+
+%.coverage : %
+	$(MAKE) clean && $(MAKE) $<
+	./$<
+	lcov --capture --directory=. --output-file coverage.info
+	genhtml coverage.info --output-directory coverage
+#UNINIT_OPTS=--track-origins=yes
+UNINIT_OPTS=--undef-value-errors=no
+
+MEMCHECK_OPTS=--leak-check=full \
+	--show-reachable=yes \
+	--vgdb=full \
+	$(UNINIT_OPTS) \
+	--error-exitcode=1
+
+MASSIF_OPTS=--tool=massif \
+	--stacks=yes
+
+%.memcheck : %
+	valgrind $(MEMCHECK_OPTS) ./$< && \
+	touch $@
+
+%.massif : %
+	valgrind $(MASSIF_OPTS) --massif-out-file=$@ ./$<
+
+memcheck: $(BINARIES:=.memcheck)
+
+massif: $(BINARIES:=.massif)
+
+OBJS=$(CPPSRCS:.cpp=.o)
+DEPS=$(CPPSRCS:.cpp=.d)
+GCOV=$(CPPSRCS:.cpp=.gcov) $(CPPSRCS:.cpp=.gcda) $(CPPSRCS:.cpp=.gcno)
+
+clean:
+	rm -f $(OBJS) $(DEPS) $(BINARIES) $(GCOV) \
+		$(BINARIES:=.run) $(BINARIES:=.memcheck) $(BINARIES:=.massif) \
+		*gcov *gcno *gcda coverage.info
+	rm -rf coverage
+
+-include $(CPPSRCS:.cpp=.d)
+-include $(CCSRCS:.cc=.d)
+
diff --git a/keystore/tests/auth_token_table_test.cpp b/keystore/tests/auth_token_table_test.cpp
new file mode 100644
index 0000000..0f74e77
--- /dev/null
+++ b/keystore/tests/auth_token_table_test.cpp
@@ -0,0 +1,391 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gtest/gtest.h>
+
+#include <keymaster/google_keymaster_utils.h>
+#include <keymaster/logger.h>
+
+#include "../auth_token_table.h"
+
+using std::vector;
+
+int main(int argc, char** argv) {
+    ::testing::InitGoogleTest(&argc, argv);
+    int result = RUN_ALL_TESTS();
+}
+
+inline bool operator==(const hw_auth_token_t& a, const hw_auth_token_t& b) {
+    return (memcmp(&a, &b, sizeof(a)) == 0);
+}
+
+namespace keymaster {
+namespace test {
+
+class StdoutLogger : public Logger {
+  public:
+    StdoutLogger() { set_instance(this); }
+
+    int log_msg(LogLevel level, const char* fmt, va_list args) const {
+        int output_len = 0;
+        switch (level) {
+        case DEBUG_LVL:
+            output_len = printf("DEBUG: ");
+            break;
+        case INFO_LVL:
+            output_len = printf("INFO: ");
+            break;
+        case WARNING_LVL:
+            output_len = printf("WARNING: ");
+            break;
+        case ERROR_LVL:
+            output_len = printf("ERROR: ");
+            break;
+        case SEVERE_LVL:
+            output_len = printf("SEVERE: ");
+            break;
+        }
+
+        output_len += vprintf(fmt, args);
+        output_len += printf("\n");
+        return output_len;
+    }
+};
+
+StdoutLogger logger;
+
+TEST(AuthTokenTableTest, Create) {
+    AuthTokenTable table;
+}
+
+static hw_auth_token_t* make_token(uint64_t rsid, uint64_t ssid = 0, uint64_t challenge = 0,
+                                   uint32_t timestamp = 0) {
+    hw_auth_token_t* token = new hw_auth_token_t;
+    token->user_id = rsid;
+    token->authenticator_id = ssid;
+    token->authenticator_type = hton(static_cast<uint32_t>(HW_AUTH_PASSWORD));
+    token->challenge = challenge;
+    token->timestamp = hton(timestamp);
+    return token;
+}
+
+static AuthorizationSet make_set(uint64_t rsid, uint32_t timeout = 10000) {
+    AuthorizationSetBuilder builder;
+    builder.Authorization(TAG_USER_ID, 10)
+        .Authorization(TAG_USER_AUTH_TYPE, HW_AUTH_PASSWORD)
+        .Authorization(TAG_USER_SECURE_ID, rsid);
+    // Use timeout == 0 to indicate tags that require auth per operation.
+    if (timeout != 0)
+        builder.Authorization(TAG_AUTH_TIMEOUT, timeout);
+    return builder.build();
+}
+
+// Tests obviously run so fast that a real-time clock with a one-second granularity rarely changes
+// output during a test run.  This test clock "ticks" one second every time it's called.
+static time_t monotonic_clock() {
+    static time_t time = 0;
+    return time++;
+}
+
+TEST(AuthTokenTableTest, SimpleAddAndFindTokens) {
+    AuthTokenTable table;
+
+    table.AddAuthenticationToken(make_token(1, 2));
+    table.AddAuthenticationToken(make_token(3, 4));
+    EXPECT_EQ(2U, table.size());
+
+    const hw_auth_token_t* found;
+
+    ASSERT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+    EXPECT_EQ(1U, found->user_id);
+    EXPECT_EQ(2U, found->authenticator_id);
+
+    ASSERT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+    EXPECT_EQ(1U, found->user_id);
+    EXPECT_EQ(2U, found->authenticator_id);
+
+    ASSERT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+    EXPECT_EQ(3U, found->user_id);
+    EXPECT_EQ(4U, found->authenticator_id);
+
+    ASSERT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(4), 0, &found));
+    EXPECT_EQ(3U, found->user_id);
+    EXPECT_EQ(4U, found->authenticator_id);
+
+    ASSERT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(5), 0, &found));
+}
+
+TEST(AuthTokenTableTest, TableOverflow) {
+    AuthTokenTable table(3, monotonic_clock);
+
+    table.AddAuthenticationToken(make_token(1));
+    table.AddAuthenticationToken(make_token(2));
+    table.AddAuthenticationToken(make_token(3));
+
+    const hw_auth_token_t* found;
+
+    // All three should be in the table.
+    EXPECT_EQ(3U, table.size());
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+    table.AddAuthenticationToken(make_token(4));
+
+    // Oldest should be gone.
+    EXPECT_EQ(3U, table.size());
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(1), 0, &found));
+
+    // Others should be there, including the new one (4).  Search for it first, then the others, so
+    // 4 becomes the least recently used.
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(4), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+    table.AddAuthenticationToken(make_token(5));
+
+    // 5 should have replaced 4.
+    EXPECT_EQ(3U, table.size());
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(4), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(5), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+    table.AddAuthenticationToken(make_token(6));
+    table.AddAuthenticationToken(make_token(7));
+
+    // 2 and 5 should be gone
+    EXPECT_EQ(3U, table.size());
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(2), 0, &found));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(5), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(6), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(7), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(3), 0, &found));
+
+    table.AddAuthenticationToken(make_token(8));
+    table.AddAuthenticationToken(make_token(9));
+    table.AddAuthenticationToken(make_token(10));
+
+    // Only the three most recent should be there.
+    EXPECT_EQ(3U, table.size());
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(1), 0, &found));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(2), 0, &found));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(3), 0, &found));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(4), 0, &found));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(5), 0, &found));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(6), 0, &found));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(7), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(8), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(9), 0, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(10), 0, &found));
+}
+
+TEST(AuthTokenTableTest, AuthenticationNotRequired) {
+    AuthTokenTable table;
+    const hw_auth_token_t* found;
+
+    EXPECT_EQ(AuthTokenTable::AUTH_NOT_REQUIRED,
+              table.FindAuthorization(AuthorizationSetBuilder().Authorization(TAG_NO_AUTH_REQUIRED),
+                                      0 /* no challenge */, &found));
+}
+
+TEST(AuthTokenTableTest, OperationHandleNotFound) {
+    AuthTokenTable table;
+    const hw_auth_token_t* found;
+
+    table.AddAuthenticationToken(make_token(1, 0, 1, 5));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(1, 0 /* no timeout */),
+                                      2 /* non-matching challenge */, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1, 0 /* no timeout */),
+                                                          1 /* matching challenge */, &found));
+    table.MarkCompleted(1);
+    EXPECT_EQ(
+        AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+        table.FindAuthorization(make_set(1, 0 /* no timeout */), 1 /* used challenge */, &found));
+}
+
+TEST(AuthTokenTableTest, OperationHandleRequired) {
+    AuthTokenTable table;
+    const hw_auth_token_t* found;
+
+    table.AddAuthenticationToken(make_token(1));
+    EXPECT_EQ(
+        AuthTokenTable::OP_HANDLE_REQUIRED,
+        table.FindAuthorization(make_set(1, 0 /* no timeout */), 0 /* no op handle */, &found));
+}
+
+TEST(AuthTokenTableTest, AuthSidChanged) {
+    AuthTokenTable table;
+    const hw_auth_token_t* found;
+
+    table.AddAuthenticationToken(make_token(1, 3, /* op handle */ 1));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_WRONG_SID,
+              table.FindAuthorization(make_set(2, 0 /* no timeout */), 1 /* op handle */, &found));
+}
+
+TEST(AuthTokenTableTest, TokenExpired) {
+    AuthTokenTable table(5, monotonic_clock);
+    const hw_auth_token_t* found;
+
+    auto key_info = make_set(1, 5 /* five second timeout */);
+
+    // monotonic_clock "ticks" one second each time it's called, which is once per request, so the
+    // sixth request should fail, since key_info says the key is good for five seconds.
+    //
+    // Note that this tests the decision of the AuthTokenTable to reject a request it knows is
+    // expired.  An additional check of the secure timestamp (in the token) will be made by
+    // keymaster when the found token is passed to it.
+    table.AddAuthenticationToken(make_token(1, 0));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_EXPIRED,
+              table.FindAuthorization(key_info, 0 /* no op handle */, &found));
+}
+
+TEST(AuthTokenTableTest, MarkNonexistentEntryCompleted) {
+    AuthTokenTable table;
+    // Marking a nonexistent entry completed is ignored.  This test is mainly for code coverage.
+    table.MarkCompleted(1);
+}
+
+TEST(AuthTokenTableTest, SupersededEntries) {
+    AuthTokenTable table;
+    const hw_auth_token_t* found;
+
+    // Add two identical tokens, without challenges.  The second should supersede the first, based
+    // on timestamp (fourth arg to make_token).
+    table.AddAuthenticationToken(make_token(1, 0, 0, 0));
+    table.AddAuthenticationToken(make_token(1, 0, 0, 1));
+    EXPECT_EQ(1U, table.size());
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+    EXPECT_EQ(1U, ntoh(found->timestamp));
+
+    // Add a third token, this with a different RSID.  It should not be superseded.
+    table.AddAuthenticationToken(make_token(2, 0, 0, 2));
+    EXPECT_EQ(2U, table.size());
+
+    // Add two more, superseding each of the two in the table.
+    table.AddAuthenticationToken(make_token(1, 0, 0, 3));
+    table.AddAuthenticationToken(make_token(2, 0, 0, 4));
+    EXPECT_EQ(2U, table.size());
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0, &found));
+    EXPECT_EQ(3U, ntoh(found->timestamp));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0, &found));
+    EXPECT_EQ(4U, ntoh(found->timestamp));
+
+    // Add another, this one with a challenge value.  It should supersede the old one since it is
+    // newer, and matches other than the challenge.
+    table.AddAuthenticationToken(make_token(1, 0, 1, 5));
+    EXPECT_EQ(2U, table.size());
+
+    // And another, also with a challenge.  Because of the challenge values, the one just added
+    // cannot be superseded.
+    table.AddAuthenticationToken(make_token(1, 0, 2, 6));
+    EXPECT_EQ(3U, table.size());
+
+    // Should be able to find each of them, by specifying their challenge, with a key that is not
+    // timed (timed keys don't care about challenges).
+    EXPECT_EQ(AuthTokenTable::OK,
+              table.FindAuthorization(make_set(1, 0 /* no timeout*/), 1 /* challenge */, &found));
+    EXPECT_EQ(5U, ntoh(found->timestamp));
+    EXPECT_EQ(AuthTokenTable::OK,
+              table.FindAuthorization(make_set(1, 0 /* no timeout */), 2 /* challenge */, &found));
+    EXPECT_EQ(6U, ntoh(found->timestamp));
+
+    // Add another, without a challenge, and the same timestamp as the last one.  This new one
+    // actually could be considered already-superseded, but the table doesn't handle that case,
+    // since it seems unlikely to occur in practice.
+    table.AddAuthenticationToken(make_token(1, 0, 0, 6));
+    EXPECT_EQ(4U, table.size());
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+    EXPECT_EQ(6U, ntoh(found->timestamp));
+
+    // Add another without a challenge but an increased timestamp. This should supersede the
+    // previous challenge-free entry.
+    table.AddAuthenticationToken(make_token(1, 0, 0, 7));
+    EXPECT_EQ(4U, table.size());
+    EXPECT_EQ(AuthTokenTable::OK,
+              table.FindAuthorization(make_set(1, 0 /* no timeout */), 2 /* challenge */, &found));
+    EXPECT_EQ(6U, ntoh(found->timestamp));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+    EXPECT_EQ(7U, ntoh(found->timestamp));
+
+    // Mark the entry with challenge 2 as complete.  Since there's a newer challenge-free entry, the
+    // challenge entry will be superseded.
+    table.MarkCompleted(2);
+    EXPECT_EQ(3U, table.size());
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(1, 0 /* no timeout */), 2 /* challenge */, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+    EXPECT_EQ(7U, ntoh(found->timestamp));
+
+    // Add another SID 1 entry with a challenge.  It supersedes the previous SID 1 entry with
+    // no challenge (timestamp 7), but not the one with challenge 1 (timestamp 5).
+    table.AddAuthenticationToken(make_token(1, 0, 3, 8));
+    EXPECT_EQ(3U, table.size());
+
+    EXPECT_EQ(AuthTokenTable::OK,
+              table.FindAuthorization(make_set(1, 0 /* no timeout */), 1 /* challenge */, &found));
+    EXPECT_EQ(5U, ntoh(found->timestamp));
+
+    EXPECT_EQ(AuthTokenTable::OK,
+              table.FindAuthorization(make_set(1, 0 /* no timeout */), 3 /* challenge */, &found));
+    EXPECT_EQ(8U, ntoh(found->timestamp));
+
+    // SID 2 entry is still there.
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(2), 0 /* challenge */, &found));
+    EXPECT_EQ(4U, ntoh(found->timestamp));
+
+    // Mark the entry with challenge 3 as complete.  Since the older challenge 1 entry is
+    // incomplete, nothing is superseded.
+    table.MarkCompleted(3);
+    EXPECT_EQ(3U, table.size());
+
+    EXPECT_EQ(AuthTokenTable::OK,
+              table.FindAuthorization(make_set(1, 0 /* no timeout */), 1 /* challenge */, &found));
+    EXPECT_EQ(5U, ntoh(found->timestamp));
+
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+    EXPECT_EQ(8U, ntoh(found->timestamp));
+
+    // Mark the entry with challenge 1 as complete.  Since there's a newer one (with challenge 3,
+    // completed), the challenge 1 entry is superseded and removed.
+    table.MarkCompleted(1);
+    EXPECT_EQ(2U, table.size());
+    EXPECT_EQ(AuthTokenTable::AUTH_TOKEN_NOT_FOUND,
+              table.FindAuthorization(make_set(1, 0 /* no timeout */), 1 /* challenge */, &found));
+    EXPECT_EQ(AuthTokenTable::OK, table.FindAuthorization(make_set(1), 0 /* challenge */, &found));
+    EXPECT_EQ(8U, ntoh(found->timestamp));
+}
+
+}  // namespace keymaster
+}  // namespace test