blob: 3852c59bb5459d8728e71fe2c198f9ee44b99c95 [file] [log] [blame]
// Copyright (c) 2012 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 "cryptohome/attestation.h"
#include <algorithm>
#include <memory>
#include <string>
#include <vector>
#include <arpa/inet.h>
#include <base/stl_util.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/stringprintf.h>
#include <base/time/time.h>
#include <brillo/data_encoding.h>
#include <brillo/http/http_utils.h>
#include <brillo/mime_utils.h>
#include <brillo/secure_blob.h>
#include <google/protobuf/repeated_field.h>
#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include "cryptohome/cryptohome_metrics.h"
#include "cryptohome/cryptolib.h"
#include "cryptohome/keystore.h"
#include "cryptohome/pkcs11_keystore.h"
#include "cryptohome/platform.h"
#include "cryptohome/tpm.h"
#include "cryptohome/tpm_init.h"
#include "attestation.pb.h" // NOLINT(build/include)
using brillo::SecureBlob;
namespace {
// Resets a boolean when it falls out of scope.
class ScopedBool {
public:
ScopedBool(bool* target, bool reset_value) : target_(target),
reset_value_(reset_value) {}
~ScopedBool() { *target_ = reset_value_; }
private:
bool* target_;
bool reset_value_;
};
} // namespace
namespace cryptohome {
const size_t Attestation::kQuoteExternalDataSize = 20;
const size_t Attestation::kCipherKeySize = 32;
#if USE_TPM2
const size_t Attestation::kNonceSize = 32;
const size_t Attestation::kDigestSize = 32;
#else
const size_t Attestation::kNonceSize = 20; // As per TPM_NONCE definition.
const size_t Attestation::kDigestSize = 20; // As per TPM_DIGEST definition.
#endif
const size_t Attestation::kChallengeSignatureNonceSize = 20; // For all TPMs.
const mode_t Attestation::kDatabasePermissions = 0600;
const char Attestation::kDatabaseOwner[] = "attestation";
const char Attestation::kDefaultDatabasePath[] =
"/mnt/stateful_partition/unencrypted/preserve/attestation.epb";
// This has been extracted from the Chrome OS PCA's encryption certificate
// for the production PCA server.
const char Attestation::kDefaultPCAPublicKey[] =
"A2976637E113CC457013F4334312A416395B08D4B2A9724FC9BAD65D0290F39C"
"866D1163C2CD6474A24A55403C968CF78FA153C338179407FE568C6E550949B1"
"B3A80731BA9311EC16F8F66060A2C550914D252DB90B44D19BC6C15E923FFCFB"
"E8A366038772803EE57C7D7E5B3D5E8090BF0960D4F6A6644CB9A456708508F0"
"6C19245486C3A49F807AB07C65D5E9954F4F8832BC9F882E9EE1AAA2621B1F43"
"4083FD98758745CBFFD6F55DA699B2EE983307C14C9990DDFB48897F26DF8FB2"
"CFFF03E631E62FAE59CBF89525EDACD1F7BBE0BA478B5418E756FF3E14AC9970"
"D334DB04A1DF267D2343C75E5D282A287060D345981ABDA0B2506AD882579FEF";
// This value is opaque; it is proprietary to the system managing the private
// key. In this case the value has been supplied by the PCA maintainers.
const char Attestation::kDefaultPCAPublicKeyID[] = "\x00\xc7\x0e\x50\xb1";
const char Attestation::kDefaultPCAWebOrigin[] =
"https://chromeos-ca.gstatic.com";
// This has been extracted from the Chrome OS PCA's encryption certificate
// for the test PCA server.
const char Attestation::kTestPCAPublicKey[] =
"A1D50D088994000492B5F3ED8A9C5FC8772706219F4C063B2F6A8C6B74D3AD6B"
"212A53D01DABB34A6261288540D420D3BA59ED279D859DE6227A7AB6BD88FADD"
"FC3078D465F4DF97E03A52A587BD0165AE3B180FE7B255B7BEDC1BE81CB1383F"
"E9E46F9312B1EF28F4025E7D332E33F4416525FEB8F0FC7B815E8FBB79CDABE6"
"327B5A155FEF13F559A7086CB8A543D72AD6ECAEE2E704FF28824149D7F4E393"
"D3C74E721ACA97F7ADBE2CCF7B4BCC165F7380F48065F2C8370F25F066091259"
"D14EA362BAF236E3CD8771A94BDEDA3900577143A238AB92B6C55F11DEFAFB31"
"7D1DC5B6AE210C52B008D87F2A7BFF6EB5C4FB32D6ECEC6505796173951A3167";
// This value is opaque; it is proprietary to the system managing the private
// key. In this case the value has been supplied by the PCA maintainers.
const char Attestation::kTestPCAPublicKeyID[] = "\x00\xc2\xb0\x56\x2d";
const char Attestation::kTestPCAWebOrigin[] =
"https://asbestos-qa.corp.google.com";
#ifdef USE_TEST_PCA
#error "Do not compile with USE_TEST_PCA"
" but provide the right PCA_type in calls."
#endif
const char Attestation::kEnterpriseSigningPublicKey[] =
"bf7fefa3a661437b26aed0801db64d7ba8b58875c351d3bdc9f653847d4a67b3"
"b67479327724d56aa0f71a3f57c2290fdc1ff05df80589715e381dfbbda2c4ac"
"114c30d0a73c5b7b2e22178d26d8b65860aa8dd65e1b3d61a07c81de87c1e7e4"
"590145624936a011ece10434c1d5d41f917c3dc4b41dd8392479130c4fd6eafc"
"3bb4e0dedcc8f6a9c28428bf8fbba8bd6438a325a9d3eabee1e89e838138ad99"
"69c292c6d9f6f52522333b84ddf9471ffe00f01bf2de5faa1621f967f49e158b"
"f2b305360f886826cc6fdbef11a12b2d6002d70d8d1e8f40e0901ff94c203cb2"
"01a36a0bd6e83955f14b494f4f2f17c0c826657b85c25ffb8a73599721fa17ab";
const char Attestation::kEnterpriseEncryptionPublicKey[] =
"edba5e723da811e41636f792c7a77aef633fbf39b542aa537c93c93eaba7a3b1"
"0bc3e484388c13d625ef5573358ec9e7fbeb6baaaa87ca87d93fb61bf5760e29"
"6813c435763ed2c81f631e26e3ff1a670261cdc3c39a4640b6bbf4ead3d6587b"
"e43ef7f1f08e7596b628ec0b44c9b7ad71c9ee3a1258852c7a986c7614f0c4ec"
"f0ce147650a53b6aa9ae107374a2d6d4e7922065f2f6eb537a994372e1936c87"
"eb08318611d44daf6044f8527687dc7ce5319b51eae6ab12bee6bd16e59c499e"
"fa53d80232ae886c7ee9ad8bc1cbd6e4ac55cb8fa515671f7e7ad66e98769f52"
"c3c309f98bf08a3b8fbb0166e97906151b46402217e65c5d01ddac8514340e8b";
// This value is opaque; it is proprietary to the system managing the private
// key. In this case the value has been supplied by the enterprise server
// maintainers.
const char Attestation::kEnterpriseEncryptionPublicKeyID[] =
"\x00\x4a\xe2\xdc\xae";
const Attestation::CertificateAuthority Attestation::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" },
{ "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 Attestation::CertificateAuthority
Attestation::kKnownCrosCoreEndorsementCA[] = {
{ "IFX TPM EK Intermediate CA 24",
"9D3F39677EBDB7B95F383021EA6EF90AD2BEA4E38B10CA65DCD84D0B33D400FA"
"E7E56FC553975FDADD425227F055C029B6544331E3BA50ED33F6CC02D833EA4E"
"0EECFE9AD1ADD7095F3A804C560F031E8705A3AD5189CBD62678B5B8205C37ED"
"780A3EDE8DE64A08980C048872E789937A49FC4048EADCAC9B3FD0F0DD085E76"
"30DDF9C0C31EFF3B77C6C3601AA7C3DCD10F08616C01435697746A61F920335C"
"0C45A41149F5D22FCD23DBE35003A9AF7FD91C18715E3709F86A38AB149113C4"
"D5273C3C90599734FF627ACBF408B082C76E486091F27446E175C50D340DA0FE"
"5C3FE3D590B8729F4E364E5BF7D854D9AE28EFBCD0CE8F19E6462B3A593983DF" }
};
const Attestation::PCRValue Attestation::kKnownPCRValues[] = {
{ false, false, kVerified },
{ false, false, kDeveloper },
{ false, true, kVerified },
{ false, true, kDeveloper },
{ true, false, kVerified },
{ true, false, kDeveloper },
{ true, true, kVerified },
{ true, true, kDeveloper }
};
const int Attestation::kNumTemporalValues = 5;
const char Attestation::kAttestationBasedEnterpriseEnrollmentContextName[] =
"attestation_based_enrollment";
Attestation::Attestation()
: database_path_(kDefaultDatabasePath),
pkcs11_key_store_(new Pkcs11KeyStore()),
key_store_(pkcs11_key_store_.get()),
enterprise_test_key_(NULL),
install_attributes_observer_(this),
is_tpm_ready_(false),
is_prepare_in_progress_(false),
retain_endorsement_data_(false),
attestation_user_(0),
attestation_group_(0) {
}
Attestation::~Attestation() {
if (!thread_.is_null())
base::PlatformThread::Join(thread_);
ClearDatabase();
}
void Attestation::Initialize(Tpm* tpm,
TpmInit* tpm_init,
Platform* platform,
Crypto* crypto,
InstallAttributes* install_attributes,
const SecureBlob& abe_data,
bool retain_endorsement_data) {
base::AutoLock lock(lock_);
// Inject dependencies.
tpm_ = tpm;
tpm_init_ = tpm_init;
platform_ = platform;
crypto_ = crypto;
if (install_attributes_ != install_attributes) {
install_attributes_ = install_attributes;
install_attributes_observer_.Add(install_attributes_);
}
abe_data_ = abe_data;
retain_endorsement_data_ = retain_endorsement_data;
if (tpm_) {
if (!platform_->GetUserId(kDatabaseOwner, &attestation_user_,
&attestation_group_)) {
LOG(WARNING) << "Attestation: Falling back to root.";
}
ExtendPCR1IfClear();
std::string serial_encrypted_db;
if (!LoadDatabase(&serial_encrypted_db)) {
LOG(INFO) << "Attestation: Attestation data not found.";
return;
}
if (!DecryptDatabase(serial_encrypted_db, &database_pb_)) {
LOG(WARNING) << "Attestation: Attestation data invalid. "
"This is normal if the TPM has been cleared.";
return;
}
FinalizeEndorsementData();
LOG(INFO) << "Attestation: Valid attestation data exists.";
// Make sure the owner password is not being held on our account.
tpm_init_->RemoveTpmOwnerDependency(
TpmPersistentState::TpmOwnerDependency::kAttestation);
}
}
bool Attestation::IsPreparedForEnrollment() {
base::AutoLock lock(lock_);
if (!database_pb_.has_credentials()) {
return false;
}
return (database_pb_.credentials().has_endorsement_credential() ||
database_pb_.credentials()
.has_default_encrypted_endorsement_credential());
}
bool Attestation::IsEnrolled() {
base::AutoLock lock(lock_);
return database_pb_.has_identity_key() &&
database_pb_.identity_key().has_identity_credential();
}
void Attestation::PrepareForEnrollment() {
// There are a few ways to trigger PrepareForEnrollment, make sure it only
// does the work once.
{
base::AutoLock lock(lock_);
if (is_prepare_in_progress_)
return;
is_prepare_in_progress_ = true;
}
// In the event of an error it is important to reset |is_prepare_in_progress_|
// so subsequent attempts are possible.
ScopedBool reset(&is_prepare_in_progress_, false);
// If there is no TPM, we have no work to do.
if (!IsTPMReady())
return;
if (IsPreparedForEnrollment())
return;
base::TimeTicks start = base::TimeTicks::Now();
LOG(INFO) << "Attestation: Preparing for enrollment...";
SecureBlob ek_public_key;
if (!tpm_->GetEndorsementPublicKey(&ek_public_key)) {
LOG(ERROR) << "Attestation: Failed to get EK public key.";
return;
}
// Create an AIK.
SecureBlob identity_public_key_der;
SecureBlob identity_public_key;
SecureBlob identity_key_blob;
SecureBlob identity_binding;
SecureBlob identity_label;
SecureBlob pca_public_key;
SecureBlob endorsement_credential;
SecureBlob platform_credential;
SecureBlob conformance_credential;
if (!tpm_->MakeIdentity(&identity_public_key_der,
&identity_public_key,
&identity_key_blob,
&identity_binding,
&identity_label,
&pca_public_key,
&endorsement_credential,
&platform_credential,
&conformance_credential)) {
LOG(ERROR) << "Attestation: Failed to make AIK.";
return;
}
// Quote PCR0.
SecureBlob external_data;
if (!tpm_->GetRandomData(kQuoteExternalDataSize, &external_data)) {
LOG(ERROR) << "Attestation: GetRandomData failed.";
return;
}
SecureBlob quoted_pcr_value0;
SecureBlob quoted_data0;
SecureBlob quote0;
if (!tpm_->QuotePCR(0,
identity_key_blob,
external_data,
&quoted_pcr_value0,
&quoted_data0,
&quote0)) {
LOG(ERROR) << "Attestation: Failed to generate quote.";
return;
}
// Quote PCR1.
SecureBlob quoted_pcr_value1;
SecureBlob quoted_data1;
SecureBlob quote1;
if (!tpm_->QuotePCR(1,
identity_key_blob,
external_data,
&quoted_pcr_value1,
&quoted_data1,
&quote1)) {
LOG(ERROR) << "Attestation: Failed to generate quote.";
return;
}
// Create a delegate so we can activate the AIK later.
SecureBlob delegate_blob;
SecureBlob delegate_secret;
if (!tpm_->CreateDelegate(identity_key_blob, &delegate_blob,
&delegate_secret)) {
LOG(ERROR) << "Attestation: Failed to create delegate.";
return;
}
// Assemble a protobuf to store locally.
base::AutoLock lock(lock_);
TPMCredentials* credentials_pb = database_pb_.mutable_credentials();
credentials_pb->set_endorsement_public_key(ek_public_key.data(),
ek_public_key.size());
credentials_pb->set_endorsement_credential(endorsement_credential.data(),
endorsement_credential.size());
credentials_pb->set_platform_credential(platform_credential.data(),
platform_credential.size());
credentials_pb->set_conformance_credential(conformance_credential.data(),
conformance_credential.size());
if (!EncryptEndorsementCredential(
kDefaultPCA,
endorsement_credential,
credentials_pb->mutable_default_encrypted_endorsement_credential())) {
LOG(ERROR) << "Attestation: Failed to encrypt EK cert.";
return;
}
if (!EncryptEndorsementCredential(
kTestPCA,
endorsement_credential,
credentials_pb->mutable_test_encrypted_endorsement_credential())) {
LOG(ERROR) << "Attestation: Failed to encrypt EK cert (test).";
return;
}
IdentityKey* key_pb = database_pb_.mutable_identity_key();
key_pb->set_identity_public_key(identity_public_key_der.data(),
identity_public_key_der.size());
key_pb->set_identity_key_blob(identity_key_blob.data(),
identity_key_blob.size());
IdentityBinding* binding_pb = database_pb_.mutable_identity_binding();
binding_pb->set_identity_binding(identity_binding.data(),
identity_binding.size());
binding_pb->set_identity_public_key_der(identity_public_key_der.data(),
identity_public_key_der.size());
binding_pb->set_identity_public_key(identity_public_key.data(),
identity_public_key.size());
binding_pb->set_identity_label(identity_label.data(), identity_label.size());
binding_pb->set_pca_public_key(pca_public_key.data(), pca_public_key.size());
Quote* quote_pb0 = database_pb_.mutable_pcr0_quote();
quote_pb0->set_quote(quote0.data(), quote0.size());
quote_pb0->set_quoted_data(quoted_data0.data(), quoted_data0.size());
quote_pb0->set_quoted_pcr_value(quoted_pcr_value0.data(),
quoted_pcr_value0.size());
Quote* quote_pb1 = database_pb_.mutable_pcr1_quote();
quote_pb1->set_quote(quote1.data(), quote1.size());
quote_pb1->set_quoted_data(quoted_data1.data(), quoted_data1.size());
quote_pb1->set_quoted_pcr_value(quoted_pcr_value1.data(),
quoted_pcr_value1.size());
quote_pb1->set_pcr_source_hint(platform_->GetHardwareID());
Delegation* delegate_pb = database_pb_.mutable_delegate();
delegate_pb->set_blob(delegate_blob.data(), delegate_blob.size());
delegate_pb->set_secret(delegate_secret.data(), delegate_secret.size());
delegate_pb->set_has_reset_lock_permissions(true);
std::string serial_encrypted_db;
if (!EncryptDatabase(database_pb_, &serial_encrypted_db)) {
LOG(ERROR) << "Attestation: Failed to encrypt db.";
return;
}
if (!StoreDatabase(serial_encrypted_db)) {
LOG(ERROR) << "Attestation: Failed to store db.";
return;
}
tpm_init_->RemoveTpmOwnerDependency(
TpmPersistentState::TpmOwnerDependency::kAttestation);
base::TimeDelta delta = (base::TimeTicks::Now() - start);
LOG(INFO) << "Attestation: Prepared successfully (" << delta.InMilliseconds()
<< "ms).";
}
void Attestation::PrepareForEnrollmentAsync() {
base::AutoLock lock(lock_);
if (!thread_.is_null()) {
if (!is_prepare_in_progress_) {
// Join the old thread and reuse the handle.
base::PlatformThread::Join(thread_);
} else {
LOG(WARNING) << "PrepareForEnrollmentAsync called multiple times.";
return;
}
}
base::PlatformThread::Create(0, this, &thread_);
}
bool Attestation::Verify(bool is_cros_core) {
if (!IsTPMReady())
return false;
LOG(INFO) << "Attestation: Verifying data.";
base::AutoLock lock(lock_);
const TPMCredentials& credentials = database_pb_.credentials();
SecureBlob ek_public_key;
SecureBlob ek_cert;
if (credentials.has_endorsement_credential()) {
ek_cert = SecureBlob(credentials.endorsement_credential());
} else {
if (!tpm_->GetEndorsementCredential(&ek_cert)) {
LOG(ERROR) << "Attestation: Endorsement cert not available.";
return false;
}
}
if (credentials.has_endorsement_public_key()) {
ek_public_key = SecureBlob(credentials.endorsement_public_key());
} else {
if (!tpm_->GetEndorsementPublicKey(&ek_public_key)) {
LOG(ERROR) << "Attestation: Endorsement key not available.";
return false;
}
}
if (!VerifyEndorsementCredential(ek_cert, ek_public_key, is_cros_core)) {
LOG(ERROR) << "Attestation: Bad endorsement credential.";
return false;
}
if (!VerifyIdentityBinding(database_pb_.identity_binding())) {
LOG(ERROR) << "Attestation: Bad identity binding.";
return false;
}
SecureBlob aik_public_key = SecureBlob(
database_pb_.identity_binding().identity_public_key_der());
if (!VerifyPCR0Quote(aik_public_key, database_pb_.pcr0_quote())) {
LOG(ERROR) << "Attestation: Bad PCR0 quote.";
return false;
}
if (!VerifyPCR1Quote(aik_public_key, database_pb_.pcr1_quote())) {
// Don't fail because many devices don't use PCR1.
LOG(WARNING) << "Attestation: Bad PCR1 quote.";
}
SecureBlob nonce;
if (!tpm_->GetRandomData(kNonceSize, &nonce)) {
LOG(ERROR) << "Attestation: GetRandomData failed.";
return false;
}
SecureBlob identity_key_blob(database_pb_.identity_key().identity_key_blob());
SecureBlob public_key;
SecureBlob public_key_der;
SecureBlob key_blob;
SecureBlob key_info;
SecureBlob proof;
if (!tpm_->CreateCertifiedKey(identity_key_blob, nonce,
&public_key, &public_key_der,
&key_blob, &key_info, &proof)) {
LOG(ERROR) << "Attestation: Failed to create certified key.";
return false;
}
if (!VerifyCertifiedKey(aik_public_key, public_key_der, key_info, proof)) {
LOG(ERROR) << "Attestation: Bad certified key.";
return false;
}
SecureBlob delegate_blob(database_pb_.delegate().blob());
SecureBlob delegate_secret(database_pb_.delegate().secret());
SecureBlob aik_public_key_tpm(
database_pb_.identity_binding().identity_public_key());
if (!VerifyActivateIdentity(delegate_blob, delegate_secret,
identity_key_blob, aik_public_key_tpm,
ek_public_key)) {
LOG(ERROR) << "Attestation: Failed to verify owner delegation.";
return false;
}
LOG(INFO) << "Attestation: Verified OK.";
return true;
}
bool Attestation::VerifyEK(bool is_cros_core) {
if (!IsTPMReady())
return false;
base::AutoLock lock(lock_);
SecureBlob ek_cert;
SecureBlob ek_public_key;
if (database_pb_.has_credentials()) {
const TPMCredentials& credentials = database_pb_.credentials();
if (credentials.has_endorsement_credential()) {
ek_public_key = SecureBlob(credentials.endorsement_public_key());
ek_cert = SecureBlob(credentials.endorsement_credential());
}
}
if (ek_cert.size() == 0 && !tpm_->GetEndorsementCredential(&ek_cert)) {
LOG(ERROR) << __func__ << ": Failed to get EK certificate.";
return false;
}
if (ek_public_key.size() == 0 &&
!tpm_->GetEndorsementPublicKey(&ek_public_key)) {
LOG(ERROR) << __func__ << ": Failed to get EK public key.";
return false;
}
return VerifyEndorsementCredential(ek_cert, ek_public_key, is_cros_core);
}
bool Attestation::CreateEnrollRequest(PCAType pca_type,
SecureBlob* pca_request) {
if (!IsTPMReady())
return false;
if (!IsPreparedForEnrollment()) {
LOG(ERROR) << __func__ << ": Enrollment is not possible, attestation data "
<< "does not exist.";
return false;
}
base::AutoLock lock(lock_);
AttestationEnrollmentRequest request_pb;
*request_pb.mutable_encrypted_endorsement_credential() =
GetEncryptedEndorsementCredential(pca_type);
request_pb.set_identity_public_key(
database_pb_.identity_binding().identity_public_key());
*request_pb.mutable_pcr0_quote() = database_pb_.pcr0_quote();
*request_pb.mutable_pcr1_quote() = database_pb_.pcr1_quote();
SecureBlob enterprise_enrollment_nonce;
if (!ComputeEnterpriseEnrollmentNonce(&enterprise_enrollment_nonce)) {
LOG(ERROR) << "Attestation: Failed to compute enterprise enrollment nonce.";
return false;
}
if (!enterprise_enrollment_nonce.empty()) {
request_pb.set_enterprise_enrollment_nonce(
enterprise_enrollment_nonce.data(),
enterprise_enrollment_nonce.size());
}
std::string tmp;
if (!request_pb.SerializeToString(&tmp)) {
LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
return false;
}
*pca_request = SecureBlob(tmp);
return true;
}
bool Attestation::Enroll(PCAType pca_type,
const SecureBlob& pca_response) {
if (!IsTPMReady())
return false;
AttestationEnrollmentResponse response_pb;
if (!response_pb.ParseFromArray(pca_response.data(), pca_response.size())) {
LOG(ERROR) << __func__ << ": Failed to parse response from Privacy CA.";
return false;
}
if (response_pb.status() != OK) {
LOG(ERROR) << __func__ << ": Error received from Privacy CA: "
<< response_pb.detail();
return false;
}
base::AutoLock lock(lock_);
SecureBlob delegate_blob(database_pb_.delegate().blob());
SecureBlob delegate_secret(database_pb_.delegate().secret());
SecureBlob aik_blob(database_pb_.identity_key().identity_key_blob());
SecureBlob encrypted_asym(
response_pb.encrypted_identity_credential().asym_ca_contents());
SecureBlob encrypted_sym(
response_pb.encrypted_identity_credential().sym_ca_attestation());
SecureBlob aik_credential;
if (!tpm_->ActivateIdentity(delegate_blob, delegate_secret,
aik_blob, encrypted_asym, encrypted_sym,
&aik_credential)) {
LOG(ERROR) << __func__ << ": Failed to activate identity.";
return false;
}
IdentityKey* key_pb = database_pb_.mutable_identity_key();
key_pb->set_identity_credential(aik_credential.to_string());
if (!PersistDatabaseChanges()) {
LOG(ERROR) << __func__ << ": Failed to persist database changes.";
return false;
}
LOG(INFO) << "Attestation: Enrollment complete.";
return true;
}
bool Attestation::CreateCertRequest(PCAType pca_type,
CertificateProfile profile,
const std::string& username,
const std::string& origin,
SecureBlob* pca_request) {
if (!IsTPMReady())
return false;
if (!IsEnrolled()) {
LOG(ERROR) << __func__ << ": Device is not enrolled for attestation.";
return false;
}
base::AutoLock lock(lock_);
AttestationCertificateRequest request_pb;
SecureBlob message_id(kNonceSize);
CryptoLib::GetSecureRandom(message_id.data(), message_id.size());
request_pb.set_message_id(message_id.to_string());
request_pb.set_identity_credential(
database_pb_.identity_key().identity_credential());
request_pb.set_profile(profile);
if (!origin.empty() &&
(profile == CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID)) {
request_pb.set_origin(origin);
request_pb.set_temporal_index(ChooseTemporalIndex(username, origin));
}
SecureBlob nonce;
if (!tpm_->GetRandomData(kNonceSize, &nonce)) {
LOG(ERROR) << __func__ << ": GetRandomData failed.";
return false;
}
SecureBlob identity_key_blob(
database_pb_.identity_key().identity_key_blob());
SecureBlob public_key;
SecureBlob public_key_der;
SecureBlob key_blob;
SecureBlob key_info;
SecureBlob proof;
if (!tpm_->CreateCertifiedKey(identity_key_blob, nonce,
&public_key, &public_key_der,
&key_blob, &key_info, &proof)) {
LOG(ERROR) << __func__ << ": Failed to create certified key.";
return false;
}
request_pb.set_certified_public_key(public_key.to_string());
request_pb.set_certified_key_info(key_info.to_string());
request_pb.set_certified_key_proof(proof.to_string());
std::string tmp;
if (!request_pb.SerializeToString(&tmp)) {
LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
return false;
}
*pca_request = SecureBlob(tmp);
ClearString(&tmp);
// Save certified key blob so we can finish the operation later.
CertifiedKey certified_key_pb;
certified_key_pb.set_key_blob(key_blob.to_string());
certified_key_pb.set_public_key(public_key_der.to_string());
if (!certified_key_pb.SerializeToString(&tmp)) {
LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
return false;
}
pending_cert_requests_[message_id.to_string()] = SecureBlob(tmp);
ClearString(&tmp);
return true;
}
bool Attestation::FinishCertRequest(const SecureBlob& pca_response,
bool is_user_specific,
const std::string& username,
const std::string& key_name,
SecureBlob* certificate_chain) {
if (!IsTPMReady())
return false;
AttestationCertificateResponse response_pb;
if (!response_pb.ParseFromArray(pca_response.data(), pca_response.size())) {
LOG(ERROR) << __func__ << ": Failed to parse response from Privacy CA.";
return false;
}
base::AutoLock lock(lock_);
CertRequestMap::iterator iter = pending_cert_requests_.find(
response_pb.message_id());
if (iter == pending_cert_requests_.end()) {
LOG(ERROR) << __func__ << ": Pending request not found.";
return false;
}
if (response_pb.status() != OK) {
LOG(ERROR) << __func__ << ": Error received from Privacy CA: "
<< response_pb.detail();
pending_cert_requests_.erase(iter);
return false;
}
CertifiedKey certified_key_pb;
if (!certified_key_pb.ParseFromArray(iter->second.data(),
iter->second.size())) {
LOG(ERROR) << __func__ << ": Failed to parse pending request.";
pending_cert_requests_.erase(iter);
return false;
}
pending_cert_requests_.erase(iter);
// The PCA issued a certificate and the response matched a pending request.
// Now we want to finish populating the CertifiedKey and store it for later.
certified_key_pb.set_certified_key_credential(
response_pb.certified_key_credential());
certified_key_pb.set_intermediate_ca_cert(response_pb.intermediate_ca_cert());
certified_key_pb.mutable_additional_intermediate_ca_cert()->MergeFrom(
response_pb.additional_intermediate_ca_cert());
certified_key_pb.set_key_name(key_name);
if (!SaveKey(is_user_specific, username, key_name, certified_key_pb))
return false;
LOG(INFO) << "Attestation: Certified key credential received and stored.";
return CreatePEMCertificateChain(certified_key_pb, certificate_chain);
}
bool Attestation::GetCertificateChain(bool is_user_specific,
const std::string& username,
const std::string& key_name,
SecureBlob* certificate_chain) {
base::AutoLock lock(lock_);
CertifiedKey key;
if (!FindKeyByName(is_user_specific, username, key_name, &key)) {
LOG(ERROR) << __func__ << ": Could not find certified key: " << key_name;
return false;
}
return CreatePEMCertificateChain(key, certificate_chain);
}
bool Attestation::GetPublicKey(bool is_user_specific,
const std::string& username,
const std::string& key_name,
SecureBlob* public_key) {
base::AutoLock lock(lock_);
CertifiedKey key;
if (!FindKeyByName(is_user_specific, username, key_name, &key)) {
LOG(ERROR) << __func__ << ": Could not find certified key: " << key_name;
return false;
}
SecureBlob public_key_der(key.public_key());
// Convert from PKCS #1 RSAPublicKey to X.509 SubjectPublicKeyInfo.
const unsigned char* asn1_ptr = public_key_der.data();
std::unique_ptr<RSA, RSADeleter> rsa(
d2i_RSAPublicKey(NULL, &asn1_ptr, public_key_der.size()));
if (!rsa.get()) {
LOG(ERROR) << __func__ << ": Failed to decode public key.";
return false;
}
unsigned char* buffer = NULL;
int length = i2d_RSA_PUBKEY(rsa.get(), &buffer);
if (length <= 0) {
LOG(ERROR) << __func__ << ": Failed to encode public key.";
return false;
}
SecureBlob tmp(buffer, buffer + length);
brillo::SecureMemset(buffer, 0, length);
OPENSSL_free(buffer);
public_key->swap(tmp);
return true;
}
bool Attestation::DoesKeyExist(bool is_user_specific,
const std::string& username,
const std::string& key_name) {
base::AutoLock lock(lock_);
CertifiedKey key;
return FindKeyByName(is_user_specific, username, key_name, &key);
}
bool Attestation::SignEnterpriseChallenge(
bool is_user_specific,
const std::string& username,
const std::string& key_name,
const std::string& domain,
const SecureBlob& device_id,
bool include_signed_public_key,
const SecureBlob& challenge,
SecureBlob* response) {
if (!IsTPMReady())
return false;
base::AutoLock lock(lock_);
CertifiedKey key;
if (!FindKeyByName(is_user_specific, username, key_name, &key)) {
LOG(ERROR) << __func__ << ": Key not found.";
return false;
}
// Validate that the challenge is coming from the expected source.
SignedData signed_challenge;
if (!signed_challenge.ParseFromArray(challenge.data(),
challenge.size())) {
LOG(ERROR) << __func__ << ": Failed to parse signed challenge.";
return false;
}
if (!ValidateEnterpriseChallenge(signed_challenge)) {
LOG(ERROR) << __func__ << ": Invalid challenge.";
return false;
}
// Assemble a response protobuf.
ChallengeResponse response_pb;
*response_pb.mutable_challenge() = signed_challenge;
SecureBlob nonce;
if (!tpm_->GetRandomData(kChallengeSignatureNonceSize, &nonce)) {
LOG(ERROR) << __func__ << ": Failed to generate nonce.";
return false;
}
response_pb.set_nonce(nonce.to_string());
KeyInfo key_info;
// EUK -> Enterprise User Key
// EMK -> Enterprise Machine Key
key_info.set_key_type(is_user_specific ? EUK : EMK);
key_info.set_domain(domain);
key_info.set_device_id(device_id.to_string());
// Only include the certificate if this is a user key.
if (is_user_specific) {
SecureBlob certificate_chain;
if (!CreatePEMCertificateChain(key, &certificate_chain)) {
LOG(ERROR) << __func__ << ": Failed to construct certificate chain.";
return false;
}
key_info.set_certificate(certificate_chain.to_string());
}
if (is_user_specific && include_signed_public_key) {
SecureBlob spkac;
if (!CreateSignedPublicKey(key, &spkac)) {
LOG(ERROR) << __func__ << ": Failed to create signed public key.";
return false;
}
key_info.set_signed_public_key_and_challenge(spkac.to_string());
}
if (!EncryptEnterpriseKeyInfo(key_info,
response_pb.mutable_encrypted_key_info())) {
LOG(ERROR) << __func__ << ": Failed to encrypt KeyInfo.";
return false;
}
// Serialize and sign the response protobuf.
std::string serialized;
if (!response_pb.SerializeToString(&serialized)) {
LOG(ERROR) << __func__ << ": Failed to serialize response protobuf.";
return false;
}
SecureBlob input_data(serialized);
ClearString(&serialized);
if (!SignChallengeData(key, input_data, response)) {
LOG(ERROR) << __func__ << ": Failed to sign data.";
return false;
}
return true;
}
bool Attestation::SignSimpleChallenge(bool is_user_specific,
const std::string& username,
const std::string& key_name,
const SecureBlob& challenge,
SecureBlob* response) {
if (!IsTPMReady())
return false;
base::AutoLock lock(lock_);
CertifiedKey key;
if (!FindKeyByName(is_user_specific, username, key_name, &key)) {
LOG(ERROR) << __func__ << ": Key not found.";
return false;
}
// Add a nonce to ensure this service cannot be used to sign arbitrary data.
SecureBlob nonce;
if (!tpm_->GetRandomData(kChallengeSignatureNonceSize, &nonce)) {
LOG(ERROR) << __func__ << ": Failed to generate nonce.";
return false;
}
SecureBlob input_data = SecureBlob::Combine(challenge, nonce);
if (!SignChallengeData(key, input_data, response)) {
LOG(ERROR) << __func__ << ": Failed to sign data.";
return false;
}
return true;
}
bool Attestation::RegisterKey(bool is_user_specific,
const std::string& username,
const std::string& key_name,
bool include_certificates) {
base::AutoLock lock(lock_);
CertifiedKey key;
if (!FindKeyByName(is_user_specific, username, key_name, &key)) {
LOG(ERROR) << __func__ << ": Key not found.";
return false;
}
SecureBlob certificate;
if (include_certificates) {
certificate = SecureBlob(key.certified_key_credential());
}
if (!key_store_->Register(is_user_specific,
username,
key_name,
SecureBlob(key.key_blob()),
SecureBlob(key.public_key()),
certificate)) {
LOG(ERROR) << __func__ << ": Failed to register key.";
return false;
}
if (include_certificates) {
if (key.has_intermediate_ca_cert()) {
if (!key_store_->RegisterCertificate(
is_user_specific,
username,
SecureBlob(key.intermediate_ca_cert()))) {
LOG(WARNING) << __func__ << ": Failed to register certificate.";
}
}
for (int i = 0; i < key.additional_intermediate_ca_cert_size(); ++i) {
if (!key_store_->RegisterCertificate(
is_user_specific,
username,
SecureBlob(key.additional_intermediate_ca_cert(i)))) {
LOG(WARNING) << __func__ << ": Failed to register certificate.";
}
}
}
// Once registered with key store, we don't want to keep our copy.
DeleteKey(is_user_specific, username, key_name);
return true;
}
bool Attestation::GetKeyPayload(bool is_user_specific,
const std::string& username,
const std::string& key_name,
SecureBlob* payload) {
base::AutoLock lock(lock_);
CertifiedKey key;
if (!FindKeyByName(is_user_specific, username, key_name, &key)) {
LOG(ERROR) << __func__ << ": Key not found.";
return false;
}
SecureBlob tmp(key.payload());
payload->swap(tmp);
return true;
}
bool Attestation::SetKeyPayload(bool is_user_specific,
const std::string& username,
const std::string& key_name,
const SecureBlob& payload) {
base::AutoLock lock(lock_);
CertifiedKey key;
if (!FindKeyByName(is_user_specific, username, key_name, &key)) {
LOG(ERROR) << __func__ << ": Key not found.";
return false;
}
key.set_payload(payload.to_string());
return SaveKey(is_user_specific, username, key_name, key);
}
bool Attestation::DeleteKeysByPrefix(bool is_user_specific,
const std::string& username,
const std::string& key_prefix) {
base::AutoLock lock(lock_);
if (is_user_specific) {
return key_store_->DeleteByPrefix(is_user_specific, username, 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.
google::protobuf::RepeatedPtrField<CertifiedKey>* device_keys =
database_pb_.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;
}
}
while (next_keep_index < device_keys->size()) {
device_keys->RemoveLast();
}
return PersistDatabaseChanges();
}
bool Attestation::GetEKInfo(std::string* ek_info) {
if (!IsTPMReady())
return false;
SecureBlob ek_cert;
if (!tpm_->GetEndorsementCredential(&ek_cert)) {
LOG(ERROR) << "Cannot get EK certificate from TPM. EK info not available.";
return false;
}
SecureBlob hash = CryptoLib::Sha256(ek_cert);
*ek_info = base::StringPrintf(
"EK Certificate:\n%s\nHash:\n%s\n",
CreatePEMCertificate(ek_cert.to_string()).c_str(),
base::HexEncode(hash.data(), hash.size()).c_str());
return true;
}
bool Attestation::GetIdentityResetRequest(const std::string& reset_token,
SecureBlob* reset_request) {
base::AutoLock lock(lock_);
AttestationResetRequest proto;
proto.set_token(reset_token);
// This only works with the default PCA right now.
*proto.mutable_encrypted_endorsement_credential() =
database_pb_.credentials().default_encrypted_endorsement_credential();
std::string serial;
if (!proto.SerializeToString(&serial)) {
LOG(ERROR) << __func__ << "Failed to serialize protobuf.";
return false;
}
SecureBlob tmp(serial);
ClearString(&serial);
reset_request->swap(tmp);
return true;
}
bool Attestation::IsPCR0VerifiedMode() {
if (!IsTPMReady())
return false;
SecureBlob current_pcr_value;
if (!tpm_->ReadPCR(0, &current_pcr_value) ||
current_pcr_value.size() != kDigestSize) {
LOG(WARNING) << "Failed to read PCR0.";
return false;
}
SecureBlob settings_blob(3);
settings_blob[0] = false; // Developer mode enabled.
settings_blob[1] = false; // Recovery mode enabled.
settings_blob[2] = kVerified; // Firmware type.
SecureBlob settings_digest = CryptoLib::Sha1(settings_blob);
brillo::Blob extend_pcr_value(kDigestSize, 0);
extend_pcr_value.insert(extend_pcr_value.end(), settings_digest.begin(),
settings_digest.end());
SecureBlob expected_pcr_value = CryptoLib::Sha1(extend_pcr_value);
return (current_pcr_value.size() == expected_pcr_value.size() &&
0 == memcmp(current_pcr_value.data(),
expected_pcr_value.data(),
kDigestSize));
}
bool Attestation::EncryptDatabase(const AttestationDatabase& db,
std::string* serial_encrypted_db) {
CHECK(crypto_);
std::string serial_string;
if (!db.SerializeToString(&serial_string)) {
LOG(ERROR) << "Failed to serialize db.";
return false;
}
SecureBlob serial_data(serial_string.begin(), serial_string.end());
if (database_key_.empty() || sealed_database_key_.empty()) {
if (!crypto_->CreateSealedKey(&database_key_, &sealed_database_key_)) {
LOG(ERROR) << "Failed to generate database key.";
return false;
}
}
if (!crypto_->EncryptData(serial_data, database_key_, sealed_database_key_,
serial_encrypted_db)) {
LOG(ERROR) << "Attestation: Failed to encrypt database.";
return false;
}
return true;
}
bool Attestation::DecryptDatabase(const std::string& serial_encrypted_db,
AttestationDatabase* db) {
CHECK(crypto_);
if (!crypto_->UnsealKey(serial_encrypted_db, &database_key_,
&sealed_database_key_)) {
LOG(ERROR) << "Attestation: Could not unseal decryption key.";
return false;
}
SecureBlob serial_blob;
if (!crypto_->DecryptData(serial_encrypted_db, database_key_, &serial_blob)) {
LOG(ERROR) << "Attestation: Failed to decrypt database with Tpm.";
return false;
}
std::string serial_string = serial_blob.to_string();
if (!db->ParseFromString(serial_string)) {
// Previously the DB was encrypted with CryptoLib::AesEncrypt which appends
// a SHA-1. This can be safely ignored.
const size_t kLegacyJunkSize = 20;
if (serial_string.size() < kLegacyJunkSize ||
!db->ParseFromArray(serial_string.data(),
serial_string.length() - kLegacyJunkSize)) {
LOG(ERROR) << "Failed to parse database.";
return false;
}
}
return true;
}
bool Attestation::StoreDatabase(const std::string& serial_encrypted_db) {
if (!platform_->WriteStringToFileAtomicDurable(database_path_,
serial_encrypted_db,
kDatabasePermissions)) {
LOG(ERROR) << "Failed to write db.";
return false;
}
if (!platform_->SetOwnership(
database_path_, attestation_user_, attestation_group_, true)) {
PLOG(ERROR) << "Failed to set db ownership";
return false;
}
return true;
}
bool Attestation::LoadDatabase(std::string* serial_encrypted_db) {
CheckDatabasePermissions();
if (!platform_->ReadFileToString(database_path_, serial_encrypted_db)) {
PLOG(ERROR) << "Failed to read db";
return false;
}
return true;
}
bool Attestation::PersistDatabaseChanges() {
std::string serial_encrypted_db;
if (!EncryptDatabase(database_pb_, &serial_encrypted_db))
return false;
return StoreDatabase(serial_encrypted_db);
}
void Attestation::CheckDatabasePermissions() {
const mode_t kMask = 0007; // No permissions for 'others'.
CHECK(platform_);
mode_t permissions = 0;
uid_t user = 0;
gid_t group = 0;
if (!platform_->GetPermissions(database_path_, &permissions) ||
!platform_->GetOwnership(database_path_, &user, &group, true)) {
if (errno != ENOENT) {
PLOG(WARNING) << "Failed to read database permissions";
}
return;
}
if ((permissions & kMask) != 0) {
LOG(WARNING) << "Fixing database permissions.";
if (!platform_->SetPermissions(database_path_, permissions & ~kMask)) {
PLOG(WARNING) << "Failed to fix database permissions";
}
}
if (user != attestation_user_ || group != attestation_group_) {
LOG(WARNING) << "Fixing database ownership.";
if (!platform_->SetOwnership(database_path_,
attestation_user_,
attestation_group_,
true /* follow links */)) {
PLOG(WARNING) << "Failed to fix database ownership";
}
}
}
bool Attestation::VerifyEndorsementCredential(const SecureBlob& credential,
const SecureBlob& public_key,
bool is_cros_core) {
const unsigned char* asn1_ptr = credential.data();
std::unique_ptr<X509, X509Deleter> x509(
d2i_X509(NULL, &asn1_ptr, credential.size()));
if (!x509.get()) {
LOG(ERROR) << "Failed to parse endorsement credential.";
return false;
}
// Manually verify the certificate signature.
char issuer[100]; // A longer CN will truncate.
X509_NAME_get_text_by_NID(x509.get()->cert_info->issuer,
NID_commonName,
issuer,
arraysize(issuer));
std::unique_ptr<EVP_PKEY, EVP_PKEYDeleter> issuer_key =
GetAuthorityPublicKey(issuer, is_cros_core);
if (!issuer_key.get()) {
LOG(ERROR) << "Unknown endorsement credential issuer.";
return false;
}
if (X509_verify(x509.get(), issuer_key.get()) != 1) {
LOG(ERROR) << "Bad endorsement credential signature.";
return false;
}
// Verify that the given public key matches the public key in the credential.
// 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.
auto public_key_data = x509.get()->cert_info->key->public_key->data;
SecureBlob credential_public_key(
public_key_data,
public_key_data + x509.get()->cert_info->key->public_key->length);
if (credential_public_key.size() != public_key.size() ||
memcmp(credential_public_key.data(),
public_key.data(),
public_key.size()) != 0) {
LOG(ERROR) << "Bad endorsement credential public key.";
return false;
}
return true;
}
bool Attestation::VerifyIdentityBinding(const IdentityBinding& binding) {
// Reconstruct and hash a serialized TPM_IDENTITY_CONTENTS structure.
const unsigned char header[] = {1, 1, 0, 0, 0, 0, 0, 0x79};
std::string label_ca = binding.identity_label() + binding.pca_public_key();
SecureBlob label_ca_digest = CryptoLib::Sha1(
SecureBlob(label_ca));
ClearString(&label_ca);
// The signed data is header + digest + pubkey.
SecureBlob contents = SecureBlob::Combine(SecureBlob::Combine(
SecureBlob(std::begin(header), std::end(header)),
label_ca_digest),
SecureBlob(binding.identity_public_key()));
// Now verify the signature.
if (!VerifySignature(SecureBlob(
binding.identity_public_key_der()),
contents,
SecureBlob(binding.identity_binding()))) {
LOG(ERROR) << "Failed to verify identity binding signature.";
return false;
}
return true;
}
bool Attestation::VerifyPCR0Quote(const SecureBlob& aik_public_key,
const Quote& quote) {
if (!VerifyQuoteSignature(aik_public_key, quote, 0)) {
return false;
}
// Check if the PCR0 value represents a known mode.
for (size_t i = 0; i < arraysize(kKnownPCRValues); ++i) {
SecureBlob settings_blob(3);
settings_blob[0] = kKnownPCRValues[i].developer_mode_enabled;
settings_blob[1] = kKnownPCRValues[i].recovery_mode_enabled;
settings_blob[2] = kKnownPCRValues[i].firmware_type;
SecureBlob settings_digest = CryptoLib::Sha1(settings_blob);
brillo::Blob extend_pcr_value(kDigestSize, 0);
extend_pcr_value.insert(extend_pcr_value.end(), settings_digest.begin(),
settings_digest.end());
SecureBlob final_pcr_value = CryptoLib::Sha1(extend_pcr_value);
if (quote.quoted_pcr_value().size() == final_pcr_value.size() &&
0 == memcmp(quote.quoted_pcr_value().data(), final_pcr_value.data(),
final_pcr_value.size())) {
std::string description = "Developer Mode: ";
description += kKnownPCRValues[i].developer_mode_enabled ? "On" : "Off";
description += ", Recovery Mode: ";
description += kKnownPCRValues[i].recovery_mode_enabled ? "On" : "Off";
description += ", Firmware Type: ";
description += (kKnownPCRValues[i].firmware_type == 1) ? "Verified" :
"Developer";
LOG(INFO) << "PCR0: " << description;
return true;
}
}
LOG(WARNING) << "PCR0 value not recognized.";
return true;
}
bool Attestation::VerifyPCR1Quote(const SecureBlob& aik_public_key,
const Quote& quote) {
if (!VerifyQuoteSignature(aik_public_key, quote, 1)) {
return false;
}
// Check that the source hint is correctly populated.
std::string hwid = platform_->GetHardwareID();
if (hwid != 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 Attestation::VerifyQuoteSignature(const SecureBlob& aik_public_key,
const Quote& quote,
int pcr_index) {
if (!VerifySignature(aik_public_key,
SecureBlob(quote.quoted_data()),
SecureBlob(quote.quote()))) {
LOG(ERROR) << "Failed to verify quote signature.";
return false;
}
// Check that the quoted value matches the given PCR value. We can verify this
// by reconstructing the TPM_PCR_COMPOSITE structure the TPM would create.
CHECK_LE(pcr_index, 16);
CHECK_LE(quote.quoted_pcr_value().size(), 256U);
const uint8_t header[] = {
// TPM_PCR_SELECTION.sizeOfSelect: 16-bit length of PCR index bitmap.
uint8_t(0), uint8_t(2),
// TPM_PCR_SELECTION.pcrSelect: PCR index bitmap.
uint8_t(pcr_index < 8 ? (1 << pcr_index) : 0),
uint8_t(pcr_index < 8 ? 0 : (1 << (pcr_index - 8))),
// TPM_PCR_COMPOSITE.valueSize: 32-bit length of PCR value.
uint8_t(0), uint8_t(0), uint8_t(0),
uint8_t(quote.quoted_pcr_value().size())};
SecureBlob pcr_composite = SecureBlob::Combine(
SecureBlob(std::begin(header), std::end(header)),
SecureBlob(quote.quoted_pcr_value()));
SecureBlob pcr_digest = CryptoLib::Sha1(pcr_composite);
SecureBlob quoted_data(quote.quoted_data());
// The PCR digest should appear 8 bytes into the quoted data. See the
// TPM_QUOTE_INFO structure.
if (search(quoted_data.begin(), quoted_data.end(),
pcr_digest.begin(), pcr_digest.end()) != quoted_data.begin() + 8) {
LOG(ERROR) << "PCR value mismatch.";
return false;
}
if (quote.has_pcr_source_hint()) {
// Check if the PCR value matches the hint.
SecureBlob hint_digest = CryptoLib::Sha256(
SecureBlob(quote.pcr_source_hint()));
brillo::Blob extend_pcr_value(kDigestSize, 0);
extend_pcr_value.insert(extend_pcr_value.end(),
hint_digest.begin(),
hint_digest.begin() + 20);
SecureBlob final_pcr_value = CryptoLib::Sha1(extend_pcr_value);
if (quote.quoted_pcr_value().size() != final_pcr_value.size() ||
0 != memcmp(quote.quoted_pcr_value().data(), final_pcr_value.data(),
final_pcr_value.size())) {
LOG(ERROR) << "PCR source hint is invalid.";
return false;
}
}
return true;
}
bool Attestation::VerifyCertifiedKey(
const SecureBlob& aik_public_key,
const SecureBlob& certified_public_key,
const SecureBlob& certified_key_info,
const SecureBlob& proof) {
std::string key_info = certified_key_info.to_string();
if (!VerifySignature(aik_public_key, certified_key_info, proof)) {
LOG(ERROR) << "Failed to verify certified key proof signature.";
return false;
}
const unsigned char* asn1_ptr = certified_public_key.data();
std::unique_ptr<RSA, RSADeleter> rsa(
d2i_RSAPublicKey(NULL, &asn1_ptr, certified_public_key.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode certified public key.";
return false;
}
SecureBlob modulus(BN_num_bytes(rsa.get()->n));
BN_bn2bin(rsa.get()->n, modulus.data());
SecureBlob key_digest = CryptoLib::Sha1(modulus);
if (std::search(certified_key_info.begin(),
certified_key_info.end(),
key_digest.begin(),
key_digest.end()) == certified_key_info.end()) {
LOG(ERROR) << "Certified public key mismatch.";
return false;
}
return true;
}
std::unique_ptr<EVP_PKEY, Attestation::EVP_PKEYDeleter>
Attestation::GetAuthorityPublicKey(const char* issuer_name,
bool is_cros_core) {
const CertificateAuthority* const kKnownCA =
is_cros_core ? kKnownCrosCoreEndorsementCA : kKnownEndorsementCA;
const int kNumIssuers =
is_cros_core ? arraysize(kKnownCrosCoreEndorsementCA) :
arraysize(kKnownEndorsementCA);
for (int i = 0; i < kNumIssuers; ++i) {
if (0 == strcmp(issuer_name, kKnownCA[i].issuer)) {
std::unique_ptr<RSA, RSADeleter> rsa = CreateRSAFromHexModulus(
kKnownCA[i].modulus);
std::unique_ptr<EVP_PKEY, EVP_PKEYDeleter> pkey(EVP_PKEY_new());
if (!pkey.get()) {
return std::unique_ptr<EVP_PKEY, EVP_PKEYDeleter>();
}
EVP_PKEY_assign_RSA(pkey.get(), rsa.release());
return pkey;
}
}
return std::unique_ptr<EVP_PKEY, EVP_PKEYDeleter>();
}
bool Attestation::VerifySignature(const SecureBlob& public_key,
const SecureBlob& signed_data,
const SecureBlob& signature) {
const unsigned char* asn1_ptr = public_key.data();
std::unique_ptr<RSA, RSADeleter> rsa(
d2i_RSAPublicKey(NULL, &asn1_ptr, public_key.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
SecureBlob digest = CryptoLib::Sha1(signed_data);
if (!RSA_verify(NID_sha1, digest.data(), digest.size(),
signature.data(), signature.size(), rsa.get())) {
LOG(ERROR) << "Failed to verify signature.";
return false;
}
return true;
}
void Attestation::ClearDatabase() {
TPMCredentials* credentials = database_pb_.mutable_credentials();
ClearString(credentials->mutable_endorsement_public_key());
ClearString(credentials->mutable_endorsement_credential());
ClearString(credentials->mutable_platform_credential());
ClearString(credentials->mutable_conformance_credential());
ClearIdentity(database_pb_.mutable_identity_binding(),
database_pb_.mutable_identity_key());
ClearQuote(database_pb_.mutable_pcr0_quote());
ClearQuote(database_pb_.mutable_pcr1_quote());
Delegation* delegate = database_pb_.mutable_delegate();
ClearString(delegate->mutable_blob());
ClearString(delegate->mutable_secret());
database_pb_.Clear();
}
void Attestation::ClearQuote(Quote* quote) {
ClearString(quote->mutable_quote());
ClearString(quote->mutable_quoted_data());
ClearString(quote->mutable_quoted_pcr_value());
ClearString(quote->mutable_pcr_source_hint());
}
void Attestation::ClearIdentity(IdentityBinding* binding, IdentityKey* key) {
ClearString(binding->mutable_identity_binding());
ClearString(binding->mutable_identity_public_key_der());
ClearString(binding->mutable_identity_public_key());
ClearString(binding->mutable_identity_label());
ClearString(binding->mutable_pca_public_key());
ClearString(key->mutable_identity_public_key());
ClearString(key->mutable_identity_key_blob());
ClearString(key->mutable_identity_credential());
}
void Attestation::ClearString(std::string* s) {
brillo::SecureMemset(string_as_array(s), 0, s->length());
s->clear();
}
bool Attestation::VerifyActivateIdentity(const SecureBlob& delegate_blob,
const SecureBlob& delegate_secret,
const SecureBlob& identity_key_blob,
const SecureBlob& identity_public_key,
const SecureBlob& ek_public_key) {
const char* kTestCredential = "test";
const uint8_t kAlgAES256 = 9; // This comes from TPM_ALG_AES256.
const uint8_t kEncModeCBC = 2; // This comes from TPM_SYM_MODE_CBC.
const uint8_t kAsymContentHeader[] =
{0, 0, 0, kAlgAES256, 0, kEncModeCBC, 0, kCipherKeySize};
const uint8_t kSymContentHeader[12] = {0};
// Generate an AES key and encrypt the credential.
SecureBlob aes_key(kCipherKeySize);
CryptoLib::GetSecureRandom(aes_key.data(), aes_key.size());
SecureBlob credential(kTestCredential,
kTestCredential + strlen(kTestCredential));
SecureBlob encrypted_credential;
if (!TssCompatibleEncrypt(aes_key, credential, &encrypted_credential)) {
LOG(ERROR) << "Failed to encrypt credential.";
return false;
}
// Construct a TPM_ASYM_CA_CONTENTS structure.
SecureBlob public_key_digest = CryptoLib::Sha1(identity_public_key);
SecureBlob asym_content = SecureBlob::Combine(SecureBlob::Combine(
SecureBlob(std::begin(kAsymContentHeader), std::end(kAsymContentHeader)),
aes_key),
public_key_digest);
// Encrypt the TPM_ASYM_CA_CONTENTS with the EK public key.
const unsigned char* asn1_ptr = ek_public_key.data();
std::unique_ptr<RSA, RSADeleter> rsa(
d2i_RSAPublicKey(NULL, &asn1_ptr, ek_public_key.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode EK public key.";
return false;
}
SecureBlob encrypted_asym_content;
if (!TpmCompatibleOAEPEncrypt(rsa.get(), asym_content,
&encrypted_asym_content)) {
LOG(ERROR) << "Failed to encrypt with EK public key.";
return false;
}
// Construct a TPM_SYM_CA_ATTESTATION structure.
uint32_t length = htonl(encrypted_credential.size());
SecureBlob length_blob(sizeof(uint32_t));
memcpy(length_blob.data(), &length, sizeof(uint32_t));
SecureBlob sym_content = SecureBlob::Combine(SecureBlob::Combine(
length_blob,
SecureBlob(std::begin(kSymContentHeader), std::end(kSymContentHeader))),
encrypted_credential);
// Attempt to activate the identity.
SecureBlob credential_out;
if (!tpm_->ActivateIdentity(delegate_blob, delegate_secret, identity_key_blob,
encrypted_asym_content, sym_content,
&credential_out)) {
LOG(ERROR) << "Failed to activate identity.";
return false;
}
if (credential.size() != credential_out.size() ||
brillo::SecureMemcmp(credential.data(), credential_out.data(),
credential.size()) != 0) {
LOG(ERROR) << "Invalid identity credential.";
return false;
}
return true;
}
bool Attestation::EncryptEndorsementCredential(
PCAType pca_type,
const SecureBlob& credential,
EncryptedData* encrypted_credential) {
std::unique_ptr<RSA, RSADeleter> rsa;
std::string key_id;
switch (pca_type) {
case kDefaultPCA:
rsa = CreateRSAFromHexModulus(kDefaultPCAPublicKey);
key_id = std::string(kDefaultPCAPublicKeyID,
arraysize(kDefaultPCAPublicKeyID) - 1);
break;
case kTestPCA:
rsa = CreateRSAFromHexModulus(kTestPCAPublicKey);
key_id = std::string(kTestPCAPublicKeyID,
arraysize(kTestPCAPublicKeyID) - 1);
break;
default:
NOTREACHED();
}
if (!rsa.get()) {
LOG(ERROR) << __func__ << ": Failed to decode public key.";
return false;
}
return EncryptData(credential, rsa.get(), key_id, encrypted_credential);
}
const EncryptedData& Attestation::GetEncryptedEndorsementCredential(
PCAType pca_type) const {
switch (pca_type) {
case kDefaultPCA:
default:
return database_pb_.credentials()
.default_encrypted_endorsement_credential();
break;
case kTestPCA:
return database_pb_.credentials()
.test_encrypted_endorsement_credential();
break;
}
}
bool Attestation::AddDeviceKey(const std::string& key_name,
const CertifiedKey& key) {
// If a key by this name already exists, reuse the field.
bool found = false;
for (int i = 0; i < database_pb_.device_keys_size(); ++i) {
if (database_pb_.device_keys(i).key_name() == key_name) {
found = true;
*database_pb_.mutable_device_keys(i) = key;
break;
}
}
if (!found)
*database_pb_.add_device_keys() = key;
return PersistDatabaseChanges();
}
void Attestation::RemoveDeviceKey(const std::string& key_name) {
bool found = false;
for (int i = 0; i < database_pb_.device_keys_size(); ++i) {
if (database_pb_.device_keys(i).key_name() == key_name) {
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 (!PersistDatabaseChanges()) {
LOG(WARNING) << __func__ << ": Failed to persist key deletion.";
}
}
}
bool Attestation::FindKeyByName(bool is_user_specific,
const std::string& username,
const std::string& key_name,
CertifiedKey* key) {
if (is_user_specific) {
SecureBlob key_data;
if (!key_store_->Read(is_user_specific, username, key_name, &key_data)) {
LOG(INFO) << "Key not found: " << key_name;
return false;
}
if (!key->ParseFromArray(key_data.data(), key_data.size())) {
LOG(ERROR) << "Failed to parse key: " << key_name;
return false;
}
return true;
}
for (int i = 0; i < database_pb_.device_keys_size(); ++i) {
if (database_pb_.device_keys(i).key_name() == key_name) {
*key = database_pb_.device_keys(i);
return true;
}
}
LOG(INFO) << "Key not found: " << key_name;
return false;
}
bool Attestation::SaveKey(bool is_user_specific,
const std::string& username,
const std::string& key_name,
const CertifiedKey& key) {
if (is_user_specific) {
std::string tmp;
if (!key.SerializeToString(&tmp)) {
LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
return false;
}
SecureBlob blob(tmp);
ClearString(&tmp);
if (!key_store_->Write(is_user_specific, username, key_name, blob)) {
LOG(ERROR) << __func__ << ": Failed to store certified key for user.";
return false;
}
} else {
if (!AddDeviceKey(key_name, key)) {
LOG(ERROR) << __func__ << ": Failed to store certified key for device.";
return false;
}
}
return true;
}
void Attestation::DeleteKey(bool is_user_specific,
const std::string& username,
const std::string& key_name) {
if (is_user_specific) {
key_store_->Delete(is_user_specific, username, key_name);
} else {
RemoveDeviceKey(key_name);
}
}
bool Attestation::CreatePEMCertificateChain(const CertifiedKey& key,
SecureBlob* certificate_chain) {
if (key.certified_key_credential().empty()) {
LOG(ERROR) << "Certificate is empty.";
return false;
}
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));
}
*certificate_chain = SecureBlob(pem);
ClearString(&pem);
return true;
}
std::string Attestation::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;
}
bool Attestation::SignChallengeData(const CertifiedKey& key,
const SecureBlob& data_to_sign,
SecureBlob* response) {
SecureBlob signature;
if (!tpm_->Sign(SecureBlob(key.key_blob()),
data_to_sign,
kNotBoundToPCR,
&signature)) {
LOG(ERROR) << "Failed to generate signature.";
return false;
}
SignedData signed_data;
signed_data.set_data(data_to_sign.to_string());
signed_data.set_signature(signature.to_string());
std::string serialized;
if (!signed_data.SerializeToString(&serialized)) {
LOG(ERROR) << "Failed to serialize signed data.";
return false;
}
SecureBlob tmp(serialized);
ClearString(&serialized);
response->swap(tmp);
return true;
}
bool Attestation::ValidateEnterpriseChallenge(
const SignedData& signed_challenge) {
const char kExpectedChallengePrefix[] = "EnterpriseKeyChallenge";
std::unique_ptr<RSA, RSADeleter> rsa =
CreateRSAFromHexModulus(kEnterpriseSigningPublicKey);
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
SecureBlob digest = CryptoLib::Sha256(SecureBlob(signed_challenge.data()));
SecureBlob signature(signed_challenge.signature());
RSA* enterprise_key = enterprise_test_key_ ? enterprise_test_key_ : rsa.get();
if (!RSA_verify(NID_sha256, digest.data(), digest.size(),
signature.data(), signature.size(), enterprise_key)) {
LOG(ERROR) << "Failed to verify challenge signature.";
return false;
}
Challenge challenge;
if (!challenge.ParseFromString(signed_challenge.data())) {
LOG(ERROR) << "Failed to parse challenge protobuf.";
return false;
}
if (challenge.prefix() != kExpectedChallengePrefix) {
LOG(ERROR) << "Unexpected challenge prefix.";
return false;
}
return true;
}
bool Attestation::EncryptEnterpriseKeyInfo(const KeyInfo& key_info,
EncryptedData* encrypted_data) {
std::unique_ptr<RSA, RSADeleter> rsa =
CreateRSAFromHexModulus(kEnterpriseEncryptionPublicKey);
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
std::string serialized;
if (!key_info.SerializeToString(&serialized)) {
LOG(ERROR) << "Failed to serialize key info.";
return false;
}
RSA* enterprise_key = enterprise_test_key_ ? enterprise_test_key_ : rsa.get();
std::string enterprise_key_id(kEnterpriseEncryptionPublicKeyID,
arraysize(kEnterpriseEncryptionPublicKeyID) - 1);
bool result = EncryptData(SecureBlob(serialized),
enterprise_key,
enterprise_key_id,
encrypted_data);
ClearString(&serialized);
return result;
}
bool Attestation::EncryptData(const SecureBlob& input,
RSA* wrapping_key,
const std::string& wrapping_key_id,
EncryptedData* output) {
// Encrypt with a randomly generated AES key.
SecureBlob aes_key;
if (!tpm_->GetRandomData(kCipherKeySize, &aes_key)) {
LOG(ERROR) << "GetRandomData failed.";
return false;
}
SecureBlob aes_iv;
if (!tpm_->GetRandomData(kAesBlockSize, &aes_iv)) {
LOG(ERROR) << "GetRandomData failed.";
return false;
}
SecureBlob encrypted;
if (!AesEncrypt(input, aes_key, aes_iv, &encrypted)) {
LOG(ERROR) << "AesEncrypt failed.";
return false;
}
output->set_encrypted_data(encrypted.data(), encrypted.size());
output->set_iv(aes_iv.data(), aes_iv.size());
output->set_wrapping_key_id(wrapping_key_id);
output->set_mac(CryptoLib::ComputeEncryptedDataHMAC(*output, aes_key));
// Wrap the AES key with the given public key.
SecureBlob encrypted_key(RSA_size(wrapping_key));
int length = RSA_public_encrypt(aes_key.size(),
aes_key.data(),
encrypted_key.data(),
wrapping_key, RSA_PKCS1_OAEP_PADDING);
if (length == -1) {
LOG(ERROR) << "RSA_public_encrypt failed.";
return false;
}
encrypted_key.resize(length);
output->set_wrapped_key(encrypted_key.to_string());
return true;
}
std::unique_ptr<RSA, Attestation::RSADeleter>
Attestation::CreateRSAFromHexModulus(
const std::string& hex_modulus) {
std::unique_ptr<RSA, RSADeleter> rsa(RSA_new());
if (!rsa.get())
return std::unique_ptr<RSA, RSADeleter>();
rsa->e = BN_new();
if (!rsa->e)
return std::unique_ptr<RSA, RSADeleter>();
BN_set_word(rsa->e, kWellKnownExponent);
rsa->n = BN_new();
if (!rsa->n)
return std::unique_ptr<RSA, RSADeleter>();
if (0 == BN_hex2bn(&rsa->n, hex_modulus.c_str()))
return std::unique_ptr<RSA, RSADeleter>();
return rsa;
}
bool Attestation::CreateSignedPublicKey(
const CertifiedKey& key,
brillo::SecureBlob* signed_public_key) {
// Get the certified public key as an EVP_PKEY.
const unsigned char* asn1_ptr =
reinterpret_cast<const unsigned char*>(key.public_key().data());
std::unique_ptr<RSA, RSADeleter> rsa(
d2i_RSAPublicKey(NULL, &asn1_ptr, key.public_key().size()));
if (!rsa.get())
return false;
std::unique_ptr<EVP_PKEY, EVP_PKEYDeleter> public_key(EVP_PKEY_new());
if (!public_key.get())
return false;
EVP_PKEY_assign_RSA(public_key.get(), rsa.release());
// Fill in the public key.
std::unique_ptr<NETSCAPE_SPKI, NETSCAPE_SPKIDeleter>
spki(NETSCAPE_SPKI_new());
if (!spki.get())
return false;
if (!NETSCAPE_SPKI_set_pubkey(spki.get(), public_key.get()))
return false;
// Fill in a random challenge.
SecureBlob challenge;
if (!tpm_->GetRandomData(kNonceSize, &challenge))
return false;
std::string challenge_hex = base::HexEncode(challenge.data(),
challenge.size());
if (!ASN1_STRING_set(spki.get()->spkac->challenge,
challenge_hex.data(),
challenge_hex.size()))
return false;
// Generate the signature.
unsigned char* buffer = NULL;
int length = i2d_NETSCAPE_SPKAC(spki.get()->spkac, &buffer);
if (length <= 0)
return false;
SecureBlob data_to_sign(buffer, buffer + length);
OPENSSL_free(buffer);
SecureBlob signature;
if (!tpm_->Sign(SecureBlob(key.key_blob()),
data_to_sign,
kNotBoundToPCR,
&signature)) {
return false;
}
// Fill in the signature and algorithm.
if (!ASN1_BIT_STRING_set(spki.get()->signature,
reinterpret_cast<unsigned char*>(signature.data()),
signature.size())) {
return false;
}
// Be explicit that there are zero unused bits; otherwise i2d below will
// automatically detect unused bits but signatures require zero unused bits.
spki.get()->signature->flags = ASN1_STRING_FLAG_BITS_LEFT;
X509_ALGOR_set0(spki.get()->sig_algor,
OBJ_nid2obj(NID_sha256WithRSAEncryption),
V_ASN1_NULL,
NULL);
// DER encode.
buffer = NULL;
length = i2d_NETSCAPE_SPKI(spki.get(), &buffer);
if (length <= 0)
return false;
SecureBlob tmp(buffer, buffer + length);
OPENSSL_free(buffer);
signed_public_key->swap(tmp);
return true;
}
bool Attestation::AesEncrypt(const brillo::SecureBlob& plaintext,
const brillo::SecureBlob& key,
const brillo::SecureBlob& iv,
brillo::SecureBlob* ciphertext) {
return CryptoLib::AesEncryptSpecifyBlockMode(plaintext, 0, plaintext.size(),
key, iv,
CryptoLib::kPaddingStandard,
CryptoLib::kCbc,
ciphertext);
}
bool Attestation::AesDecrypt(const brillo::SecureBlob& ciphertext,
const brillo::SecureBlob& key,
const brillo::SecureBlob& iv,
brillo::SecureBlob* plaintext) {
return CryptoLib::AesDecryptSpecifyBlockMode(ciphertext, 0, ciphertext.size(),
key, iv,
CryptoLib::kPaddingStandard,
CryptoLib::kCbc,
plaintext);
}
bool Attestation::TssCompatibleEncrypt(const SecureBlob& key,
const SecureBlob& input,
SecureBlob* output) {
CHECK(output);
if (key.size() != kCipherKeySize) {
LOG(ERROR) << "Wrong key size!";
return false;
}
SecureBlob iv(AES_BLOCK_SIZE);
CryptoLib::GetSecureRandom(reinterpret_cast<unsigned char*>(iv.data()),
AES_BLOCK_SIZE);
SecureBlob encrypted_input;
if (!AesEncrypt(input, key, iv, &encrypted_input)) {
LOG(ERROR) << "Error encrypting input.";
return false;
}
*output = SecureBlob::Combine(iv, encrypted_input);
return true;
}
bool Attestation::TpmCompatibleOAEPEncrypt(RSA* key,
const brillo::SecureBlob& input,
brillo::SecureBlob* output) {
CHECK(output);
// The custom OAEP parameter as specified in TPM Main Part 1, Section 31.1.1.
const unsigned char oaep_param[4] = {'T', 'C', 'P', 'A'};
brillo::SecureBlob padded_input(RSA_size(key));
unsigned char* padded_buffer = padded_input.data();
const unsigned char* input_buffer = input.data();
int result = RSA_padding_add_PKCS1_OAEP(padded_buffer, padded_input.size(),
input_buffer, input.size(),
oaep_param, arraysize(oaep_param));
if (!result) {
LOG(ERROR) << "Failed to add OAEP padding.";
return false;
}
output->resize(padded_input.size());
unsigned char* output_buffer = output->data();
result = RSA_public_encrypt(padded_input.size(), padded_buffer,
output_buffer, key, RSA_NO_PADDING);
if (result == -1) {
LOG(ERROR) << "Failed to encrypt OAEP padded input.";
return false;
}
return true;
}
int Attestation::ChooseTemporalIndex(const std::string& user,
const std::string& origin) {
std::string user_hash = CryptoLib::Sha256(SecureBlob(user)).to_string();
std::string origin_hash = CryptoLib::Sha256(SecureBlob(origin)).to_string();
int histogram[kNumTemporalValues] = {0};
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.";
ReportCrosEvent(kAttestationOriginSpecificIdentifiersExhausted);
}
// 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);
PersistDatabaseChanges();
return least_used_index;
}
void Attestation::FinalizeEndorsementData() {
if (retain_endorsement_data_) {
return;
}
// Only finalize endorsement data after install attributes are finalized.
if (install_attributes_->is_first_install()) {
return;
}
if (!database_pb_.has_credentials()) {
return;
}
TPMCredentials* credentials = database_pb_.mutable_credentials();
if (!credentials->has_endorsement_credential()) {
return;
}
if (!credentials->has_default_encrypted_endorsement_credential()) {
LOG(INFO) << "Attestation: Migrating default endorsement data.";
if (!EncryptEndorsementCredential(
kDefaultPCA,
SecureBlob(credentials->endorsement_credential()),
credentials->mutable_default_encrypted_endorsement_credential())) {
LOG(ERROR) << "Attestation: Failed to encrypt EK cert.";
return;
}
}
if (!credentials->has_test_encrypted_endorsement_credential()) {
LOG(INFO) << "Attestation: Migrating endorsement data (test).";
if (!EncryptEndorsementCredential(
kTestPCA,
SecureBlob(credentials->endorsement_credential()),
credentials->mutable_test_encrypted_endorsement_credential())) {
LOG(ERROR) << "Attestation: Failed to encrypt EK cert (test).";
return;
}
}
LOG(INFO) << "Attestation: Clearing endorsement data.";
ClearString(credentials->mutable_endorsement_public_key());
credentials->clear_endorsement_public_key();
ClearString(credentials->mutable_endorsement_credential());
credentials->clear_endorsement_credential();
if (!PersistDatabaseChanges()) {
LOG(ERROR) << "Attestation: Failed to persist database changes.";
}
}
bool Attestation::GetDelegateCredentials(brillo::SecureBlob* blob,
brillo::SecureBlob* secret,
bool* has_reset_lock_permissions) {
if (!IsPreparedForEnrollment()) {
return false;
}
SecureBlob tmp_blob(database_pb_.delegate().blob());
blob->swap(tmp_blob);
SecureBlob tmp_secret(database_pb_.delegate().secret());
secret->swap(tmp_secret);
*has_reset_lock_permissions =
database_pb_.delegate().has_reset_lock_permissions();
return true;
}
bool Attestation::GetCachedEndorsementData(
brillo::SecureBlob* ek_public_key,
brillo::SecureBlob* ek_certificate) {
if (!database_pb_.has_credentials()) {
return false;
}
const TPMCredentials& credentials = database_pb_.credentials();
// If the TPM is not owned it's possible we have access to the public key but
// not to the certificate.
if (!credentials.has_endorsement_public_key()) {
return false;
}
SecureBlob tmp_public_key(credentials.endorsement_public_key());
ek_public_key->swap(tmp_public_key);
SecureBlob tmp_credential(credentials.endorsement_credential());
ek_certificate->swap(tmp_credential);
return true;
}
void Attestation::CacheEndorsementData() {
// Before taking ownership we can only cache the public key. After taking
// ownership this is taken care of by the prepare-for-enrollment process.
if (tpm_->IsOwned() ||
(database_pb_.has_credentials() &&
database_pb_.credentials().has_endorsement_public_key())) {
return;
}
SecureBlob public_key_blob;
if (!tpm_->GetEndorsementPublicKey(&public_key_blob)) {
LOG(WARNING) << "TPM is not owned but failed to cache EK public key.";
return;
}
database_pb_.mutable_credentials()->set_endorsement_public_key(
public_key_blob.data(),
public_key_blob.size());
}
bool Attestation::IsTPMReady() {
if (!is_tpm_ready_ && tpm_)
is_tpm_ready_ = tpm_->IsEnabled() &&
tpm_->IsOwned() &&
!tpm_->IsBeingOwned();
return is_tpm_ready_;
}
void Attestation::ExtendPCR1IfClear() {
SecureBlob current_pcr_value;
if (!tpm_->ReadPCR(1, &current_pcr_value) ||
current_pcr_value.size() != kDigestSize) {
LOG(WARNING) << "Failed to read PCR1.";
return;
}
brillo::Blob default_pcr_value(kDigestSize, 0);
if (!std::equal(default_pcr_value.begin(), default_pcr_value.end(),
current_pcr_value.begin())) {
// The PCR has already been extended.
return;
}
std::string hwid = platform_->GetHardwareID();
LOG(WARNING) << "Extending PCR1.";
// Take the first 20 bytes of a SHA-256 hash because this is what firmware
// would do. (Using SHA-256 allows a single precomputed hash to be stored
// along with the HWID for both TPM 1.2 and 2.0 platforms).
SecureBlob hwid_hash = CryptoLib::Sha256(SecureBlob(hwid));
SecureBlob extension(hwid_hash.begin(), hwid_hash.begin() + kDigestSize);
if (hwid.length() == 0 || !tpm_->ExtendPCR(1, extension)) {
LOG(WARNING) << "Failed to extend PCR1.";
}
}
bool Attestation::SendPCARequestAndBlock(PCAType pca_type,
PCARequestType request_type,
const brillo::SecureBlob& request,
brillo::SecureBlob* reply) {
std::shared_ptr<brillo::http::Transport> transport = http_transport_;
if (!transport) {
transport = brillo::http::Transport::CreateDefault();
}
std::unique_ptr<brillo::http::Response> response = PostBinaryAndBlock(
GetPCAURL(pca_type, request_type),
request.data(),
request.size(),
brillo::mime::application::kOctet_stream,
{}, // headers
transport,
NULL); // error
if (!response->IsSuccessful()) {
LOG(ERROR) << "HTTP request to PCA failed.";
return false;
}
std::vector<uint8_t> response_data = response->ExtractData();
SecureBlob tmp(response_data.begin(), response_data.end());
reply->swap(tmp);
return true;
}
std::string Attestation::GetPCAURL(PCAType pca_type,
PCARequestType request_type) const {
std::string url;
switch (pca_type) {
case kDefaultPCA:
url = kDefaultPCAWebOrigin;
break;
default:
NOTREACHED();
}
switch (request_type) {
case kEnroll:
url += "/enroll";
break;
case kGetCertificate:
url += "/sign";
break;
default:
NOTREACHED();
}
return url;
}
bool Attestation::ComputeEnterpriseEnrollmentNonce(
brillo::SecureBlob* enterprise_enrollment_nonce) {
if (abe_data_.empty()) {
// If there is no device secret we cannot compute the DEN. We do not
// want to fail attestation for those devices.
enterprise_enrollment_nonce->clear();
return true;
}
const uint8_t* context_name = reinterpret_cast<const uint8_t*>(
kAttestationBasedEnterpriseEnrollmentContextName);
SecureBlob context_key(context_name, context_name
+ sizeof(kAttestationBasedEnterpriseEnrollmentContextName) - 1);
SecureBlob nonce = CryptoLib::HmacSha256(context_key, abe_data_);
enterprise_enrollment_nonce->swap(nonce);
return true;
}
void Attestation::RSADeleter::operator()(void* ptr) const {
if (ptr)
RSA_free(reinterpret_cast<RSA*>(ptr));
}
void Attestation::X509Deleter::operator()(void* ptr) const {
if (ptr)
X509_free(reinterpret_cast<X509*>(ptr));
}
void Attestation::EVP_PKEYDeleter::operator()(void* ptr) const {
if (ptr)
EVP_PKEY_free(reinterpret_cast<EVP_PKEY*>(ptr));
}
void Attestation::NETSCAPE_SPKIDeleter::operator()(void* ptr) const {
if (ptr)
NETSCAPE_SPKI_free(reinterpret_cast<NETSCAPE_SPKI*>(ptr));
}
} // namespace cryptohome