Merge "C++ AIDL on-device GateKeeper / SharedSecret impl" into main am: e23e450487
am: e6273eff1d
Original change: https://android-review.googlesource.com/c/platform/hardware/interfaces/+/3023986 Change-Id: I6bda0795988c354187e4b4903be553cc42a780d3 Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
commit
1e3f885162
13 changed files with 737 additions and 9 deletions
71
gatekeeper/aidl/software/Android.bp
Normal file
71
gatekeeper/aidl/software/Android.bp
Normal file
|
@ -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,
|
||||||
|
}
|
149
gatekeeper/aidl/software/GateKeeper.cpp
Normal file
149
gatekeeper/aidl/software/GateKeeper.cpp
Normal file
|
@ -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
|
66
gatekeeper/aidl/software/GateKeeper.h
Normal file
66
gatekeeper/aidl/software/GateKeeper.h
Normal file
|
@ -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
|
131
gatekeeper/aidl/software/SharedSecret.cpp
Normal file
131
gatekeeper/aidl/software/SharedSecret.cpp
Normal file
|
@ -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
|
43
gatekeeper/aidl/software/SharedSecret.h
Normal file
43
gatekeeper/aidl/software/SharedSecret.h
Normal file
|
@ -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
|
189
gatekeeper/aidl/software/SoftGateKeeper.h
Normal file
189
gatekeeper/aidl/software/SoftGateKeeper.h
Normal file
|
@ -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_
|
|
@ -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
|
|
@ -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>
|
|
@ -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>
|
3
gatekeeper/aidl/software/file_contexts
Normal file
3
gatekeeper/aidl/software/file_contexts
Normal file
|
@ -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
|
5
gatekeeper/aidl/software/manifest.json
Normal file
5
gatekeeper/aidl/software/manifest.json
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
{
|
||||||
|
"name": "com.android.hardware.gatekeeper",
|
||||||
|
"version": 1,
|
||||||
|
"vendorBootstrap": true
|
||||||
|
}
|
47
gatekeeper/aidl/software/service.cpp
Normal file
47
gatekeeper/aidl/software/service.cpp
Normal file
|
@ -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.
|
||||||
|
}
|
|
@ -16,25 +16,29 @@
|
||||||
//! Monotonic clock implementation.
|
//! Monotonic clock implementation.
|
||||||
|
|
||||||
use kmr_common::crypto;
|
use kmr_common::crypto;
|
||||||
use std::time::Instant;
|
|
||||||
|
|
||||||
/// Monotonic clock.
|
/// Monotonic clock.
|
||||||
pub struct StdClock {
|
pub struct StdClock;
|
||||||
start: Instant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl StdClock {
|
impl StdClock {
|
||||||
/// Create new clock instance, holding time since construction.
|
/// Create new clock instance, holding time since construction.
|
||||||
pub fn new() -> Self {
|
pub fn new() -> Self {
|
||||||
Self {
|
Self {}
|
||||||
start: Instant::now(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl crypto::MonotonicClock for StdClock {
|
impl crypto::MonotonicClock for StdClock {
|
||||||
fn now(&self) -> crypto::MillisecondsSinceEpoch {
|
fn now(&self) -> crypto::MillisecondsSinceEpoch {
|
||||||
let duration = self.start.elapsed();
|
let mut time = libc::timespec { tv_sec: 0, tv_nsec: 0 };
|
||||||
crypto::MillisecondsSinceEpoch(duration.as_millis().try_into().unwrap())
|
// 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())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue