Apply authorization checks only during begin().

Bug: 22077675
Change-Id: I29f4a913abc485b5b80cae5eb1eb9914fa6802a5
diff --git a/include/keymaster/keymaster_enforcement.h b/include/keymaster/keymaster_enforcement.h
index 5b5d33b..a0fccdf 100644
--- a/include/keymaster/keymaster_enforcement.h
+++ b/include/keymaster/keymaster_enforcement.h
@@ -59,6 +59,37 @@
                                          bool is_begin_operation);
 
     /**
+     * Iterates through the authorization set and returns the corresponding keymaster error. Will
+     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
+     * the given operation params. Used for encrypt, decrypt sign, and verify.
+     */
+    keymaster_error_t AuthorizeBegin(const keymaster_purpose_t purpose, const km_id_t keyid,
+                                     const AuthorizationSet& auth_set,
+                                     const AuthorizationSet& operation_params);
+
+    /**
+     * Iterates through the authorization set and returns the corresponding keymaster error. Will
+     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
+     * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
+     */
+    keymaster_error_t AuthorizeUpdate(const AuthorizationSet& auth_set,
+                                      const AuthorizationSet& operation_params,
+                                      keymaster_operation_handle_t op_handle) {
+        return AuthorizeUpdateOrFinish(auth_set, operation_params, op_handle);
+    }
+
+    /**
+     * Iterates through the authorization set and returns the corresponding keymaster error. Will
+     * return KM_ERROR_OK if all criteria is met for the given purpose in the authorization set with
+     * the given operation params and handle. Used for encrypt, decrypt sign, and verify.
+     */
+    keymaster_error_t AuthorizeFinish(const AuthorizationSet& auth_set,
+                                      const AuthorizationSet& operation_params,
+                                      keymaster_operation_handle_t op_handle) {
+        return AuthorizeUpdateOrFinish(auth_set, operation_params, op_handle);
+    }
+
+    /**
      * Creates a key ID for use in subsequent calls to AuthorizeOperation.  Clients needn't use this
      * method of creating key IDs, as long as they use something consistent and unique.  This method
      * hashes the key blob.
@@ -116,6 +147,10 @@
     virtual bool ValidateTokenSignature(const hw_auth_token_t& token) const = 0;
 
   private:
+    keymaster_error_t AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set,
+                                              const AuthorizationSet& operation_params,
+                                              keymaster_operation_handle_t op_handle);
+
     bool MinTimeBetweenOpsPassed(uint32_t min_time_between, const km_id_t keyid);
     bool MaxUsesPerBootNotExceeded(const km_id_t keyid, uint32_t max_uses);
     bool AuthTokenMatches(const AuthorizationSet& auth_set,
diff --git a/keymaster_enforcement.cpp b/keymaster_enforcement.cpp
index aac5d12..8273cf5 100644
--- a/keymaster_enforcement.cpp
+++ b/keymaster_enforcement.cpp
@@ -65,19 +65,70 @@
     return purpose == KM_PURPOSE_DECRYPT || purpose == KM_PURPOSE_VERIFY;
 }
 
-inline bool can_skip_authentication(bool is_begin_operation, bool is_auth_per_op_key) {
-    // Durign begin with auth-per-op keys, we don't require authentication because it can't be
-    // performed until after begin returns the operation handle used to for the authentication
-    // challenge.
-    return is_begin_operation && is_auth_per_op_key;
-}
-
 keymaster_error_t KeymasterEnforcement::AuthorizeOperation(const keymaster_purpose_t purpose,
                                                            const km_id_t keyid,
                                                            const AuthorizationSet& auth_set,
                                                            const AuthorizationSet& operation_params,
                                                            keymaster_operation_handle_t op_handle,
                                                            bool is_begin_operation) {
+    if (is_begin_operation)
+        return AuthorizeBegin(purpose, keyid, auth_set, operation_params);
+    else
+        return AuthorizeUpdateOrFinish(auth_set, operation_params, op_handle);
+}
+
+// For update and finish the only thing to check is user authentication, and then only if it's not
+// timeout-based.
+keymaster_error_t
+KeymasterEnforcement::AuthorizeUpdateOrFinish(const AuthorizationSet& auth_set,
+                                              const AuthorizationSet& operation_params,
+                                              keymaster_operation_handle_t op_handle) {
+    int auth_type_index = -1;
+    for (size_t pos = 0; pos < auth_set.size(); ++pos) {
+        switch (auth_set[pos].tag) {
+        case KM_TAG_NO_AUTH_REQUIRED:
+        case KM_TAG_AUTH_TIMEOUT:
+            // If no auth is required or if auth is timeout-based, we have nothing to check.
+            return KM_ERROR_OK;
+
+        case KM_TAG_USER_AUTH_TYPE:
+            auth_type_index = pos;
+            break;
+
+        default:
+            break;
+        }
+    }
+
+    // Note that at this point we should be able to assume that authentication is required, because
+    // authentication is required if KM_TAG_NO_AUTH_REQUIRED is absent.  However, there are legacy
+    // keys which have no authentication-related tags, so we assume that absence is equivalent to
+    // presence of KM_TAG_NO_AUTH_REQUIRED.
+    //
+    // So, if we found KM_TAG_USER_AUTH_TYPE or if we find KM_TAG_USER_SECURE_ID then authentication
+    // is required.  If we find neither, then we assume authentication is not required and return
+    // success.
+    bool authentication_required = (auth_type_index != -1);
+    for (auto& param : auth_set) {
+        if (param.tag == KM_TAG_USER_SECURE_ID) {
+            authentication_required = true;
+            int auth_timeout_index = -1;
+            if (AuthTokenMatches(auth_set, operation_params, param.long_integer, auth_type_index,
+                                 auth_timeout_index, op_handle, false /* is_begin_operation */))
+                return KM_ERROR_OK;
+        }
+    }
+
+    if (authentication_required)
+        return KM_ERROR_KEY_USER_NOT_AUTHENTICATED;
+
+    return KM_ERROR_OK;
+}
+
+keymaster_error_t KeymasterEnforcement::AuthorizeBegin(const keymaster_purpose_t purpose,
+                                                       const km_id_t keyid,
+                                                       const AuthorizationSet& auth_set,
+                                                       const AuthorizationSet& operation_params) {
     // Find some entries that may be needed to handle KM_TAG_USER_SECURE_ID
     int auth_timeout_index = -1;
     int auth_type_index = -1;
@@ -150,12 +201,13 @@
             if (no_auth_required_index != -1) {
                 // Key has both KM_TAG_USER_SECURE_ID and KM_TAG_NO_AUTH_REQUIRED
                 return KM_ERROR_INVALID_KEY_BLOB;
-            } else if (!can_skip_authentication(is_begin_operation, auth_timeout_index == -1) ||
-                       operation_params.find(KM_TAG_AUTH_TOKEN) != -1) {
+            }
+
+            if (auth_timeout_index != -1) {
                 authentication_required = true;
                 if (AuthTokenMatches(auth_set, operation_params, param.long_integer,
-                                     auth_type_index, auth_timeout_index, op_handle,
-                                     is_begin_operation))
+                                     auth_type_index, auth_timeout_index, 0 /* op_handle */,
+                                     true /* is_begin_operation */))
                     auth_token_matched = true;
             }
             break;
