Merge "C++ AIDL on-device GateKeeper / SharedSecret impl" into main
diff --git a/gatekeeper/aidl/software/Android.bp b/gatekeeper/aidl/software/Android.bp
new file mode 100644
index 0000000..d244461
--- /dev/null
+++ b/gatekeeper/aidl/software/Android.bp
@@ -0,0 +1,71 @@
+package {
+    // See: http://go/android-license-faq
+    // A large-scale-change added 'default_applicable_licenses' to import
+    // all of the 'license_kinds' from "hardware_interfaces_license"
+    // to get the below license kinds:
+    //   SPDX-license-identifier-Apache-2.0
+    default_applicable_licenses: ["hardware_interfaces_license"],
+}
+
+cc_binary {
+    name: "android.hardware.gatekeeper-service.nonsecure",
+    cflags: [
+        "-fvisibility=hidden",
+        "-Wall",
+        "-Werror",
+    ],
+    installable: false, // installed in APEX
+    relative_install_path: "hw",
+    shared_libs: [
+        "android.hardware.gatekeeper-V1-ndk",
+        "android.hardware.security.sharedsecret-V1-ndk",
+        "lib_android_keymaster_keymint_utils",
+        "libbase",
+        "libbinder_ndk",
+        "libcrypto",
+        "libcutils",
+        "libgatekeeper",
+        "libhardware",
+        "libkeymaster_portable",
+        "liblog",
+        "libutils",
+    ],
+    srcs: [
+        "GateKeeper.cpp",
+        "SharedSecret.cpp",
+        "service.cpp",
+    ],
+    static_libs: ["libscrypt_static"],
+    vendor: true,
+}
+
+prebuilt_etc {
+    name: "gatekeeper_nonsecure_vintf",
+    srcs: [
+        "android.hardware.gatekeeper-service.nonsecure.xml",
+        "android.hardware.security.sharedsecret-gatekeeper.xml",
+    ],
+    sub_dir: "vintf",
+    installable: false,
+}
+
+prebuilt_etc {
+    name: "android.hardware.gatekeeper-service.nonsecure.rc",
+    src: "android.hardware.gatekeeper-service.nonsecure.rc",
+    installable: false,
+}
+
+apex {
+    name: "com.android.hardware.gatekeeper.nonsecure",
+    binaries: ["android.hardware.gatekeeper-service.nonsecure"],
+    certificate: ":com.google.cf.apex.certificate",
+    file_contexts: "file_contexts",
+    key: "com.google.cf.apex.key",
+    manifest: "manifest.json",
+    prebuilts: [
+        "gatekeeper_nonsecure_vintf",
+        "android.hardware.gatekeeper-service.nonsecure.rc",
+    ],
+    updatable: false,
+    vendor: true,
+}
diff --git a/gatekeeper/aidl/software/GateKeeper.cpp b/gatekeeper/aidl/software/GateKeeper.cpp
new file mode 100644
index 0000000..1fc3682
--- /dev/null
+++ b/gatekeeper/aidl/software/GateKeeper.cpp
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#define LOG_TAG "android.hardware.gatekeeper-service.nonsecure"
+
+#include <endian.h>
+
+#include <android-base/logging.h>
+
+#include "GateKeeper.h"
+
+using ::gatekeeper::EnrollRequest;
+using ::gatekeeper::EnrollResponse;
+using ::gatekeeper::ERROR_NONE;
+using ::gatekeeper::ERROR_RETRY;
+using ::gatekeeper::SizedBuffer;
+using ::gatekeeper::VerifyRequest;
+using ::gatekeeper::VerifyResponse;
+
+namespace aidl::android::hardware::gatekeeper {
+
+SizedBuffer vec2sized_buffer(const std::vector<uint8_t>& vec) {
+    if (vec.size() == 0 || vec.size() > std::numeric_limits<uint32_t>::max()) {
+        return {};
+    }
+    auto unused = new uint8_t[vec.size()];
+    std::copy(vec.begin(), vec.end(), unused);
+    return {unused, static_cast<uint32_t>(vec.size())};
+}
+
+void sizedBuffer2AidlHWToken(SizedBuffer& buffer,
+                             android::hardware::security::keymint::HardwareAuthToken* aidlToken) {
+    const hw_auth_token_t* authToken =
+            reinterpret_cast<const hw_auth_token_t*>(buffer.Data<uint8_t>());
+    aidlToken->challenge = authToken->challenge;
+    aidlToken->userId = authToken->user_id;
+    aidlToken->authenticatorId = authToken->authenticator_id;
+    // these are in network order: translate to host
+    aidlToken->authenticatorType =
+            static_cast<android::hardware::security::keymint::HardwareAuthenticatorType>(
+                    be32toh(authToken->authenticator_type));
+    aidlToken->timestamp.milliSeconds = be64toh(authToken->timestamp);
+    aidlToken->mac.insert(aidlToken->mac.begin(), std::begin(authToken->hmac),
+                          std::end(authToken->hmac));
+}
+
+SoftGateKeeperDevice::SoftGateKeeperDevice(::gatekeeper::SoftGateKeeper& impl) : impl_(impl) {}
+
+::ndk::ScopedAStatus SoftGateKeeperDevice::enroll(int32_t uid,
+                                                  const std::vector<uint8_t>& currentPasswordHandle,
+                                                  const std::vector<uint8_t>& currentPassword,
+                                                  const std::vector<uint8_t>& desiredPassword,
+                                                  GatekeeperEnrollResponse* rsp) {
+    if (desiredPassword.size() == 0) {
+        LOG(ERROR) << "Desired password size is 0";
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
+    }
+
+    if (currentPasswordHandle.size() > 0) {
+        if (currentPasswordHandle.size() != sizeof(::gatekeeper::password_handle_t)) {
+            LOG(ERROR) << "Password handle has wrong length";
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
+        }
+    }
+
+    EnrollRequest request(uid, vec2sized_buffer(currentPasswordHandle),
+                          vec2sized_buffer(desiredPassword), vec2sized_buffer(currentPassword));
+    EnrollResponse response;
+    impl_.Enroll(request, &response);
+    if (response.error == ERROR_RETRY) {
+        LOG(ERROR) << "Enroll response has a retry error";
+        *rsp = {ERROR_RETRY_TIMEOUT, static_cast<int32_t>(response.retry_timeout), 0, {}};
+        return ndk::ScopedAStatus::ok();
+    } else if (response.error != ERROR_NONE) {
+        LOG(ERROR) << "Enroll response has an error: " << response.error;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
+    } else {
+        const ::gatekeeper::password_handle_t* password_handle =
+                response.enrolled_password_handle.Data<::gatekeeper::password_handle_t>();
+        *rsp = {STATUS_OK,
+                0,
+                static_cast<int64_t>(password_handle->user_id),
+                {response.enrolled_password_handle.Data<uint8_t>(),
+                 (response.enrolled_password_handle.Data<uint8_t>() +
+                  response.enrolled_password_handle.size())}};
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus SoftGateKeeperDevice::verify(
+        int32_t uid, int64_t challenge, const std::vector<uint8_t>& enrolledPasswordHandle,
+        const std::vector<uint8_t>& providedPassword, GatekeeperVerifyResponse* rsp) {
+    if (enrolledPasswordHandle.size() == 0) {
+        LOG(ERROR) << "Enrolled password size is 0";
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
+    }
+
+    if (enrolledPasswordHandle.size() > 0) {
+        if (enrolledPasswordHandle.size() != sizeof(::gatekeeper::password_handle_t)) {
+            LOG(ERROR) << "Password handle has wrong length";
+            return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
+        }
+    }
+
+    VerifyRequest request(uid, challenge, vec2sized_buffer(enrolledPasswordHandle),
+                          vec2sized_buffer(providedPassword));
+    VerifyResponse response;
+    impl_.Verify(request, &response);
+
+    if (response.error == ERROR_RETRY) {
+        LOG(ERROR) << "Verify request response gave retry error";
+        *rsp = {ERROR_RETRY_TIMEOUT, static_cast<int32_t>(response.retry_timeout), {}};
+        return ndk::ScopedAStatus::ok();
+    } else if (response.error != ERROR_NONE) {
+        LOG(ERROR) << "Verify request response gave error: " << response.error;
+        return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_GENERAL_FAILURE));
+    } else {
+        // On Success, return GatekeeperVerifyResponse with Success Status, timeout{0} and
+        // valid HardwareAuthToken.
+        *rsp = {response.request_reenroll ? STATUS_REENROLL : STATUS_OK, 0, {}};
+        // Convert the hw_auth_token_t to HardwareAuthToken in the response.
+        sizedBuffer2AidlHWToken(response.auth_token, &rsp->hardwareAuthToken);
+    }
+    return ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus SoftGateKeeperDevice::deleteUser(int32_t /*uid*/) {
+    LOG(ERROR) << "deleteUser is unimplemented";
+    return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_NOT_IMPLEMENTED));
+}
+
+::ndk::ScopedAStatus SoftGateKeeperDevice::deleteAllUsers() {
+    LOG(ERROR) << "deleteAllUsers is unimplemented";
+    return ndk::ScopedAStatus(AStatus_fromServiceSpecificError(ERROR_NOT_IMPLEMENTED));
+}
+
+}  // namespace aidl::android::hardware::gatekeeper
diff --git a/gatekeeper/aidl/software/GateKeeper.h b/gatekeeper/aidl/software/GateKeeper.h
new file mode 100644
index 0000000..1327fad
--- /dev/null
+++ b/gatekeeper/aidl/software/GateKeeper.h
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+#pragma once
+
+#include <aidl/android/hardware/gatekeeper/BnGatekeeper.h>
+#include <gatekeeper/gatekeeper_messages.h>
+
+#include "SoftGateKeeper.h"
+
+namespace aidl::android::hardware::gatekeeper {
+
+class SoftGateKeeperDevice : public BnGatekeeper {
+  public:
+    SoftGateKeeperDevice(::gatekeeper::SoftGateKeeper&);
+    /**
+     * Enrolls password_payload, which should be derived from a user selected pin
+     * or password, with the authentication factor private key used only for
+     * enrolling authentication factor data.
+     *
+     * Returns: 0 on success or an error code less than 0 on error.
+     * On error, enrolled_password_handle will not be allocated.
+     */
+    ::ndk::ScopedAStatus enroll(int32_t uid, const std::vector<uint8_t>& currentPasswordHandle,
+                                const std::vector<uint8_t>& currentPassword,
+                                const std::vector<uint8_t>& desiredPassword,
+                                GatekeeperEnrollResponse* _aidl_return) override;
+    /**
+     * Verifies provided_password matches enrolled_password_handle.
+     *
+     * Implementations of this module may retain the result of this call
+     * to attest to the recency of authentication.
+     *
+     * On success, writes the address of a verification token to auth_token,
+     * usable to attest password verification to other trusted services. Clients
+     * may pass NULL for this value.
+     *
+     * Returns: 0 on success or an error code less than 0 on error
+     * On error, verification token will not be allocated
+     */
+    ::ndk::ScopedAStatus verify(int32_t uid, int64_t challenge,
+                                const std::vector<uint8_t>& enrolledPasswordHandle,
+                                const std::vector<uint8_t>& providedPassword,
+                                GatekeeperVerifyResponse* _aidl_return) override;
+
+    ::ndk::ScopedAStatus deleteAllUsers() override;
+
+    ::ndk::ScopedAStatus deleteUser(int32_t uid) override;
+
+  private:
+    ::gatekeeper::SoftGateKeeper& impl_;
+};
+
+}  // namespace aidl::android::hardware::gatekeeper
diff --git a/gatekeeper/aidl/software/SharedSecret.cpp b/gatekeeper/aidl/software/SharedSecret.cpp
new file mode 100644
index 0000000..f693700
--- /dev/null
+++ b/gatekeeper/aidl/software/SharedSecret.cpp
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2024, 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 "SharedSecret.h"
+
+#include <algorithm>
+#include <cstring>
+#include <mutex>
+#include <vector>
+
+#include <openssl/rand.h>
+
+#include <KeyMintUtils.h>
+#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+#include <android-base/logging.h>
+#include <keymaster/android_keymaster_messages.h>
+#include <keymaster/android_keymaster_utils.h>
+#include <keymaster/km_openssl/ckdf.h>
+#include <keymaster/km_openssl/hmac.h>
+
+namespace aidl::android::hardware::security::sharedsecret {
+
+::ndk::ScopedAStatus SoftSharedSecret::getSharedSecretParameters(
+        SharedSecretParameters* out_params) {
+    std::lock_guard lock(mutex_);
+    if (seed_.empty()) {
+        seed_.resize(32, 0);
+    }
+    out_params->seed = seed_;
+    if (nonce_.empty()) {
+        nonce_.resize(32, 0);
+        RAND_bytes(nonce_.data(), 32);
+    }
+    out_params->nonce = nonce_;
+    LOG(INFO) << "Presented shared secret parameters with seed size " << out_params->seed.size()
+              << " and nonce size " << out_params->nonce.size();
+    return ::ndk::ScopedAStatus::ok();
+}
+
+::ndk::ScopedAStatus SoftSharedSecret::computeSharedSecret(
+        const std::vector<SharedSecretParameters>& params, std::vector<uint8_t>* sharing_check) {
+    std::lock_guard lock(mutex_);
+    LOG(INFO) << "Computing shared secret";
+    // Reimplemented based on SoftKeymasterEnforcement, which does not expose
+    // enough functionality to satisfy the GateKeeper interface
+    keymaster::KeymasterKeyBlob key_agreement_key;
+    if (key_agreement_key.Reset(32) == nullptr) {
+        LOG(ERROR) << "key agreement key memory allocation failed";
+        return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_MEMORY_ALLOCATION_FAILED);
+    }
+    // Matching:
+    // - kFakeAgreementKey in system/keymaster/km_openssl/soft_keymaster_enforcement.cpp
+    // - Keys::kak in hardware/interfaces/security/keymint/aidl/default/ta/soft.rs
+    std::memset(key_agreement_key.writable_data(), 0, 32);
+    keymaster::KeymasterBlob label((uint8_t*)KEY_AGREEMENT_LABEL, strlen(KEY_AGREEMENT_LABEL));
+    if (label.data == nullptr) {
+        LOG(ERROR) << "label memory allocation failed";
+        return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_MEMORY_ALLOCATION_FAILED);
+    }
+
+    static_assert(sizeof(keymaster_blob_t) == sizeof(keymaster::KeymasterBlob));
+
+    bool found_mine = false;
+    std::vector<keymaster::KeymasterBlob> context_blobs;
+    for (const auto& param : params) {
+        auto& seed_blob = context_blobs.emplace_back();
+        if (seed_blob.Reset(param.seed.size()) == nullptr) {
+            LOG(ERROR) << "seed memory allocation failed";
+            return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_MEMORY_ALLOCATION_FAILED);
+        }
+        std::copy(param.seed.begin(), param.seed.end(), seed_blob.writable_data());
+        auto& nonce_blob = context_blobs.emplace_back();
+        if (nonce_blob.Reset(param.nonce.size()) == nullptr) {
+            LOG(ERROR) << "Nonce memory allocation failed";
+            return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_MEMORY_ALLOCATION_FAILED);
+        }
+        std::copy(param.nonce.begin(), param.nonce.end(), nonce_blob.writable_data());
+        if (param.seed == seed_ && param.nonce == nonce_) {
+            found_mine = true;
+        }
+    }
+    if (!found_mine) {
+        LOG(ERROR) << "Did not receive my own shared secret parameter back";
+        return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_INVALID_ARGUMENT);
+    }
+    auto context_blobs_ptr = reinterpret_cast<keymaster_blob_t*>(context_blobs.data());
+    if (hmac_key_.Reset(32) == nullptr) {
+        LOG(ERROR) << "hmac key allocation failed";
+        return keymint::km_utils::kmError2ScopedAStatus(KM_ERROR_MEMORY_ALLOCATION_FAILED);
+    }
+    auto error = keymaster::ckdf(key_agreement_key, label, context_blobs_ptr, context_blobs.size(),
+                                 &hmac_key_);
+    if (error != KM_ERROR_OK) {
+        LOG(ERROR) << "CKDF failed";
+        return keymint::km_utils::kmError2ScopedAStatus(error);
+    }
+
+    keymaster::HmacSha256 hmac_impl;
+    if (!hmac_impl.Init(hmac_key_.key_material, hmac_key_.key_material_size)) {
+        LOG(ERROR) << "hmac initialization failed";
+        return ::ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    sharing_check->clear();
+    sharing_check->resize(32, 0);
+    if (!hmac_impl.Sign((const uint8_t*)KEY_CHECK_LABEL, strlen(KEY_CHECK_LABEL),
+                        sharing_check->data(), sharing_check->size())) {
+        return ::ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
+    }
+    return ::ndk::ScopedAStatus::ok();
+}
+
+keymaster::KeymasterKeyBlob SoftSharedSecret::HmacKey() const {
+    std::lock_guard lock(mutex_);
+    return hmac_key_;
+}
+
+}  // namespace aidl::android::hardware::security::sharedsecret
diff --git a/gatekeeper/aidl/software/SharedSecret.h b/gatekeeper/aidl/software/SharedSecret.h
new file mode 100644
index 0000000..1b804e7
--- /dev/null
+++ b/gatekeeper/aidl/software/SharedSecret.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2020, 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.
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <mutex>
+
+#include <aidl/android/hardware/security/sharedsecret/BnSharedSecret.h>
+#include <aidl/android/hardware/security/sharedsecret/SharedSecretParameters.h>
+#include <keymaster/km_openssl/soft_keymaster_enforcement.h>
+
+namespace aidl::android::hardware::security::sharedsecret {
+
+class SoftSharedSecret : public BnSharedSecret {
+  public:
+    ::ndk::ScopedAStatus getSharedSecretParameters(SharedSecretParameters* params) override;
+    ::ndk::ScopedAStatus computeSharedSecret(const std::vector<SharedSecretParameters>& params,
+                                             std::vector<uint8_t>* sharingCheck) override;
+
+    keymaster::KeymasterKeyBlob HmacKey() const;
+
+  private:
+    mutable std::mutex mutex_;
+    std::vector<std::uint8_t> seed_;
+    std::vector<std::uint8_t> nonce_;
+    keymaster::KeymasterKeyBlob hmac_key_;
+};
+
+}  // namespace aidl::android::hardware::security::sharedsecret
diff --git a/gatekeeper/aidl/software/SoftGateKeeper.h b/gatekeeper/aidl/software/SoftGateKeeper.h
new file mode 100644
index 0000000..305d997
--- /dev/null
+++ b/gatekeeper/aidl/software/SoftGateKeeper.h
@@ -0,0 +1,189 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef SOFT_GATEKEEPER_H_
+#define SOFT_GATEKEEPER_H_
+
+extern "C" {
+#include <openssl/rand.h>
+#include <openssl/sha.h>
+
+#include <crypto_scrypt.h>
+}
+
+#include <memory>
+#include <unordered_map>
+
+#include <android-base/logging.h>
+#include <android-base/memory.h>
+#include <gatekeeper/gatekeeper.h>
+#include <keymaster/km_openssl/hmac.h>
+
+#include "SharedSecret.h"
+
+namespace gatekeeper {
+
+struct fast_hash_t {
+    uint64_t salt;
+    uint8_t digest[SHA256_DIGEST_LENGTH];
+};
+
+class SoftGateKeeper : public GateKeeper {
+  public:
+    static const uint32_t SIGNATURE_LENGTH_BYTES = 32;
+
+    // scrypt params
+    static const uint64_t N = 16384;
+    static const uint32_t r = 8;
+    static const uint32_t p = 1;
+
+    static const int MAX_UINT_32_CHARS = 11;
+
+    SoftGateKeeper(const ::aidl::android::hardware::security::sharedsecret::SoftSharedSecret&
+                           shared_secret)
+        : shared_secret_(shared_secret) {
+        key_.reset(new uint8_t[SIGNATURE_LENGTH_BYTES]);
+        memset(key_.get(), 0, SIGNATURE_LENGTH_BYTES);
+    }
+
+    virtual ~SoftGateKeeper() {}
+
+    virtual bool GetAuthTokenKey(const uint8_t** auth_token_key, uint32_t* length) const {
+        if (auth_token_key == NULL || length == NULL) return false;
+        if (hmac_key_.key_material == nullptr) {
+            hmac_key_ = shared_secret_.HmacKey();
+        }
+        *auth_token_key = hmac_key_.key_material;
+        *length = hmac_key_.key_material_size;
+        return true;
+    }
+
+    virtual void GetPasswordKey(const uint8_t** password_key, uint32_t* length) {
+        if (password_key == NULL || length == NULL) return;
+        *password_key = key_.get();
+        *length = SIGNATURE_LENGTH_BYTES;
+    }
+
+    virtual void ComputePasswordSignature(uint8_t* signature, uint32_t signature_length,
+                                          const uint8_t*, uint32_t, const uint8_t* password,
+                                          uint32_t password_length, salt_t salt) const {
+        if (signature == NULL) return;
+        crypto_scrypt(password, password_length, reinterpret_cast<uint8_t*>(&salt), sizeof(salt), N,
+                      r, p, signature, signature_length);
+    }
+
+    virtual void GetRandom(void* random, uint32_t requested_length) const {
+        if (random == NULL) return;
+        RAND_pseudo_bytes((uint8_t*)random, requested_length);
+    }
+
+    virtual void ComputeSignature(uint8_t* signature, uint32_t signature_length, const uint8_t* key,
+                                  uint32_t key_length, const uint8_t* message,
+                                  const uint32_t message_length) const {
+        if (signature == NULL) return;
+        keymaster::HmacSha256 hmac_calculator;
+        if (!hmac_calculator.Init(key, key_length)) {
+            LOG(ERROR) << "ComputeSignature: Failed to initialize hmac calculator";
+            return;
+        }
+        if (!hmac_calculator.Sign(message, message_length, signature, signature_length)) {
+            LOG(ERROR) << "ComputeSignature: failed to create hmac";
+        }
+    }
+
+    virtual uint64_t GetMillisecondsSinceBoot() const {
+        struct timespec time;
+        int res = clock_gettime(CLOCK_BOOTTIME, &time);
+        if (res < 0) return 0;
+        return (time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000);
+    }
+
+    virtual bool IsHardwareBacked() const { return false; }
+
+    virtual bool GetFailureRecord(uint32_t uid, secure_id_t user_id, failure_record_t* record,
+                                  bool /* secure */) {
+        failure_record_t* stored = &failure_map_[uid];
+        if (user_id != stored->secure_user_id) {
+            stored->secure_user_id = user_id;
+            stored->last_checked_timestamp = 0;
+            stored->failure_counter = 0;
+        }
+        memcpy(record, stored, sizeof(*record));
+        return true;
+    }
+
+    virtual bool ClearFailureRecord(uint32_t uid, secure_id_t user_id, bool /* secure */) {
+        failure_record_t* stored = &failure_map_[uid];
+        stored->secure_user_id = user_id;
+        stored->last_checked_timestamp = 0;
+        stored->failure_counter = 0;
+        return true;
+    }
+
+    virtual bool WriteFailureRecord(uint32_t uid, failure_record_t* record, bool /* secure */) {
+        failure_map_[uid] = *record;
+        return true;
+    }
+
+    fast_hash_t ComputeFastHash(const SizedBuffer& password, uint64_t salt) {
+        fast_hash_t fast_hash;
+        size_t digest_size = password.size() + sizeof(salt);
+        std::unique_ptr<uint8_t[]> digest(new uint8_t[digest_size]);
+        memcpy(digest.get(), &salt, sizeof(salt));
+        memcpy(digest.get() + sizeof(salt), password.Data<uint8_t>(), password.size());
+
+        SHA256(digest.get(), digest_size, (uint8_t*)&fast_hash.digest);
+
+        fast_hash.salt = salt;
+        return fast_hash;
+    }
+
+    bool VerifyFast(const fast_hash_t& fast_hash, const SizedBuffer& password) {
+        fast_hash_t computed = ComputeFastHash(password, fast_hash.salt);
+        return memcmp(computed.digest, fast_hash.digest, SHA256_DIGEST_LENGTH) == 0;
+    }
+
+    bool DoVerify(const password_handle_t* expected_handle, const SizedBuffer& password) {
+        uint64_t user_id = android::base::get_unaligned<secure_id_t>(&expected_handle->user_id);
+        FastHashMap::const_iterator it = fast_hash_map_.find(user_id);
+        if (it != fast_hash_map_.end() && VerifyFast(it->second, password)) {
+            return true;
+        } else {
+            if (GateKeeper::DoVerify(expected_handle, password)) {
+                uint64_t salt;
+                GetRandom(&salt, sizeof(salt));
+                fast_hash_map_[user_id] = ComputeFastHash(password, salt);
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+  private:
+    typedef std::unordered_map<uint32_t, failure_record_t> FailureRecordMap;
+    typedef std::unordered_map<uint64_t, fast_hash_t> FastHashMap;
+
+    const ::aidl::android::hardware::security::sharedsecret::SoftSharedSecret& shared_secret_;
+    std::unique_ptr<uint8_t[]> key_;
+    FailureRecordMap failure_map_;
+    FastHashMap fast_hash_map_;
+    mutable ::keymaster::KeymasterKeyBlob hmac_key_;
+};
+}  // namespace gatekeeper
+
+#endif  // SOFT_GATEKEEPER_H_
diff --git a/gatekeeper/aidl/software/android.hardware.gatekeeper-service.nonsecure.rc b/gatekeeper/aidl/software/android.hardware.gatekeeper-service.nonsecure.rc
new file mode 100644
index 0000000..d361730
--- /dev/null
+++ b/gatekeeper/aidl/software/android.hardware.gatekeeper-service.nonsecure.rc
@@ -0,0 +1,4 @@
+service vendor.gatekeeper_nonsecure /apex/com.android.hardware.gatekeeper/bin/hw/android.hardware.gatekeeper-service.nonsecure
+    class early_hal
+    user system
+    group system
diff --git a/gatekeeper/aidl/software/android.hardware.gatekeeper-service.nonsecure.xml b/gatekeeper/aidl/software/android.hardware.gatekeeper-service.nonsecure.xml
new file mode 100644
index 0000000..c35421e
--- /dev/null
+++ b/gatekeeper/aidl/software/android.hardware.gatekeeper-service.nonsecure.xml
@@ -0,0 +1,10 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.gatekeeper</name>
+        <version>1</version>
+        <interface>
+            <name>IGatekeeper</name>
+            <instance>default</instance>
+        </interface>
+    </hal>
+</manifest>
diff --git a/gatekeeper/aidl/software/android.hardware.security.sharedsecret-gatekeeper.xml b/gatekeeper/aidl/software/android.hardware.security.sharedsecret-gatekeeper.xml
new file mode 100644
index 0000000..5d94985
--- /dev/null
+++ b/gatekeeper/aidl/software/android.hardware.security.sharedsecret-gatekeeper.xml
@@ -0,0 +1,6 @@
+<manifest version="1.0" type="device">
+    <hal format="aidl">
+        <name>android.hardware.security.sharedsecret</name>
+        <fqname>ISharedSecret/gatekeeper</fqname>
+    </hal>
+</manifest>
diff --git a/gatekeeper/aidl/software/file_contexts b/gatekeeper/aidl/software/file_contexts
new file mode 100644
index 0000000..23a62ea
--- /dev/null
+++ b/gatekeeper/aidl/software/file_contexts
@@ -0,0 +1,3 @@
+(/.*)?                                                          u:object_r:vendor_file:s0
+/etc(/.*)?                                                      u:object_r:vendor_configs_file:s0
+/bin/hw/android\.hardware\.gatekeeper-service\.nonsecure        u:object_r:hal_gatekeeper_remote_exec:s0
diff --git a/gatekeeper/aidl/software/manifest.json b/gatekeeper/aidl/software/manifest.json
new file mode 100644
index 0000000..d0def36
--- /dev/null
+++ b/gatekeeper/aidl/software/manifest.json
@@ -0,0 +1,5 @@
+{
+    "name": "com.android.hardware.gatekeeper",
+    "version": 1,
+    "vendorBootstrap": true
+}
diff --git a/gatekeeper/aidl/software/service.cpp b/gatekeeper/aidl/software/service.cpp
new file mode 100644
index 0000000..beef8c4
--- /dev/null
+++ b/gatekeeper/aidl/software/service.cpp
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2016 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.
+ */
+#define LOG_TAG "android.hardware.gatekeeper-service.nonsecure"
+
+#include <android-base/logging.h>
+#include <android/binder_manager.h>
+#include <android/binder_process.h>
+#include <cutils/properties.h>
+
+#include "GateKeeper.h"
+#include "SharedSecret.h"
+#include "SoftGateKeeper.h"
+
+using aidl::android::hardware::gatekeeper::SoftGateKeeperDevice;
+using aidl::android::hardware::security::sharedsecret::SoftSharedSecret;
+
+int main(int, char** argv) {
+    ::android::base::InitLogging(argv, ::android::base::KernelLogger);
+    ABinderProcess_setThreadPoolMaxThreadCount(0);
+
+    auto secret = ndk::SharedRefBase::make<SoftSharedSecret>();
+    std::string secret_instance = SoftSharedSecret::descriptor + std::string("/gatekeeper");
+    auto status = AServiceManager_addService(secret->asBinder().get(), secret_instance.c_str());
+    CHECK_EQ(status, STATUS_OK);
+
+    ::gatekeeper::SoftGateKeeper implementation(*secret);
+    auto gatekeeper = ndk::SharedRefBase::make<SoftGateKeeperDevice>(implementation);
+    const std::string instance = SoftGateKeeperDevice::descriptor + std::string("/default");
+    status = AServiceManager_addService(gatekeeper->asBinder().get(), instance.c_str());
+    CHECK_EQ(status, STATUS_OK);
+
+    ABinderProcess_joinThreadPool();
+    return -1;  // Should never get here.
+}
diff --git a/security/keymint/aidl/default/ta/clock.rs b/security/keymint/aidl/default/ta/clock.rs
index ad8509a..03e04ec 100644
--- a/security/keymint/aidl/default/ta/clock.rs
+++ b/security/keymint/aidl/default/ta/clock.rs
@@ -16,25 +16,29 @@
 //! Monotonic clock implementation.
 
 use kmr_common::crypto;
-use std::time::Instant;
 
 /// Monotonic clock.
-pub struct StdClock {
-    start: Instant,
-}
+pub struct StdClock;
 
 impl StdClock {
     /// Create new clock instance, holding time since construction.
     pub fn new() -> Self {
-        Self {
-            start: Instant::now(),
-        }
+        Self {}
     }
 }
 
 impl crypto::MonotonicClock for StdClock {
     fn now(&self) -> crypto::MillisecondsSinceEpoch {
-        let duration = self.start.elapsed();
-        crypto::MillisecondsSinceEpoch(duration.as_millis().try_into().unwrap())
+        let mut time = libc::timespec { tv_sec: 0, tv_nsec: 0 };
+        // Use `CLOCK_BOOTTIME` for consistency with the times used by the Cuttlefish
+        // C++ implementation of Gatekeeper.
+        let rc =
+        // Safety: `time` is a valid structure.
+            unsafe { libc::clock_gettime(libc::CLOCK_BOOTTIME, &mut time as *mut libc::timespec) };
+        if rc < 0 {
+            log::warn!("failed to get time!");
+            return crypto::MillisecondsSinceEpoch(0);
+        }
+        crypto::MillisecondsSinceEpoch(((time.tv_sec * 1000) + (time.tv_nsec / 1000 / 1000)).into())
     }
 }