OSDN Git Service

Add auth token parsing to IKeymasterDevice.hal
authorShawn Willden <swillden@google.com>
Fri, 17 Feb 2017 19:23:51 +0000 (12:23 -0700)
committerShawn Willden <swillden@google.com>
Sat, 25 Mar 2017 04:22:34 +0000 (22:22 -0600)
Auth tokens have an unfortunate dual character. To most of the system
they are opaque blobs that are intended only to be obtained from one
HAL (e.g. gatekeeper or fingerprint) and passed to another
HAL (keymaster), but keystore actually needs to extract some bits of
information from them in order to determine which of the available blobs
should be provided for a given keymaster key operation.

This CL adds a method that resolves this dual nature by moving the
responsibility of parsing blobs to the HAL so that no component of the
framework has to make any assumptions about their content and all can
treat them as fully opaque. This still means that the various HAL
implementers have to agree on content, but they also have to agree on an
HMAC key which much be securely distributed to all at every boot, so
asking them to agree on an auth token format is perfectly
acceptable. But now the Android system doesn't have to care about the
format.

Bug: 32962548
Test: CTS tests pass, plus manual testing.
Change-Id: I78aa6e4ea9c5d8f34906b0969909387e2c5894e6

keymaster/3.0/IKeymasterDevice.hal
keymaster/3.0/default/KeymasterDevice.cpp
keymaster/3.0/default/KeymasterDevice.h
keymaster/3.0/types.hal

