blob: 1c4b5e60f43710bc443fdfe63b47307b6d40e61b [file] [log] [blame]
/*
* Copyright 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "trusty_gatekeeper.h"
#include <inttypes.h>
#include <time.h>
#include <uapi/err.h>
#include <lib/hwkey/hwkey.h>
#include <lib/keymaster/keymaster.h>
#include <lib/rng/trusty_rng.h>
#include <lib/storage/storage.h>
#include <openssl/hmac.h>
#define CALLS_BETWEEN_RNG_RESEEDS 32
#define RNG_RESEED_SIZE 64
#define HMAC_SHA_256_KEY_SIZE 32
#define GATEKEEPER_PREFIX "gatekeeper."
#define STORAGE_ID_LENGTH_MAX 64
#define MAX_FAILURE_RECORDS 10
namespace gatekeeper {
static const uint8_t DERIVATION_DATA[HMAC_SHA_256_KEY_SIZE] =
"TrustyGateKeeperDerivationData0";
TrustyGateKeeper::TrustyGateKeeper() : GateKeeper(),
cached_auth_token_key_len_(0) {
rng_initialized_ = false;
calls_since_reseed_ = 0;
num_mem_records_ = 0;
SeedRngIfNeeded();
}
long TrustyGateKeeper::OpenSession() {
if (!SeedRngIfNeeded()) {
return ERR_NOT_READY;
}
return DerivePasswordKey();
}
void TrustyGateKeeper::CloseSession() {
ClearPasswordKey();
}
bool TrustyGateKeeper::SeedRngIfNeeded() {
if (ShouldReseedRng())
rng_initialized_ = ReseedRng();
return rng_initialized_;
}
bool TrustyGateKeeper::ShouldReseedRng() {
if (!rng_initialized_) {
return true;
}
if (++calls_since_reseed_ % CALLS_BETWEEN_RNG_RESEEDS == 0) {
return true;
}
return false;
}
bool TrustyGateKeeper::ReseedRng() {
UniquePtr<uint8_t[]> rand_seed(new uint8_t[RNG_RESEED_SIZE]);
memset(rand_seed.get(), 0, RNG_RESEED_SIZE);
if (trusty_rng_secure_rand(rand_seed.get(), RNG_RESEED_SIZE) != NO_ERROR) {
return false;
}
trusty_rng_add_entropy(rand_seed.get(), RNG_RESEED_SIZE);
return true;
}
long TrustyGateKeeper::DerivePasswordKey() {
long rc = hwkey_open();
if (rc < 0) {
return rc;
}
hwkey_session_t session = (hwkey_session_t)rc;
password_key_.reset(new uint8_t[HMAC_SHA_256_KEY_SIZE]);
uint32_t kdf_version = HWKEY_KDF_VERSION_1;
rc = hwkey_derive(session, &kdf_version, DERIVATION_DATA,
password_key_.get(), HMAC_SHA_256_KEY_SIZE);
hwkey_close(session);
return rc;
}
void TrustyGateKeeper::ClearPasswordKey() {
memset_s(password_key_.get(), 0, HMAC_SHA_256_KEY_SIZE);
password_key_.reset();
}
/*
* While the GetAuthTokenKey header file says this value cannot be cached,
* after consulting with the GK/KM team this is incorrect - this is a per-boot
* key, and so in-memory caching is acceptable.
*/
bool TrustyGateKeeper::GetAuthTokenKey(const uint8_t** auth_token_key,
uint32_t* length) const {
*length = 0;
*auth_token_key = nullptr;
if (!cached_auth_token_key_) {
long rc = keymaster_open();
if (rc < 0) {
return false;
}
keymaster_session_t session = (keymaster_session_t)rc;
uint8_t* key = nullptr;
uint32_t local_length = 0;
rc = keymaster_get_auth_token_key(session, &key, &local_length);
keymaster_close(session);
if (rc == NO_ERROR) {
cached_auth_token_key_.reset(key);
cached_auth_token_key_len_ = local_length;
} else {
return false;
}
}
*auth_token_key = cached_auth_token_key_.get();
*length = cached_auth_token_key_len_;
return true;
}
void TrustyGateKeeper::GetPasswordKey(const uint8_t** password_key,
uint32_t* length) {
*password_key = const_cast<const uint8_t*>(password_key_.get());
*length = HMAC_SHA_256_KEY_SIZE;
}
void TrustyGateKeeper::ComputePasswordSignature(uint8_t* signature,
uint32_t signature_length,
const uint8_t* key,
uint32_t key_length,
const uint8_t* password,
uint32_t password_length,
salt_t salt) const {
// todo: heap allocate
uint8_t salted_password[password_length + sizeof(salt)];
memcpy(salted_password, &salt, sizeof(salt));
memcpy(salted_password + sizeof(salt), password, password_length);
ComputeSignature(signature, signature_length, key, key_length,
salted_password, password_length + sizeof(salt));
}
void TrustyGateKeeper::GetRandom(void* random, uint32_t requested_size) const {
if (random == NULL)
return;
trusty_rng_secure_rand(reinterpret_cast<uint8_t*>(random), requested_size);
}
void TrustyGateKeeper::ComputeSignature(uint8_t* signature,
uint32_t signature_length,
const uint8_t* key,
uint32_t key_length,
const uint8_t* message,
const uint32_t length) const {
uint8_t buf[HMAC_SHA_256_KEY_SIZE];
unsigned int buf_len;
HMAC(EVP_sha256(), key, key_length, message, length, buf, &buf_len);
size_t to_write = buf_len;
if (buf_len > signature_length)
to_write = signature_length;
memset(signature, 0, signature_length);
memcpy(signature, buf, to_write);
}
uint64_t TrustyGateKeeper::GetMillisecondsSinceBoot() const {
int rc;
int64_t secure_time_ns = 0;
rc = trusty_gettime(0, &secure_time_ns);
if (rc != NO_ERROR) {
secure_time_ns = 0;
TLOGE("%s Error:[0x%x].\n", __func__, rc);
}
return secure_time_ns / 1000 / 1000;
}
bool TrustyGateKeeper::GetSecureFailureRecord(uint32_t uid,
secure_id_t user_id,
failure_record_t* record) {
storage_session_t session;
int rc = storage_open_session(&session, STORAGE_CLIENT_TD_PORT);
if (rc < 0) {
TLOGE("Error: [%d] opening storage session\n", rc);
return false;
}
char id[STORAGE_ID_LENGTH_MAX];
memset(id, 0, sizeof(id));
file_handle_t handle;
snprintf(id, STORAGE_ID_LENGTH_MAX, GATEKEEPER_PREFIX "%u", uid);
rc = storage_open_file(session, &handle, id, 0, 0);
if (rc < 0) {
TLOGE("Error:[%d] opening storage object.\n", rc);
storage_close_session(session);
return false;
}
failure_record_t owner_record;
rc = storage_read(handle, 0, &owner_record, sizeof(owner_record));
storage_close_file(handle);
storage_close_session(session);
if (rc < 0) {
TLOGE("Error:[%d] reading storage object.\n", rc);
return false;
}
if ((size_t)rc < sizeof(owner_record)) {
TLOGE("Error: invalid object size [%d].\n", rc);
return false;
}
if (owner_record.secure_user_id != user_id) {
TLOGE("Error:[%" PRIu64 " != %" PRIu64 "] secure storage corrupt.\n",
owner_record.secure_user_id, user_id);
return false;
}
*record = owner_record;
return true;
}
bool TrustyGateKeeper::GetFailureRecord(uint32_t uid,
secure_id_t user_id,
failure_record_t* record,
bool secure) {
if (secure) {
return GetSecureFailureRecord(uid, user_id, record);
} else {
return GetMemoryRecord(user_id, record);
}
}
bool TrustyGateKeeper::ClearFailureRecord(uint32_t uid,
secure_id_t user_id,
bool secure) {
failure_record_t record;
record.secure_user_id = user_id;
record.last_checked_timestamp = 0;
record.failure_counter = 0;
return WriteFailureRecord(uid, &record, secure);
}
bool TrustyGateKeeper::WriteSecureFailureRecord(uint32_t uid,
failure_record_t* record) {
storage_session_t session;
int rc = storage_open_session(&session, STORAGE_CLIENT_TD_PORT);
if (rc < 0) {
TLOGE("Error: [%d] failed to open storage session\n", rc);
return false;
}
char id[STORAGE_ID_LENGTH_MAX];
memset(id, 0, sizeof(id));
snprintf(id, STORAGE_ID_LENGTH_MAX, GATEKEEPER_PREFIX "%u", uid);
file_handle_t handle;
rc = storage_open_file(session, &handle, id, STORAGE_FILE_OPEN_CREATE, 0);
if (rc < 0) {
TLOGE("Error: [%d] failed to open storage object %s\n", rc, id);
storage_close_session(session);
return false;
}
rc = storage_write(handle, 0, record, sizeof(*record), STORAGE_OP_COMPLETE);
storage_close_file(handle);
storage_close_session(session);
if (rc < 0) {
TLOGE("Error:[%d] writing storage object.\n", rc);
return false;
}
if ((size_t)rc < sizeof(*record)) {
TLOGE("Error: invalid object size [%d].\n", rc);
return false;
}
return true;
}
bool TrustyGateKeeper::WriteFailureRecord(uint32_t uid,
failure_record_t* record,
bool secure) {
if (secure) {
return WriteSecureFailureRecord(uid, record);
} else {
return WriteMemoryRecord(record);
}
}
bool TrustyGateKeeper::IsHardwareBacked() const {
return true;
}
void TrustyGateKeeper::InitMemoryRecords() {
if (!mem_records_.get()) {
failure_record_t* mem_recs = new failure_record_t[MAX_FAILURE_RECORDS];
memset(mem_recs, 0, sizeof(*mem_recs));
mem_records_.reset(mem_recs);
num_mem_records_ = 0;
}
}
bool TrustyGateKeeper::GetMemoryRecord(secure_id_t user_id,
failure_record_t* record) {
InitMemoryRecords();
for (int i = 0; i < num_mem_records_; i++) {
if (mem_records_[i].secure_user_id == user_id) {
*record = mem_records_[i];
return true;
}
}
record->secure_user_id = user_id;
record->failure_counter = 0;
record->last_checked_timestamp = 0;
return true;
}
bool TrustyGateKeeper::WriteMemoryRecord(failure_record_t* record) {
InitMemoryRecords();
int idx = 0;
int min_idx = 0;
uint64_t min_timestamp = ~0ULL;
for (idx = 0; idx < num_mem_records_; idx++) {
if (mem_records_[idx].secure_user_id == record->secure_user_id) {
break;
}
if (mem_records_[idx].last_checked_timestamp <= min_timestamp) {
min_timestamp = mem_records_[idx].last_checked_timestamp;
min_idx = idx;
}
}
if (idx >= MAX_FAILURE_RECORDS) {
// replace oldest element
idx = min_idx;
} else if (idx == num_mem_records_) {
num_mem_records_++;
}
mem_records_[idx] = *record;
return true;
}
} // namespace gatekeeper