blob: e4f61e7b8878c10022a53a6d168952290f68aefa [file] [log] [blame]
//
// Copyright (C) 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.
#include "tpm_gatekeeper.h"
#include <algorithm>
#include <optional>
#include <vector>
#include <android-base/logging.h>
#include <tss2/tss2_esys.h>
#include <tss2/tss2_mu.h>
#include <tss2/tss2_rc.h>
#include "host/commands/secure_env/primary_key_builder.h"
#include "host/commands/secure_env/tpm_auth.h"
#include "host/commands/secure_env/tpm_hmac.h"
#include "host/commands/secure_env/tpm_random_source.h"
TpmGatekeeper::TpmGatekeeper(
TpmResourceManager& resource_manager,
GatekeeperStorage& secure_storage,
GatekeeperStorage& insecure_storage)
: resource_manager_(resource_manager)
, secure_storage_(secure_storage)
, insecure_storage_(insecure_storage) {
}
/*
* The reinterpret_cast and kPasswordUnique data is combined together with TPM
* internal state to create the actual key used for Gatekeeper operations.
*/
bool TpmGatekeeper::GetAuthTokenKey(
const uint8_t** auth_token_key, uint32_t* length) const {
static constexpr char kAuthTokenUnique[] = "TpmGatekeeper auth token key";
*auth_token_key = reinterpret_cast<const uint8_t*>(kAuthTokenUnique);
*length = sizeof(kAuthTokenUnique);
return true;
}
void TpmGatekeeper::GetPasswordKey(
const uint8_t** password_key, uint32_t* length) {
static constexpr char kPasswordUnique[] = "TpmGatekeeper password key";
*password_key = reinterpret_cast<const uint8_t*>(kPasswordUnique);
*length = sizeof(kPasswordUnique);
}
void TpmGatekeeper::ComputePasswordSignature(
uint8_t* signature,
uint32_t signature_length,
const uint8_t* key,
uint32_t key_length,
const uint8_t* password,
uint32_t password_length,
gatekeeper::salt_t salt) const {
std::vector<uint8_t> message(password_length + sizeof(salt));
memcpy(message.data(), password, password_length);
memcpy(message.data() + password_length, &salt, sizeof(salt));
return ComputeSignature(
signature,
signature_length,
key,
key_length,
message.data(),
message.size());
}
void TpmGatekeeper::GetRandom(void* random, uint32_t requested_size) const {
auto random_uint8 = reinterpret_cast<uint8_t*>(random);
TpmRandomSource(resource_manager_.Esys())
.GenerateRandom(random_uint8, requested_size);
}
void TpmGatekeeper::ComputeSignature(
uint8_t* signature,
uint32_t signature_length,
const uint8_t* key,
uint32_t key_length,
const uint8_t* message,
uint32_t length) const {
memset(signature, 0, signature_length);
std::string key_unique(reinterpret_cast<const char*>(key), key_length);
PrimaryKeyBuilder key_builder;
key_builder.UniqueData(key_unique);
key_builder.SigningKey();
auto key_slot = key_builder.CreateKey(resource_manager_);
if (!key_slot) {
LOG(ERROR) << "Unable to load signing key into TPM memory";
return;
}
auto calculated_signature =
TpmHmac(
resource_manager_,
key_slot->get(),
TpmAuth(ESYS_TR_PASSWORD),
message,
length);
if (!calculated_signature) {
LOG(ERROR) << "Failure in calculating signature";
return;
}
memcpy(
signature,
calculated_signature->buffer,
std::min((int) calculated_signature->size, (int) signature_length));
}
uint64_t TpmGatekeeper::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);
}
gatekeeper::failure_record_t DefaultRecord(
gatekeeper::secure_id_t secure_user_id) {
return (gatekeeper::failure_record_t) {
.secure_user_id = secure_user_id,
.last_checked_timestamp = 0,
.failure_counter = 0,
};
}
static std::unique_ptr<TPM2B_MAX_NV_BUFFER> RecordToNvBuffer(
const gatekeeper::failure_record_t& record) {
auto ret = std::make_unique<TPM2B_MAX_NV_BUFFER>();
static_assert(sizeof(ret->buffer) >= sizeof(record));
ret->size = sizeof(record);
std::memcpy(ret->buffer, &record, sizeof(record));
return ret;
}
static std::optional<gatekeeper::failure_record_t> NvBufferToRecord(
const TPM2B_MAX_NV_BUFFER& buffer) {
gatekeeper::failure_record_t ret;
if (buffer.size != sizeof(ret)) {
LOG(ERROR) << "NV Buffer had an incorrect size.";
return {};
}
memcpy(&ret, buffer.buffer, sizeof(ret));
return ret;
}
static bool GetFailureRecordImpl(
GatekeeperStorage& storage,
uint32_t uid,
gatekeeper::secure_id_t secure_user_id,
gatekeeper::failure_record_t *record) {
Json::Value key{std::to_string(uid)}; // jsoncpp integer comparisons are janky
if (!storage.HasKey(key)) {
if (!storage.Allocate(key, sizeof(gatekeeper::failure_record_t))) {
LOG(ERROR) << "Allocation failed for user " << uid;
return false;
}
auto buf = RecordToNvBuffer(DefaultRecord(secure_user_id));
if (!storage.Write(key, *buf)) {
LOG(ERROR) << "Failed to write record for " << uid;
return false;
}
}
auto record_read = storage.Read(key);
if (!record_read) {
LOG(ERROR) << "Failed to read record for " << uid;
return false;
}
auto record_decoded = NvBufferToRecord(*record_read);
if (!record_decoded) {
LOG(ERROR) << "Failed to deserialize record for " << uid;
return false;
}
if (record_decoded->secure_user_id == secure_user_id) {
*record = *record_decoded;
return true;
}
LOG(DEBUG) << "User id mismatch for " << uid;
auto buf = RecordToNvBuffer(DefaultRecord(secure_user_id));
if (!storage.Write(key, *buf)) {
LOG(ERROR) << "Failed to write record for " << uid;
return false;
}
*record = DefaultRecord(secure_user_id);
return true;
}
bool TpmGatekeeper::GetFailureRecord(
uint32_t uid,
gatekeeper::secure_id_t secure_user_id,
gatekeeper::failure_record_t *record,
bool secure) {
GatekeeperStorage& storage = secure ? secure_storage_ : insecure_storage_;
return GetFailureRecordImpl(storage, uid, secure_user_id, record);
}
static bool WriteFailureRecordImpl(
GatekeeperStorage& storage,
uint32_t uid,
gatekeeper::failure_record_t* record) {
Json::Value key{std::to_string(uid)}; // jsoncpp integer comparisons are janky
if (!storage.HasKey(key)) {
if (!storage.Allocate(key, sizeof(gatekeeper::failure_record_t))) {
LOG(ERROR) << "Allocation failed for user " << uid;
return false;
}
}
auto buf = RecordToNvBuffer(*record);
if (!storage.Write(key, *buf)) {
LOG(ERROR) << "Failed to write record for " << uid;
return false;
}
return true;
}
bool TpmGatekeeper::ClearFailureRecord(
uint32_t uid, gatekeeper::secure_id_t secure_user_id, bool secure) {
GatekeeperStorage& storage = secure ? secure_storage_ : insecure_storage_;
gatekeeper::failure_record_t record = DefaultRecord(secure_user_id);
return WriteFailureRecordImpl(storage, uid, &record);
}
bool TpmGatekeeper::WriteFailureRecord(
uint32_t uid, gatekeeper::failure_record_t *record, bool secure) {
GatekeeperStorage& storage = secure ? secure_storage_ : insecure_storage_;
return WriteFailureRecordImpl(storage, uid, record);
}
bool TpmGatekeeper::IsHardwareBacked() const {
return true;
}