blob: a5368c57e3ab0cf60841627f2d9e5ca7e2eebaeb [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 "host/commands/secure_env/tpm_keymaster_enforcement.h"
#include <android-base/endian.h>
#include <android-base/logging.h>
#include "host/commands/secure_env/primary_key_builder.h"
#include "host/commands/secure_env/tpm_hmac.h"
#include "host/commands/secure_env/tpm_key_blob_maker.h"
#include "host/commands/secure_env/tpm_random_source.h"
namespace cuttlefish {
using keymaster::HmacSharingParameters;
using keymaster::HmacSharingParametersArray;
using keymaster::KeymasterBlob;
using keymaster::KeymasterEnforcement;
using keymaster::km_id_t;
using keymaster::VerifyAuthorizationRequest;
using keymaster::VerifyAuthorizationResponse;
namespace {
inline bool operator==(const keymaster_blob_t& a, const keymaster_blob_t& b) {
if (!a.data_length && !b.data_length) return true;
if (!(a.data && b.data)) return a.data == b.data;
return (a.data_length == b.data_length &&
!memcmp(a.data, b.data, a.data_length));
}
bool operator==(const HmacSharingParameters& a,
const HmacSharingParameters& b) {
return a.seed == b.seed && !memcmp(a.nonce, b.nonce, sizeof(a.nonce));
}
} // namespace
class CompareHmacSharingParams {
public:
bool operator()(const HmacSharingParameters& a,
const HmacSharingParameters& b) const {
if (a.seed.data_length != b.seed.data_length) {
return a.seed.data_length < b.seed.data_length;
}
auto res = memcmp(a.seed.data, b.seed.data, a.seed.data_length);
if (res != 0) {
return res < 0;
}
static_assert(sizeof(a.nonce) == sizeof(b.nonce));
return memcmp(a.nonce, b.nonce, sizeof(a.nonce)) < 0;
}
};
namespace {
uint64_t timespec_to_ms(const struct timespec& tp) {
if (tp.tv_sec < 0) {
return 0;
}
return static_cast<uint64_t>(tp.tv_sec) * 1000 +
static_cast<uint64_t>(tp.tv_nsec) / 1000000;
}
uint64_t get_wall_clock_time_ms() {
struct timespec tp;
int err = clock_gettime(CLOCK_REALTIME, &tp);
if (err) {
return 0;
}
return timespec_to_ms(tp);
}
} // namespace
TpmKeymasterEnforcement::TpmKeymasterEnforcement(
TpmResourceManager& resource_manager, TpmGatekeeper& gatekeeper)
: KeymasterEnforcement(64, 64),
resource_manager_(resource_manager),
gatekeeper_(gatekeeper) {}
TpmKeymasterEnforcement::~TpmKeymasterEnforcement() {}
bool TpmKeymasterEnforcement::activation_date_valid(
uint64_t activation_date) const {
return activation_date < get_wall_clock_time_ms();
}
bool TpmKeymasterEnforcement::expiration_date_passed(
uint64_t expiration_date) const {
return expiration_date < get_wall_clock_time_ms();
}
bool TpmKeymasterEnforcement::auth_token_timed_out(const hw_auth_token_t& token,
uint32_t timeout) const {
// timeout comes in seconds, token.timestamp comes in milliseconds
uint64_t timeout_ms = 1000 * (uint64_t)timeout;
return (be64toh(token.timestamp) + timeout_ms) < get_current_time_ms();
}
uint64_t TpmKeymasterEnforcement::get_current_time_ms() const {
struct timespec tp;
int err = clock_gettime(CLOCK_BOOTTIME, &tp);
if (err) {
return 0;
}
return timespec_to_ms(tp);
}
keymaster_security_level_t TpmKeymasterEnforcement::SecurityLevel() const {
return KM_SECURITY_LEVEL_TRUSTED_ENVIRONMENT;
}
bool TpmKeymasterEnforcement::ValidateTokenSignature(
const hw_auth_token_t& token) const {
hw_auth_token_t comparison_token = token;
memset(comparison_token.hmac, 0, sizeof(comparison_token.hmac));
/*
* Should match implementation in system/gatekeeper/gatekeeper.cpp
* GateKeeper::MintAuthToken
*/
const uint8_t* auth_token_key = nullptr;
uint32_t auth_token_key_len = 0;
if (!gatekeeper_.GetAuthTokenKey(&auth_token_key, &auth_token_key_len)) {
LOG(WARNING) << "Unable to get gatekeeper auth token";
return false;
}
constexpr uint32_t hashable_length =
sizeof(token.version) + sizeof(token.challenge) + sizeof(token.user_id) +
sizeof(token.authenticator_id) + sizeof(token.authenticator_type) +
sizeof(token.timestamp);
static_assert(offsetof(hw_auth_token_t, hmac) == hashable_length,
"hw_auth_token_t does not appear to be packed");
gatekeeper_.ComputeSignature(
comparison_token.hmac, sizeof(comparison_token.hmac), auth_token_key,
auth_token_key_len, reinterpret_cast<uint8_t*>(&comparison_token),
hashable_length);
static_assert(sizeof(token.hmac) == sizeof(comparison_token.hmac));
return memcmp(token.hmac, comparison_token.hmac, sizeof(token.hmac)) == 0;
}
keymaster_error_t TpmKeymasterEnforcement::GetHmacSharingParameters(
HmacSharingParameters* params) {
if (!have_saved_params_) {
saved_params_.seed = {};
TpmRandomSource random_source{resource_manager_.Esys()};
auto rc = random_source.GenerateRandom(saved_params_.nonce,
sizeof(saved_params_.nonce));
if (rc != KM_ERROR_OK) {
LOG(ERROR) << "Failed to generate HmacSharingParameters nonce";
return rc;
}
have_saved_params_ = true;
}
params->seed = saved_params_.seed;
memcpy(params->nonce, saved_params_.nonce, sizeof(params->nonce));
return KM_ERROR_OK;
}
keymaster_error_t TpmKeymasterEnforcement::ComputeSharedHmac(
const HmacSharingParametersArray& hmac_array, KeymasterBlob* sharingCheck) {
std::set<HmacSharingParameters, CompareHmacSharingParams> sorted_hmac_inputs;
bool found_mine = false;
for (int i = 0; i < hmac_array.num_params; i++) {
HmacSharingParameters sharing_params;
sharing_params.seed =
keymaster::KeymasterBlob(hmac_array.params_array[i].seed);
memcpy(sharing_params.nonce, hmac_array.params_array[i].nonce,
sizeof(sharing_params.nonce));
found_mine = found_mine || (sharing_params == saved_params_);
sorted_hmac_inputs.emplace(std::move(sharing_params));
}
if (!found_mine) return KM_ERROR_INVALID_ARGUMENT;
// unique data has a low maximum size, so combine the hmac parameters
char unique_data[] = "\0\0\0\0\0\0\0\0\0\0";
int unique_index = 0;
for (const auto& hmac_sharing : sorted_hmac_inputs) {
for (size_t j = 0; j < hmac_sharing.seed.data_length; j++) {
unique_data[unique_index % sizeof(unique_data)] ^=
hmac_sharing.seed.data[j];
unique_index++;
}
for (auto nonce_byte : hmac_sharing.nonce) {
unique_data[unique_index % sizeof(unique_data)] ^= nonce_byte;
unique_index++;
}
}
auto signing_key_builder = PrimaryKeyBuilder();
signing_key_builder.SigningKey();
signing_key_builder.UniqueData(std::string(unique_data, sizeof(unique_data)));
auto signing_key = signing_key_builder.CreateKey(resource_manager_);
if (!signing_key) {
LOG(ERROR) << "Could not make signing key for key id";
return KM_ERROR_UNKNOWN_ERROR;
}
static const uint8_t signing_input[] = "Keymaster HMAC Verification";
auto hmac =
TpmHmac(resource_manager_, signing_key->get(), TpmAuth(ESYS_TR_PASSWORD),
signing_input, sizeof(signing_input));
if (!hmac) {
LOG(ERROR) << "Unable to complete signing check";
return KM_ERROR_UNKNOWN_ERROR;
}
*sharingCheck = KeymasterBlob(hmac->buffer, hmac->size);
return KM_ERROR_OK;
}
VerifyAuthorizationResponse TpmKeymasterEnforcement::VerifyAuthorization(
const VerifyAuthorizationRequest& request) {
struct VerificationData {
uint64_t challenge;
uint64_t timestamp;
keymaster_security_level_t security_level;
};
VerifyAuthorizationResponse response(keymaster::kDefaultMessageVersion);
response.error = KM_ERROR_UNKNOWN_ERROR;
response.token.challenge = request.challenge;
response.token.timestamp = get_current_time_ms();
response.token.security_level = SecurityLevel();
VerificationData verify_data{
.challenge = response.token.challenge,
.timestamp = response.token.timestamp,
.security_level = response.token.security_level,
};
auto signing_key_builder = PrimaryKeyBuilder();
signing_key_builder.SigningKey();
signing_key_builder.UniqueData("verify_authorization");
auto signing_key = signing_key_builder.CreateKey(resource_manager_);
if (!signing_key) {
LOG(ERROR) << "Could not make signing key for verifying authorization";
return response;
}
auto hmac =
TpmHmac(resource_manager_, signing_key->get(), TpmAuth(ESYS_TR_PASSWORD),
reinterpret_cast<uint8_t*>(&verify_data), sizeof(verify_data));
if (!hmac) {
LOG(ERROR) << "Could not calculate verification hmac";
return response;
} else if (hmac->size == 0) {
LOG(ERROR) << "hmac was too short";
return response;
}
response.token.mac = KeymasterBlob(hmac->buffer, hmac->size);
response.error = KM_ERROR_OK;
return response;
}
keymaster_error_t TpmKeymasterEnforcement::GenerateTimestampToken(
keymaster::TimestampToken* token) {
token->timestamp = get_current_time_ms();
token->security_level = SecurityLevel();
token->mac = KeymasterBlob();
auto signing_key_builder = PrimaryKeyBuilder();
signing_key_builder.SigningKey();
signing_key_builder.UniqueData("timestamp_token");
auto signing_key = signing_key_builder.CreateKey(resource_manager_);
if (!signing_key) {
LOG(ERROR) << "Could not make signing key for verifying authorization";
return KM_ERROR_UNKNOWN_ERROR;
}
std::vector<uint8_t> token_buf_to_sign(token->SerializedSize(), 0);
auto hmac =
TpmHmac(resource_manager_, signing_key->get(), TpmAuth(ESYS_TR_PASSWORD),
token_buf_to_sign.data(), token_buf_to_sign.size());
if (!hmac) {
LOG(ERROR) << "Could not calculate timestamp token hmac";
return KM_ERROR_UNKNOWN_ERROR;
} else if (hmac->size == 0) {
LOG(ERROR) << "hmac was too short";
return KM_ERROR_UNKNOWN_ERROR;
}
token->mac = KeymasterBlob(hmac->buffer, hmac->size);
return KM_ERROR_OK;
}
keymaster::KmErrorOr<std::array<uint8_t, 32>>
TpmKeymasterEnforcement::ComputeHmac(
const std::vector<uint8_t>& data_to_mac) const {
std::array<uint8_t, 32> result;
const uint8_t* auth_token_key = nullptr;
uint32_t auth_token_key_len = 0;
if (!gatekeeper_.GetAuthTokenKey(&auth_token_key, &auth_token_key_len)) {
LOG(WARNING) << "Unable to get gatekeeper auth token";
return KM_ERROR_UNKNOWN_ERROR;
}
gatekeeper_.ComputeSignature(result.data(), result.size(), auth_token_key,
auth_token_key_len, data_to_mac.data(),
data_to_mac.size());
return result;
}
bool TpmKeymasterEnforcement::CreateKeyId(const keymaster_key_blob_t& key_blob,
km_id_t* keyid) const {
auto signing_key_builder = PrimaryKeyBuilder();
signing_key_builder.SigningKey();
signing_key_builder.UniqueData("key_id");
auto signing_key = signing_key_builder.CreateKey(resource_manager_);
if (!signing_key) {
LOG(ERROR) << "Could not make signing key for key id";
return false;
}
auto hmac =
TpmHmac(resource_manager_, signing_key->get(), TpmAuth(ESYS_TR_PASSWORD),
key_blob.key_material, key_blob.key_material_size);
if (!hmac) {
LOG(ERROR) << "Failed to make a signature for a key id";
return false;
}
if (hmac->size < sizeof(km_id_t)) {
LOG(ERROR) << "hmac return size was less than " << sizeof(km_id_t)
<< ", got " << hmac->size;
return false;
}
memcpy(keyid, hmac->buffer, sizeof(km_id_t));
return true;
}
} // namespace cuttlefish