index 2664765..0c59e6c 100644 (file)
@@ -59,6 +59,22 @@ interface IKeymasterDevice {
                   string keymasterAuthorName);
 
     /**
+     * Parses a hardware authentication token blob to extract the details needed to determine if the
+     * token is applicable to a given keymaster operation. This method is intended to be
+     * implemented by the HAL, without requiring a call into the trusted hardware.  It is not
+     * necessary for this method to verify that the values in the blob are correct.
+     *
+     * @param token The token blob provided by the authentication app.
+     *
+     * @return error ErrorCode::OK or, if the blob is invalid, ErrorCode::INVALD_ARGUMENT if the
+     *             blob is the wrong size, the wrong version, or incorrectly structured.
+     *
+     * @return tokenInfo Information extracted from the auth token.
+     */
+    parseHardwareAuthToken(vec<uint8_t> token)
+        generates(ErrorCode error, HardwareAuthTokenInfo tokenInfo);
+
+    /**
      * Adds entropy to the RNG used by keymaster. Entropy added through this method is guaranteed
      * not to be the only source of entropy used, and the mixing function is required to be secure,
      * in the sense that if the RNG is seeded (from any source) with any data the attacker cannot
index 720b946..b2baa2b 100644 (file)
@@ -33,6 +33,8 @@ namespace implementation {
 
 using ::keymaster::SoftKeymasterDevice;
 
+namespace {
+
 class SoftwareOnlyHidlKeymasterEnforcement : public ::keymaster::KeymasterEnforcement {
   public:
     SoftwareOnlyHidlKeymasterEnforcement() : KeymasterEnforcement(64, 64) {}
@@ -60,7 +62,7 @@ class SoftwareOnlyHidlKeymasterContext : public ::keymaster::SoftKeymasterContex
     std::unique_ptr<::keymaster::KeymasterEnforcement> enforcement_;
 };
 
-static int keymaster0_device_initialize(const hw_module_t* mod, keymaster2_device_t** dev) {
+int keymaster0_device_initialize(const hw_module_t* mod, keymaster2_device_t** dev) {
     assert(mod->module_api_version < KEYMASTER_MODULE_API_VERSION_1_0);
     ALOGI("Found keymaster0 module %s, version %x", mod->name, mod->module_api_version);
 
@@ -137,7 +139,7 @@ err:
     return rc;
 }
 
-static int keymaster2_device_initialize(const hw_module_t* mod, keymaster2_device_t** dev) {
+int keymaster2_device_initialize(const hw_module_t* mod, keymaster2_device_t** dev) {
     assert(mod->module_api_version >= KEYMASTER_MODULE_API_VERSION_2_0);
     ALOGI("Found keymaster2 module %s, version %x", mod->name, mod->module_api_version);
 
@@ -191,6 +193,30 @@ static int keymaster_device_initialize(keymaster2_device_t** dev, uint32_t* vers
     }
 }
 
+template <typename IntType, uint32_t byteOrder> struct choose_ntoh;
+
+template <typename IntType> struct choose_ntoh<IntType, __ORDER_LITTLE_ENDIAN__> {
+    inline static IntType ntoh(const IntType& value) {
+        IntType result = 0;
+        const unsigned char* inbytes = reinterpret_cast<const unsigned char*>(&value);
+        unsigned char* outbytes = reinterpret_cast<unsigned char*>(&result);
+        for (int i = sizeof(IntType) - 1; i >= 0; --i) {
+            *(outbytes++) = inbytes[i];
+        }
+        return result;
+    }
+};
+
+template <typename IntType> struct choose_ntoh<IntType, __ORDER_BIG_ENDIAN__> {
+    inline static IntType hton(const IntType& value) { return value; }
+};
+
+template <typename IntType> inline IntType ntoh(const IntType& value) {
+    return choose_ntoh<IntType, __BYTE_ORDER__>::ntoh(value);
+}
+
+}  // anonymous namespace
+
 KeymasterDevice::~KeymasterDevice() {
     if (keymaster_device_) keymaster_device_->common.close(&keymaster_device_->common);
 }
@@ -376,6 +402,34 @@ Return<void> KeymasterDevice::getHardwareFeatures(getHardwareFeatures_cb _hidl_c
     return Void();
 }
 
+Return<void> KeymasterDevice::parseHardwareAuthToken(const hidl_vec<uint8_t>& token,
+                                                     parseHardwareAuthToken_cb _hidl_cb) {
+    HardwareAuthTokenInfo parsedToken;
+    if (token.size() != sizeof(hw_auth_token_t)) {
+        ALOGE("Received auth token of length %zu, expected %zu", token.size(),
+              sizeof(hw_auth_token_t));
+        _hidl_cb(ErrorCode::INVALID_ARGUMENT, parsedToken);
+        return Void();
+    }
+
+    const hw_auth_token_t* authToken = reinterpret_cast<const hw_auth_token_t*>(token.data());
+    if (authToken->version != 0) {
+        ALOGE("Auth token version %u, expected version ", authToken->version);
+        _hidl_cb(ErrorCode::INVALID_ARGUMENT, parsedToken);
+        return Void();
+    }
+
+    parsedToken.challenge = authToken->challenge;
+    parsedToken.userId = authToken->user_id;
+    parsedToken.authenticatorId = authToken->authenticator_id;
+    parsedToken.authenticatorType =
+        static_cast<HardwareAuthenticatorType>(ntoh(authToken->authenticator_type));
+    parsedToken.timestamp = ntoh(authToken->timestamp);
+
+    _hidl_cb(ErrorCode::OK, parsedToken);
+    return Void();
+}
+
 Return<ErrorCode> KeymasterDevice::addRngEntropy(const hidl_vec<uint8_t>& data) {
     if (!data.size()) return ErrorCode::OK;
     return legacy_enum_conversion(
index e048d5b..8198eca 100644 (file)
@@ -53,6 +53,8 @@ class KeymasterDevice : public IKeymasterDevice {
 
     // Methods from ::android::hardware::keymaster::V3_0::IKeymasterDevice follow.
     Return<void> getHardwareFeatures(getHardwareFeatures_cb _hidl_cb);
+    Return<void> parseHardwareAuthToken(const hidl_vec<uint8_t>& token,
+                                        parseHardwareAuthToken_cb _hidl_cb);
     Return<ErrorCode> addRngEntropy(const hidl_vec<uint8_t>& data) override;
     Return<void> generateKey(const hidl_vec<KeyParameter>& keyParams,
                              generateKey_cb _hidl_cb) override;
index 1f4a0cc..3ff4145 100644 (file)
@@ -400,19 +400,15 @@ struct KeyCharacteristics {
 };
 
 /**
- * Data used to prove successful authentication.
+ * Data used to describe an authentication.  This data is retrieved from an authentication token by
+ * calling the parseHardwareAuthToken method of the HAL.
  */
-struct HardwareAuthToken {
+struct HardwareAuthTokenInfo {
     uint64_t challenge;
-    uint64_t userId;             // Secure User ID, not Android user ID.
-    uint64_t authenticatorId;    // Secure authenticator ID.
-    uint32_t authenticatorType;  // HardwareAuthenticatorType, in network order.
-    uint64_t timestamp;          // In network order.
-    uint8_t[32] hmac;            // HMAC is computed over 0 || challenge || user_id ||
-                                 // authenticator_id || authenticator_type || timestamp, with a
-                                 // prefixed 0 byte (which was a version field in Keymaster1 and
-                                 // Keymaster2) and the fields packed (no padding; so you probably
-                                 // can't just compute over the bytes of the struct).
+    uint64_t userId;           // Secure User ID, not Android user ID.
+    uint64_t authenticatorId;  // Secure authenticator ID.
+    HardwareAuthenticatorType authenticatorType;
+    uint64_t timestamp;
 };
 
 enum SecurityLevel : uint32_t {