blob: 9682e294572754861e8dfa0ced8c93f314beeddc [file] [log] [blame]
// Copyright 2015 The Chromium OS Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "attestation/server/attestation_service.h"
#include <algorithm>
#include <climits>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <attestation/proto_bindings/attestation_ca.pb.h>
#include <base/bind.h>
#include <base/bind_helpers.h>
#include <base/callback.h>
#include <base/hash/sha1.h>
#include <base/stl_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <brillo/cryptohome.h>
#include <brillo/data_encoding.h>
#include <crypto/sha2.h>
#include <openssl/objects.h>
#include <policy/device_policy.h>
#include <policy/libpolicy.h>
#if USE_TPM2
#include <trunks/tpm_utility.h>
#endif
extern "C" {
#if USE_TPM2
#include <trunks/cr50_headers/virtual_nvmem.h>
#endif
#include <vboot/crossystem.h>
}
#include "attestation/common/database.pb.h"
#include "attestation/common/tpm_utility_factory.h"
#include "attestation/pca_agent/client/proxy_factory.h"
#include "attestation/server/attestation_flow.h"
#include "attestation/server/database_impl.h"
#include "attestation/server/google_keys.h"
namespace {
const size_t kNonceSize = 20; // As per TPM_NONCE definition.
const int kNumTemporalValues = 5;
const char kKnownBootModes[8][3] = {{0, 0, 0}, {0, 0, 1}, {0, 1, 0}, {0, 1, 1},
{1, 0, 0}, {1, 0, 1}, {1, 1, 0}, {1, 1, 1}};
const char kVerifiedBootMode[3] = {0, 0, 1};
// Context name to derive stable secret for attestation-based enterprise
// enrollment.
const char kAttestationBasedEnterpriseEnrollmentContextName[] =
"attestation_based_enrollment";
struct CertificateAuthority {
const char* issuer;
const char* modulus; // In hex format.
};
const CertificateAuthority kKnownEndorsementCA[] = {
{"IFX TPM EK Intermediate CA 06",
"de9e58a353313d21d683c687d6aaaab240248717557c077161c5e515f41d8efa"
"48329f45658fb550f43f91d1ba0c2519429fb6ef964f89657098c90a9783ad6d"
"3baea625db044734c478768db53b6022c556d8174ed744bd6e4455665715cd5c"
"beb7c3fcb822ab3dfab1ecee1a628c3d53f6085983431598fb646f04347d5ae0"
"021d5757cc6e3027c1e13f10633ae48bbf98732c079c17684b0db58bd0291add"
"e277b037dd13fa3db910e81a4969622a79c85ac768d870f079b54c2b98c856e7"
"15ef0ba9c01ee1da1241838a1307fe94b1ddfa65cdf7eeaa7e5b4b8a94c3dcd0"
"29bb5ebcfc935e56641f4c8cb5e726c68f9dd6b41f8602ef6dc78d870a773571"},
{"IFX TPM EK Intermediate CA 07",
"f04c9b5b9f3cbc2509179f5e0f31dceb302900f528458e002c3e914d6b29e5e0"
"924b0bcab2dd053f65d9d4a8eea8269c85c419dba640a88e14dc5f8c8c1a4269"
"7a5ac4594b36f923110f91d1803d385540c01a433140b06054c77a144ee3a6a6"
"5950c20f9215be3473b1002eb6b1756a22fbc18d21efacbbc8c270c66cf74982"
"e24f057825cab51c0dd840a4f2d059032239c33e3f52c6ca06fe49bf4f60cc28"
"a0fb1173d2ee05a141d30e8ffa32dbb86c1aeb5b309f76c2e462965612ec929a"
"0d3b04acfa4525912c76f765e948be71f505d619cc673a889f0ed9e1d75f237b"
"7af6a68550253cb4c3a8ff16c8091dbcbdea0ff8eee3d5bd92f49c53c5a15c93"},
{"IFX TPM EK Intermediate CA 14",
"D5B2EB8F8F23DD0B5CA0C15D4376E27A0380FD8EB1E52C2C270D961E8C0F66FD"
"62E6ED6B3660FFBD8B0735179476F5E9C2EA4C762F5FEEDD3B5EB91785A724BC"
"4C0617B83966336DD9DC407640871BF99DF4E1701EB5A1F5647FC57879CBB973"
"B2A72BABA8536B2646A37AA5B73E32A4C8F03E35C8834B391AD363F1F7D1DF2B"
"EE39233F47384F3E2D2E8EF83C9539B4DFC360C8AEB88B6111E757AF646DC01A"
"68DAA908C7F8068894E9E991C59005068DD9B0F87113E6A80AB045DB4C1B23FF"
"38A106098C2E184E1CF42A43EA68753F2649999048E8A3C3406032BEB1457070"
"BCBE3A93E122638F6F18FF505C35FB827CE5D0C12F27F45C0F59C8A4A8697849"},
{"IFX TPM EK Intermediate CA 16",
"B98D42D5284620036A6613ED05A1BE11431AE7DE435EC55F72814652B9265EC2"
"9035D401B538A9C84BB5B875450FAE8FBEDEF3430C4108D8516404F3DE4D4615"
"2F471013673A7C7F236304C7363B91C0E0FD9FC7A9EC751521A60A6042839CF7"
"7AEDE3243D0F51F47ACC39676D236BD5298E18B9A4783C60B2A1CD1B32124909"
"D5844649EE4539D6AA05A5902C147B4F062D5145708EAE224EC65A8B51D7A418"
"6327DA8F3B9E7C796F8B2DB3D2BDB39B829BDEBA8D2BF882CBADDB75D76FA8FA"
"313682688BCD2835533A3A68A4AFDF7E597D8B965402FF22A5A4A418FDB4B549"
"F218C3908E66BDCEAB3E2FE5EE0A4A1D9EB41A286ED07B6C112581FDAEA088D9"},
{"IFX TPM EK Intermediate CA 17",
"B0F3CC6F02E8C0486501102731069644A815F631ED41676C05CE3F7E5E5E40DF"
"B3BF6D99787F2A9BE8F8B8035C03D5C2226072985230D4CE8407ACD6403F72E1"
"A4DBF069504E56FA8C0807A704526EAC1E379AE559EB4BBAD9DB4E652B3B14E5"
"38497A5E7768BCE0BFFAF800C61F1F2262775C526E1790A2BECF9A072A58F6A0"
"F3042B5279FE9957BCADC3C9725428B66B15D5263F00C528AC47716DE6938199"
"0FF23BC28F2C33B72D89B5F8EEEF9053B60D230431081D656EA8EC16C7CEFD9E"
"F5A9061A3C921394D453D9AC77397D59B4C3BAF258266F65559469C3007987D5"
"A8338E10FC54CD930303C37007D6E1E6C63F36BCFBA1E494AFB3ECD9A2407FF9"},
{"IFX TPM EK Intermediate CA 21",
"8149397109974D6C0850C8A60304ED7D209B1B88F435B695394DAD9FB4E64180"
"02A3940966D2F04103C88659600EEA8E2A5C697C5F989F62D33A06DA10B50075"
"F37F3CE6AD070413A0E109E16FE652B393C4DAFC5579CCB9915E9A70F5C05BCE"
"0D341D6B887F43C4334BD8EC6A293FFAB737F77A45069CD0345D3D534E84D029"
"029C37A267C0CC2D8DCE3E2C76F21A40F5D8D463882A8CBB92D8235685266753"
"E8F051E78B681E87810A5B21EF719662A8208DFD94C55A126A112E39E0D732D7"
"3C599095FAFF52BBC0E8C5B3DCD904D05DE00D5C5112F3DF7B76602ABE5DC0F8"
"F89B55889A24C54EDBA1234AE498BE9B02CB5C8048D1DC90210705BAFC0E2837"},
{"IFX TPM EK Intermediate CA 29",
"cd424370776890ace339c62d7faae843bb2c765d27685c0441d278361a929062"
"b4c95cc57213c864e91cbb92b1151f17a346a4e754c666f2a3e07ea9ffb9c80f"
"e54d9479f73458c64bf7b0ca4e38821dd318e82d6fe387903ca73ca3e59db48e"
"fe3b3c7c89599be87bb5e439a6f5843a412d4a321f154955448b71ca0b5fda47"
"5c86a1c999dde7a01aa16436e65f0b04874c0db3970546bd806157058c5576a5"
"c00b2bce7173c887f388dc4d5267c68fa5c47fcee3d8491071cd7742d43162cb"
"285f5ba5e0daa0e910fdce566c5bbf7b3701d51660090344195fd7278456bd98"
"48382fc5fceaebf93a2ec88c5722723519692e90d23f869c34d8b1af499d4127"},
{"IFX TPM EK Intermediate CA 30",
"a01cc43c4b66076d483086d0713a336f435e33ed23d3cda05f3c60a6f707416a"
"9e53f0ef0de62c82a720e9ad94df29805b56b44279fd7389de4c60d498c81e3b"
"a27692a045d993e9aaae152768588e5c62213721154529c95b09b201bcb3e573"
"3d98e398d6e05215867d94e3d222e5b7df9f948c14533285821658b282be4bd7"
"fe7197baa642f556d4f18738adef26b2eebfc64045cf4c5dcbff661aa95429f4"
"e2c4921a8723bd8116f0efc038cd4530bb6e9299b7d70327e3fe8790d3d6db3a"
"ebd3ccd12aef3d43cf89463a28ad1306a9d430b08c3411bfeeda63b9fdcc9a23"
"1ff5cc203a7f5ee713d50e1930add1cd32ff64637fc740edb63380a5e6725381"},
{"IFX TPM EK Intermediate CA 49",
"b0bd7dd4a197edae12edeb5c98a31f57af00142ca98ed9d412e1a1e8c3d1f81b"
"c152936ee6b1259cb49a870f358a7dca0c98d866df332727e6f897edcac5ea14"
"2ec2be2f0bb814a72d5986dead0ad20ecefa492966c1ca44fefb0533c311783c"
"d48d3f4027b996b6703d110a257d4bd0326f09e8f928020a6b953de4fb8f1dcb"
"ec3eaa6142f6068c38b4c8e41e85444965a04dfe64cc2ea1c09e374cfd1f4d4d"
"a76f31b57057ae79a803a8e96f5fd158920928ebcf1ff0fee75abce44ade9e71"
"56122cc4a11a4baa0ddf73f926ae58743b493d8c4bc8a393018041b543d8b223"
"d294de1d4fe8ec8f4d4e84646d1b6b78deadd34e507cccf472de1ca9ed0455bb"},
{"NTC TPM EK Root CA 01",
"e836ac61b43e3252d5e1a8a4061997a6a0a272ba3d519d6be6360cc8b4b79e8c"
"d53c07a7ce9e9310ca84b82bbdad32184544ada357d458cf224c4a3130c97d00"
"4933b5db232d8b6509412eb4777e9e1b093c58b82b1679c84e57a6b218b4d61f"
"6dd4c3a66b2dd33b52cb1ffdff543289fa36dd71b7c83b66c1aae37caf7fe88d"
"851a3523e3ea92b59a6b0ca095c5e1d191484c1bff8a33048c3976e826d4c12a"
"e198f7199d183e0e70c8b46e8106edec3914397e051ae2b9a7f0b4bb9cd7f2ed"
"f71064eb0eb473df27b7ccef9a018d715c5fe6ab012a8315f933c7f4fc35d34c"
"efc27de224b2e3de3b3ba316d5df8b90b2eb879e219d270141b78dbb671a3a05"},
{"STM TPM EK Intermediate CA 03",
"a5152b4fbd2c70c0c9a0dd919f48ddcde2b5c0c9988cff3b04ecd844f6cc0035"
"6c4e01b52463deb5179f36acf0c06d4574327c37572292fcd0f272c2d45ea7f2"
"2e8d8d18aa62354c279e03be9220f0c3822d16de1ea1c130b59afc56e08f22f1"
"902a07f881ebea3703badaa594ecbdf8fd1709211ba16769f73e76f348e2755d"
"bba2f94c1869ef71e726f56f8ece987f345c622e8b5c2a5466d41093c0dc2982"
"e6203d96f539b542347a08e87fc6e248a346d61a505f52add7f768a5203d70b8"
"68b6ec92ef7a83a4e6d1e1d259018705755d812175489fae83c4ab2957f69a99"
"9394ac7a243a5c1cd85f92b8648a8e0d23165fdd86fad06990bfd16fb3293379"},
/* TODO(ngm): remove by: Aug 30 08:44:33 2020 GMT */
{"CROS TPM DEV EK ROOT CA",
"cdc108745dc50dd6a1098c31486fb31578607fd64f64b0d91b994244ca1a9a69"
"a74c6bccc7f24923e1513e132dc0d9dbcb1b22089299bb6cb669cbf4b704c992"
"27bb769fa1f91ab11f67fb464a065b34b1a0e824136af5e59d1ac04bda22c199"
"9f7a5b34bd6b50c81b4a88cc097d4dfeb4dc695096463d9529d69f116e2a26de"
"070ef3118287072bdbe94466b8737049809bb8e1276b245930051b2bbbad71dd"
"20d26349d1d83cdb2ff9c65251a17dae4f400ecc3e77f89e27a75fe0709dc81f"
"e172008a3e65de685d9df43e036c557e88f1a9aedf7a91644391523d9728f946"
"45c0e8adaf37e9a15777021ad43b675583302402912d66233c59ad05fa3b34ed"},
{"CROS TPM PRD EK ROOT CA",
"bd6f0198ffa7f7d20c15f81642096e335e2cd74734f73008265fc9957bbe018d"
"fbac0d2a0ea99f5fb7bbff6f0d367b81199e837c390527972aa5392c2ca0f2a3"
"506ee7d4a938f47158a7c56a390df2b781344a82b885a62f1de78f37ec105749"
"69d8abf3163f0cf5c67fa05dd4fb3eb07a7571888b7a87ed57735ce476156bf7"
"d6eff6cb8c8b303c21ebfe0e11b660edbdf903c70ac16927345d0b38c72f1e60"
"1460743584f5a3eaef303dbc5cfda48e4c7a1f338108c7f0c70a694f814b6691"
"ba9d058ab988152bb7097a010e400462187811c3e062001bce8aa808db485bd8"
"2f7f0e1e2a2ddb95c364dffea4c23e872fc3874c4756e85e6cf8eca6eb6a07bf"},
};
const CertificateAuthority kKnownCrosCoreEndorsementCA[] = {
{"IFX TPM EK Intermediate CA 24",
"9D3F39677EBDB7B95F383021EA6EF90AD2BEA4E38B10CA65DCD84D0B33D400FA"
"E7E56FC553975FDADD425227F055C029B6544331E3BA50ED33F6CC02D833EA4E"
"0EECFE9AD1ADD7095F3A804C560F031E8705A3AD5189CBD62678B5B8205C37ED"
"780A3EDE8DE64A08980C048872E789937A49FC4048EADCAC9B3FD0F0DD085E76"
"30DDF9C0C31EFF3B77C6C3601AA7C3DCD10F08616C01435697746A61F920335C"
"0C45A41149F5D22FCD23DBE35003A9AF7FD91C18715E3709F86A38AB149113C4"
"D5273C3C90599734FF627ACBF408B082C76E486091F27446E175C50D340DA0FE"
"5C3FE3D590B8729F4E364E5BF7D854D9AE28EFBCD0CE8F19E6462B3A593983DF"},
{"IFX TPM EK Intermediate CA 50",
"ACB01856664D0C81B545DB926D25019FC2D06B4A97DFB91FD7A5AB1A803AA6F4"
"12FEEE5E3DEF3634172F1271E893C6848B4D156485917DF6F0504947B39F0A5A"
"E14FFBAB9FF00E70448E51F11DEEA1EA16287ABAAE05D3D00FEB1AA064F1CBD9"
"E1E67C057087110F9D3023BFA0545C97BD51E473C5B183E50C2984BD9A2DA39B"
"7D028B895BD939FF0822595DDC948640D06E57ED72EF43B8D8071D2C3C0497A0"
"EC52F682D1637F06979733BAF56DD809D24C20354D73D3849A1C0DAD23AD5CCB"
"F8C679242D13FFFE055CC2AB2692897F0329EEA55AF3BB10A4EB4E2937601196"
"90D64FB352E3D34E05AB53BD4E01EFE3EF56F6DBE315B76A31B0100BF7096093"},
};
// Returns a human-readable description for a known 3-byte |mode|.
std::string GetDescriptionForMode(const char* mode) {
return base::StringPrintf(
"Developer Mode: %s, Recovery Mode: %s, Firmware Type: %s",
mode[0] ? "On" : "Off", mode[1] ? "On" : "Off",
mode[2] ? "Verified" : "Developer");
}
std::string GetHardwareID() {
char buffer[VB_MAX_STRING_PROPERTY];
const char* property =
VbGetSystemPropertyString("hwid", buffer, base::size(buffer));
if (property != nullptr) {
return std::string(property);
}
LOG(WARNING) << "Could not read hwid property.";
return std::string();
}
// Finds CA by |issuer_name| and |is_cros_core| flag. On success returns true
// and fills |public_key_hex| with CA public key hex modulus.
bool GetAuthorityPublicKey(const std::string& issuer_name,
bool is_cros_core,
std::string* public_key_hex) {
const CertificateAuthority* const kKnownCA =
is_cros_core ? kKnownCrosCoreEndorsementCA : kKnownEndorsementCA;
const int kNumIssuers = is_cros_core ? base::size(kKnownCrosCoreEndorsementCA)
: base::size(kKnownEndorsementCA);
for (int i = 0; i < kNumIssuers; ++i) {
if (issuer_name == kKnownCA[i].issuer) {
public_key_hex->assign(kKnownCA[i].modulus);
return true;
}
}
return false;
}
std::string GetACAName(attestation::ACAType aca_type) {
switch (aca_type) {
case attestation::DEFAULT_ACA:
return "the default ACA";
case attestation::TEST_ACA:
return "the test ACA";
default: {
std::ostringstream stream;
stream << "ACA " << aca_type;
return stream.str();
}
}
}
std::string GetIdentityFeaturesString(int identity_features) {
unsigned features_count = 0;
std::ostringstream stream;
if (identity_features == attestation::NO_IDENTITY_FEATURES) {
stream << "NO_IDENTITY_FEATURES";
} else {
// We don't have reflection, copy/paste and adapt these few lines when
// adding a new enum value.
if (identity_features &
attestation::IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID) {
++features_count;
if (stream.tellp() > 0) {
stream << ", ";
}
stream << "IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID";
identity_features &=
~attestation::IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID;
}
// Print other bits which may have been forgotten above.
if (identity_features) {
features_count += 2; // Forces plural.
if (stream.tellp() > 0) {
stream << ", ";
}
stream << "(undecoded features: " << identity_features << ")";
}
}
return std::string("identity feature") + (features_count != 1 ? "s " : " ") +
stream.str();
}
std::string GetKeyTypeName(attestation::KeyType key_type) {
switch (key_type) {
case attestation::KEY_TYPE_ECC:
return "ECC";
case attestation::KEY_TYPE_RSA:
return "RSA";
}
return "unknown";
}
void LogErrorFromCA(const std::string& func,
const std::string& details,
const std::string& extra_details) {
std::ostringstream stream;
stream << func << ": Received error from Attestation CA";
if (!details.empty()) {
stream << ": " << details;
if (!extra_details.empty()) {
stream << ". Extra details: " << extra_details;
}
}
LOG(ERROR) << stream.str() << ".";
}
} // namespace
namespace attestation {
namespace {
// Last PCR index to quote (we start at 0).
constexpr int kLastPcrToQuote = 1;
pca_agent::EnrollRequest ToPcaAgentEnrollRequest(
const AttestationFlowData& data) {
pca_agent::EnrollRequest ret;
ret.set_aca_type(data.aca_type());
ret.set_request(data.result_request());
return ret;
}
pca_agent::GetCertificateRequest ToPcaAgentCertRequest(
const AttestationFlowData& data) {
pca_agent::GetCertificateRequest ret;
ret.set_aca_type(data.aca_type());
ret.set_request(data.result_request());
return ret;
}
} // namespace
#if USE_TPM2
// Description of virtual NVRAM indices used for attestation.
const struct {
NVRAMQuoteType quote_type;
const char* quote_name;
uint32_t nv_index; // From CertifyNV().
uint16_t nv_size; // From CertifyNV().
} kNvramIndexData[] = {
{BOARD_ID, "BoardId", VIRTUAL_NV_INDEX_BOARD_ID,
VIRTUAL_NV_INDEX_BOARD_ID_SIZE},
{SN_BITS, "SN Bits", VIRTUAL_NV_INDEX_SN_DATA,
VIRTUAL_NV_INDEX_SN_DATA_SIZE},
{RSA_PUB_EK_CERT, "RSA Public EK Certificate",
trunks::kRsaEndorsementCertificateIndex, 0},
{RSU_DEVICE_ID, "RSU Device ID", VIRTUAL_NV_INDEX_RSU_DEV_ID,
VIRTUAL_NV_INDEX_RSU_DEV_ID_SIZE}};
// Types of quotes being cached in the identity data.
const NVRAMQuoteType kNvramQuoteTypeInIdentityData[] = {BOARD_ID, SN_BITS};
// Types of quotes needed to obtain an enrollment certificate.
const NVRAMQuoteType kNvramQuoteTypeForEnrollmentCertificate[] = {
BOARD_ID, SN_BITS, RSU_DEVICE_ID};
#endif
using QuoteMap = google::protobuf::Map<int, Quote>;
const size_t kChallengeSignatureNonceSize = 20; // For all TPMs.
AttestationService::AttestationService(brillo::SecureBlob* abe_data)
: abe_data_(abe_data), weak_factory_(this) {}
bool AttestationService::Initialize() {
return InitializeWithCallback(base::DoNothing());
}
bool AttestationService::InitializeWithCallback(
InitializeCompleteCallback callback) {
if (!worker_thread_) {
worker_thread_.reset(new ServiceWorkerThread(this));
worker_thread_->StartWithOptions(
base::Thread::Options(base::MessagePumpType::IO, 0));
LOG(INFO) << "Attestation service started.";
}
// Creates |default_pca_agent_proxy_| here if needed; unlike other objects,
// |default_pca_agent_proxy_| is used in the origin thread instead of worker
// thread.
if (!pca_agent_proxy_) {
default_pca_agent_proxy_ =
attestation::pca_agent::client::CreateWithDBusTaskRunner(
worker_thread_->task_runner());
pca_agent_proxy_ = default_pca_agent_proxy_.get();
}
worker_thread_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&AttestationService::InitializeTask,
base::Unretained(this), std::move(callback)));
return true;
}
void AttestationService::InitializeTask(InitializeCompleteCallback callback) {
if (!tpm_utility_) {
default_tpm_utility_.reset(TpmUtilityFactory::New());
CHECK(default_tpm_utility_->Initialize());
tpm_utility_ = default_tpm_utility_.get();
}
if (!crypto_utility_) {
default_crypto_utility_.reset(new CryptoUtilityImpl(tpm_utility_));
crypto_utility_ = default_crypto_utility_.get();
}
#if USE_TPM2
for (int i = 0; i < base::size(kNvramIndexData); ++i) {
nvram_quote_type_to_index_data_[kNvramIndexData[i].quote_type] = i;
}
#endif
bool existing_database;
if (database_) {
existing_database = true;
} else {
default_database_.reset(new DatabaseImpl(crypto_utility_, tpm_utility_));
existing_database = default_database_->Initialize();
database_ = default_database_.get();
}
if (existing_database && MigrateAttestationDatabase()) {
if (!database_->SaveChanges()) {
LOG(WARNING) << "Attestation: Failed to persist database changes.";
}
}
if (!key_store_) {
pkcs11_token_manager_.reset(new chaps::TokenManagerClient());
default_key_store_.reset(new Pkcs11KeyStore(pkcs11_token_manager_.get()));
key_store_ = default_key_store_.get();
}
if (hwid_.empty()) {
hwid_ = GetHardwareID();
}
if (!IsPreparedForEnrollment()) {
worker_thread_->task_runner()->PostTask(
FROM_HERE, base::BindOnce(&AttestationService::PrepareForEnrollment,
base::Unretained(this), std::move(callback)));
} else {
// Ignore errors. If failed this time, will be re-attempted on next boot.
tpm_utility_->RemoveOwnerDependency();
std::move(callback).Run(false);
}
}
bool AttestationService::MigrateAttestationDatabase() {
bool migrated = false;
auto* database_pb = database_->GetMutableProtobuf();
if (database_pb->has_credentials()) {
if (!database_pb->credentials().encrypted_endorsement_credentials().count(
DEFAULT_ACA) &&
database_pb->credentials()
.has_default_encrypted_endorsement_credential()) {
LOG(INFO) << "Attestation: Migrating endorsement credential for "
<< GetACAName(DEFAULT_ACA) << ".";
(*database_pb->mutable_credentials()
->mutable_encrypted_endorsement_credentials())[DEFAULT_ACA] =
database_pb->credentials().default_encrypted_endorsement_credential();
migrated = true;
}
if (!database_pb->credentials().encrypted_endorsement_credentials().count(
TEST_ACA) &&
database_pb->credentials()
.has_test_encrypted_endorsement_credential()) {
LOG(INFO) << "Attestation: Migrating endorsement credential for "
<< GetACAName(TEST_ACA) << ".";
(*database_pb->mutable_credentials()
->mutable_encrypted_endorsement_credentials())[TEST_ACA] =
database_pb->credentials().test_encrypted_endorsement_credential();
migrated = true;
}
}
// Migrate identity data if needed.
migrated |= MigrateIdentityData();
if (migrated) {
EncryptAllEndorsementCredentials();
LOG(INFO) << "Attestation: Migrated attestation database.";
}
// Migrate Rsa PublicKey Format to SubjectPublicKeyInfo
if (database_pb->credentials().has_legacy_endorsement_public_key()) {
std::string public_key_info;
if (GetSubjectPublicKeyInfo(
database_pb->credentials().endorsement_key_type(),
database_pb->credentials().legacy_endorsement_public_key(),
&public_key_info)) {
database_pb->mutable_credentials()->set_endorsement_public_key(
public_key_info);
} else {
// If the format conversion fails, that means the EK public key is broken
// somehow, which should not happen. If it does, that means EK data
// becomes invalid. Clean up all EK metadata to resolve this problem.
LOG(ERROR) << __func__ << ": Migrate public format fail.";
database_pb->mutable_credentials()->clear_endorsement_key_type();
database_pb->mutable_credentials()->clear_endorsement_credential();
}
database_pb->mutable_credentials()->clear_legacy_endorsement_public_key();
migrated |= true;
}
return migrated;
}
bool AttestationService::MigrateIdentityData() {
auto* database_pb = database_->GetMutableProtobuf();
if (database_pb->identities().size() > 0) {
// We already migrated identity data.
return false;
}
bool error = false;
// The identity we're creating will have the next index in identities.
LOG(INFO) << "Attestation: Migrating existing identity into identity "
<< database_pb->identities().size() << ".";
CHECK(database_pb->identities().size() == kFirstIdentity);
AttestationDatabase::Identity* identity_data =
database_pb->mutable_identities()->Add();
identity_data->set_features(IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID);
if (database_pb->has_identity_binding()) {
identity_data->mutable_identity_binding()->CopyFrom(
database_pb->identity_binding());
}
if (database_pb->has_identity_key()) {
identity_data->mutable_identity_key()->CopyFrom(
database_pb->identity_key());
identity_data->mutable_identity_key()->clear_identity_credential();
if (database_pb->identity_key().has_identity_credential()) {
// Create an identity certificate for this identity and the default ACA.
AttestationDatabase::IdentityCertificate identity_certificate;
identity_certificate.set_identity(kFirstIdentity);
identity_certificate.set_aca(DEFAULT_ACA);
identity_certificate.set_identity_credential(
database_pb->identity_key().identity_credential());
auto* map = database_pb->mutable_identity_certificates();
auto in = map->insert(IdentityCertificateMap::value_type(
DEFAULT_ACA, identity_certificate));
if (!in.second) {
LOG(ERROR) << "Attestation: Could not migrate existing identity.";
error = true;
}
}
if (database_pb->identity_key().has_enrollment_id()) {
database_->GetMutableProtobuf()->set_enrollment_id(
database_pb->identity_key().enrollment_id());
}
}
if (database_pb->has_pcr0_quote()) {
auto in = identity_data->mutable_pcr_quotes()->insert(
QuoteMap::value_type(0, database_pb->pcr0_quote()));
if (!in.second) {
LOG(ERROR) << "Attestation: Could not migrate existing identity.";
error = true;
}
} else {
LOG(ERROR) << "Attestation: Missing PCR0 quote in existing database.";
error = true;
}
if (database_pb->has_pcr1_quote()) {
auto in = identity_data->mutable_pcr_quotes()->insert(
QuoteMap::value_type(1, database_pb->pcr1_quote()));
if (!in.second) {
LOG(ERROR) << "Attestation: Could not migrate existing identity.";
error = true;
}
} else {
LOG(ERROR) << "Attestation: Missing PCR1 quote in existing database.";
error = true;
}
if (error) {
database_pb->mutable_identities()->RemoveLast();
database_pb->mutable_identity_certificates()->erase(DEFAULT_ACA);
}
return !error;
}
void AttestationService::ShutdownTask() {
database_ = nullptr;
default_database_.reset(nullptr);
crypto_utility_ = nullptr;
default_crypto_utility_.reset(nullptr);
tpm_utility_ = nullptr;
default_tpm_utility_.reset(nullptr);
}
void AttestationService::GetKeyInfo(const GetKeyInfoRequest& request,
const GetKeyInfoCallback& callback) {
auto result = std::make_shared<GetKeyInfoReply>();
base::Closure task = base::Bind(&AttestationService::GetKeyInfoTask,
base::Unretained(this), request, result);
base::Closure reply =
base::Bind(&AttestationService::TaskRelayCallback<GetKeyInfoReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::GetKeyInfoTask(
const GetKeyInfoRequest& request,
const std::shared_ptr<GetKeyInfoReply>& result) {
CertifiedKey key;
if (!FindKeyByLabel(request.username(), request.key_label(), &key)) {
result->set_status(STATUS_INVALID_PARAMETER);
return;
}
std::string public_key_info;
if (!GetSubjectPublicKeyInfo(key.key_type(), key.public_key(),
&public_key_info)) {
LOG(ERROR) << __func__ << ": Bad public key.";
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
result->set_key_type(key.key_type());
result->set_key_usage(key.key_usage());
result->set_public_key(public_key_info);
result->set_certify_info(key.certified_key_info());
result->set_certify_info_signature(key.certified_key_proof());
if (key.has_intermediate_ca_cert()) {
result->set_certificate(CreatePEMCertificateChain(key));
} else {
result->set_certificate(key.certified_key_credential());
}
result->set_payload(key.payload());
}
void AttestationService::GetEndorsementInfo(
const GetEndorsementInfoRequest& request,
const GetEndorsementInfoCallback& callback) {
auto result = std::make_shared<GetEndorsementInfoReply>();
base::Closure task = base::Bind(&AttestationService::GetEndorsementInfoTask,
base::Unretained(this), request, result);
base::Closure reply = base::Bind(
&AttestationService::TaskRelayCallback<GetEndorsementInfoReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
base::Optional<std::string> AttestationService::GetEndorsementPublicKey()
const {
const auto& database_pb = database_->GetProtobuf();
if (database_pb.has_credentials() &&
database_pb.credentials().has_endorsement_public_key()) {
return database_pb.credentials().endorsement_public_key();
}
// Try to read the public key directly.
std::string public_key;
if (!tpm_utility_->GetEndorsementPublicKey(GetEndorsementKeyType(),
&public_key)) {
return base::nullopt;
}
return public_key;
}
base::Optional<std::string> AttestationService::GetEndorsementCertificate()
const {
const auto& database_pb = database_->GetProtobuf();
if (database_pb.has_credentials() &&
database_pb.credentials().has_endorsement_credential()) {
return database_pb.credentials().endorsement_credential();
}
// Try to read the certificate directly.
std::string certificate;
if (!tpm_utility_->GetEndorsementCertificate(GetEndorsementKeyType(),
&certificate)) {
return base::nullopt;
}
return certificate;
}
void AttestationService::GetEndorsementInfoTask(
const GetEndorsementInfoRequest& request,
const std::shared_ptr<GetEndorsementInfoReply>& result) {
const auto& key_type = GetEndorsementKeyType();
if (key_type != KEY_TYPE_RSA && key_type != KEY_TYPE_ECC) {
result->set_status(STATUS_INVALID_PARAMETER);
return;
}
base::Optional<std::string> public_key = GetEndorsementPublicKey();
if (!public_key.has_value()) {
LOG(ERROR) << __func__ << ": Endorsement public key not available.";
result->set_status(STATUS_NOT_AVAILABLE);
return;
}
base::Optional<std::string> certificate = GetEndorsementCertificate();
if (!certificate.has_value()) {
LOG(ERROR) << __func__ << ": Endorsement cert not available.";
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
result->set_ek_public_key(public_key.value());
result->set_ek_certificate(certificate.value());
std::string hash = crypto::SHA256HashString(certificate.value());
result->set_ek_info(
base::StringPrintf("EK Certificate:\n%s\nHash:\n%s\n",
CreatePEMCertificate(certificate.value()).c_str(),
base::HexEncode(hash.data(), hash.size()).c_str()));
}
void AttestationService::GetAttestationKeyInfo(
const GetAttestationKeyInfoRequest& request,
const GetAttestationKeyInfoCallback& callback) {
auto result = std::make_shared<GetAttestationKeyInfoReply>();
base::Closure task =
base::Bind(&AttestationService::GetAttestationKeyInfoTask,
base::Unretained(this), request, result);
base::Closure reply = base::Bind(
&AttestationService::TaskRelayCallback<GetAttestationKeyInfoReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::GetAttestationKeyInfoTask(
const GetAttestationKeyInfoRequest& request,
const std::shared_ptr<GetAttestationKeyInfoReply>& result) {
const int identity = kFirstIdentity;
auto aca_type = request.aca_type();
auto found = FindIdentityCertificate(identity, aca_type);
if (found ==
database_->GetMutableProtobuf()->mutable_identity_certificates()->end()) {
LOG(ERROR) << __func__ << ": Identity " << identity
<< " is not enrolled for attestation with "
<< GetACAName(aca_type) << ".";
result->set_status(STATUS_INVALID_PARAMETER);
return;
}
const auto& identity_certificate = found->second;
if (!IsPreparedForEnrollment() ||
identity_certificate.identity() >=
database_->GetProtobuf().identities().size()) {
result->set_status(STATUS_NOT_AVAILABLE);
return;
}
const auto& identity_pb = database_->GetProtobuf().identities().Get(
identity_certificate.identity());
if (!identity_pb.has_identity_key()) {
result->set_status(STATUS_NOT_AVAILABLE);
return;
}
if (identity_pb.identity_key().has_identity_public_key_der()) {
// TODO(crbug/942487): Use SubjectPublicKeyInfo for identity_public_key_der
std::string public_key_info;
if (!GetSubjectPublicKeyInfo(
identity_pb.identity_key().identity_key_type(),
identity_pb.identity_key().identity_public_key_der(),
&public_key_info)) {
LOG(ERROR) << __func__ << ": Bad public key.";
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
result->set_public_key(public_key_info);
}
if (identity_pb.has_identity_binding() &&
identity_pb.identity_binding().has_identity_public_key_tpm_format()) {
result->set_public_key_tpm_format(
identity_pb.identity_binding().identity_public_key_tpm_format());
}
if (identity_certificate.has_identity_credential()) {
result->set_certificate(identity_certificate.identity_credential());
}
if (identity_pb.pcr_quotes().count(0)) {
*result->mutable_pcr0_quote() = identity_pb.pcr_quotes().at(0);
}
if (identity_pb.pcr_quotes().count(1)) {
*result->mutable_pcr1_quote() = identity_pb.pcr_quotes().at(1);
}
}
void AttestationService::ActivateAttestationKey(
const ActivateAttestationKeyRequest& request,
const ActivateAttestationKeyCallback& callback) {
auto result = std::make_shared<ActivateAttestationKeyReply>();
base::Closure task =
base::Bind(&AttestationService::ActivateAttestationKeyTask,
base::Unretained(this), request, result);
base::Closure reply = base::Bind(
&AttestationService::TaskRelayCallback<ActivateAttestationKeyReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::ActivateAttestationKeyTask(
const ActivateAttestationKeyRequest& request,
const std::shared_ptr<ActivateAttestationKeyReply>& result) {
if (request.encrypted_certificate().tpm_version() !=
tpm_utility_->GetVersion()) {
result->set_status(STATUS_INVALID_PARAMETER);
LOG(ERROR) << __func__ << ": TPM version mismatch.";
return;
}
std::string certificate;
if (!ActivateAttestationKeyInternal(
kFirstIdentity, request.aca_type(), GetEndorsementKeyType(),
request.encrypted_certificate(), request.save_certificate(),
&certificate, nullptr /* certificate_index */)) {
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
result->set_certificate(certificate);
}
void AttestationService::CreateCertifiableKey(
const CreateCertifiableKeyRequest& request,
const CreateCertifiableKeyCallback& callback) {
auto result = std::make_shared<CreateCertifiableKeyReply>();
base::Closure task = base::Bind(&AttestationService::CreateCertifiableKeyTask,
base::Unretained(this), request, result);
base::Closure reply = base::Bind(
&AttestationService::TaskRelayCallback<CreateCertifiableKeyReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::CreateCertifiableKeyTask(
const CreateCertifiableKeyRequest& request,
const std::shared_ptr<CreateCertifiableKeyReply>& result) {
CertifiedKey key;
if (!CreateKey(request.username(), request.key_label(), request.key_type(),
request.key_usage(), &key)) {
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
std::string public_key_info;
if (!GetSubjectPublicKeyInfo(key.key_type(), key.public_key(),
&public_key_info)) {
LOG(ERROR) << __func__ << ": Bad public key.";
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
result->set_public_key(public_key_info);
result->set_certify_info(key.certified_key_info());
result->set_certify_info_signature(key.certified_key_proof());
}
void AttestationService::Decrypt(const DecryptRequest& request,
const DecryptCallback& callback) {
auto result = std::make_shared<DecryptReply>();
base::Closure task = base::Bind(&AttestationService::DecryptTask,
base::Unretained(this), request, result);
base::Closure reply =
base::Bind(&AttestationService::TaskRelayCallback<DecryptReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::DecryptTask(
const DecryptRequest& request,
const std::shared_ptr<DecryptReply>& result) {
CertifiedKey key;
if (!FindKeyByLabel(request.username(), request.key_label(), &key)) {
result->set_status(STATUS_INVALID_PARAMETER);
return;
}
std::string data;
if (!tpm_utility_->Unbind(key.key_blob(), request.encrypted_data(), &data)) {
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
result->set_decrypted_data(data);
}
void AttestationService::Sign(const SignRequest& request,
const SignCallback& callback) {
auto result = std::make_shared<SignReply>();
base::Closure task = base::Bind(&AttestationService::SignTask,
base::Unretained(this), request, result);
base::Closure reply =
base::Bind(&AttestationService::TaskRelayCallback<SignReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::SignTask(const SignRequest& request,
const std::shared_ptr<SignReply>& result) {
CertifiedKey key;
if (!FindKeyByLabel(request.username(), request.key_label(), &key)) {
result->set_status(STATUS_INVALID_PARAMETER);
return;
}
std::string signature;
if (!tpm_utility_->Sign(key.key_blob(), request.data_to_sign(), &signature)) {
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
result->set_signature(signature);
}
void AttestationService::RegisterKeyWithChapsToken(
const RegisterKeyWithChapsTokenRequest& request,
const RegisterKeyWithChapsTokenCallback& callback) {
auto result = std::make_shared<RegisterKeyWithChapsTokenReply>();
base::Closure task =
base::Bind(&AttestationService::RegisterKeyWithChapsTokenTask,
base::Unretained(this), request, result);
base::Closure reply = base::Bind(
&AttestationService::TaskRelayCallback<RegisterKeyWithChapsTokenReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::RegisterKeyWithChapsTokenTask(
const RegisterKeyWithChapsTokenRequest& request,
const std::shared_ptr<RegisterKeyWithChapsTokenReply>& result) {
CertifiedKey key;
if (!FindKeyByLabel(request.username(), request.key_label(), &key)) {
result->set_status(STATUS_INVALID_PARAMETER);
return;
}
std::string certificate;
if (request.include_certificates()) {
certificate = key.certified_key_credential();
}
if (!key_store_->Register(request.username(), request.key_label(),
key.key_type(), key.key_usage(), key.key_blob(),
key.public_key(), certificate)) {
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
if (request.include_certificates()) {
if (key.has_intermediate_ca_cert() &&
!key_store_->RegisterCertificate(request.username(),
key.intermediate_ca_cert())) {
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
for (int i = 0; i < key.additional_intermediate_ca_cert_size(); ++i) {
if (!key_store_->RegisterCertificate(
request.username(), key.additional_intermediate_ca_cert(i))) {
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
}
}
DeleteKey(request.username(), request.key_label());
}
bool AttestationService::IsPreparedForEnrollment() {
if (!tpm_utility_->IsTpmReady()) {
return false;
}
const auto& database_pb = database_->GetProtobuf();
// Note that this function only checks for the existence of endorsement
// credentials, but the identity key, identity key binding and pcr quotes
// signed by the identity key are also required for enrollment.
// In normal circumstances, existence of the endorsement credentials implies
// the existence of the other identity key related pieces, but it is
// possible for that not to be true, for instance, see crbug.com/899932
return database_pb.credentials().has_endorsement_credential() ||
database_pb.credentials().encrypted_endorsement_credentials().size() >
TEST_ACA;
}
bool AttestationService::IsPreparedForEnrollmentWithACA(ACAType aca_type) {
const auto& database_pb = database_->GetProtobuf();
return database_pb.credentials().encrypted_endorsement_credentials().count(
aca_type);
}
bool AttestationService::IsEnrolled() {
return IsEnrolledWithACA(DEFAULT_ACA) || IsEnrolledWithACA(TEST_ACA);
}
bool AttestationService::IsEnrolledWithACA(ACAType aca_type) {
return HasIdentityCertificate(kFirstIdentity, aca_type);
}
AttestationService::IdentityCertificateMap::iterator
AttestationService::FindIdentityCertificate(int identity, ACAType aca_type) {
auto* database_pb = database_->GetMutableProtobuf();
auto end = database_pb->mutable_identity_certificates()->end();
for (auto it = database_pb->mutable_identity_certificates()->begin();
it != end; ++it) {
if (it->second.identity() == identity && it->second.aca() == aca_type) {
return it;
}
}
return end;
}
AttestationDatabase_IdentityCertificate*
AttestationService::FindOrCreateIdentityCertificate(int identity,
ACAType aca_type,
int* cert_index) {
// Find an identity certificate to reuse or create a new one.
int index;
auto* database_pb = database_->GetMutableProtobuf();
auto found = FindIdentityCertificate(identity, aca_type);
if (found == database_pb->mutable_identity_certificates()->end()) {
index = identity == kFirstIdentity
? aca_type
: std::max(static_cast<size_t>(kMaxACATypeInternal),
database_pb->identity_certificates().size());
AttestationDatabase::IdentityCertificate new_identity_certificate;
new_identity_certificate.set_identity(identity);
new_identity_certificate.set_aca(aca_type);
auto* map = database_pb->mutable_identity_certificates();
auto in = map->insert(
IdentityCertificateMap::value_type(index, new_identity_certificate));
if (!in.second) {
LOG(ERROR) << __func__ << ": Failed to create identity certificate "
<< index << " for identity " << identity << " and "
<< GetACAName(aca_type) << ".";
if (cert_index) {
*cert_index = -1;
}
return nullptr;
}
found = in.first;
LOG(INFO) << "Attestation: Creating identity certificate " << index
<< " for identity " << identity << " enrolled with "
<< GetACAName(aca_type);
} else {
index = found->first;
}
if (cert_index) {
*cert_index = index;
}
return &found->second;
}
bool AttestationService::HasIdentityCertificate(int identity,
ACAType aca_type) {
return FindIdentityCertificate(identity, aca_type) !=
database_->GetMutableProtobuf()
->mutable_identity_certificates()
->end();
}
bool AttestationService::CreateEnrollRequestInternal(
ACAType aca_type, std::string* enroll_request) {
const int identity = kFirstIdentity;
if (!IsPreparedForEnrollmentWithACA(aca_type)) {
LOG(ERROR) << __func__ << ": Enrollment with " << GetACAName(aca_type)
<< " is not possible, attestation data does not exist.";
return false;
}
const auto& database_pb = database_->GetProtobuf();
if (database_pb.identities().size() <= identity) {
LOG(ERROR) << __func__ << ": Enrollment with " << GetACAName(aca_type)
<< " is not possible, identity " << identity
<< " does not exist.";
return false;
}
AttestationEnrollmentRequest request_pb;
request_pb.set_tpm_version(tpm_utility_->GetVersion());
*request_pb.mutable_encrypted_endorsement_credential() =
database_pb.credentials().encrypted_endorsement_credentials().at(
aca_type);
const AttestationDatabase::Identity& identity_data =
database_pb.identities().Get(identity);
request_pb.set_identity_public_key(
identity_data.identity_binding().identity_public_key_tpm_format());
*request_pb.mutable_pcr0_quote() = identity_data.pcr_quotes().at(0);
*request_pb.mutable_pcr1_quote() = identity_data.pcr_quotes().at(1);
if (identity_data.features() & IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID) {
std::string enterprise_enrollment_nonce =
ComputeEnterpriseEnrollmentNonce();
if (!enterprise_enrollment_nonce.empty()) {
request_pb.set_enterprise_enrollment_nonce(
enterprise_enrollment_nonce.data(),
enterprise_enrollment_nonce.size());
}
if (GetEndorsementKeyType() != KEY_TYPE_RSA) {
// Include an encrypted quote of the RSA pub EK certificate so that
// an EID can be computed during enrollment.
auto found = identity_data.nvram_quotes().find(RSA_PUB_EK_CERT);
if (found == identity_data.nvram_quotes().end()) {
LOG(ERROR) << __func__
<< ": Cannot find RSA pub EK certificate quote in identity "
<< identity << ".";
return false;
}
std::string serialized_quote;
if (!found->second.SerializeToString(&serialized_quote)) {
LOG(ERROR) << __func__
<< ": Failed to serialize RSA pub EK quote protobuf.";
return false;
}
if (!EncryptDataForAttestationCA(
aca_type, serialized_quote,
request_pb.mutable_encrypted_rsa_endorsement_quote())) {
LOG(ERROR)
<< "Attestation: Failed to encrypt RSA pub EK certificate for "
<< GetACAName(aca_type) << ".";
return false;
}
}
}
if (!request_pb.SerializeToString(enroll_request)) {
LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
return false;
}
return true;
}
bool AttestationService::FinishEnrollInternal(
ACAType aca_type,
const std::string& enroll_response,
std::string* server_error) {
const int identity = kFirstIdentity;
if (!tpm_utility_->IsTpmReady()) {
LOG(ERROR) << __func__
<< ": Cannot finish enrollment as the TPM is not ready.";
return false;
}
AttestationEnrollmentResponse response_pb;
if (!response_pb.ParseFromString(enroll_response)) {
LOG(ERROR) << __func__ << ": Failed to parse response from CA.";
return false;
}
if (response_pb.status() != OK) {
*server_error = response_pb.detail();
LogErrorFromCA(__func__, response_pb.detail(), response_pb.extra_details());
return false;
}
if (response_pb.encrypted_identity_credential().tpm_version() !=
tpm_utility_->GetVersion()) {
LOG(ERROR) << __func__ << ": TPM version mismatch.";
return false;
}
int certificate_index;
if (!ActivateAttestationKeyInternal(
identity, aca_type, GetEndorsementKeyType(),
response_pb.encrypted_identity_credential(),
true /* save_certificate */, nullptr /* certificate */,
&certificate_index)) {
return false;
}
LOG(INFO) << __func__ << ": Enrollment of identity " << identity << " with "
<< GetACAName(aca_type) << " complete. Certificate #"
<< certificate_index << ".";
return true;
}
bool AttestationService::CreateCertificateRequestInternal(
ACAType aca_type,
const std::string& username,
const CertifiedKey& key,
CertificateProfile profile,
const std::string& origin,
std::string* certificate_request,
std::string* message_id) {
if (!tpm_utility_->IsTpmReady()) {
return false;
}
if (!IsEnrolledWithACA(aca_type)) {
LOG(ERROR) << __func__ << ": Device is not enrolled for attestation with "
<< GetACAName(aca_type) << ".";
return false;
}
auto found = FindIdentityCertificate(kFirstIdentity, aca_type);
if (found ==
database_->GetMutableProtobuf()->mutable_identity_certificates()->end()) {
LOG(ERROR) << __func__ << ": Identity " << kFirstIdentity
<< " is not enrolled for attestation with "
<< GetACAName(aca_type) << ".";
return false;
}
const auto& identity_certificate = found->second;
if (!crypto_utility_->GetRandom(kNonceSize, message_id)) {
LOG(ERROR) << __func__ << ": GetRandom(message_id) failed.";
return false;
}
AttestationCertificateRequest request_pb;
request_pb.set_tpm_version(tpm_utility_->GetVersion());
request_pb.set_message_id(*message_id);
request_pb.set_identity_credential(
identity_certificate.identity_credential());
request_pb.set_profile(profile);
#if USE_TPM2
if (profile == ENTERPRISE_ENROLLMENT_CERTIFICATE) {
const int identity = identity_certificate.identity();
const AttestationDatabase::Identity& identity_data =
database_->GetProtobuf().identities().Get(identity);
// Copy NVRAM quotes to include in an enrollment certificate from the
// identity if possible.
std::set<NVRAMQuoteType> not_in_identity;
for (int i = 0; i < base::size(kNvramQuoteTypeForEnrollmentCertificate);
++i) {
const auto quote_type = kNvramQuoteTypeForEnrollmentCertificate[i];
const auto found = identity_data.nvram_quotes().find(quote_type);
if (found != identity_data.nvram_quotes().cend()) {
(*request_pb.mutable_nvram_quotes())[quote_type] = found->second;
} else {
not_in_identity.insert(quote_type);
}
}
// Data that is supposed to be in the identiy but is missing won't be
// quoted now, as we want to drive everything from the identity.
for (int i = 0; i < base::size(kNvramQuoteTypeInIdentityData); ++i) {
const auto quote_type = kNvramQuoteTypeInIdentityData[i];
if (not_in_identity.erase(quote_type)) {
LOG(WARNING)
<< "Could not find "
<< kNvramIndexData[nvram_quote_type_to_index_data_[quote_type]]
.quote_name
<< " quote in identity " << identity
<< " to provide in enrollment cert request.";
}
}
// Quote the other data now.
for (auto it = not_in_identity.cbegin(), end = not_in_identity.cend();
it != end; ++it) {
Quote quote;
if (QuoteNvramData(*it, identity_data.identity_key(), &quote)) {
(*request_pb.mutable_nvram_quotes())[*it] = quote;
} else {
LOG(WARNING)
<< "Could not provide "
<< kNvramIndexData[nvram_quote_type_to_index_data_[*it]].quote_name
<< " quote in enrollment cert request.";
}
}
}
#endif
if (!origin.empty() &&
(profile == CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID)) {
request_pb.set_origin(origin);
request_pb.set_temporal_index(ChooseTemporalIndex(username, origin));
}
request_pb.set_certified_public_key(key.public_key_tpm_format());
request_pb.set_certified_key_info(key.certified_key_info());
request_pb.set_certified_key_proof(key.certified_key_proof());
if (!request_pb.SerializeToString(certificate_request)) {
LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
return false;
}
return true;
}
bool AttestationService::FinishCertificateRequestInternal(
const std::string& certificate_response,
const std::string& username,
const std::string& key_label,
const std::string& message_id,
CertifiedKey* key,
std::string* certificate_chain,
std::string* server_error) {
if (!tpm_utility_->IsTpmReady()) {
return false;
}
AttestationCertificateResponse response_pb;
if (!response_pb.ParseFromString(certificate_response)) {
LOG(ERROR) << __func__ << ": Failed to parse response from Attestation CA.";
return false;
}
if (response_pb.status() != OK) {
*server_error = response_pb.detail();
LogErrorFromCA(__func__, response_pb.detail(), response_pb.extra_details());
return false;
}
if (message_id != response_pb.message_id()) {
LOG(ERROR) << __func__ << ": Message ID mismatch.";
return false;
}
return PopulateAndStoreCertifiedKey(response_pb, username, key_label, key,
certificate_chain);
}
bool AttestationService::PopulateAndStoreCertifiedKey(
const AttestationCertificateResponse& response_pb,
const std::string& username,
const std::string& key_label,
CertifiedKey* key,
std::string* certificate_chain) {
// Finish populating the CertifiedKey protobuf and store it.
key->set_key_name(key_label);
key->set_certified_key_credential(response_pb.certified_key_credential());
key->set_intermediate_ca_cert(response_pb.intermediate_ca_cert());
key->mutable_additional_intermediate_ca_cert()->MergeFrom(
response_pb.additional_intermediate_ca_cert());
if (!SaveKey(username, key_label, *key)) {
return false;
}
LOG(INFO) << "Attestation: Certified key credential received and stored.";
*certificate_chain = CreatePEMCertificateChain(*key);
return true;
}
bool AttestationService::FindKeyByLabel(const std::string& username,
const std::string& key_label,
CertifiedKey* key) {
if (!username.empty()) {
std::string key_data;
if (!key_store_->Read(username, key_label, &key_data)) {
LOG(INFO) << "Key not found: " << key_label;
return false;
}
if (key && !key->ParseFromString(key_data)) {
LOG(ERROR) << "Failed to parse key: " << key_label;
return false;
}
return true;
}
auto database_pb = database_->GetProtobuf();
for (int i = 0; i < database_pb.device_keys_size(); ++i) {
if (database_pb.device_keys(i).key_name() == key_label) {
*key = database_pb.device_keys(i);
return true;
}
}
LOG(INFO) << "Key not found: " << key_label;
return false;
}
bool AttestationService::CreateKey(const std::string& username,
const std::string& key_label,
KeyType key_type,
KeyUsage key_usage,
CertifiedKey* key) {
auto database_pb = database_->GetProtobuf();
const int identity = kFirstIdentity;
if (database_pb.identities().size() <= identity) {
LOG(ERROR) << __func__ << ": Cannot create a certificate request, identity "
<< identity << " does not exist.";
return false;
}
std::string nonce;
if (!crypto_utility_->GetRandom(kNonceSize, &nonce)) {
LOG(ERROR) << __func__ << ": GetRandom(nonce) failed.";
return false;
}
std::string key_blob;
std::string public_key;
std::string public_key_tpm_format;
std::string key_info;
std::string proof;
const auto& identity_data = database_pb.identities().Get(identity);
if (!tpm_utility_->CreateCertifiedKey(
key_type, key_usage, identity_data.identity_key().identity_key_blob(),
nonce, &key_blob, &public_key, &public_key_tpm_format, &key_info,
&proof)) {
return false;
}
key->set_key_blob(key_blob);
key->set_public_key(public_key);
key->set_key_name(key_label);
key->set_public_key_tpm_format(public_key_tpm_format);
key->set_certified_key_info(key_info);
key->set_certified_key_proof(proof);
key->set_key_type(key_type);
key->set_key_usage(key_usage);
return SaveKey(username, key_label, *key);
}
bool AttestationService::SaveKey(const std::string& username,
const std::string& key_label,
const CertifiedKey& key) {
if (!username.empty()) {
std::string key_data;
if (!key.SerializeToString(&key_data)) {
LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
return false;
}
if (!key_store_->Write(username, key_label, key_data)) {
LOG(ERROR) << __func__ << ": Failed to store certified key for user.";
return false;
}
} else {
if (!AddDeviceKey(key_label, key)) {
LOG(ERROR) << __func__ << ": Failed to store certified key for device.";
return false;
}
}
return true;
}
bool AttestationService::DeleteKey(const std::string& username,
const std::string& key_label) {
if (!username.empty()) {
return key_store_->Delete(username, key_label);
} else {
return RemoveDeviceKey(key_label);
}
}
bool AttestationService::DeleteKeysByPrefix(const std::string& username,
const std::string& key_prefix) {
if (!username.empty()) {
return key_store_->DeleteByPrefix(username, key_prefix);
}
return RemoveDeviceKeysByPrefix(key_prefix);
}
bool AttestationService::AddDeviceKey(const std::string& key_label,
const CertifiedKey& key) {
// If a key by this name already exists, reuse the field.
auto* database_pb = database_->GetMutableProtobuf();
bool found = false;
for (int i = 0; i < database_pb->device_keys_size(); ++i) {
if (database_pb->device_keys(i).key_name() == key_label) {
found = true;
*database_pb->mutable_device_keys(i) = key;
break;
}
}
if (!found)
*database_pb->add_device_keys() = key;
return database_->SaveChanges();
}
bool AttestationService::RemoveDeviceKey(const std::string& key_label) {
auto* database_pb = database_->GetMutableProtobuf();
bool found = false;
for (int i = 0; i < database_pb->device_keys_size(); ++i) {
if (database_pb->device_keys(i).key_name() == key_label) {
found = true;
int last = database_pb->device_keys_size() - 1;
if (i < last) {
database_pb->mutable_device_keys()->SwapElements(i, last);
}
database_pb->mutable_device_keys()->RemoveLast();
break;
}
}
if (found) {
if (!database_->SaveChanges()) {
LOG(WARNING) << __func__ << ": Failed to persist key deletion.";
return false;
}
}
return true;
}
bool AttestationService::RemoveDeviceKeysByPrefix(
const std::string& key_prefix) {
// Manipulate the device keys protobuf field. Linear time strategy is to swap
// all elements we want to keep to the front and then truncate.
auto* device_keys = database_->GetMutableProtobuf()->mutable_device_keys();
int next_keep_index = 0;
for (int i = 0; i < device_keys->size(); ++i) {
if (device_keys->Get(i).key_name().find(key_prefix) != 0) {
// Prefix doesn't match -> keep.
if (i != next_keep_index)
device_keys->SwapElements(next_keep_index, i);
++next_keep_index;
}
}
// If no matching keys, do nothing and return success.
if (next_keep_index == device_keys->size()) {
return true;
}
while (next_keep_index < device_keys->size()) {
device_keys->RemoveLast();
}
return database_->SaveChanges();
}
std::string AttestationService::CreatePEMCertificateChain(
const CertifiedKey& key) {
if (key.certified_key_credential().empty()) {
LOG(WARNING) << "Certificate is empty.";
return std::string();
}
std::string pem = CreatePEMCertificate(key.certified_key_credential());
if (!key.intermediate_ca_cert().empty()) {
pem += "\n";
pem += CreatePEMCertificate(key.intermediate_ca_cert());
}
for (int i = 0; i < key.additional_intermediate_ca_cert_size(); ++i) {
pem += "\n";
pem += CreatePEMCertificate(key.additional_intermediate_ca_cert(i));
}
return pem;
}
std::string AttestationService::CreatePEMCertificate(
const std::string& certificate) {
const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----\n";
const char kEndCertificate[] = "-----END CERTIFICATE-----";
std::string pem = kBeginCertificate;
pem += brillo::data_encoding::Base64EncodeWrapLines(certificate);
pem += kEndCertificate;
return pem;
}
int AttestationService::ChooseTemporalIndex(const std::string& user,
const std::string& origin) {
std::string user_hash = crypto::SHA256HashString(user);
std::string origin_hash = crypto::SHA256HashString(origin);
int histogram[kNumTemporalValues] = {};
auto database_pb = database_->GetProtobuf();
for (int i = 0; i < database_pb.temporal_index_record_size(); ++i) {
const AttestationDatabase::TemporalIndexRecord& record =
database_pb.temporal_index_record(i);
// Ignore out-of-range index values.
if (record.temporal_index() < 0 ||
record.temporal_index() >= kNumTemporalValues)
continue;
if (record.origin_hash() == origin_hash) {
if (record.user_hash() == user_hash) {
// We've previously chosen this index for this user, reuse it.
return record.temporal_index();
} else {
// We've previously chosen this index for another user.
++histogram[record.temporal_index()];
}
}
}
int least_used_index = 0;
for (int i = 1; i < kNumTemporalValues; ++i) {
if (histogram[i] < histogram[least_used_index])
least_used_index = i;
}
if (histogram[least_used_index] > 0) {
LOG(WARNING) << "Unique origin-specific identifiers have been exhausted.";
}
// Record our choice for later reference.
AttestationDatabase::TemporalIndexRecord* new_record =
database_pb.add_temporal_index_record();
new_record->set_origin_hash(origin_hash);
new_record->set_user_hash(user_hash);
new_record->set_temporal_index(least_used_index);
if (!database_->SaveChanges()) {
LOG(ERROR) << "Failed to save attestation db when choosing temporal index";
// TODO(louiscollard): Check if any further actions are necessary.
}
return least_used_index;
}
bool AttestationService::GetSubjectPublicKeyInfo(
KeyType key_type,
const std::string& public_key,
std::string* public_key_info) const {
if (key_type == KEY_TYPE_RSA) {
return crypto_utility_->GetRSASubjectPublicKeyInfo(public_key,
public_key_info);
} else if (key_type == KEY_TYPE_ECC) {
// Do nothing, since we always store SubjectPublicKeyInfo in |public_key|
// field and will pass it this utility
*public_key_info = public_key;
return true;
} else {
LOG(ERROR) << __func__ << ": key_type " << key_type << " isn't supported.";
return false;
}
}
void AttestationService::PrepareForEnrollment(
InitializeCompleteCallback callback) {
if (IsPreparedForEnrollment()) {
std::move(callback).Run(true);
return;
}
if (!tpm_utility_->IsTpmReady()) {
// Try again later.
worker_thread_->task_runner()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&AttestationService::PrepareForEnrollment,
base::Unretained(this), std::move(callback)),
base::TimeDelta::FromSeconds(3));
return;
}
base::TimeTicks start = base::TimeTicks::Now();
LOG(INFO) << "Attestation: Preparing for enrollment...";
if (!tpm_utility_->IsPCR0Valid()) {
LOG(ERROR) << __func__ << "Invalid PCR0 value, aborting.";
metrics_.ReportAttestationOpsStatus(
kAttestationPrepareForEnrollment,
AttestationOpsStatus::kInvalidPcr0Value);
return;
}
KeyType key_type = GetEndorsementKeyType();
// Gather information about the endorsement key.
std::string ek_public_key;
if (!tpm_utility_->GetEndorsementPublicKey(key_type, &ek_public_key)) {
LOG(ERROR) << __func__ << ": Failed to get EK public key with key_type "
<< key_type;
std::move(callback).Run(false);
return;
}
LOG(INFO) << "GetEndorsementPublicKey done. (from start: "
<< (base::TimeTicks::Now() - start).InMilliseconds() << "ms.)";
std::string ek_certificate;
if (!tpm_utility_->GetEndorsementCertificate(key_type, &ek_certificate)) {
LOG(ERROR) << __func__ << ": Failed to get " << GetKeyTypeName(key_type)
<< " EK certificate.";
std::move(callback).Run(false);
return;
}
LOG(INFO) << "GetEndorsementCertificate done. (from start: "
<< (base::TimeTicks::Now() - start).InMilliseconds() << "ms.)";
// Create a new AIK and PCR quotes for the first identity with default
// identity features.
if (CreateIdentity(default_identity_features_) < 0) {
std::move(callback).Run(false);
return;
}
LOG(INFO) << "CreateIdentity done. (from start: "
<< (base::TimeTicks::Now() - start).InMilliseconds() << "ms.)";
// Store all this in the attestation database.
auto* database_pb = database_->GetMutableProtobuf();
TPMCredentials* credentials_pb = database_pb->mutable_credentials();
credentials_pb->set_endorsement_key_type(key_type);
credentials_pb->set_endorsement_public_key(ek_public_key);
credentials_pb->set_endorsement_credential(ek_certificate);
// Encrypt the endorsement credential for all the ACAs we know of.
EncryptAllEndorsementCredentials();
if (!database_->SaveChanges()) {
LOG(ERROR) << "Attestation: Failed to write database.";
std::move(callback).Run(false);
return;
}
// Ignore errors when removing dependency. If failed this time, will be
// re-attempted on next boot.
tpm_utility_->RemoveOwnerDependency();
base::TimeDelta delta = (base::TimeTicks::Now() - start);
LOG(INFO) << "Attestation: Prepared successfully (" << delta.InMilliseconds()
<< "ms) with " << GetKeyTypeName(key_type) << " EK.";
metrics_.ReportAttestationOpsStatus(kAttestationPrepareForEnrollment,
AttestationOpsStatus::kSuccess);
std::move(callback).Run(true);
}
int AttestationService::CreateIdentity(int identity_features) {
// The identity we're creating will have the next index in identities.
auto* database_pb = database_->GetMutableProtobuf();
const int identity = database_pb->identities().size();
KeyType identity_key_type = GetAttestationIdentityKeyType();
LOG(INFO) << "Attestation: Creating identity " << identity << " with "
<< GetIdentityFeaturesString(identity_features) << " and "
<< GetKeyTypeName(identity_key_type) << " AIK.";
AttestationDatabase::Identity new_identity_pb;
new_identity_pb.set_features(identity_features);
if (identity_features & IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID) {
auto* identity_key = new_identity_pb.mutable_identity_key();
identity_key->set_enrollment_id(database_pb->enrollment_id());
}
if (!tpm_utility_->CreateIdentity(identity_key_type, &new_identity_pb)) {
LOG(ERROR) << __func__ << " failed to make a new identity.";
return -1;
}
std::string identity_key_blob_for_quote =
new_identity_pb.identity_key().identity_key_blob();
// Quote PCRs and store them in the identity. These quotes are intended to
// be valid for the lifetime of the identity key so they do not need
// external data. This only works when firmware ensures that these PCRs
// will not change unless the TPM owner is cleared.
auto* pcr_quote_map = new_identity_pb.mutable_pcr_quotes();
for (int pcr = 0; pcr <= kLastPcrToQuote; ++pcr) {
std::string quoted_pcr_value;
std::string quoted_data;
std::string quote;
if (tpm_utility_->QuotePCR(pcr, identity_key_blob_for_quote,
&quoted_pcr_value, &quoted_data, &quote)) {
Quote quote_pb;
quote_pb.set_quote(quote);
quote_pb.set_quoted_data(quoted_data);
quote_pb.set_quoted_pcr_value(quoted_pcr_value);
switch (pcr) {
case 1:
quote_pb.set_pcr_source_hint(hwid_);
break;
}
auto in = pcr_quote_map->insert(QuoteMap::value_type(pcr, quote_pb));
if (!in.second) {
LOG(ERROR) << "Attestation: Failed to store PCR" << pcr
<< " quote for identity " << identity << ".";
return -1;
}
} else {
LOG(ERROR) << "Attestation: Failed to generate quote for PCR" << pcr
<< ".";
return -1;
}
}
#if USE_TPM2
// Certify device-specific NV data and insert them in the identity when
// we can certify them. This is an almost identical process to the PCR
// quotes above.
for (int i = 0; i < base::size(kNvramQuoteTypeInIdentityData); ++i) {
if (!InsertCertifiedNvramData(kNvramQuoteTypeInIdentityData[i],
false /* must_be_present */,
&new_identity_pb)) {
return -1;
}
}
// Certify the RSA EK cert only when we are using non-RSA EK. In this case, we
// don't provide the RSA EK cert which originally is used for calculating the
// Enrollment ID.
if ((identity_features & IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID) &&
GetEndorsementKeyType() != KEY_TYPE_RSA) {
if (!InsertCertifiedNvramData(RSA_PUB_EK_CERT, true /* must_be_present */,
&new_identity_pb)) {
return -1;
}
}
#endif
database_pb->add_identities()->CopyFrom(new_identity_pb);
// Return the index of the newly created identity.
return database_pb->identities().size() - 1;
}
bool AttestationService::QuoteNvramData(NVRAMQuoteType quote_type,
const IdentityKey& identity_key,
Quote* quote) {
#if USE_TPM2
const int id = nvram_quote_type_to_index_data_[quote_type];
const char* quote_name = kNvramIndexData[id].quote_name;
const uint32_t nv_index = kNvramIndexData[id].nv_index;
uint16_t nv_size = kNvramIndexData[id].nv_size;
if (!nv_size) {
uint16_t nv_data_size = 0;
if (tpm_utility_->GetNVDataSize(nv_index, &nv_data_size)) {
nv_size = nv_data_size;
} else {
LOG(ERROR) << "Attestation: Failed to obtain data about the "
<< quote_name << ".";
}
}
if (nv_size) {
auto identity_key_blob = identity_key.identity_key_blob();
std::string certified_value;
std::string signature;
if (tpm_utility_->CertifyNV(nv_index, nv_size, identity_key_blob,
&certified_value, &signature)) {
quote->set_quote(signature);
quote->set_quoted_data(certified_value);
return true;
} else {
LOG(WARNING) << "Attestation: Failed to certify " << quote_name
<< " NV data of size " << nv_size << " at index " << std::hex
<< std::showbase << nv_index << ".";
}
}
#else
LOG(ERROR) << __func__ << ": Should not be called for TPM 1.2 devices.";
#endif
return false;
}
bool AttestationService::InsertCertifiedNvramData(
NVRAMQuoteType quote_type,
bool must_be_present,
AttestationDatabase::Identity* identity) {
#if USE_TPM2
Quote quote;
if (!QuoteNvramData(quote_type, identity->identity_key(), &quote)) {
return !must_be_present;
}
auto* nv_quote_map = identity->mutable_nvram_quotes();
auto in_bid = nv_quote_map->insert(QuoteMap::value_type(quote_type, quote));
if (in_bid.second) {
return true;
} else {
LOG(ERROR) << "Attestation: Failed to store "
<< kNvramIndexData[nvram_quote_type_to_index_data_[quote_type]]
.quote_name
<< " quote for identity " << identity << ".";
}
#else
LOG(ERROR) << __func__ << ": Should not be called for TPM 1.2 devices.";
#endif
return false;
}
int AttestationService::GetIdentitiesCount() const {
return database_->GetProtobuf().identities().size();
}
int AttestationService::GetIdentityFeatures(int identity) const {
return database_->GetProtobuf().identities().Get(identity).features();
}
AttestationService::IdentityCertificateMap
AttestationService::GetIdentityCertificateMap() const {
return database_->GetProtobuf().identity_certificates();
}
bool AttestationService::EncryptAllEndorsementCredentials() {
auto* database_pb = database_->GetMutableProtobuf();
base::Optional<std::string> ek_certificate = GetEndorsementCertificate();
if (!ek_certificate.has_value()) {
LOG(ERROR) << "Attestation: Failed to obtain endorsement certificate.";
return false;
}
TPMCredentials* credentials_pb = database_pb->mutable_credentials();
for (int aca = kDefaultACA; aca < kMaxACATypeInternal; ++aca) {
if (credentials_pb->encrypted_endorsement_credentials().count(aca)) {
continue;
}
ACAType aca_type = GetACAType(static_cast<ACATypeInternal>(aca));
LOG(INFO) << "Attestation: Encrypting endorsement credential for "
<< GetACAName(aca_type) << ".";
if (!EncryptDataForAttestationCA(
aca_type, ek_certificate.value(),
&(*credentials_pb
->mutable_encrypted_endorsement_credentials())[aca])) {
LOG(ERROR) << "Attestation: Failed to encrypt EK certificate for "
<< GetACAName(static_cast<ACAType>(aca)) << ".";
return false;
}
}
return true;
}
bool AttestationService::EncryptDataForAttestationCA(
ACAType aca_type, const std::string& data, EncryptedData* encrypted_data) {
const GoogleRsaPublicKey& key = google_keys_.ca_encryption_key(aca_type);
if (!crypto_utility_->EncryptDataForGoogle(data, key.modulus_in_hex(),
key.key_id(), encrypted_data)) {
return false;
}
return true;
}
bool AttestationService::ActivateAttestationKeyInternal(
int identity,
ACAType aca_type,
KeyType ek_key_type,
const EncryptedIdentityCredential& encrypted_certificate,
bool save_certificate,
std::string* certificate,
int* certificate_index) {
const auto& database_pb = database_->GetProtobuf();
if (database_pb.identities().size() <= identity) {
LOG(ERROR) << __func__ << ": Enrollment is not possible, identity "
<< identity << " does not exist.";
return false;
}
const auto& identity_data = database_pb.identities().Get(identity);
std::string certificate_local;
if (encrypted_certificate.tpm_version() == TPM_1_2) {
// TPM 1.2 style activate.
if (!tpm_utility_->ActivateIdentity(
identity_data.identity_key().identity_key_blob(),
encrypted_certificate.asym_ca_contents(),
encrypted_certificate.sym_ca_attestation(), &certificate_local)) {
LOG(ERROR) << __func__ << ": Failed to activate identity " << identity
<< ".";
return false;
}
} else {
// TPM 2.0 style activate.
std::string credential;
if (!tpm_utility_->ActivateIdentityForTpm2(
ek_key_type, identity_data.identity_key().identity_key_blob(),
encrypted_certificate.encrypted_seed(),
encrypted_certificate.credential_mac(),
encrypted_certificate.wrapped_certificate().wrapped_key(),
&credential)) {
LOG(ERROR) << __func__ << ": Failed to activate identity " << identity
<< ".";
return false;
}
if (!crypto_utility_->DecryptIdentityCertificateForTpm2(
credential, encrypted_certificate.wrapped_certificate(),
&certificate_local)) {
LOG(ERROR) << __func__ << ": Failed to decrypt certificate for identity "
<< identity << ".";
return false;
}
}
if (save_certificate) {
if (!tpm_utility_->IsPCR0Valid()) {
LOG(ERROR) << __func__ << "Invalid PCR0 value, aborting.";
metrics_.ReportAttestationOpsStatus(
kAttestationActivateAttestationKey,
AttestationOpsStatus::kInvalidPcr0Value);
return false;
}
int index;
AttestationDatabase_IdentityCertificate* identity_certificate =
FindOrCreateIdentityCertificate(identity, aca_type, &index);
if (!identity_certificate) {
LOG(ERROR) << __func__ << ": Failed to find or create an identity"
<< " certificate for identity " << identity << " with "
<< GetACAName(aca_type) << ".";
return false;
}
// Set the credential obtained when activating the identity with the
// response.
identity_certificate->set_identity_credential(certificate_local);
if (!database_->SaveChanges()) {
LOG(ERROR) << __func__ << ": Failed to persist database changes.";
return false;
}
if (certificate_index) {
*certificate_index = index;
}
}
if (certificate) {
*certificate = certificate_local;
}
return true;
}
void AttestationService::GetEnrollmentPreparations(
const GetEnrollmentPreparationsRequest& request,
const GetEnrollmentPreparationsCallback& callback) {
auto result = std::make_shared<GetEnrollmentPreparationsReply>();
base::Closure task =
base::Bind(&AttestationService::GetEnrollmentPreparationsTask,
base::Unretained(this), request, result);
base::Closure reply = base::Bind(
&AttestationService::TaskRelayCallback<GetEnrollmentPreparationsReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::GetEnrollmentPreparationsTask(
const GetEnrollmentPreparationsRequest& request,
const std::shared_ptr<GetEnrollmentPreparationsReply>& result) {
for (int aca = kDefaultACA; aca < kMaxACATypeInternal; ++aca) {
ACAType aca_type = GetACAType(static_cast<ACATypeInternal>(aca));
if (!request.has_aca_type() || aca_type == request.aca_type()) {
(*result->mutable_enrollment_preparations())[aca_type] =
IsPreparedForEnrollmentWithACA(aca_type);
}
}
}
void AttestationService::GetStatus(const GetStatusRequest& request,
const GetStatusCallback& callback) {
auto result = std::make_shared<GetStatusReply>();
base::Closure task = base::Bind(&AttestationService::GetStatusTask,
base::Unretained(this), request, result);
base::Closure reply =
base::Bind(&AttestationService::TaskRelayCallback<GetStatusReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
bool AttestationService::IsVerifiedMode() const {
if (!tpm_utility_->IsTpmReady()) {
VLOG(2) << __func__ << ": Tpm is not ready.";
return false;
}
std::string pcr_value;
if (!tpm_utility_->ReadPCR(0, &pcr_value)) {
LOG(WARNING) << __func__ << ": Failed to read PCR0.";
return false;
}
return (pcr_value == GetPCRValueForMode(kVerifiedBootMode));
}
void AttestationService::GetStatusTask(
const GetStatusRequest& request,
const std::shared_ptr<GetStatusReply>& result) {
result->set_prepared_for_enrollment(IsPreparedForEnrollment());
result->set_enrolled(IsEnrolled());
for (int i = 0, count = GetIdentitiesCount(); i < count; ++i) {
GetStatusReply::Identity* identity = result->mutable_identities()->Add();
identity->set_features(GetIdentityFeatures(i));
}
AttestationService::IdentityCertificateMap map = GetIdentityCertificateMap();
for (auto it = map.cbegin(), end = map.cend(); it != end; ++it) {
GetStatusReply::IdentityCertificate identity_certificate;
identity_certificate.set_identity(it->second.identity());
identity_certificate.set_aca(it->second.aca());
result->mutable_identity_certificates()->insert(
google::protobuf::Map<int, GetStatusReply::IdentityCertificate>::
value_type(it->first, identity_certificate));
}
for (int aca = kDefaultACA; aca < kMaxACATypeInternal; ++aca) {
ACAType aca_type = GetACAType(static_cast<ACATypeInternal>(aca));
(*result->mutable_enrollment_preparations())[aca_type] =
IsPreparedForEnrollmentWithACA(aca_type);
}
if (request.extended_status()) {
result->set_verified_boot(IsVerifiedMode());
}
}
void AttestationService::Verify(const VerifyRequest& request,
const VerifyCallback& callback) {
auto result = std::make_shared<VerifyReply>();
base::Closure task = base::Bind(&AttestationService::VerifyTask,
base::Unretained(this), request, result);
base::Closure reply =
base::Bind(&AttestationService::TaskRelayCallback<VerifyReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
bool AttestationService::VerifyIdentityBinding(const IdentityBinding& binding) {
if (tpm_utility_->GetVersion() == TPM_1_2) {
// Reconstruct and hash a serialized TPM_IDENTITY_CONTENTS structure.
const std::string header("\x01\x01\x00\x00\x00\x00\x00\x79", 8);
std::string digest = base::SHA1HashString(binding.identity_label() +
binding.pca_public_key());
std::string identity_public_key_info;
if (!GetSubjectPublicKeyInfo(KEY_TYPE_RSA,
binding.identity_public_key_der(),
&identity_public_key_info)) {
LOG(ERROR) << __func__ << ": Failed to get identity public key info.";
return false;
}
if (!crypto_utility_->VerifySignature(
crypto_utility_->DefaultDigestAlgoForSignature(),
identity_public_key_info,
header + digest + binding.identity_public_key_tpm_format(),
binding.identity_binding())) {
LOG(ERROR) << __func__
<< ": Failed to verify identity binding signature.";
return false;
}
} else if (tpm_utility_->GetVersion() == TPM_2_0) {
VLOG(1) << __func__ << ": Nothing to do for TPM 2.0.";
} else {
LOG(ERROR) << __func__ << ": Unsupported TPM version.";
return false;
}
return true;
}
bool AttestationService::VerifyQuoteSignature(
const std::string& aik_public_key_info,
const Quote& quote,
uint32_t pcr_index) {
if (!crypto_utility_->VerifySignature(
crypto_utility_->DefaultDigestAlgoForSignature(), aik_public_key_info,
quote.quoted_data(), quote.quote())) {
LOG(ERROR) << __func__ << ": Signature mismatch.";
return false;
}
if (!tpm_utility_->IsQuoteForPCR(quote.quoted_pcr_value(),
quote.quoted_data(), quote.quote(),
pcr_index)) {
LOG(ERROR) << __func__ << ": Invalid quote.";
return false;
}
return true;
}
std::string AttestationService::GetPCRValueForMode(const char* mode) const {
std::string mode_str(mode, 3);
std::string mode_digest = base::SHA1HashString(mode_str);
std::string pcr_value;
if (tpm_utility_->GetVersion() == TPM_1_2) {
// Use SHA-1 digests for TPM 1.2.
std::string initial(base::kSHA1Length, 0);
pcr_value = base::SHA1HashString(initial + mode_digest);
} else if (tpm_utility_->GetVersion() == TPM_2_0) {
// Use SHA-256 digests for TPM 2.0.
std::string initial(crypto::kSHA256Length, 0);
mode_digest.resize(crypto::kSHA256Length);
pcr_value = crypto::SHA256HashString(initial + mode_digest);
} else {
LOG(ERROR) << __func__ << ": Unsupported TPM version.";
}
return pcr_value;
}
bool AttestationService::VerifyPCR0Quote(const std::string& aik_public_key_info,
const Quote& pcr0_quote) {
if (!VerifyQuoteSignature(aik_public_key_info, pcr0_quote, 0)) {
return false;
}
// Check if the PCR0 value represents a known mode.
for (size_t i = 0; i < base::size(kKnownBootModes); ++i) {
std::string pcr_value = GetPCRValueForMode(kKnownBootModes[i]);
if (pcr0_quote.quoted_pcr_value() == pcr_value) {
LOG(INFO) << "PCR0: " << GetDescriptionForMode(kKnownBootModes[i]);
return true;
}
}
LOG(WARNING) << "PCR0 value not recognized.";
return true;
}
bool AttestationService::VerifyPCR1Quote(const std::string& aik_public_key_info,
const Quote& pcr1_quote) {
if (!VerifyQuoteSignature(aik_public_key_info, pcr1_quote, 1)) {
return false;
}
// Check that the source hint is correctly populated.
if (hwid_ != pcr1_quote.pcr_source_hint()) {
LOG(ERROR) << "PCR1 source hint does not match HWID: " << hwid_;
return false;
}
LOG(INFO) << "PCR1 verified as " << hwid_;
return true;
}
bool AttestationService::GetCertifiedKeyDigest(
const std::string& public_key_info,
const std::string& public_key_tpm_format,
std::string* key_digest) {
if (tpm_utility_->GetVersion() == TPM_1_2) {
return crypto_utility_->GetKeyDigest(public_key_info, key_digest);
} else if (tpm_utility_->GetVersion() == TPM_2_0) {
// TPM_ALG_SHA256 = 0x000B, here in big-endian order.
std::string prefix("\x00\x0B", 2);
key_digest->assign(prefix +
crypto::SHA256HashString(public_key_tpm_format));
return true;
}
LOG(ERROR) << __func__ << ": Unsupported TPM version.";
return false;
}
bool AttestationService::VerifyCertifiedKey(
const std::string& aik_public_key_info,
const std::string& public_key_info,
const std::string& public_key_tpm_format,
const std::string& key_info,
const std::string& proof) {
if (!crypto_utility_->VerifySignature(
crypto_utility_->DefaultDigestAlgoForSignature(), aik_public_key_info,
key_info, proof)) {
LOG(ERROR) << __func__ << ": Bad key signature.";
return false;
}
std::string key_digest;
if (!GetCertifiedKeyDigest(public_key_info, public_key_tpm_format,
&key_digest)) {
LOG(ERROR) << __func__ << ": Failed to get key digest.";
return false;
}
if (key_info.find(key_digest) == std::string::npos) {
LOG(ERROR) << __func__ << ": Public key mismatch.";
return false;
}
return true;
}
bool AttestationService::VerifyCertifiedKeyGeneration(
const std::string& aik_key_blob, const std::string& aik_public_key_info) {
std::string nonce;
if (!crypto_utility_->GetRandom(kNonceSize, &nonce)) {
LOG(ERROR) << __func__ << ": GetRandom(nonce) failed.";
return false;
}
std::vector<KeyType> key_type_candidates;
if (tpm_utility_->GetVersion() == TPM_2_0) {
key_type_candidates = {KEY_TYPE_RSA, KEY_TYPE_ECC};
} else {
key_type_candidates = {KEY_TYPE_RSA};
}
for (KeyType key_type : key_type_candidates) {
std::string key_blob;
std::string public_key_der;
std::string public_key_tpm_format;
std::string key_info;
std::string proof;
if (!tpm_utility_->CreateCertifiedKey(
key_type, KEY_USAGE_SIGN, aik_key_blob, nonce, &key_blob,
&public_key_der, &public_key_tpm_format, &key_info, &proof)) {
LOG(ERROR) << __func__
<< ": Failed to create certified key for key_type: "
<< key_type;
return false;
}
std::string public_key_info;
if (!GetSubjectPublicKeyInfo(key_type, public_key_der, &public_key_info)) {
LOG(ERROR) << __func__ << ": Failed to get public key info for key_type: "
<< key_type;
return false;
}
if (!VerifyCertifiedKey(aik_public_key_info, public_key_info,
public_key_tpm_format, key_info, proof)) {
LOG(ERROR) << __func__
<< ": Bad certified key for key_type: " << key_type;
return false;
}
}
return true;
}
bool AttestationService::VerifyActivateIdentity(
const std::string& aik_public_key_tpm_format) {
std::string rsa_ek_public_key;
if (!tpm_utility_->GetEndorsementPublicKey(KEY_TYPE_RSA,
&rsa_ek_public_key)) {
LOG(ERROR) << __func__
<< ": Can't get RSA EK public key for VerifyActivateIdentity.";
return false;
}
std::string test_credential = "test credential";
EncryptedIdentityCredential encrypted_credential;
if (!crypto_utility_->EncryptIdentityCredential(
tpm_utility_->GetVersion(), test_credential, rsa_ek_public_key,
aik_public_key_tpm_format, &encrypted_credential)) {
LOG(ERROR) << __func__ << ": Failed to encrypt identity credential";
return false;
}
if (!ActivateAttestationKeyInternal(kFirstIdentity, DEFAULT_ACA, KEY_TYPE_RSA,
encrypted_credential, false, nullptr,
nullptr)) {
LOG(ERROR) << __func__ << ": Failed to activate identity";
return false;
}
return true;
}
void AttestationService::VerifyTask(
const VerifyRequest& request, const std::shared_ptr<VerifyReply>& result) {
result->set_verified(false);
base::Optional<std::string> ek_public_key = GetEndorsementPublicKey();
if (!ek_public_key.has_value()) {
LOG(ERROR) << __func__ << ": Endorsement key not available.";
return;
}
base::Optional<std::string> ek_cert = GetEndorsementCertificate();
if (!ek_cert.has_value()) {
LOG(ERROR) << __func__ << ": Endorsement cert not available.";
return;
}
std::string issuer;
if (!crypto_utility_->GetCertificateIssuerName(ek_cert.value(), &issuer)) {
LOG(ERROR) << __func__ << ": Failed to get certificate issuer.";
return;
}
std::string ca_public_key;
if (!GetAuthorityPublicKey(issuer, request.cros_core(), &ca_public_key)) {
LOG(ERROR) << __func__ << ": Failed to get CA public key.";
return;
}
if (!crypto_utility_->VerifyCertificate(ek_cert.value(), ca_public_key)) {
LOG(WARNING) << __func__ << ": Bad endorsement credential.";
return;
}
// Verifies that the given public key matches the one in the credential.
// Gets the public key by GetCertificatePublicKey and
// GetCertificateSubjectPublicKeyInfo for TPM1.2 and TPM2.0 respectively.
// TODO(crbug/942487): Only use GetCertificateSubjectPublicKeyInfo after the
// bug is resolved.
std::string cert_public_key_info;
switch (tpm_utility_->GetVersion()) {
case TPM_1_2:
if (!crypto_utility_->GetCertificatePublicKey(ek_cert.value(),
&cert_public_key_info)) {
LOG(ERROR) << __func__ << ": Failed to call GetCertificatePublicKey.";
return;
}
break;
case TPM_2_0:
if (!crypto_utility_->GetCertificateSubjectPublicKeyInfo(
ek_cert.value(), &cert_public_key_info)) {
LOG(ERROR) << __func__
<< ": Failed to call GetCertificateSubjectPublicKeyInfo.";
return;
}
break;
default:
NOTREACHED() << "Unexpected TPM version.";
}
// Note: Do not use any openssl functions that attempt to decode the public
// key. These will fail because openssl does not recognize the OAEP key type,
// which could happen for some TPM1.2 chips.
if (cert_public_key_info != ek_public_key.value()) {
LOG(ERROR) << __func__ << ": Bad certificate public key.";
return;
}
// All done if we only needed to verify EK. Otherwise, continue with full
// verification.
if (request.ek_only()) {
result->set_verified(true);
return;
}
auto database_pb = database_->GetProtobuf();
const auto& identity_data = database_pb.identities().Get(kFirstIdentity);
std::string identity_public_key_info;
if (!GetSubjectPublicKeyInfo(
identity_data.identity_key().identity_key_type(),
identity_data.identity_key().identity_public_key_der(),
&identity_public_key_info)) {
LOG(ERROR) << __func__ << ": Failed to get identity public key info.";
return;
}
if (!VerifyIdentityBinding(identity_data.identity_binding())) {
LOG(ERROR) << __func__ << ": Bad identity binding.";
return;
}
if (!tpm_utility_->IsPCR0Valid()) {
LOG(ERROR) << __func__ << ": Bad PCR0 value.";
metrics_.ReportAttestationOpsStatus(
kAttestationVerify, AttestationOpsStatus::kInvalidPcr0Value);
}
if (!VerifyPCR0Quote(identity_public_key_info,
identity_data.pcr_quotes().at(0))) {
LOG(ERROR) << __func__ << ": Bad PCR0 quote.";
return;
}
if (!VerifyPCR1Quote(identity_public_key_info,
identity_data.pcr_quotes().at(1))) {
// Don't fail because many devices don't use PCR1.
LOG(WARNING) << __func__ << ": Bad PCR1 quote.";
}
if (!VerifyCertifiedKeyGeneration(
identity_data.identity_key().identity_key_blob(),
identity_public_key_info)) {
LOG(ERROR) << __func__ << ": Failed to verify certified key generation.";
return;
}
// Originally, we use VerifyActivateIdentity to test ActivateIdentity TPM
// command works in TPM 1.2, but we don't really need it for TPM 2.0. Because
// we have more complete tests in Cr50, we are planning to obsolete the tests
// in the service daemon. Moreover, supporting this would require simulating
// the entire attestation flow but pointless. We decide to not to support this
// test for ECC EK.
if (!VerifyActivateIdentity(
identity_data.identity_binding().identity_public_key_tpm_format())) {
LOG(ERROR) << __func__ << ": Failed to verify identity activation.";
return;
}
LOG(INFO) << "Attestation: Verified OK.";
result->set_verified(true);
}
void AttestationService::CreateEnrollRequest(
const CreateEnrollRequestRequest& request,
const CreateEnrollRequestCallback& callback) {
auto result = std::make_shared<CreateEnrollRequestReply>();
base::Closure task = base::Bind(
&AttestationService::CreateEnrollRequestTask<CreateEnrollRequestRequest>,
base::Unretained(this), request, result);
base::Closure reply = base::Bind(
&AttestationService::TaskRelayCallback<CreateEnrollRequestReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
template <typename RequestType>
void AttestationService::CreateEnrollRequestTask(
const RequestType& request,
const std::shared_ptr<CreateEnrollRequestReply>& result) {
if (!CreateEnrollRequestInternal(request.aca_type(),
result->mutable_pca_request())) {
result->clear_pca_request();
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
}
}
void AttestationService::FinishEnroll(const FinishEnrollRequest& request,
const FinishEnrollCallback& callback) {
auto result = std::make_shared<FinishEnrollReply>();
base::Closure task =
base::Bind(&AttestationService::FinishEnrollTask<FinishEnrollReply>,
base::Unretained(this), request, result);
base::Closure reply =
base::Bind(&AttestationService::TaskRelayCallback<FinishEnrollReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
template <typename ReplyType>
void AttestationService::FinishEnrollTask(
const FinishEnrollRequest& request,
const std::shared_ptr<ReplyType>& result) {
std::string server_error;
if (!FinishEnrollInternal(request.aca_type(), request.pca_response(),
&server_error)) {
if (server_error.empty()) {
result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
} else {
result->set_status(STATUS_REQUEST_DENIED_BY_CA);
}
}
}
void AttestationService::PostStartEnrollTask(
const std::shared_ptr<AttestationFlowData>& data) {
base::Closure task = base::Bind(&AttestationService::StartEnrollTask,
base::Unretained(this), data);
base::Closure reply =
base::Bind(&AttestationService::OnEnrollAction, GetWeakPtr(), data);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::Enroll(const EnrollRequest& request,
const EnrollCallback& callback) {
PostStartEnrollTask(std::make_shared<AttestationFlowData>(request, callback));
}
void AttestationService::SendEnrollRequest(
const std::shared_ptr<AttestationFlowData>& data) {
auto pca_request = ToPcaAgentEnrollRequest(*data);
auto on_success = base::Bind(&AttestationService::HandlePcaAgentEnrollReply,
GetWeakPtr(), data);
auto on_error =
base::Bind(&AttestationService::HandlePcaAgentEnrollRequestError,
GetWeakPtr(), data);
pca_agent_proxy_->EnrollAsync(pca_request, on_success, on_error);
}
void AttestationService::HandlePcaAgentEnrollRequestError(
const std::shared_ptr<AttestationFlowData>& data, brillo::Error*) {
LOG(ERROR) << __func__ << ": Error sending enroll request to |pca_agent|";
data->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
enrollment_statuses_[data->aca_type()] = EnrollmentStatus::kNotEnrolled;
data->set_action(AttestationFlowAction::kAbort);
OnEnrollAction(data);
}
void AttestationService::OnEnrollAction(
const std::shared_ptr<AttestationFlowData>& data) {
VLOG(1) << __func__ << ": action is : " << static_cast<int>(data->action());
switch (data->action()) {
default:
case AttestationFlowAction::kUnknown:
LOG(DFATAL) << "Unexpected action code: "
<< static_cast<int>(data->action());
data->set_status(STATUS_NOT_SUPPORTED);
data->ReturnStatus();
return;
case AttestationFlowAction::kAbort:
data->ReturnStatus();
for (const auto& alias : enrollment_queue_.PopAll(data->aca_type())) {
alias->set_status(data->status());
alias->ReturnStatus();
}
return;
case AttestationFlowAction::kProcessRequest:
SendEnrollRequest(data);
return;
case AttestationFlowAction::kEnqueue:
// If in the dbus calling thread the status has changed, re-posts the
// task.
if (enrollment_statuses_[data->aca_type()] !=
EnrollmentStatus::kInProgress) {
PostStartEnrollTask(data);
} else {
if (!enrollment_queue_.Push(data)) {
data->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
data->ReturnStatus();
}
}
return;
case AttestationFlowAction::kNoop:
PostStartCertificateTaskOrReturn(data);
for (const auto& alias : enrollment_queue_.PopAll(data->aca_type())) {
PostStartCertificateTaskOrReturn(alias);
}
return;
}
}
void AttestationService::OnGetCertificateAction(
const std::shared_ptr<AttestationFlowData>& data) {
VLOG(1) << __func__ << ": action is : " << static_cast<int>(data->action());
switch (data->action()) {
default:
case AttestationFlowAction::kUnknown:
LOG(DFATAL) << "Unexpected action code: "
<< static_cast<int>(data->action());
data->set_status(STATUS_NOT_SUPPORTED);
data->ReturnStatus();
return;
case AttestationFlowAction::kProcessRequest:
SendGetCertificateRequest(data);
return;
case AttestationFlowAction::kEnqueue:
// If no alias found, re-posts the task. Sadly the task will check the key
// store again. The good news is, this case should be rare in practice.
if (!certificate_queue_.HasAnyAlias(data)) {
// Despite of the callee name, it always posts the task here since we
// shall get certificate.
PostStartCertificateTaskOrReturn(data);
} else {
// TODO(b/149723745): UMA for respective failure cases.
CertificateQueue::PushResult result = certificate_queue_.Push(data);
if (result != CertificateQueue::PushResult::kSuccess) {
data->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
data->ReturnStatus();
}
}
return;
case AttestationFlowAction::kAbort:
case AttestationFlowAction::kNoop:
ReturnForAllCertificateRequestAliases(data);
return;
}
}
void AttestationService::ReturnForAllCertificateRequestAliases(
const std::shared_ptr<AttestationFlowData>& data) {
if (data->status() == STATUS_SUCCESS) {
data->ReturnCertificate();
std::string certificate = data->certificate();
for (const auto& alias : certificate_queue_.PopAllAliases(data)) {
if (data != alias) {
alias->set_certificate(data->certificate());
alias->ReturnCertificate();
}
}
} else {
data->ReturnStatus();
for (const auto& alias : certificate_queue_.PopAllAliases(data)) {
if (data != alias) {
alias->set_status(data->status());
alias->ReturnStatus();
}
}
}
}
void AttestationService::StartEnrollTask(
const std::shared_ptr<AttestationFlowData>& data) {
// When the enrollment is in progress, all the attestation flow entries listen
// to the same response.
if (enrollment_statuses_[data->aca_type()] == EnrollmentStatus::kInProgress) {
data->set_action(AttestationFlowAction::kEnqueue);
return;
}
// This is the place we initialize the enrollment statuses; at this moment
// there is no write operation in other threads, so we don't bother using
// compare_and_exchange atomic operation.
if (enrollment_statuses_[data->aca_type()] == EnrollmentStatus::kUnknown) {
enrollment_statuses_[data->aca_type()] =
IsEnrolledWithACA(data->aca_type()) ? EnrollmentStatus::kEnrolled
: EnrollmentStatus::kNotEnrolled;
}
// At this point, possible statuses are : kEnrolled and kNotEnrolled.
const bool is_enrolled =
enrollment_statuses_[data->aca_type()] == EnrollmentStatus::kEnrolled;
if (is_enrolled && !data->forced_enrollment()) {
data->set_action(AttestationFlowAction::kNoop);
return;
}
if (!is_enrolled && !data->shall_enroll()) {
data->set_action(AttestationFlowAction::kAbort);
data->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
std::string result_request;
if (!CreateEnrollRequestInternal(data->aca_type(), &result_request)) {
data->set_action(AttestationFlowAction::kAbort);
data->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
return;
}
enrollment_statuses_[data->aca_type()] = EnrollmentStatus::kInProgress;
data->emplace_result_request(std::move(result_request));
data->set_action(AttestationFlowAction::kProcessRequest);
}
void AttestationService::FinishEnrollTaskV2(
const std::shared_ptr<AttestationFlowData>& data) {
std::string server_error;
if (!FinishEnrollInternal(data->aca_type(), data->result_response(),
&server_error)) {
if (server_error.empty()) {
data->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
} else {
data->set_status(STATUS_REQUEST_DENIED_BY_CA);
}
data->set_action(AttestationFlowAction::kAbort);
enrollment_statuses_[data->aca_type()] = EnrollmentStatus::kNotEnrolled;
} else {
data->set_action(AttestationFlowAction::kNoop);
enrollment_statuses_[data->aca_type()] = EnrollmentStatus::kEnrolled;
}
}
void AttestationService::PostStartCertificateTaskOrReturn(
const std::shared_ptr<AttestationFlowData>& data) {
if (!data->shall_get_certificate()) {
data->ReturnStatus();
return;
}
base::Closure task = base::Bind(&AttestationService::StartCertificateTask,
base::Unretained(this), data);
base::Closure reply = base::Bind(&AttestationService::OnGetCertificateAction,
GetWeakPtr(), data);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::StartCertificateTask(
const std::shared_ptr<AttestationFlowData>& data) {
DCHECK(data->shall_get_certificate());
if (certificate_queue_.HasAnyAlias(data)) {
data->set_action(AttestationFlowAction::kEnqueue);
return;
}
CertifiedKey key;
if (!data->forced_get_certificate() &&
FindKeyByLabel(data->username(), data->key_label(), &key)) {
std::string public_key_info;
if (!GetSubjectPublicKeyInfo(key.key_type(), key.public_key(),
&public_key_info)) {
LOG(ERROR) << __func__ << ": Failed to call `GetSubjectPublicKeyInfo()`.";
data->set_status(STATUS_UNEXPECTED_DEVICE_ERROR);
data->set_action(AttestationFlowAction::kAbort);
return;
}
data->set_public_key(std::move(public_key_info));
data->set_certificate(CreatePEMCertificateChain(key));
data->set_action(AttestationFlowAction::kNoop);
return;
}
// Indirects to the existing logic; by doing this we don't have to worry about
// changing the current behavior.
const GetCertificateRequest& request = data->get_certificate_request();
auto reply = std::make_shared<CreateCertificateRequestReply>();
CreateCertificateRequestTask(request, reply);
if (reply->status() != STATUS_SUCCESS) {
data->set_status(reply->status());
data->set_action(AttestationFlowAction::kAbort);
return;
}
data->emplace_result_request(std::move(*(reply->mutable_pca_request())));
data->set_action(AttestationFlowAction::kProcessRequest);
// Different from the way we handle enrollment, enqueues |data| right here so
// we can check the existence of aliases to tell if the certification in
// progress.
CertificateQueue::PushResult push_result = certificate_queue_.Push(data);
// the certificate queue is in a broken state since we already ensure
// |HasAnyAlias| to be |false|; crashes |attestationd| to bring it back to
// initial state in any case.
CHECK_EQ(push_result, CertificateQueue::PushResult::kSuccess)
<< "Unexpected error during pushing the first alias to certificate "
"queue.";
}
void AttestationService::FinishCertificateTask(
const std::shared_ptr<AttestationFlowData>& data) {
// Indirects to the existing logic; by doing this we don't have to worry about
// changing the current behavior.
FinishCertificateRequestRequest request;
request.set_pca_response(data->result_response());
request.set_username(data->username());
request.set_key_label(data->key_label());
auto reply = std::make_shared<FinishCertificateRequestReply>();
FinishCertificateRequestTask(request, reply);
if (reply->status() != STATUS_SUCCESS) {
data->set_status(reply->status());
data->set_action(AttestationFlowAction::kAbort);
return;
}
data->set_public_key(std::move(*(reply->mutable_public_key())));
data->set_certificate(std::move(*(reply->mutable_certificate())));
data->set_action(AttestationFlowAction::kNoop);
}
void AttestationService::HandlePcaAgentEnrollReply(
const std::shared_ptr<AttestationFlowData>& data,
const pca_agent::EnrollReply& pca_reply) {
if (pca_reply.status() != STATUS_SUCCESS) {
enrollment_statuses_[data->aca_type()] = EnrollmentStatus::kNotEnrolled;
data->set_status(pca_reply.status());
data->set_action(AttestationFlowAction::kAbort);
OnEnrollAction(data);
return;
}
data->set_result_response(pca_reply.response());
base::Closure task = base::Bind(&AttestationService::FinishEnrollTaskV2,
base::Unretained(this), data);
base::Closure reply =
base::Bind(&AttestationService::OnEnrollAction, GetWeakPtr(), data);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
void AttestationService::CreateCertificateRequest(
const CreateCertificateRequestRequest& request,
const CreateCertificateRequestCallback& callback) {
auto result = std::make_shared<CreateCertificateRequestReply>();
base::Closure task =
base::Bind(&AttestationService::CreateCertificateRequestTask<
CreateCertificateRequestRequest>,
base::Unretained(this), request, result);
base::Closure reply = base::Bind(
&AttestationService::TaskRelayCallback<CreateCertificateRequestReply>,
GetWeakPtr(), callback, result);
worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
}
template <typename RequestType>
void AttestationService::CreateCertificateRequestTask(
const RequestType& request,
const std::shared_ptr<CreateCertificateRequestReply>& result) {
const int identity = kFirstIdentity;
auto database_pb