blob: 2400f6361c431563b960aa052485057b117141f6 [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 <iterator>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include <arpa/inet.h>
#include <base/logging.h>
#include <base/optional.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_proxy.h>
#include <brillo/http/http_utils.h>
#include <brillo/mime_utils.h>
#include <brillo/secure_blob.h>
#include <crypto/libcrypto-compat.h>
#include <crypto/scoped_openssl_types.h>
#include <google/protobuf/descriptor.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::Blob;
using brillo::BlobFromString;
using brillo::BlobToString;
using brillo::CombineBlobs;
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_;
};
std::string GetPCAName(cryptohome::Attestation::PCAType pca_type) {
switch (pca_type) {
case cryptohome::Attestation::kDefaultPCA:
return "the default PCA";
case cryptohome::Attestation::kTestPCA:
return "the test PCA";
default: {
std::ostringstream stream;
stream << "PCA " << pca_type;
return stream.str();
}
}
}
std::string GetIdentityFeaturesString(int identity_features) {
unsigned features_count = 0;
std::ostringstream stream;
if (identity_features == cryptohome::NO_IDENTITY_FEATURES) {
stream << cryptohome::IdentityFeatures_Name(
cryptohome::NO_IDENTITY_FEATURES);
} else {
const google::protobuf::EnumDescriptor* desc =
cryptohome::IdentityFeatures_descriptor();
for (int i = 0, count = desc->value_count(); i < count; ++i) {
const google::protobuf::EnumValueDescriptor* value_desc = desc->value(i);
if (identity_features & value_desc->number()) {
++features_count;
if (stream.tellp() > 0) {
stream << ", ";
}
stream << value_desc->name();
}
}
}
return std::string("identity feature") + (features_count != 1 ? "s " : " ")
+ stream.str();
}
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 cryptohome {
using QuoteMap = google::protobuf::Map<int, Quote>;
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.
// As of this writing, it is a zero (0x00) byte followed by the base64-decoded
// value of the Keymaster hash (doc. added for later memory jogging).
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.
// As of this writing, it is a zero (0x00) byte followed by the base64-decoded
// value of the Keymaster hash (doc. added for later memory jogging).
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, pass the right PCA type in calls."
#endif
const char Attestation::kDefaultEnterpriseSigningPublicKey[] =
"bf7fefa3a661437b26aed0801db64d7ba8b58875c351d3bdc9f653847d4a67b3"
"b67479327724d56aa0f71a3f57c2290fdc1ff05df80589715e381dfbbda2c4ac"
"114c30d0a73c5b7b2e22178d26d8b65860aa8dd65e1b3d61a07c81de87c1e7e4"
"590145624936a011ece10434c1d5d41f917c3dc4b41dd8392479130c4fd6eafc"
"3bb4e0dedcc8f6a9c28428bf8fbba8bd6438a325a9d3eabee1e89e838138ad99"
"69c292c6d9f6f52522333b84ddf9471ffe00f01bf2de5faa1621f967f49e158b"
"f2b305360f886826cc6fdbef11a12b2d6002d70d8d1e8f40e0901ff94c203cb2"
"01a36a0bd6e83955f14b494f4f2f17c0c826657b85c25ffb8a73599721fa17ab";
const char Attestation::kDefaultEnterpriseEncryptionPublicKey[] =
"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 PCA maintainers.
// As of this writing, it is a zero (0x00) byte followed by the base64-decoded
// value of the Keymaster hash (doc. added for later memory jogging).
const char Attestation::kDefaultEnterpriseEncryptionPublicKeyID[] =
"\x00\x4a\xe2\xdc\xae";
const char Attestation::kTestEnterpriseSigningPublicKey[] =
"baab3e277518c65b1b98290bb55061df9a50b9f32a4b0ff61c7c61c51e966fcd"
"c891799a39ee0b7278f204a2b45a7e615080ff8f69f668e05adcf3486b319f80"
"f9da814d9b86b16a3e68b4ce514ab5591112838a68dc3bfdcc4043a5aa8de52c"
"ae936847a271971ecaa188172692c13f3b0321239c90559f3b7ba91e66d38ef4"
"db4c75104ac5f2f15e55a463c49753a88e56906b1725fd3f0c1372beb16d4904"
"752c74452b0c9f757ee12877a859dd0666cafaccbfc33fe67d98a89a2c12ef52"
"5e4b16ea8972577dbfc567c2625a3eee6bcaa6cb4939b941f57236d1d57243f8"
"c9766938269a8034d82fbd44044d2ee6a5c7275589afc3790b60280c0689900f";
const char Attestation::kTestEnterpriseEncryptionPublicKey[] =
"c0c116e7ded8d7c1e577f9c8fb0d267c3c5c3e3b6800abb0309c248eaa5cd9bf"
"91945132e4bb0111711356a388b756788e20bc1ecc9261ea9bcae8369cfd050e"
"d8dc00b50fbe36d2c1c8a9b335f2e11096be76bebce8b5dcb0dc39ac0fd963b0"
"51474f794d4289cc0c52d0bab451b9e69a43ecd3a84330b0b2de4365c038ffce"
"ec0f1999d789615849c2f3c29d1d9ed42ccb7f330d5b56f40fb7cc6556190c3b"
"698c20d83fb341a442fd69701fe0bdc41bdcf8056ccbc8d9b4275e8e43ec6b63"
"c1ae70d52838dfa90a9cd9e7b6bd88ed3abf4fab444347104e30e635f4f296ac"
"4c91939103e317d0eca5f36c48102e967f176a19a42220f3cf14634b6773be07";
// 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.
// As of this writing, it is a zero (0x00) byte followed by the base64-decoded
// value of the Keymaster hash (doc. added for later memory jogging).
const char Attestation::kTestEnterpriseEncryptionPublicKeyID[] =
"\x00\xef\x22\x0f\xb0";
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" },
{ "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 Attestation::CertificateAuthority
Attestation::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" },
};
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()),
install_attributes_observer_(this),
is_tpm_ready_(false),
is_prepare_in_progress_(false),
retain_endorsement_data_(false),
attestation_user_(0),
attestation_group_(0) {
DCHECK(sizeof(kDefaultPCAPublicKey) == 512 + 1);
DCHECK(sizeof(kTestPCAPublicKey) == 512 + 1);
DCHECK(sizeof(kDefaultPCAPublicKeyID) == 5 + 1);
DCHECK(sizeof(kTestPCAPublicKeyID) == 5 + 1);
DCHECK(sizeof(kDefaultEnterpriseSigningPublicKey) == 512 + 1);
DCHECK(sizeof(kTestEnterpriseSigningPublicKey) == 512 + 1);
DCHECK(sizeof(kDefaultEnterpriseEncryptionPublicKey) == 512 + 1);
DCHECK(sizeof(kTestEnterpriseEncryptionPublicKey) == 512 + 1);
DCHECK(sizeof(kDefaultEnterpriseEncryptionPublicKeyID) == 5 + 1);
DCHECK(sizeof(kTestEnterpriseEncryptionPublicKeyID) == 5 + 1);
}
Attestation::~Attestation() {
if (!thread_.is_null())
base::PlatformThread::Join(thread_);
ClearDatabase();
}
void Attestation::set_default_identity_features_for_test(
int default_identity_features) {
default_identity_features_ = default_identity_features;
}
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;
}
if (MigrateAttestationDatabase()) {
if (PersistDatabaseChanges() != AttestationResult::kSuccess) {
LOG(WARNING) << "Attestation: Failed to persist database changes.";
}
}
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);
// Pass the delegate data to TPM.
tpm_->SetDelegateData(database_pb_.delegate().blob(),
database_pb_.delegate().has_reset_lock_permissions());
}
}
bool Attestation::IsPreparedForEnrollment() {
base::AutoLock lock(lock_);
if (!database_pb_.has_credentials()) {
return false;
}
return database_pb_.credentials().has_endorsement_credential() ||
database_pb_.credentials().encrypted_endorsement_credentials().size();
}
bool Attestation::IsPreparedForEnrollmentWith(PCAType pca_type) {
base::AutoLock lock(lock_);
return database_pb_.credentials().encrypted_endorsement_credentials().count(
pca_type);
}
bool Attestation::IsEnrolled() {
base::AutoLock lock(lock_);
return HasIdentityCertificate(kFirstIdentity, kDefaultPCA) ||
HasIdentityCertificate(kFirstIdentity, kTestPCA);
}
bool Attestation::IsEnrolledWith(PCAType pca_type) {
base::AutoLock lock(lock_);
return HasIdentityCertificate(kFirstIdentity, pca_type);
}
Attestation::IdentityCertificateMap::iterator
Attestation::FindIdentityCertificate(int identity, PCAType pca_type) {
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() == pca_type) {
return it;
}
}
return end;
}
bool Attestation::HasIdentityCertificate(int identity, PCAType pca_type) {
return FindIdentityCertificate(identity, pca_type) !=
database_pb_.mutable_identity_certificates()->end();
}
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 we are prepared for enrollment, we are done.
if (IsPreparedForEnrollment())
return;
base::TimeTicks start = base::TimeTicks::Now();
LOG(INFO) << "Attestation: Preparing for enrollment...";
// If the PCR0 state is bad, don't bother to get EKpub, create AIK, or
// do other preparation below to avoid unnecessary overheads.
if (!tpm_->IsCurrentPCR0ValueValid()) {
LOG(ERROR) << "Attestation: Bad PCR0 state.";
ReportAttestationOpsStatus(kAttestationPrepareForEnrollment,
AttestationOpsStatus::kInvalidPcr0Value);
return;
}
SecureBlob ek_public_key;
if (tpm_->GetEndorsementPublicKey(&ek_public_key) != Tpm::kTpmRetryNone) {
LOG(ERROR) << "Attestation: Failed to get EK public key.";
ReportAttestationOpsStatus(kAttestationPrepareForEnrollment,
AttestationOpsStatus::kFailure);
return;
}
base::AutoLock lock(lock_);
// Compute and store the device EID if needed.
if (!database_pb_.has_enrollment_id()) {
ComputeEnterpriseEnrollmentIdInternal(&enterprise_enrollment_id_);
database_pb_.set_enrollment_id(enterprise_enrollment_id_.data(),
enterprise_enrollment_id_.size());
}
// Create a new AIK and PCR quotes for the first identity with default
// identity features.
AttestationResult result = CreateIdentity(default_identity_features_,
ek_public_key);
if (result != AttestationResult::kSuccess) {
AttestationOpsStatus status =
result == AttestationResult::kInvalidPcr0Value ?
AttestationOpsStatus::kInvalidPcr0Value :
AttestationOpsStatus::kFailure;
ReportAttestationOpsStatus(kAttestationPrepareForEnrollment, status);
return;
}
// Encrypt the endorsement credential for all the PCAs we know of.
TPMCredentials* credentials_pb = database_pb_.mutable_credentials();
SecureBlob endorsement_credential(credentials_pb->endorsement_credential());
for (int pca = kDefaultPCA; pca < kMaxPCAType; ++pca) {
PCAType pca_type = static_cast<PCAType>(pca);
LOG(INFO) << "Attestation: Encrypting endorsement credential for "
<< GetPCAName(pca_type) << ".";
if (!EncryptEndorsementCredential(
pca_type, endorsement_credential,
&(*credentials_pb
->mutable_encrypted_endorsement_credentials())[pca_type])) {
LOG(ERROR) << "Attestation: Failed to encrypt EK cert for "
<< GetPCAName(pca_type) << ".";
ReportAttestationOpsStatus(kAttestationPrepareForEnrollment,
AttestationOpsStatus::kFailure);
return;
}
}
// Create a delegate so we can activate the AIKs later.
// Note that the delegate is created with no PCR0 binding, since some Chrome
// OS devices have a bug causing the PCR0 to get into an unexpected value (see
// https://crbug.com/873099).
Blob delegate_blob;
Blob delegate_secret;
if (!tpm_->CreateDelegate(/*bound_pcrs=*/{}, Tpm::kDefaultDelegateFamilyLabel,
Tpm::kDefaultDelegateLabel, &delegate_blob,
&delegate_secret)) {
LOG(ERROR) << "Attestation: Failed to create delegate.";
ReportAttestationOpsStatus(kAttestationPrepareForEnrollment,
AttestationOpsStatus::kFailure);
return;
}
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);
delegate_pb->set_can_read_internal_pub(true);
result = PersistDatabaseChanges();
if (result != AttestationResult::kSuccess) {
AttestationOpsStatus status =
result == AttestationResult::kInvalidPcr0Value ?
AttestationOpsStatus::kInvalidPcr0Value :
AttestationOpsStatus::kFailure;
ReportAttestationOpsStatus(kAttestationPrepareForEnrollment, status);
return;
}
tpm_init_->RemoveTpmOwnerDependency(
TpmPersistentState::TpmOwnerDependency::kAttestation);
base::TimeDelta delta = (base::TimeTicks::Now() - start);
LOG(INFO) << "Attestation: Prepared successfully (" << delta.InMilliseconds()
<< "ms).";
ReportAttestationOpsStatus(kAttestationPrepareForEnrollment,
AttestationOpsStatus::kSuccess);
}
AttestationResult Attestation::CreateIdentity(
int identity_features, const SecureBlob& ek_public_key) {
// The identity we're creating will have the next index in identities.
const int identity = database_pb_.identities().size();
LOG(INFO) << "Attestation: Creating identity " << identity << " with "
<< GetIdentityFeaturesString(identity_features) << ".";
// Create the 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 for identity " << identity
<< ".";
return AttestationResult::kFailure;
}
// This only needs to be done once when we haven't stored credentials yet.
TPMCredentials* credentials_pb = database_pb_.mutable_credentials();
if (!credentials_pb->has_endorsement_credential()) {
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());
}
AttestationDatabase::Identity* identity_data =
database_pb_.mutable_identities()->Add();
identity_data->set_features(identity_features);
IdentityKey* key_pb = identity_data->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());
if (identity_features & IDENTITY_FEATURE_ENTERPRISE_ENROLLMENT_ID) {
key_pb->set_enrollment_id(database_pb_.enrollment_id());
}
IdentityBinding* binding_pb = identity_data->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());
// Store PCR quotes in the identity.
auto* map = identity_data->mutable_pcr_quotes();
// Quote PCR0 and PCR1
for (int i = 0; i <= 1; i++) {
Quote quote_pb;
AttestationResult result = CreatePCRQuote(i, identity_key_blob, &quote_pb);
if (result != AttestationResult::kSuccess) {
// Note that if in the future we regularly uses multiple AIK
// (i.e. identity key), then we'll need to print error messages here to
// indicate which one of the identity key failed the PCR quote process.
return result;
}
// PCR1 quote needs to have the source hint
if (i == 1) {
quote_pb.set_pcr_source_hint(platform_->GetHardwareID());
}
auto in = map->insert(QuoteMap::value_type(i, quote_pb));
if (!in.second) {
LOG(ERROR) << "Attestation: Failed to store PCR" << i << " quote for "
<< "identity " << identity << ".";
return AttestationResult::kFailure;
}
}
return AttestationResult::kSuccess;
}
int Attestation::GetIdentitiesCount() const {
return database_pb_.identities().size();
}
int Attestation::GetIdentityFeatures(int identity) const {
return database_pb_.identities().Get(identity).features();
}
Attestation::IdentityCertificateMap Attestation::GetIdentityCertificateMap()
const {
base::AutoLock lock(lock_);
return database_pb_.identity_certificates();
}
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::EnrollEx(PCAType pca_type, bool forced) {
if (IsEnrolledWith(pca_type) && !forced) {
return true;
}
SecureBlob request;
if (!CreateEnrollRequest(pca_type, &request)) {
LOG(ERROR) << __func__ << ": Failed to create enroll request.";
return false;
}
SecureBlob reply;
if (!SendPCARequestWithProxyAndBlock(pca_type, PCARequestType::kEnroll,
request, &reply)) {
LOG(ERROR) << __func__ << ": Failed to send PCA request.";
return false;
}
if (!Enroll(pca_type, reply)) {
LOG(ERROR) << __func__ << ": Failed to finish enrollment.";
return false;
}
return true;
}
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) != Tpm::kTpmRetryNone) {
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;
}
// Verify() is only used with the first identity.
const AttestationDatabase::Identity& identity_data =
database_pb_.identities().Get(kFirstIdentity);
if (!VerifyIdentityBinding(identity_data.identity_binding())) {
LOG(ERROR) << "Attestation: Bad identity binding.";
return false;
}
SecureBlob aik_public_key =
SecureBlob(identity_data.identity_binding().identity_public_key_der());
if (!VerifyPCR0Quote(aik_public_key, identity_data.pcr_quotes().at(0))) {
LOG(ERROR) << "Attestation: Bad PCR0 quote.";
return false;
}
if (!VerifyPCR1Quote(aik_public_key, identity_data.pcr_quotes().at(1))) {
// Don't fail because many devices don't use PCR1.
LOG(WARNING) << "Attestation: Bad PCR1 quote.";
}
SecureBlob nonce;
if (!tpm_->GetRandomDataSecureBlob(kNonceSize, &nonce)) {
LOG(ERROR) << __func__ << ": GetRandomDataSecureBlob failed.";
return false;
}
SecureBlob identity_key_blob(
identity_data.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;
}
brillo::Blob delegate_blob =
brillo::BlobFromString(database_pb_.delegate().blob());
brillo::Blob delegate_secret =
brillo::BlobFromString(database_pb_.delegate().secret());
SecureBlob aik_public_key_tpm(
identity_data.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) != Tpm::kTpmRetryNone) {
LOG(ERROR) << __func__ << ": Failed to get EK public key.";
return false;
}
return VerifyEndorsementCredential(ek_cert, ek_public_key, is_cros_core);
}
bool Attestation::GetEnterpriseEnrollmentId(
SecureBlob* enterprise_enrollment_id) {
return !tpm_->IsTransient(GetEnterpriseEnrollmentIdInternal(
enterprise_enrollment_id));
}
Tpm::TpmRetryAction Attestation::GetEnterpriseEnrollmentIdInternal(
SecureBlob* enterprise_enrollment_id) {
if (!enterprise_enrollment_id_.empty()) {
*enterprise_enrollment_id = enterprise_enrollment_id_;
return Tpm::kTpmRetryNone;
}
if (database_pb_.has_enrollment_id()) {
enterprise_enrollment_id_ = SecureBlob(database_pb_.enrollment_id());
*enterprise_enrollment_id = enterprise_enrollment_id_;
return Tpm::kTpmRetryNone;
}
Tpm::TpmRetryAction action = ComputeEnterpriseEnrollmentIdInternal(
enterprise_enrollment_id);
if (action == Tpm::kTpmRetryNone) {
// Cache the computed value.
enterprise_enrollment_id_ = *enterprise_enrollment_id;
}
return action;
}
bool Attestation::CreateEnrollRequest(PCAType pca_type,
SecureBlob* pca_request) {
const int identity = kFirstIdentity;
if (!IsTPMReady())
return false;
if (!IsPreparedForEnrollmentWith(pca_type)) {
LOG(ERROR) << __func__ << ": Enrollment is not possible, attestation data"
" for " << GetPCAName(pca_type) << " does not exist.";
return false;
}
if (database_pb_.identities().size() <= identity) {
LOG(ERROR) << __func__ << ": Enrollment is not possible, identity "
<< identity << " does not exist.";
return false;
}
base::AutoLock lock(lock_);
AttestationEnrollmentRequest request_pb;
*request_pb.mutable_encrypted_endorsement_credential() =
database_pb_.credentials().encrypted_endorsement_credentials().at(
pca_type);
const AttestationDatabase::Identity& identity_data =
database_pb_.identities().Get(identity);
request_pb.set_identity_public_key(
identity_data.identity_binding().identity_public_key());
*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) {
SecureBlob enterprise_enrollment_nonce;
ComputeEnterpriseEnrollmentNonce(&enterprise_enrollment_nonce);
if (enterprise_enrollment_nonce.empty()) {
LOG(WARNING)
<< "Attestation: Failed to compute enterprise enrollment nonce.";
} else {
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) {
const int identity = kFirstIdentity;
if (!IsTPMReady())
return false;
if (database_pb_.identities().size() <= identity) {
LOG(ERROR) << __func__ << ": Enrollment is not possible, identity "
<< identity << " does not exist.";
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) {
LogErrorFromCA(__func__, response_pb.detail(),
response_pb.extra_details());
return false;
}
base::AutoLock lock(lock_);
brillo::Blob delegate_blob =
brillo::BlobFromString(database_pb_.delegate().blob());
brillo::Blob delegate_secret =
brillo::BlobFromString(database_pb_.delegate().secret());
SecureBlob aik_blob(database_pb_.identities()
.Get(identity)
.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 " << identity
<< ".";
return false;
}
// Find an identity certificate to reuse or create a new one.
int index;
AttestationDatabase_IdentityCertificate* identity_certificate;
auto found = FindIdentityCertificate(identity, pca_type);
if (found == database_pb_.mutable_identity_certificates()->end()) {
index = identity == kFirstIdentity
? pca_type
: std::max(static_cast<size_t>(kMaxPCAType),
database_pb_.identity_certificates().size());
AttestationDatabase::IdentityCertificate new_identity_certificate;
new_identity_certificate.set_identity(identity);
new_identity_certificate.set_aca(pca_type);
auto* map = database_pb_.mutable_identity_certificates();
auto in = map->insert(
IdentityCertificateMap::value_type(index, new_identity_certificate));
if (!in.second) {
return false;
}
found = in.first;
} else {
index = found->first;
}
identity_certificate = &found->second;
// Set the credential obtained when activating the identity with the response.
identity_certificate->set_identity_credential(aik_credential.to_string());
if (PersistDatabaseChanges() != AttestationResult::kSuccess) {
LOG(ERROR) << __func__ << ": Failed to persist database changes.";
return false;
}
LOG(INFO) << "Attestation: Enrollment of identity " << identity << " with "
<< GetPCAName(pca_type) << " complete. Certificate #" << index <<
".";
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;
base::AutoLock lock(lock_);
auto found = FindIdentityCertificate(kFirstIdentity, pca_type);
if (found == database_pb_.mutable_identity_certificates()->end()) {
LOG(ERROR) << __func__ << ": Identity " << kFirstIdentity
<< " is not enrolled for attestation with "
<< GetPCAName(pca_type) << ".";
return false;
}
const auto& identity_certificate = found->second;
AttestationCertificateRequest request_pb;
request_pb.set_identity_credential(
identity_certificate.identity_credential());
const auto message_id = CryptoLib::CreateSecureRandomBlob(kNonceSize);
request_pb.set_message_id(message_id.to_string());
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_->GetRandomDataSecureBlob(kNonceSize, &nonce)) {
LOG(ERROR) << __func__ << ": GetRandomDataSecureBlob failed.";
return false;
}
SecureBlob identity_key_blob(database_pb_.identities()
.Get(identity_certificate.identity())
.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) {
LogErrorFromCA(__func__, response_pb.detail(),
response_pb.extra_details());
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::GetCertificate(CertificateProfile profile,
const std::string& username,
const std::string& origin,
PCAType pca_type,
const std::string& key_name,
bool forced,
bool shall_trigger_enrollment,
brillo::SecureBlob* certificate_chain) {
if (shall_trigger_enrollment && !IsEnrolledWith(pca_type)) {
if (!EnrollEx(pca_type, /*forced=*/false)) {
LOG(ERROR) << __func__ << ": Failed to enroll.";
return false;
}
}
const bool is_user_specific = !username.empty();
if (!forced && GetCertificateChain(is_user_specific, username, key_name,
certificate_chain)) {
return true;
}
SecureBlob pca_request;
if (!CreateCertRequest(pca_type, profile, username, origin, &pca_request)) {
LOG(ERROR) << __func__ << ": Failed to create cert request.";
return false;
}
SecureBlob reply;
if (!SendPCARequestWithProxyAndBlock(
pca_type, PCARequestType::kGetCertificate, pca_request, &reply)) {
LOG(ERROR) << __func__ << ": Failed to send cert request.";
return false;
}
if (!FinishCertRequest(reply, is_user_specific, username, key_name,
certificate_chain)) {
LOG(ERROR) << __func__ << ": Failed to finish cert request.";
return false;
}
return true;
}
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();
crypto::ScopedRSA rsa(
d2i_RSAPublicKey(nullptr, &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) {
return SignEnterpriseVaChallenge(kDefaultVA, is_user_specific, username,
key_name, domain, device_id,
include_signed_public_key, challenge,
std::string() /*key_name_for_spkac*/,
response);
}
bool Attestation::SignEnterpriseVaChallenge(
VAType va_type,
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,
const std::string& key_name_for_spkac,
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(va_type, 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_->GetRandomDataSecureBlob(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());
base::Optional<CertifiedKey> key_for_certificate_and_spkac;
if (is_user_specific) {
// Always include the EUK certificate if an EUK is being challenged.
// Note that if including SPKAC has been requested when challenging an EUK,
// the SPKAC will also be created for the EUK. In other words,
// |key_name_for_spkac| is currently ignored for EUKs.
key_for_certificate_and_spkac = key;
} else if (include_signed_public_key && !key_name_for_spkac.empty()) {
// If a specific key name for SPKAC has been requested when challenging an
// EMK, include the certificate for that key.
CertifiedKey key_for_spkac;
if (!FindKeyByName(false /* is_user_specific */,
std::string() /* username */,
key_name_for_spkac,
&key_for_spkac)) {
LOG(ERROR) << __func__ << ": Key " << key_name_for_spkac
<< " for SPKAC not found ";
return false;
}
key_for_certificate_and_spkac = key_for_spkac;
}
if (key_for_certificate_and_spkac) {
SecureBlob certificate_chain;
if (!CreatePEMCertificateChain(key_for_certificate_and_spkac.value(),
&certificate_chain)) {
LOG(ERROR) << __func__ << ": Failed to construct certificate chain.";
return false;
}
key_info.set_certificate(certificate_chain.to_string());
if (include_signed_public_key) {
SecureBlob spkac;
if (!CreateSignedPublicKey(key_for_certificate_and_spkac.value(),
&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(va_type, 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_->GetRandomDataSecureBlob(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() == AttestationResult::kSuccess;
}
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 because the method does
// note take a PCA type. As far as we know, this call isn't supported either.
*proto.mutable_encrypted_endorsement_credential() =
(*database_pb_.mutable_credentials()
->mutable_encrypted_endorsement_credentials())[kDefaultPCA];
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;
Blob current_pcr_value;
if (!tpm_->ReadPCR(0, &current_pcr_value) ||
current_pcr_value.size() != kDigestSize) {
LOG(WARNING) << "Failed to read PCR0.";
return false;
}
Blob settings_blob(3);
settings_blob[0] = false; // Developer mode enabled.
settings_blob[1] = false; // Recovery mode enabled.
settings_blob[2] = kVerified; // Firmware type.
const Blob settings_digest = CryptoLib::Sha1(settings_blob);
const Blob extend_pcr_value =
CombineBlobs({Blob(kDigestSize), settings_digest});
const Blob expected_pcr_value = CryptoLib::Sha1(extend_pcr_value);
return current_pcr_value == expected_pcr_value;
}
AttestationResult 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 AttestationResult::kFailure;
}
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 attestation DB key.";
return AttestationResult::kFailure;
}
if (!tpm_->IsCurrentPCR0ValueValid()) {
LOG(ERROR) << "Bad PCR0 state. Abort encrypting attestation DB.";
return AttestationResult::kInvalidPcr0Value;
}
}
if (!crypto_->EncryptData(serial_data, database_key_, sealed_database_key_,
serial_encrypted_db)) {
LOG(ERROR) << "Attestation: Failed to encrypt database.";
return AttestationResult::kFailure;
}
return AttestationResult::kSuccess;
}
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.";
// Unseal failure doesn't increase DA counter, so check the PCR0 value
// afterward to save a TPM call in the success case.
if (!tpm_->IsCurrentPCR0ValueValid()) {
LOG(ERROR) << "Unseal failed due to a bad PCR0 state.";
ReportAttestationOpsStatus(kAttestationDecryptDatabase,
AttestationOpsStatus::kInvalidPcr0Value);
} else if (IsTPMReady()) {
// UnsealKey() is expected to fail if TPM is just cleared, in which case
// don't make a noise in UMA.
ReportAttestationOpsStatus(kAttestationDecryptDatabase,
AttestationOpsStatus::kFailure);
}
return false;
}
SecureBlob serial_blob;
if (!crypto_->DecryptData(serial_encrypted_db, database_key_, &serial_blob)) {
LOG(ERROR) << "Attestation: Failed to decrypt database with Tpm.";
ReportAttestationOpsStatus(kAttestationDecryptDatabase,
AttestationOpsStatus::kFailure);
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.";
ReportAttestationOpsStatus(kAttestationDecryptDatabase,
AttestationOpsStatus::kFailure);
return false;
}
}
ReportAttestationOpsStatus(kAttestationDecryptDatabase,
AttestationOpsStatus::kSuccess);
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;
}
AttestationResult Attestation::PersistDatabaseChanges() {
return PersistDatabase(database_pb_);
}
AttestationResult Attestation::PersistDatabase(const AttestationDatabase& db) {
std::string serial_encrypted_db;
AttestationResult result = EncryptDatabase(db, &serial_encrypted_db);
if (result != AttestationResult::kSuccess) {
LOG(ERROR) << "Attestation: Failed to encrypt db.";
return result;
}
if (!StoreDatabase(serial_encrypted_db)) {
LOG(ERROR) << "Attestation: Failed to store db.";
return AttestationResult::kFailure;
}
return AttestationResult::kSuccess;
}
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_issuer_name(x509.get()), NID_commonName,
issuer, base::size(issuer));
crypto::ScopedEVP_PKEY 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.
X509_PUBKEY* x509_pubkey = X509_get_X509_PUBKEY(x509.get());
if (!x509_pubkey) {
LOG(ERROR) << "Could not extract X509 public key.";
return false;
}
const unsigned char *public_key_data;
int pubkey_len;
X509_PUBKEY_get0_param(nullptr, &public_key_data, &pubkey_len, nullptr,
x509_pubkey);
SecureBlob credential_public_key(public_key_data,
public_key_data + pubkey_len);
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 < base::size(kKnownPCRValues); ++i) {
Blob 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;
const Blob settings_digest = CryptoLib::Sha1(settings_blob);
const Blob extend_pcr_value =
CombineBlobs({Blob(kDigestSize), settings_digest});
const Blob final_pcr_value = CryptoLib::Sha1(extend_pcr_value);
if (BlobFromString(quote.quoted_pcr_value()) == final_pcr_value) {
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,
uint32_t 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())};
const Blob pcr_composite =
CombineBlobs({Blob(std::begin(header), std::end(header)),
BlobFromString(quote.quoted_pcr_value())});
const Blob 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.
const Blob hint_digest =
CryptoLib::Sha256(BlobFromString(quote.pcr_source_hint()));
const Blob hint_digest_prefix(hint_digest.begin(),
hint_digest.begin() + 20);
const Blob extend_pcr_value =
CombineBlobs({Blob(kDigestSize), hint_digest_prefix});
const Blob final_pcr_value = CryptoLib::Sha1(extend_pcr_value);
if (BlobFromString(quote.quoted_pcr_value()) != final_pcr_value) {
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();
crypto::ScopedRSA rsa(
d2i_RSAPublicKey(nullptr, &asn1_ptr, certified_public_key.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode certified public key.";
return false;
}
SecureBlob modulus(RSA_size(rsa.get()));
const BIGNUM* n;
RSA_get0_key(rsa.get(), &n, nullptr, nullptr);
BN_bn2bin(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;
}
crypto::ScopedEVP_PKEY
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 ? base::size(kKnownCrosCoreEndorsementCA)
: base::size(kKnownEndorsementCA);
for (int i = 0; i < kNumIssuers; ++i) {
if (0 == strcmp(issuer_name, kKnownCA[i].issuer)) {
crypto::ScopedRSA rsa = CreateRSAFromHexModulus(kKnownCA[i].modulus);
if (!rsa.get())
return nullptr;
crypto::ScopedEVP_PKEY pkey(EVP_PKEY_new());
if (!pkey.get())
return nullptr;
EVP_PKEY_assign_RSA(pkey.get(), rsa.release());
return pkey;
}
}
return nullptr;
}
bool Attestation::VerifySignature(const SecureBlob& public_key,
const SecureBlob& signed_data,
const SecureBlob& signature) {
const unsigned char* asn1_ptr = public_key.data();
crypto::ScopedRSA rsa(
d2i_RSAPublicKey(nullptr, &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;
}
AttestationResult Attestation::MigrateIdentityData() {
// The identity we're creating will have the next index in identities.
LOG(INFO) << "Attestation: Migrating existing identity into identity "
<< database_pb_.identities().size() << ".";
AttestationDatabase::Identity identity_data;
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());
} else {
LOG(ERROR) << "Attestation: Identity Key Binding not found, not migrating.";
return AttestationResult::kFailure;
}
if (database_pb_.has_identity_key()) {
identity_data.mutable_identity_key()->CopyFrom(
database_pb_.identity_key());
identity_data.mutable_identity_key()->clear_identity_credential();
// Migration for the other part of the identity_key is moved to the end of
// the function, this is so that we don't have to rollback too many things
// when there's an error.
} else {
LOG(ERROR) << "Attestation: Identity Key not found, not migrating.";
return AttestationResult::kFailure;
}
for (int i = 0; i <= 1; i++) {
Quote quote_pb;
if (i == 0 && database_pb_.has_pcr0_quote()) {
quote_pb.CopyFrom(database_pb_.pcr0_quote());
} else if (i == 1 && database_pb_.has_pcr1_quote()) {
quote_pb.CopyFrom(database_pb_.pcr1_quote());
} else {
// Attempt to generate the missing PCR quote.
SecureBlob identity_key_blob(
identity_data.identity_key().identity_key_blob());
AttestationResult result =
CreatePCRQuote(i, identity_key_blob, &quote_pb);
if (result == AttestationResult::kSuccess) {
if (i == 1) {
quote_pb.set_pcr_source_hint(platform_->GetHardwareID());
}
LOG(WARNING) << "Attestation: Regenerated missing PCR" << i
<< " quote during migration.";
} else {
LOG(ERROR) << "Attestation: Failed to regenerate missing PCR"
<< i << " quote during migration.";
return result;
}
}
// If we arrive here, quote_pb is definitely filled.
auto in = identity_data.mutable_pcr_quotes()->insert(
QuoteMap::value_type(i, quote_pb));
if (!in.second) {
LOG(ERROR) << "Attestation: Could not migrate existing identity.";
return AttestationResult::kFailure;
}
}
// Migrate the other part of identity_key, note that since we are here,
// identity_key is guaranteed to exist.
if (database_pb_.identity_key().has_identity_credential()) {
// Create an identity certificate for this identity and the default PCA.
AttestationDatabase::IdentityCertificate identity_certificate;
identity_certificate.set_identity(kFirstIdentity);
identity_certificate.set_aca(kDefaultPCA);
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(
kDefaultPCA, identity_certificate));
if (!in.second) {
LOG(ERROR) << "Attestation: Could not migrate existing identity.";
return AttestationResult::kFailure;
}
}
if (database_pb_.identity_key().has_enrollment_id()) {
database_pb_.set_enrollment_id(
database_pb_.identity_key().enrollment_id());
}
// If we are here, we can be sure that identity_data actually holds a
// valid identity object, so we save it back into the DB.
AttestationDatabase::Identity* new_identity_data =
database_pb_.mutable_identities()->Add();
new_identity_data->CopyFrom(identity_data);
return AttestationResult::kSuccess;
}
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());
ClearString(database_pb_.mutable_enrollment_id());
for (auto it = database_pb_.mutable_identities()->begin();
it != database_pb_.mutable_identities()->end(); ++it) {
ClearIdentity(&*it);
}
for (auto it = database_pb_.mutable_identity_certificates()->begin();
it != database_pb_.mutable_identity_certificates()->end(); ++it) {
ClearIdentityCertificate(&it->second);
}
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::ClearIdentityCertificate(
AttestationDatabase::IdentityCertificate* identity_certificate) {
ClearString(identity_certificate->mutable_identity_credential());
}
void Attestation::ClearIdentity(AttestationDatabase::Identity* identity) {
auto binding = identity->mutable_identity_binding();
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());
auto key = identity->mutable_identity_key();
ClearString(key->mutable_identity_public_key());
ClearString(key->mutable_identity_key_blob());
ClearString(key->mutable_identity_credential());
ClearString(key->mutable_enrollment_id());
auto end = identity->mutable_pcr_quotes()->end();
for (auto it = identity->mutable_pcr_quotes()->begin(); it != end; ++it) {
ClearQuote(&it->second);
}
}
void Attestation::ClearString(std::string* s) {
brillo::SecureMemset(base::data(*s), 0, s->length());
s->clear();
}
bool Attestation::VerifyActivateIdentity(const brillo::Blob& delegate_blob,
const brillo::Blob& 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.
const auto aes_key = CryptoLib::CreateSecureRandomBlob(kCipherKeySize);
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();
crypto::ScopedRSA rsa(
d2i_RSAPublicKey(nullptr, &asn1_ptr, ek_public_key.size()));
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode EK public key.";
return false;
}
SecureBlob encrypted_asym_content;
if (!CryptoLib::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) {
crypto::ScopedRSA rsa;
std::string key_id;
switch (pca_type) {
case kDefaultPCA:
rsa = CreateRSAFromHexModulus(kDefaultPCAPublicKey);
key_id = std::string(kDefaultPCAPublicKeyID,
base::size(kDefaultPCAPublicKeyID) - 1);
break;
case kTestPCA:
rsa = CreateRSAFromHexModulus(kTestPCAPublicKey);
key_id =
std::string(kTestPCAPublicKeyID, base::size(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);
}
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() == AttestationResult::kSuccess;
}
bool 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() != AttestationResult::kSuccess) {
LOG(WARNING) << __func__ << ": Failed to persist key deletion.";
return false;
}
}
return true;
}
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;
}
bool Attestation::DeleteKey(bool is_user_specific,
const std::string& username,
const std::string& key_name) {
if (is_user_specific) {
return key_store_->Delete(is_user_specific, username, key_name);
} else {
return 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(
VAType va_type,
const SignedData& signed_challenge) {
RSA* signing_key = GetEnterpriseSigningKey(va_type);
if (!signing_key)
return false;
const char kExpectedChallengePrefix[] = "EnterpriseKeyChallenge";
SecureBlob digest = CryptoLib::Sha256(SecureBlob(signed_challenge.data()));
SecureBlob signature(signed_challenge.signature());
if (!RSA_verify(NID_sha256, digest.data(), digest.size(),
signature.data(), signature.size(), signing_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(VAType va_type,
const KeyInfo& key_info,
EncryptedData* encrypted_data) {
std::string serialized;
if (!key_info.SerializeToString(&serialized)) {
LOG(ERROR) << "Failed to serialize key info.";
return false;
}
RSA* enterprise_key = GetEnterpriseEncryptionKey(va_type);
bool result = EncryptData(SecureBlob(serialized),
enterprise_key,
GetEnterpriseEncryptionPublicKeyID(va_type),
encrypted_data);
ClearString(&serialized);
return result;
}
RSA* Attestation::GetEnterpriseSigningKey(Attestation::VAType va_type) {
auto search = enterprise_signing_keys_.find(va_type);
if (search != enterprise_signing_keys_.end())
return search->second.get();
// Create the key and remember it in the keys map.
crypto::ScopedRSA rsa = CreateRSAFromHexModulus(
va_type == kDefaultVA ? kDefaultEnterpriseSigningPublicKey
: kTestEnterpriseSigningPublicKey);
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public signing key.";
return nullptr;
}
auto inserted = enterprise_signing_keys_.insert(
KeysMap::value_type(va_type, std::move(rsa)));
if (!inserted.second) {
LOG(ERROR) << "Failed to insert public signing key in map.";
return nullptr;
}
return inserted.first->second.get();
}
RSA* Attestation::GetEnterpriseEncryptionKey(Attestation::VAType va_type) {
auto search = enterprise_encryption_keys_.find(va_type);
if (search != enterprise_encryption_keys_.end())
return search->second.get();
// Create the key and remember it in the keys map.
crypto::ScopedRSA rsa = CreateRSAFromHexModulus(
va_type == kDefaultVA ? kDefaultEnterpriseEncryptionPublicKey
: kTestEnterpriseEncryptionPublicKey);
if (!rsa.get()) {
LOG(ERROR) << "Failed to decode public encryption key.";
return nullptr;
}
auto inserted = enterprise_encryption_keys_.insert(
KeysMap::value_type(va_type, std::move(rsa)));
if (!inserted.second) {
LOG(ERROR) << "Failed to insert public encryption key in map.";
return nullptr;
}
return inserted.first->second.get();
}
std::string Attestation::GetEnterpriseEncryptionPublicKeyID(
Attestation::VAType va_type) const {
return std::string(
va_type == kDefaultVA ? kDefaultEnterpriseEncryptionPublicKeyID
: kTestEnterpriseEncryptionPublicKeyID,
base::size(va_type == kDefaultVA ? kDefaultEnterpriseEncryptionPublicKeyID
: kTestEnterpriseEncryptionPublicKeyID) -
1);
}
void Attestation::set_enterprise_test_keys(VAType va_type,
RSA* signing_key,
RSA* encryption_key) {
enterprise_signing_keys_[va_type] = crypto::ScopedRSA(signing_key);
enterprise_encryption_keys_[va_type] = crypto::ScopedRSA(encryption_key);
}
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_->GetRandomDataSecureBlob(kCipherKeySize, &aes_key)) {
LOG(ERROR) << __func__ << ": GetRandomDataSecureBlob failed.";
return false;
}
SecureBlob aes_iv;
if (!tpm_->GetRandomDataSecureBlob(kAesBlockSize, &aes_iv)) {
LOG(ERROR) << __func__ << ": GetRandomDataSecureBlob 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;
}
crypto::ScopedRSA
Attestation::CreateRSAFromHexModulus(
const std::string& hex_modulus) {
crypto::ScopedRSA rsa(RSA_new());
crypto::ScopedBIGNUM n(BN_new()), e(BN_new());
if (!rsa || !n || !e)
return nullptr;
if (!BN_set_word(e.get(), kWellKnownExponent))
return nullptr;
BIGNUM* pn = n.get();
if (0 == BN_hex2bn(&pn, hex_modulus.c_str()))
return nullptr;
if (!RSA_set0_key(rsa.get(), n.release(), e.release(), nullptr))
return nullptr;
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());
crypto::ScopedRSA rsa(
d2i_RSAPublicKey(NULL, &asn1_ptr, key.public_key().size()));
if (!rsa.get())
return false;
crypto::ScopedEVP_PKEY 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_->GetRandomDataSecureBlob(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;
// TODO(crbug.com/984789): Remove once openssl <1.1 support is dropped.
#if OPENSSL_VERSION_NUMBER < 0x10100000L
X509_ALGOR* sig_algor = spki.get()->sig_algor;
#else
X509_ALGOR* sig_algor = &spki.get()->sig_algor;
#endif
X509_ALGOR_set0(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;
}
const auto iv = CryptoLib::CreateSecureRandomBlob(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;
}
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;
}
bool Attestation::MigrateAttestationDatabase() {
bool migrated = false;
if (database_pb_.has_credentials()) {
if (!database_pb_.credentials().encrypted_endorsement_credentials().count(
kDefaultPCA) &&
database_pb_.credentials()
.has_default_encrypted_endorsement_credential()) {
LOG(INFO) << "Attestation: Migrating endorsement credential for "
<< GetPCAName(kDefaultPCA) << ".";
(*database_pb_.mutable_credentials()
->mutable_encrypted_endorsement_credentials())[kDefaultPCA] =
database_pb_.credentials().default_encrypted_endorsement_credential();
migrated = true;
}
if (!database_pb_.credentials().encrypted_endorsement_credentials().count(
kTestPCA) &&
database_pb_.credentials()
.has_test_encrypted_endorsement_credential()) {
LOG(INFO) << "Attestation: Migrating endorsement credential for "
<< GetPCAName(kTestPCA) << ".";
(*database_pb_.mutable_credentials()
->mutable_encrypted_endorsement_credentials())[kTestPCA] =
database_pb_.credentials().test_encrypted_endorsement_credential();
migrated = true;
}
}
if (database_pb_.identities().size() > 0) {
// We already migrated identity data.
if (migrated) {
ReportAttestationOpsStatus(kAttestationMigrateDatabase,
AttestationOpsStatus::kSuccess);
}
return migrated;
}
AttestationResult result = MigrateIdentityData();
migrated |= (result == AttestationResult::kSuccess);
AttestationOpsStatus status;
switch (result) {
case AttestationResult::kSuccess:
status = AttestationOpsStatus::kSuccess;
break;
case AttestationResult::kFailure:
status = AttestationOpsStatus::kFailure;
break;
case AttestationResult::kInvalidPcr0Value:
status = AttestationOpsStatus::kInvalidPcr0Value;
break;
}
ReportAttestationOpsStatus(kAttestationMigrateDatabase, status);
return migrated;
}
void Attestation::FinalizeEndorsementData() {
if (retain_endorsement_data_) {
return;
}
// Only finalize endorsement data after install attributes are finalized.
if (install_attributes_->status() ==
InstallAttributes::Status::kFirstInstall) {
return;
}
if (!database_pb_.has_credentials()) {
return;
}
TPMCredentials* credentials = database_pb_.mutable_credentials();
if (!credentials->has_endorsement_credential()) {
return;
}
for (int pca = kDefaultPCA; pca < kMaxPCAType; ++pca) {
if (!credentials->mutable_encrypted_endorsement_credentials()->count(pca)) {
LOG(INFO) << "Attestation: Migrating endorsement data for "
<< GetPCAName(static_cast<PCAType>(pca)) << ".";
if (!EncryptEndorsementCredential(
static_cast<PCAType>(pca),
SecureBlob(credentials->endorsement_credential()),
&(*credentials
->mutable_encrypted_endorsement_credentials())[pca])) {
LOG(ERROR) << "Attestation: Failed to encrypt EK cert for "
<< GetPCAName(static_cast<PCAType>(pca)) << ".";
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() != AttestationResult::kSuccess) {
LOG(ERROR) << "Attestation: Failed to persist database changes.";
}
}
bool Attestation::GetDelegateCredentials(brillo::Blob* blob,
brillo::Blob* secret,
bool* has_reset_lock_permissions) {
if (!database_pb_.has_delegate()) {
return false;
}
brillo::Blob tmp_blob =
brillo::BlobFromString(database_pb_.delegate().blob());
blob->swap(tmp_blob);
brillo::Blob tmp_secret =
brillo::BlobFromString(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) != Tpm::kTpmRetryNone) {
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() {
Blob current_pcr_value;
if (!tpm_->ReadPCR(1, &current_pcr_value) ||
current_pcr_value.size() != kDigestSize) {
LOG(WARNING) << "Failed to read PCR1.";
return;
}
Blob default_pcr_value(kDigestSize);
if (current_pcr_value != default_pcr_value) {
// The PCR has already been extended.
return;
}
std::string hwid = platform_->GetHardwareID();
LOG(WARNING) << "Extending PCR1.";
// Take the first n 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).
const Blob hwid_hash = CryptoLib::Sha256(BlobFromString(hwid));
const Blob extension(hwid_hash.begin(), hwid_hash.begin() + kDigestSize);
if (hwid.length() == 0 || !tpm_->ExtendPCR(1, extension)) {
LOG(WARNING) << "Failed to extend PCR1.";
}
}
AttestationResult Attestation::CreatePCRQuote(
uint32_t pcr_index,
const SecureBlob& identity_key_blob,
Quote* output) {
const auto external_data =
CryptoLib::CreateSecureRandomBlob(kQuoteExternalDataSize);
Blob quoted_pcr_value;
SecureBlob quoted_data;
SecureBlob quote;
const bool should_check_pcr_value = pcr_index == 0;
Tpm::QuotePcrResult result = tpm_->QuotePCR(pcr_index,
should_check_pcr_value,
identity_key_blob,
external_data,
&quoted_pcr_value,
&quoted_data,
&quote);
if (result != Tpm::QuotePcrResult::kSuccess) {
LOG(ERROR) << "Attestation: Failed to generate PCR" << pcr_index
<< " quote.";
return (result == Tpm::QuotePcrResult::kInvalidPcrValue &&
should_check_pcr_value) ?
AttestationResult::kInvalidPcr0Value :
AttestationResult::kFailure;
}
output->set_quote(quote.data(), quote.size());
output->set_quoted_data(quoted_data.data(), quoted_data.size());
output->set_quoted_pcr_value(BlobToString(quoted_pcr_value));
return AttestationResult::kSuccess;
}
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();
}
return SendPCARequestWithTransportAndBlock(
GetPCAURL(pca_type, request_type), std::move(transport), request, reply);
}
bool Attestation::SendPCARequestWithTransportAndBlock(
const std::string& pca_server_url,
std::shared_ptr<brillo::http::Transport> transport,
const brillo::SecureBlob& request,
brillo::SecureBlob* reply) {
DCHECK(transport.get());
std::unique_ptr<brillo::http::Response> response = PostBinaryAndBlock(
pca_server_url, 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;
}
bool Attestation::InitializeDBus() {
if (!bus_.get()) {
dbus::Bus::Options options;
options.bus_type = dbus::Bus::SYSTEM;
bus_ = base::MakeRefCounted<dbus::Bus>(options);
}
return bus_.get() != nullptr;
}
bool Attestation::SendPCARequestWithProxyAndBlock(
PCAType pca_type,
PCARequestType request_type,
const brillo::SecureBlob& request,
brillo::SecureBlob* reply) {
if (!InitializeDBus()) {
LOG(WARNING)
<< __func__
<< ": Cannot initialize dbus connection to get proxy information.";
return SendPCARequestAndBlock(pca_type, request_type, request, reply);
}
const std::string pca_server_url = GetPCAURL(pca_type, request_type);
std::vector<std::string> proxy_servers;
// |GetChromeProxyServers| promises to give a non-empty list, no matter if the
// dbus call succeeds. Thus, logs the warning and continue.
if (!brillo::http::GetChromeProxyServers(bus_, pca_server_url,
&proxy_servers)) {
LOG(WARNING) << __func__ << ": Failed to get proxy server list.";
}
for (const std::string& proxy_server : proxy_servers) {
if (SendPCARequestWithTransportAndBlock(
pca_server_url,
brillo::http::Transport::CreateDefaultWithProxy(proxy_server),
request, reply)) {
return true;
}
}
LOG(ERROR) << __func__
<< ": Failed to send pca request with all proxy servers.";
return false;
}
std::string Attestation::GetPCAURL(PCAType pca_type,
PCARequestType request_type) const {
std::string url;
switch (pca_type) {
case kDefaultPCA:
url = kDefaultPCAWebOrigin;
break;
case kTestPCA:
url = kTestPCAWebOrigin;
break;
default:
NOTREACHED();
}
switch (request_type) {
case kEnroll:
url += "/enroll";
break;
case kGetCertificate:
url += "/sign";
break;
default:
NOTREACHED();
}
return url;
}
void Attestation::ComputeEnterpriseEnrollmentNonce(
brillo::SecureBlob* enterprise_enrollment_nonce) {
if (abe_data_.empty()) {
// If there is no ABE data we cannot compute the DEN. We do not
// want to fail attestation for those devices.
enterprise_enrollment_nonce->clear();
return;
}
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);
}
Tpm::TpmRetryAction Attestation::GetTpmEndorsementPublicKey(
SecureBlob* ek_public_key) {
Tpm::TpmRetryAction action = Tpm::kTpmRetryFailNoRetry;
if (database_pb_.has_delegate()) {
// Try to call using the delegate unless we know that it does not have
// proper permissions.
if (!database_pb_.delegate().has_can_read_internal_pub()
|| database_pb_.delegate().can_read_internal_pub()) {
brillo::Blob delegate_blob =
brillo::BlobFromString(database_pb_.delegate().blob());
brillo::Blob delegate_secret =
brillo::BlobFromString(database_pb_.delegate().secret());
action = tpm_->GetEndorsementPublicKeyWithDelegate(
ek_public_key, delegate_blob, delegate_secret);
// Warn if we know we will not be able to compute the EID. We can't
// say no for sure because if the TPM becomes unowned then the EID
// can be computed, but that will not happen without specific action
// and the attestation database will not be in a great state then.
if (!tpm_->IsTransient(action) &&
!database_pb_.delegate().has_can_read_internal_pub()) {
// If the results are definitively success or a final failure, record
// whether the delegate had permission or not.
database_pb_.mutable_delegate()->set_can_read_internal_pub(
action == Tpm::kTpmRetryNone);
PersistDatabaseChanges();
}
// Return in case of success or if the caller can retry.
if (action == Tpm::kTpmRetryNone || tpm_->IsTransient(action)) {
return action;
}
// Fall through in case the TPM has been reset but not the database.
LOG(WARNING) << "Attestation: Computing the EID is likely to fail."
<< " Request an enrollment certificate instead.";
}
}
// Always calling this if we fail with the delegate allows us to retrieve
// the key in situations where we have a database with a delegate but we
// cleared the owner password later.
action = tpm_->GetEndorsementPublicKey(ek_public_key);
if (action != Tpm::kTpmRetryNone) {
ek_public_key->clear();
}
return action;
}
bool Attestation::ComputeEnterpriseEnrollmentId(
SecureBlob* enterprise_enrollment_id) {
return !tpm_->IsTransient(ComputeEnterpriseEnrollmentIdInternal(
enterprise_enrollment_id));
}
Tpm::TpmRetryAction Attestation::ComputeEnterpriseEnrollmentIdInternal(
brillo::SecureBlob* enterprise_enrollment_id) {
brillo::SecureBlob den;
ComputeEnterpriseEnrollmentNonce(&den);
if (den.empty()) {
enterprise_enrollment_id->clear();
return Tpm::kTpmRetryNone;
}
SecureBlob ek_public_key;
Tpm::TpmRetryAction action = GetTpmEndorsementPublicKey(&ek_public_key);
if (tpm_->IsTransient(action)) {
return action;
}
if (ek_public_key.empty()) {
enterprise_enrollment_id->clear();
return action;
}
// Extract the modulus from the public key.
const unsigned char* asn1_ptr =
reinterpret_cast<const unsigned char*>(ek_public_key.data());
crypto::ScopedRSA public_key(
d2i_RSAPublicKey(nullptr, &asn1_ptr, ek_public_key.size()));
if (!public_key.get()) {
LOG(ERROR) << "Attestation: Failed to decode public endorsement key.";
return Tpm::kTpmRetryFailNoRetry;
}
brillo::Blob modulus(RSA_size(public_key.get()), 0);
const BIGNUM* n;
RSA_get0_key(public_key.get(), &n, nullptr, nullptr);
int length = BN_bn2bin(n, modulus.data());
if (length <= 0) {
LOG(ERROR)
<< "Attestation: Failed to extract public endorsement key modulus.";
return Tpm::kTpmRetryFailNoRetry;
}
modulus.resize(length);
// Compute the EID based on den and modulus.
*enterprise_enrollment_id = CryptoLib::HmacSha256(den, modulus);
return Tpm::kTpmRetryNone;
}
void Attestation::X509Deleter::operator()(void* ptr) const {
if (ptr)
X509_free(reinterpret_cast<X509*>(ptr));
}
void Attestation::NETSCAPE_SPKIDeleter::operator()(void* ptr) const {
if (ptr)
NETSCAPE_SPKI_free(reinterpret_cast<NETSCAPE_SPKI*>(ptr));
}
} // namespace cryptohome