diff --git a/keymaster_enforcement_test.cpp b/keymaster_enforcement_test.cpp
index ef8ae16..c91d7e8 100644
--- a/keymaster_enforcement_test.cpp
+++ b/keymaster_enforcement_test.cpp
@@ -36,7 +36,7 @@
                                          const AuthorizationSet& auth_set) {
         AuthorizationSet empty_set;
         return KeymasterEnforcement::AuthorizeOperation(
-            purpose, keyid, auth_set, empty_set, 0 /* op_handle */, false /* is_begin_operation */);
+            purpose, keyid, auth_set, empty_set, 0 /* op_handle */, true /* is_begin_operation */);
     }
     using KeymasterEnforcement::AuthorizeOperation;
 
@@ -432,16 +432,16 @@
 
     EXPECT_EQ(KM_ERROR_OK,
               kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, caller_nonce, begin_params,
-                                      0 /* challenge */, false /* is_begin_operation */));
+                                      0 /* challenge */, true /* is_begin_operation */));
     EXPECT_EQ(KM_ERROR_OK,
               kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, caller_nonce, begin_params,
-                                      0 /* challenge */, false /* is_begin_operation */));
+                                      0 /* challenge */, true /* is_begin_operation */));
     EXPECT_EQ(KM_ERROR_CALLER_NONCE_PROHIBITED,
               kmen.AuthorizeOperation(KM_PURPOSE_ENCRYPT, key_id, no_caller_nonce, begin_params,
-                                      0 /* challenge */, false /* is_begin_operation */));
+                                      0 /* challenge */, true /* is_begin_operation */));
     EXPECT_EQ(KM_ERROR_OK,
               kmen.AuthorizeOperation(KM_PURPOSE_DECRYPT, key_id, no_caller_nonce, begin_params,
-                                      0 /* challenge */, false /* is_begin_operation */));
+                                      0 /* challenge */, true /* is_begin_operation */));
 }
 
 TEST_F(KeymasterBaseTest, TestBootloaderOnly) {
@@ -752,8 +752,8 @@
               kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
                                       true /* is_begin_operation */));
 
-    // And later (though begin would fail, so there wouldn't be a later).
-    EXPECT_EQ(KM_ERROR_KEY_USER_NOT_AUTHENTICATED,
+    // Later we don't check (though begin would fail, so there wouldn't be a later).
+    EXPECT_EQ(KM_ERROR_OK,
               kmen.AuthorizeOperation(KM_PURPOSE_SIGN, key_id, auth_set, op_params, token.challenge,
                                       false /* is_begin_operation */));
 }