Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e23e4504 authored by Cody Schuffelen's avatar Cody Schuffelen Committed by Gerrit Code Review
Browse files

Merge "C++ AIDL on-device GateKeeper / SharedSecret impl" into main

parents 056aa140 097e6253
Loading
Loading
Loading
Loading
+71 −0
Original line number Diff line number Diff line
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,
}
+149 −0
Original line number Diff line number Diff line
/*
 * 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
+66 −0
Original line number Diff line number Diff line
/*
 * 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
+131 −0
Original line number Diff line number Diff line
/*
 * 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
+43 −0
Original line number Diff line number Diff line
/*
 * 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
Loading