Rename keyguard to gatekeeper
Prevents confusion between this component and the
actual Keyguard that shows when the device is locked.
Change-Id: I47bc02b73947d56841ed126aae4eaf007749038a
diff --git a/gatekeeper.cpp b/gatekeeper.cpp
new file mode 100644
index 0000000..a63b221
--- /dev/null
+++ b/gatekeeper.cpp
@@ -0,0 +1,219 @@
+/*
+ * Copyright 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 <time.h>
+#include <iostream>
+#include <iomanip>
+#include <UniquePtr.h>
+
+#include <gatekeeper/gatekeeper.h>
+
+namespace gatekeeper {
+
+/**
+ * Internal only structure for easy serialization
+ * and deserialization of password handles.
+ */
+static const uint8_t HANDLE_VERSION = 0;
+struct __attribute__ ((__packed__)) password_handle_t {
+ // fields included in signature
+ uint8_t version = HANDLE_VERSION;
+ secure_id_t user_id;
+ secure_id_t authenticator_id;
+
+ // fields not included in signature
+ salt_t salt;
+ uint8_t signature[32];
+};
+
+void GateKeeper::Enroll(const EnrollRequest &request, EnrollResponse *response) {
+ if (response == NULL) return;
+
+ if (!request.provided_password.buffer.get()) {
+ response->error = ERROR_INVALID;
+ return;
+ }
+
+ secure_id_t user_id = 0;
+ uint8_t *current_password = NULL;
+ size_t current_password_size = 0;
+
+ if (request.password_handle.buffer.get() == NULL) {
+ // Password handle does not match what is stored, generate new SecureID
+ GetRandom(&user_id, sizeof(secure_id_t));
+ } else {
+ if (!ValidatePasswordFile(request.user_id, request.password_handle)) {
+ response->error = ERROR_INVALID;
+ return;
+ } else {
+ // Password handle matches password file
+ password_handle_t *pw_handle =
+ reinterpret_cast<password_handle_t *>(request.password_handle.buffer.get());
+ if (!DoVerify(pw_handle, request.enrolled_password)) {
+ // incorrect old password
+ response->error = ERROR_INVALID;
+ return;
+ }
+
+ user_id = pw_handle->user_id;
+ }
+ }
+
+ salt_t salt;
+ GetRandom(&salt, sizeof(salt));
+
+ secure_id_t authenticator_id;
+ GetRandom(&authenticator_id, sizeof(authenticator_id));
+
+
+ SizedBuffer password_handle;
+ if(!CreatePasswordHandle(&password_handle,
+ salt, user_id, authenticator_id, request.provided_password.buffer.get(),
+ request.provided_password.length)) {
+ response->error = ERROR_INVALID;
+ return;
+ }
+
+
+ WritePasswordFile(request.user_id, password_handle);
+
+ response->SetEnrolledPasswordHandle(&password_handle);
+}
+
+void GateKeeper::Verify(const VerifyRequest &request, VerifyResponse *response) {
+ if (response == NULL) return;
+
+ if (!request.provided_password.buffer.get() || !request.password_handle.buffer.get()) {
+ response->error = ERROR_INVALID;
+ return;
+ }
+
+ secure_id_t user_id, authenticator_id;
+ password_handle_t *password_handle = reinterpret_cast<password_handle_t *>(
+ request.password_handle.buffer.get());
+
+ // Sanity check
+ if (password_handle->version != HANDLE_VERSION) {
+ response->error = ERROR_INVALID;
+ return;
+ }
+
+ if (!ValidatePasswordFile(request.user_id, request.password_handle)) {
+ // we don't allow access to keys if we can't validate the file.
+ // we must allow this case to support authentication before we decrypt
+ // /data, however.
+ user_id = 0;
+ authenticator_id = 0;
+ } else {
+ user_id = password_handle->user_id;
+ authenticator_id = password_handle->authenticator_id;
+ }
+
+ struct timespec time;
+ uint64_t timestamp;
+ clock_gettime(CLOCK_MONOTONIC_RAW, &time);
+ timestamp = static_cast<uint32_t>(time.tv_sec);
+
+ if (DoVerify(password_handle, request.provided_password)) {
+ // Signature matches
+ SizedBuffer auth_token;
+ MintAuthToken(&auth_token.buffer, &auth_token.length, timestamp,
+ user_id, authenticator_id);
+ response->SetVerificationToken(&auth_token);
+ } else {
+ response->error = ERROR_INVALID;
+ }
+}
+
+bool GateKeeper::CreatePasswordHandle(SizedBuffer *password_handle_buffer, salt_t salt,
+ secure_id_t user_id, secure_id_t authenticator_id, const uint8_t *password,
+ size_t password_length) {
+ password_handle_buffer->buffer.reset(new uint8_t[sizeof(password_handle_t)]);
+ password_handle_buffer->length = sizeof(password_handle_t);
+
+ password_handle_t *password_handle = reinterpret_cast<password_handle_t *>(
+ password_handle_buffer->buffer.get());
+ password_handle->version = HANDLE_VERSION;
+ password_handle->salt = salt;
+ password_handle->user_id = user_id;
+ password_handle->authenticator_id = authenticator_id;
+
+ size_t metadata_length = sizeof(user_id) /* user id */
+ + sizeof(authenticator_id) /* auth id */ + sizeof(uint8_t) /* version */;
+ uint8_t to_sign[password_length + metadata_length];
+ memcpy(to_sign, &password_handle->version, metadata_length);
+ memcpy(to_sign + metadata_length, password, password_length);
+
+ UniquePtr<uint8_t> password_key;
+ size_t password_key_length = 0;
+ GetPasswordKey(&password_key, &password_key_length);
+
+ if (!password_key.get() || password_key_length == 0) {
+ return false;
+ }
+
+ ComputePasswordSignature(password_handle->signature, sizeof(password_handle->signature),
+ password_key.get(), password_key_length, to_sign, sizeof(to_sign), salt);
+ return true;
+}
+
+bool GateKeeper::DoVerify(const password_handle_t *expected_handle, const SizedBuffer &password) {
+ if (!password.buffer.get()) return false;
+
+ SizedBuffer provided_handle;
+ if (!CreatePasswordHandle(&provided_handle, expected_handle->salt, expected_handle->user_id,
+ expected_handle->authenticator_id, password.buffer.get(), password.length)) {
+ return false;
+ }
+
+ return memcmp_s(provided_handle.buffer.get(), expected_handle, sizeof(*expected_handle)) == 0;
+}
+
+bool GateKeeper::ValidatePasswordFile(uint32_t uid, const SizedBuffer &provided_handle) {
+ SizedBuffer stored_handle;
+ ReadPasswordFile(uid, &stored_handle);
+
+ if (!stored_handle.buffer.get() || stored_handle.length == 0) return false;
+
+ // do we also verify the signature here?
+ return stored_handle.length == provided_handle.length &&
+ memcmp_s(stored_handle.buffer.get(), provided_handle.buffer.get(), stored_handle.length)
+ == 0;
+}
+
+void GateKeeper::MintAuthToken(UniquePtr<uint8_t> *auth_token, size_t *length,
+ uint32_t timestamp, secure_id_t user_id, secure_id_t authenticator_id) {
+ if (auth_token == NULL) return;
+
+ AuthToken *token = new AuthToken;
+ SizedBuffer serialized_auth_token;
+
+ token->root_secure_user_id = user_id;
+ token->auxiliary_secure_user_id = authenticator_id;
+ token->timestamp = timestamp;
+
+ UniquePtr<uint8_t> auth_token_key;
+ size_t key_len;
+ GetAuthTokenKey(&auth_token_key, &key_len);
+
+ size_t hash_len = (size_t)((uint8_t *)&token->hmac - (uint8_t *)token);
+ ComputeSignature(token->hmac, sizeof(token->hmac), auth_token_key.get(), key_len,
+ reinterpret_cast<uint8_t *>(token), hash_len);
+
+ if (length != NULL) *length = sizeof(AuthToken);
+ auth_token->reset(reinterpret_cast<uint8_t *>(token));
+}
+
+}