blob: 91ae5e34b1005bf39754e3b21cd191728f19a7df [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 "attestation.h"
#include <algorithm>
#include <string>
#include <arpa/inet.h>
#include <base/file_util.h>
#include <base/time.h>
#include <chromeos/secure_blob.h>
#include <openssl/evp.h>
#include <openssl/hmac.h>
#include <openssl/rsa.h>
#include <openssl/sha.h>
#include <openssl/x509.h>
#include "attestation.pb.h"
#include "cryptolib.h"
#include "tpm.h"
using chromeos::SecureBlob;
using std::string;
namespace cryptohome {
const size_t Attestation::kQuoteExternalDataSize = 20;
const size_t Attestation::kCipherKeySize = 32;
const size_t Attestation::kCipherBlockSize = 16;
const size_t Attestation::kNonceSize = 20; // As per TPM_NONCE definition.
const size_t Attestation::kDigestSize = 20; // As per TPM_DIGEST definition.
const char* Attestation::kDefaultDatabasePath =
"/mnt/stateful_partition/unencrypted/preserve/attestation.epb";
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"},
{"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"}
};
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 }
};
bool Attestation::IsPreparedForEnrollment() {
base::AutoLock lock(prepare_lock_);
if (!is_prepared_ && tpm_) {
EncryptedDatabase encrypted_db;
if (!LoadDatabase(&encrypted_db)) {
LOG(INFO) << "Attestation: Attestation data not found.";
return false;
}
if (!DecryptDatabase(encrypted_db, &database_pb_)) {
LOG(ERROR) << "Attestation: Attestation data invalid.";
return false;
}
LOG(INFO) << "Attestation: Valid attestation data exists.";
// Make sure the owner password is not being held on our account.
tpm_->RemoveOwnerDependency(Tpm::kAttestation);
is_prepared_ = true;
}
return is_prepared_;
}
void Attestation::PrepareForEnrollment() {
// If there is no TPM, we have no work to do.
if (!tpm_)
return;
if (IsPreparedForEnrollment())
return;
base::TimeTicks start = base::TimeTicks::Now();
LOG(INFO) << "Attestation: Preparing for enrollment...";
SecureBlob ek_public_key;
if (!tpm_->GetEndorsementPublicKey(&ek_public_key)) {
LOG(ERROR) << "Attestation: Failed to get EK public key.";
return;
}
// Create an AIK.
SecureBlob identity_public_key_der;
SecureBlob identity_public_key;
SecureBlob identity_key_blob;
SecureBlob identity_binding;
SecureBlob identity_label;
SecureBlob pca_public_key;
SecureBlob endorsement_credential;
SecureBlob platform_credential;
SecureBlob conformance_credential;
if (!tpm_->MakeIdentity(&identity_public_key_der, &identity_public_key,
&identity_key_blob, &identity_binding,
&identity_label, &pca_public_key,
&endorsement_credential, &platform_credential,
&conformance_credential)) {
LOG(ERROR) << "Attestation: Failed to make AIK.";
return;
}
// Quote PCR0.
SecureBlob external_data;
if (!tpm_->GetRandomData(kQuoteExternalDataSize, &external_data)) {
LOG(ERROR) << "Attestation: GetRandomData failed.";
return;
}
SecureBlob quoted_pcr_value;
SecureBlob quoted_data;
SecureBlob quote;
if (!tpm_->QuotePCR0(identity_key_blob, external_data, &quoted_pcr_value,
&quoted_data, &quote)) {
LOG(ERROR) << "Attestation: Failed to generate quote.";
return;
}
// Create a delegate so we can activate the AIK later.
SecureBlob delegate_blob;
SecureBlob delegate_secret;
if (!tpm_->CreateDelegate(identity_key_blob, &delegate_blob,
&delegate_secret)) {
LOG(ERROR) << "Attestation: Failed to create delegate.";
return;
}
// Assemble a protobuf to store locally.
base::AutoLock lock(prepare_lock_);
TPMCredentials* credentials_pb = database_pb_.mutable_credentials();
credentials_pb->set_endorsement_public_key(ek_public_key.data(),
ek_public_key.size());
credentials_pb->set_endorsement_credential(endorsement_credential.data(),
endorsement_credential.size());
credentials_pb->set_platform_credential(platform_credential.data(),
platform_credential.size());
credentials_pb->set_conformance_credential(conformance_credential.data(),
conformance_credential.size());
IdentityKey* key_pb = database_pb_.mutable_identity_key();
key_pb->set_identity_public_key(identity_public_key_der.data(),
identity_public_key_der.size());
key_pb->set_identity_key_blob(identity_key_blob.data(),
identity_key_blob.size());
IdentityBinding* binding_pb = database_pb_.mutable_identity_binding();
binding_pb->set_identity_binding(identity_binding.data(),
identity_binding.size());
binding_pb->set_identity_public_key_der(identity_public_key_der.data(),
identity_public_key_der.size());
binding_pb->set_identity_public_key(identity_public_key.data(),
identity_public_key.size());
binding_pb->set_identity_label(identity_label.data(), identity_label.size());
binding_pb->set_pca_public_key(pca_public_key.data(), pca_public_key.size());
Quote* quote_pb = database_pb_.mutable_pcr0_quote();
quote_pb->set_quote(quote.data(), quote.size());
quote_pb->set_quoted_data(quoted_data.data(), quoted_data.size());
quote_pb->set_quoted_pcr_value(quoted_pcr_value.data(),
quoted_pcr_value.size());
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());
if (!tpm_->GetRandomData(kCipherKeySize, &database_key_)) {
LOG(ERROR) << "Attestation: GetRandomData failed.";
return;
}
SecureBlob sealed_key;
if (!tpm_->SealToPCR0(database_key_, &sealed_key)) {
LOG(ERROR) << "Attestation: Failed to seal cipher key.";
return;
}
EncryptedDatabase encrypted_pb;
encrypted_pb.set_sealed_key(sealed_key.data(), sealed_key.size());
if (!EncryptDatabase(database_pb_, &encrypted_pb)) {
LOG(ERROR) << "Attestation: Failed to encrypt db.";
return;
}
if (!StoreDatabase(encrypted_pb)) {
LOG(ERROR) << "Attestation: Failed to store db.";
return;
}
tpm_->RemoveOwnerDependency(Tpm::kAttestation);
base::TimeDelta delta = (base::TimeTicks::Now() - start);
LOG(INFO) << "Attestation: Prepared successfully (" << delta.InMilliseconds()
<< "ms).";
}
bool Attestation::Verify() {
if (!tpm_)
return false;
LOG(INFO) << "Attestation: Verifying data.";
EncryptedDatabase encrypted_db;
if (!LoadDatabase(&encrypted_db)) {
LOG(INFO) << "Attestation: Attestation data not found.";
return false;
}
if (!DecryptDatabase(encrypted_db, &database_pb_)) {
LOG(ERROR) << "Attestation: Attestation data invalid.";
return false;
}
const TPMCredentials& credentials = database_pb_.credentials();
SecureBlob ek_public_key = ConvertStringToBlob(
credentials.endorsement_public_key());
if (!VerifyEndorsementCredential(
ConvertStringToBlob(credentials.endorsement_credential()),
ek_public_key)) {
LOG(ERROR) << "Attestation: Bad endorsement credential.";
return false;
}
if (!VerifyIdentityBinding(database_pb_.identity_binding())) {
LOG(ERROR) << "Attestation: Bad identity binding.";
return false;
}
SecureBlob aik_public_key = ConvertStringToBlob(
database_pb_.identity_binding().identity_public_key_der());
if (!VerifyQuote(aik_public_key, database_pb_.pcr0_quote())) {
LOG(ERROR) << "Attestation: Bad PCR0 quote.";
return false;
}
SecureBlob nonce;
if (!tpm_->GetRandomData(kNonceSize, &nonce)) {
LOG(ERROR) << "Attestation: GetRandomData failed.";
return false;
}
SecureBlob identity_key_blob = ConvertStringToBlob(
database_pb_.identity_key().identity_key_blob());
SecureBlob public_key;
SecureBlob key_blob;
SecureBlob key_info;
SecureBlob proof;
if (!tpm_->CreateCertifiedKey(identity_key_blob, nonce, &public_key,
&key_blob, &key_info, &proof)) {
LOG(ERROR) << "Attestation: Failed to create certified key.";
return false;
}
if (!VerifyCertifiedKey(aik_public_key, public_key, key_info, proof)) {
LOG(ERROR) << "Attestation: Bad certified key.";
return false;
}
SecureBlob delegate_blob =
ConvertStringToBlob(database_pb_.delegate().blob());
SecureBlob delegate_secret =
ConvertStringToBlob(database_pb_.delegate().secret());
SecureBlob aik_public_key_tpm = ConvertStringToBlob(
database_pb_.identity_binding().identity_public_key());
if (!VerifyActivateIdentity(delegate_blob, delegate_secret,
identity_key_blob, aik_public_key_tpm,
ek_public_key)) {
LOG(ERROR) << "Attestation: Failed to verify owner delegation.";
return false;
}
LOG(INFO) << "Attestation: Verified OK.";
return true;
}
bool Attestation::VerifyEK() {
SecureBlob ek_cert;
if (!tpm_->GetEndorsementCredential(&ek_cert)) {
LOG(ERROR) << "VerifyEK: Failed to get EK cert.";
return false;
}
SecureBlob ek_public_key;
if (!tpm_->GetEndorsementPublicKey(&ek_public_key)) {
LOG(ERROR) << "VerifyEK: Failed to get EK public key.";
return false;
}
return VerifyEndorsementCredential(ek_cert, ek_public_key);
}
SecureBlob Attestation::ConvertStringToBlob(const string& s) {
return SecureBlob(s.data(), s.length());
}
string Attestation::ConvertBlobToString(const chromeos::Blob& blob) {
return string(reinterpret_cast<const char*>(&blob.front()), blob.size());
}
SecureBlob Attestation::SecureCat(const SecureBlob& blob1,
const SecureBlob& blob2) {
SecureBlob result(blob1.size() + blob2.size());
unsigned char* buffer = const_cast<unsigned char*>(&result.front());
memcpy(buffer, blob1.const_data(), blob1.size());
memcpy(buffer + blob1.size(), blob2.const_data(), blob2.size());
return SecureBlob(result.begin(), result.end());
}
bool Attestation::EncryptDatabase(const AttestationDatabase& db,
EncryptedDatabase* encrypted_db) {
SecureBlob iv;
if (!tpm_->GetRandomData(kCipherBlockSize, &iv)) {
LOG(ERROR) << "GetRandomData failed.";
return false;
}
string serial_string;
if (!db.SerializeToString(&serial_string)) {
LOG(ERROR) << "Failed to serialize db.";
return false;
}
SecureBlob serial_data(serial_string.data(), serial_string.size());
SecureBlob encrypted_data;
if (!CryptoLib::AesEncrypt(serial_data, database_key_, iv, &encrypted_data)) {
LOG(ERROR) << "Failed to encrypt db.";
return false;
}
encrypted_db->set_encrypted_data(encrypted_data.data(),
encrypted_data.size());
encrypted_db->set_iv(iv.data(), iv.size());
encrypted_db->set_mac(ComputeHMAC(*encrypted_db));
return true;
}
bool Attestation::DecryptDatabase(const EncryptedDatabase& encrypted_db,
AttestationDatabase* db) {
SecureBlob sealed_key(encrypted_db.sealed_key().data(),
encrypted_db.sealed_key().length());
if (!tpm_->Unseal(sealed_key, &database_key_)) {
LOG(ERROR) << "Cannot unseal database key.";
return false;
}
string mac = ComputeHMAC(encrypted_db);
if (mac.length() != encrypted_db.mac().length()) {
LOG(ERROR) << "Corrupted database.";
return false;
}
if (0 != chromeos::SafeMemcmp(mac.data(), encrypted_db.mac().data(),
mac.length())) {
LOG(ERROR) << "Corrupted database.";
return false;
}
SecureBlob iv(encrypted_db.iv().data(), encrypted_db.iv().length());
SecureBlob encrypted_data(encrypted_db.encrypted_data().data(),
encrypted_db.encrypted_data().length());
SecureBlob serial_db;
if (!CryptoLib::AesDecrypt(encrypted_data, database_key_, iv, &serial_db)) {
LOG(ERROR) << "Failed to decrypt database.";
return false;
}
if (!db->ParseFromArray(serial_db.data(), serial_db.size())) {
LOG(ERROR) << "Failed to parse database.";
return false;
}
return true;
}
string Attestation::ComputeHMAC(const EncryptedDatabase& encrypted_db) {
SecureBlob hmac_input = SecureCat(
ConvertStringToBlob(encrypted_db.iv()),
ConvertStringToBlob(encrypted_db.encrypted_data()));
return ConvertBlobToString(CryptoLib::HmacSha512(database_key_, hmac_input));
}
bool Attestation::StoreDatabase(const EncryptedDatabase& encrypted_db) {
string database_serial;
if (!encrypted_db.SerializeToString(&database_serial)) {
LOG(ERROR) << "Failed to serialize encrypted db.";
return false;
}
if (-1 == file_util::WriteFile(database_path_, database_serial.data(),
database_serial.length())) {
LOG(ERROR) << "Failed to write db.";
return false;
}
return true;
}
bool Attestation::LoadDatabase(EncryptedDatabase* encrypted_db) {
string serial;
if (!file_util::ReadFileToString(database_path_, &serial)) {
return false;
}
if (!encrypted_db->ParseFromString(serial)) {
LOG(ERROR) << "Failed to parse encrypted db.";
return false;
}
return true;
}
bool Attestation::VerifyEndorsementCredential(const SecureBlob& credential,
const SecureBlob& public_key) {
const unsigned char* asn1_ptr = &credential.front();
X509* x509 = d2i_X509(NULL, &asn1_ptr, credential.size());
if (!x509) {
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->cert_info->issuer, NID_commonName, issuer,
arraysize(issuer));
EVP_PKEY* issuer_key = GetAuthorityPublicKey(issuer);
if (!issuer_key) {
LOG(ERROR) << "Unknown endorsement credential issuer.";
return false;
}
if (!X509_verify(x509, issuer_key)) {
LOG(ERROR) << "Bad endorsement credential signature.";
EVP_PKEY_free(issuer_key);
return false;
}
EVP_PKEY_free(issuer_key);
// 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.
SecureBlob credential_public_key(x509->cert_info->key->public_key->data,
x509->cert_info->key->public_key->length);
if (credential_public_key.size() != public_key.size() ||
memcmp(&credential_public_key.front(),
&public_key.front(),
public_key.size()) != 0) {
LOG(ERROR) << "Bad endorsement credential public key.";
return false;
}
X509_free(x509);
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};
string label_ca = binding.identity_label() + binding.pca_public_key();
SecureBlob label_ca_digest = CryptoLib::Sha1(ConvertStringToBlob(label_ca));
ClearString(&label_ca);
// The signed data is header + digest + pubkey.
SecureBlob contents = SecureCat(SecureCat(
SecureBlob(header, arraysize(header)),
label_ca_digest),
ConvertStringToBlob(binding.identity_public_key()));
// Now verify the signature.
if (!VerifySignature(ConvertStringToBlob(binding.identity_public_key_der()),
contents,
ConvertStringToBlob(binding.identity_binding()))) {
LOG(ERROR) << "Failed to verify identity binding signature.";
return false;
}
return true;
}
bool Attestation::VerifyQuote(const SecureBlob& aik_public_key,
const Quote& quote) {
if (!VerifySignature(aik_public_key,
ConvertStringToBlob(quote.quoted_data()),
ConvertStringToBlob(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.
const uint8_t header[] = {
static_cast<uint8_t>(0), static_cast<uint8_t>(2),
static_cast<uint8_t>(1), static_cast<uint8_t>(0),
static_cast<uint8_t>(0), static_cast<uint8_t>(0),
static_cast<uint8_t>(0),
static_cast<uint8_t>(quote.quoted_pcr_value().size())};
SecureBlob pcr_composite = SecureCat(
SecureBlob(header, arraysize(header)),
ConvertStringToBlob(quote.quoted_pcr_value()));
SecureBlob pcr_digest = CryptoLib::Sha1(pcr_composite);
SecureBlob quoted_data = ConvertStringToBlob(quote.quoted_data());
if (search(quoted_data.begin(), quoted_data.end(),
pcr_digest.begin(), pcr_digest.end()) == quoted_data.end()) {
LOG(ERROR) << "PCR0 value mismatch.";
return false;
}
// Check if the PCR0 value represents a known mode.
for (size_t i = 0; i < arraysize(kKnownPCRValues); ++i) {
SecureBlob settings_blob(3);
settings_blob[0] = kKnownPCRValues[i].developer_mode_enabled;
settings_blob[1] = kKnownPCRValues[i].recovery_mode_enabled;
settings_blob[2] = kKnownPCRValues[i].firmware_type;
SecureBlob settings_digest = CryptoLib::Sha1(settings_blob);
chromeos::Blob extend_pcr_value(kDigestSize, 0);
extend_pcr_value.insert(extend_pcr_value.end(), settings_digest.begin(),
settings_digest.end());
SecureBlob final_pcr_value = CryptoLib::Sha1(extend_pcr_value);
if (quote.quoted_pcr_value().size() == final_pcr_value.size() &&
0 == memcmp(quote.quoted_pcr_value().data(), final_pcr_value.data(),
final_pcr_value.size())) {
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::VerifyCertifiedKey(
const SecureBlob& aik_public_key,
const SecureBlob& certified_public_key,
const SecureBlob& certified_key_info,
const SecureBlob& proof) {
string key_info = ConvertBlobToString(certified_key_info);
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.front();
RSA* rsa = d2i_RSAPublicKey(NULL, &asn1_ptr, certified_public_key.size());
if (!rsa) {
LOG(ERROR) << "Failed to decode certified public key.";
return false;
}
SecureBlob modulus(BN_num_bytes(rsa->n));
BN_bn2bin(rsa->n, const_cast<unsigned char*>(&modulus.front()));
RSA_free(rsa);
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) << "Cerified public key mismatch.";
return false;
}
return true;
}
EVP_PKEY* Attestation::GetAuthorityPublicKey(const char* issuer_name) {
const int kNumIssuers = arraysize(kKnownEndorsementCA);
for (int i = 0; i < kNumIssuers; ++i) {
if (0 == strcmp(issuer_name, kKnownEndorsementCA[i].issuer)) {
RSA* rsa = RSA_new();
if (!rsa)
return NULL;
rsa->e = BN_new();
if (!rsa->e) {
RSA_free(rsa);
return NULL;
}
BN_set_word(rsa->e, kWellKnownExponent);
rsa->n = BN_new();
if (!rsa->n) {
RSA_free(rsa);
return NULL;
}
BN_hex2bn(&rsa->n, kKnownEndorsementCA[i].modulus);
EVP_PKEY* pkey = EVP_PKEY_new();
if (!pkey) {
RSA_free(rsa);
return NULL;
}
EVP_PKEY_assign_RSA(pkey, rsa);
return pkey;
}
}
return NULL;
}
bool Attestation::VerifySignature(const SecureBlob& public_key,
const SecureBlob& signed_data,
const SecureBlob& signature) {
const unsigned char* asn1_ptr = &public_key.front();
RSA* rsa = d2i_RSAPublicKey(NULL, &asn1_ptr, public_key.size());
if (!rsa) {
LOG(ERROR) << "Failed to decode public key.";
return false;
}
SecureBlob digest = CryptoLib::Sha1(signed_data);
if (!RSA_verify(NID_sha1, &digest.front(), digest.size(),
const_cast<unsigned char*>(&signature.front()),
signature.size(), rsa)) {
LOG(ERROR) << "Failed to verify signature.";
RSA_free(rsa);
return false;
}
RSA_free(rsa);
return true;
}
void Attestation::ClearDatabase() {
if (database_pb_.has_credentials()) {
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());
}
if (database_pb_.has_identity_binding()) {
IdentityBinding* binding = database_pb_.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());
}
if (database_pb_.has_identity_key()) {
IdentityKey* key = database_pb_.mutable_identity_key();
ClearString(key->mutable_identity_public_key());
ClearString(key->mutable_identity_key_blob());
ClearString(key->mutable_identity_credential());
}
if (database_pb_.has_pcr0_quote()) {
Quote* quote = database_pb_.mutable_pcr0_quote();
ClearString(quote->mutable_quote());
ClearString(quote->mutable_quoted_data());
ClearString(quote->mutable_quoted_pcr_value());
}
if (database_pb_.has_delegate()) {
Delegation* delegate = database_pb_.mutable_delegate();
ClearString(delegate->mutable_blob());
ClearString(delegate->mutable_secret());
}
}
void Attestation::ClearString(string* s) {
chromeos::SecureMemset(const_cast<char*>(s->data()), 0, s->length());
}
bool Attestation::VerifyActivateIdentity(const SecureBlob& delegate_blob,
const SecureBlob& delegate_secret,
const SecureBlob& identity_key_blob,
const SecureBlob& identity_public_key,
const SecureBlob& ek_public_key) {
const char* kTestCredential = "test";
const uint8_t kAlgAES256 = 9; // This comes from TPM_ALG_AES256.
const uint8_t kEncModeCBC = 2; // This comes from TPM_SYM_MODE_CBC.
const uint8_t kAsymContentHeader[] =
{0, 0, 0, kAlgAES256, 0, kEncModeCBC, 0, kCipherKeySize};
const uint8_t kSymContentHeader[12] = {0};
// Generate an AES key and encrypt the credential.
SecureBlob aes_key(kCipherKeySize);
CryptoLib::GetSecureRandom(const_cast<unsigned char*>(&aes_key.front()),
aes_key.size());
SecureBlob credential(kTestCredential, strlen(kTestCredential));
SecureBlob encrypted_credential;
if (!tpm_->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 = SecureCat(SecureCat(
SecureBlob(kAsymContentHeader, arraysize(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[0];
RSA* rsa = d2i_RSAPublicKey(NULL, &asn1_ptr, ek_public_key.size());
if (!rsa) {
LOG(ERROR) << "Failed to decode EK public key.";
return false;
}
SecureBlob encrypted_asym_content;
if (!tpm_->TpmCompatibleOAEPEncrypt(rsa, 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 = SecureCat(SecureCat(
length_blob,
SecureBlob(kSymContentHeader, arraysize(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() ||
chromeos::SafeMemcmp(credential.data(), credential_out.data(),
credential.size()) != 0) {
LOG(ERROR) << "Invalid identity credential.";
return false;
}
return true;
}
}