blob: 55fdf4e81297115b031f0ac3d2bb6ebf8e7384ad [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.
#ifndef CRYPTOHOME_ATTESTATION_H_
#define CRYPTOHOME_ATTESTATION_H_
#include <sys/types.h>
#include <map>
#include <memory>
#include <string>
#include <base/files/file_path.h>
#include <base/scoped_observer.h>
#include <base/synchronization/lock.h>
#include <base/threading/platform_thread.h>
#include <brillo/http/http_transport.h>
#include <brillo/secure_blob.h>
#include <openssl/evp.h>
#include "cryptohome/crypto.h"
#include "cryptohome/install_attributes.h"
#include "attestation.pb.h" // NOLINT(build/include)
namespace cryptohome {
class KeyStore;
class Pkcs11KeyStore;
class Platform;
class Tpm;
class TpmInit;
// This class performs tasks which enable attestation enrollment. These tasks
// include creating an AIK and recording all information about the AIK and EK
// that an attestation server will need to issue credentials for this system. If
// a platform does not have a TPM, this class does nothing. This class is
// thread-safe.
class Attestation : public base::PlatformThread::Delegate,
public InstallAttributes::Observer {
public:
enum PCAType {
kDefaultPCA, // The Google-operated Privacy CA.
kAlternatePCA, // An alternate Privacy CA specified by enterprise policy.
kMaxPCAType
};
enum PCARequestType {
kEnroll, // Enrolls a device, certifying an identity key.
kGetCertificate, // Issues a certificate for a TPM-backed key.
};
Attestation();
virtual ~Attestation();
// Must be called before any other method. If |retain_endorsement_data| is set
// then this instance will not perform any finalization of endorsement data.
// That is, it will continue to be available in an unencrypted state.
// The caller retains ownership of all pointers.
virtual void Initialize(Tpm* tpm,
TpmInit* tpm_init,
Platform* platform,
Crypto* crypto,
InstallAttributes* install_attributes,
bool retain_endorsement_data);
// Returns true if the attestation enrollment blobs already exist.
virtual bool IsPreparedForEnrollment();
// Returns true if an AIK certificate exists.
virtual bool IsEnrolled();
// Creates attestation enrollment blobs if they do not already exist. This
// includes extracting the EK information from the TPM and generating an AIK
// to be used later during enrollment.
virtual void PrepareForEnrollment();
// Like PrepareForEnrollment(), but asynchronous.
virtual void PrepareForEnrollmentAsync();
// Verifies all attestation data as an attestation server would. Returns true
// if all data is valid. If |is_cros_core| is true, checks that the EK is
// endorsed for that configuration.
virtual bool Verify(bool is_cros_core);
// Verifies the EK certificate. If |is_cros_core| is true, checks that the EK
// is endorsed for that configuration.
virtual bool VerifyEK(bool is_cros_core);
// Creates an enrollment request to be sent to the Privacy CA. This request
// is a serialized AttestationEnrollmentRequest protobuf. Attestation
// enrollment is a process by which the Privacy CA verifies the EK certificate
// of a device and issues a certificate for an AIK. The enrollment process can
// be finished by calling Enroll() with the response from the Privacy CA. This
// method can only succeed if IsPreparedForEnrollment() returns true.
//
// Parameters
// pca_type - Specifies to which Privacy CA the request will be sent.
// pca_request - The request to be sent to the Privacy CA.
//
// Returns true on success.
virtual bool CreateEnrollRequest(PCAType pca_type,
brillo::SecureBlob* pca_request);
// Finishes the enrollment process. On success, IsEnrolled() will return true.
// The response from the Privacy CA is a serialized
// AttestationEnrollmentResponse protobuf. This method recovers the AIK
// certificate by calling TPM_ActivateIdentity and stores this certificate to
// be used later during a certificate request.
//
// Parameters
// pca_type - Specifies which Privacy CA created the response.
// pca_response - The Privacy CA's response to an enrollment request as
// returned by CreateEnrollRequest().
//
// Returns true on success.
virtual bool Enroll(PCAType pca_type,
const brillo::SecureBlob& pca_response);
// Creates an attestation certificate request to be sent to the Privacy CA.
// The request is a serialized AttestationCertificateRequest protobuf. The
// certificate request process generates and certifies a key in the TPM and
// sends the AIK certificate along with information about the certified key to
// the Privacy CA. The PCA verifies the information and issues a certificate
// for the certified key. The certificate request process can be finished by
// calling FinishCertRequest() with the response from the Privacy CA. This
// method can only succeed if IsEnrolled() returns true.
//
// Parameters
// pca_type - Specifies to which Privacy CA the request will be sent.
// profile - Specifies the type of certificate to be requested.
// username - The user requesting the certificate.
// origin - Some certificate requests require information about the origin
// of the request. If no origin is needed, this can be empty.
// pca_request - The request to be sent to the Privacy CA.
//
// Returns true on success.
virtual bool CreateCertRequest(PCAType pca_type,
CertificateProfile profile,
const std::string& username,
const std::string& origin,
brillo::SecureBlob* pca_request);
// Finishes the certificate request process. The |pca_response| from the PCA
// is a serialized AttestationCertificateResponse protobuf. This final step
// verifies the PCA operation succeeded and extracts the certificate for the
// certified key. The certificate is stored with the key in association with
// the |key_name|. If the key is a user-specific key it is stored in
// association with the currently signed-in user.
//
// Parameters
// pca_response - The Privacy CA's response to a certificate request as
// returned by CreateCertRequest().
// is_user_specific - Whether the key is associated with the current user.
// username - The canonical username of the active user profile. Ignored if
// |is_user_specific| is false.
// key_name - A name for the key. If a key already exists with this name it
// will be destroyed and replaced with this one. This name may
// subsequently be used to query the certificate for the key
// using GetCertificateChain.
// certificate_chain - The PCA issued certificate chain in PEM format. By
// convention the certified key certificate is listed
// first followed by intermediate CA certificate(s).
// The PCA root certificate is not included.
virtual bool FinishCertRequest(const brillo::SecureBlob& pca_response,
bool is_user_specific,
const std::string& username,
const std::string& key_name,
brillo::SecureBlob* certificate_chain);
// Gets the PCA issued certificate chain for an existing certified key.
// Returns true on success.
//
// Parameters
//
// is_user_specific - Whether the key is associated with the current user.
// username - The canonical username of the active user profile. Ignored if
// |is_user_specific| is false.
// key_name - The key name; this is the same name previously passed to
// FinishCertRequest.
// certificate_chain - The PCA issued certificate chain in the same format
// used by FinishCertRequest.
virtual bool GetCertificateChain(bool is_user_specific,
const std::string& username,
const std::string& key_name,
brillo::SecureBlob* certificate_chain);
// Gets the public key for an existing certified key. Returns true on
// success. This method is provided for convenience, the same public key can
// also be extracted from the certificate chain.
//
// Parameters
// is_user_specific - Whether the key is associated with the current user.
// username - The canonical username of the active user profile. Ignored if
// |is_user_specific| is false.
// key_name - The key name; this is the same name previously passed to
// FinishCertRequest.
// public_key - The public key in DER form, according to the RSAPublicKey
// ASN.1 type defined in PKCS #1 A.1.1.
virtual bool GetPublicKey(bool is_user_specific,
const std::string& username,
const std::string& key_name,
brillo::SecureBlob* public_key);
// Returns true iff a given key exists.
virtual bool DoesKeyExist(bool is_user_specific,
const std::string& username,
const std::string& key_name);
// Signs a challenge for an enterprise device / user. This challenge is
// typically generated by and the response verified by DMServer. Returns true
// on success.
//
// Parameters
//
// is_user_specific - Whether the key is associated with the current user.
// username - The canonical username of the active user profile. Ignored if
// |is_user_specific| is false.
// key_name - The key name; this is the same name previously passed to
// FinishCertRequest.
// domain - A domain value to be included in the challenge response. The
// value is opaque to this class.
// device_id - A device identifier to be included in the challenge response.
// This value is opaque to this class.
// include_signed_public_key - Whether the challenge response should include
// a SignedPublicKeyAndChallenge.
// challenge - The challenge to be signed.
// response - On success is populated with the challenge response.
virtual bool SignEnterpriseChallenge(
bool is_user_specific,
const std::string& username,
const std::string& key_name,
const std::string& domain,
const brillo::SecureBlob& device_id,
bool include_signed_public_key,
const brillo::SecureBlob& challenge,
brillo::SecureBlob* response);
// Signs a challenge outside of an enterprise context. This challenge is
// typically generated by some module running within Chrome. Returns true
// on success.
//
// Parameters
//
// is_user_specific - Whether the key is associated with the current user.
// username - The canonical username of the active user profile. Ignored if
// |is_user_specific| is false.
// key_name - The key name; this is the same name previously passed to
// FinishCertRequest.
// challenge - The challenge to be signed.
// response - On success is populated with the challenge response.
virtual bool SignSimpleChallenge(bool is_user_specific,
const std::string& username,
const std::string& key_name,
const brillo::SecureBlob& challenge,
brillo::SecureBlob* response);
// Registers a key with the user's PKCS #11 token. On success, the key is
// removed from its old location and will no longer be available via this
// interface. Returns true on success.
//
// Parameters
//
// is_user_specific - Whether the key is associated with the current user.
// username - The canonical username of the active user profile. Ignored if
// |is_user_specific| is false.
// key_name - The key name; this is the same name previously passed to
// FinishCertRequest.
// include_certificates - Whether to also register the certificate chain
// associated with the key.
virtual bool RegisterKey(bool is_user_specific,
const std::string& username,
const std::string& key_name,
bool include_certificates);
// Gets a payload previously set for a key. If the key exists but no payload
// has been set, the payload will be empty. Returns true on success.
//
// Parameters
//
// is_user_specific - Whether the key is associated with the current user.
// username - The canonical username of the active user profile. Ignored if
// |is_user_specific| is false.
// key_name - The key name; this is the same name previously passed to
// FinishCertRequest.
// payload - Receives the payload data.
virtual bool GetKeyPayload(bool is_user_specific,
const std::string& key_name,
const std::string& username,
brillo::SecureBlob* payload);
// Sets a payload for a key; any previous payload will be overwritten.
// Returns true on success.
//
// Parameters
//
// is_user_specific - Whether the key is associated with the current user.
// username - The canonical username of the active user profile. Ignored if
// |is_user_specific| is false.
// key_name - The key name; this is the same name previously passed to
// FinishCertRequest.
// payload - The payload data.
virtual bool SetKeyPayload(bool is_user_specific,
const std::string& key_name,
const std::string& username,
const brillo::SecureBlob& payload);
// Deletes all keys where the key name has the given |key_prefix|.
// Returns true on success.
//
// Parameters
//
// is_user_specific - Whether the key is associated with the current user.
// username - The current user canonical email address.
// key_prefix - The key name prefix.
virtual bool DeleteKeysByPrefix(bool is_user_specific,
const std::string& username,
const std::string& key_prefix);
// Gets the TPM Endorsement Key (EK) certificate and returns it in PEM format
// in addition to a hex SHA256 hash of the raw DER encoded certificate. The
// result is intended to be human readable and is always valid ASCII. Returns
// true on success. This method requires the TPM owner password to be
// available.
virtual bool GetEKInfo(std::string* ek_info);
// Creates a request to be sent to the PCA which will reset the identity for
// this device on future AIK enrollments. The |reset_token| is put in the
// request protobuf verbatim. On success returns true and copies the
// serialized request into |reset_request|.
virtual bool GetIdentityResetRequest(const std::string& reset_token,
brillo::SecureBlob* reset_request);
// Helper method to determine PCR0 status. Returns true iff the current PCR0
// value shows a verified boot measurement.
virtual bool IsPCR0VerifiedMode();
// Ensures all endorsement data which uniquely identifies the device no longer
// exists in the attestation database unencrypted. Encrypted endorsement data
// cannot be decrypted locally.
virtual void FinalizeEndorsementData();
// Provides the owner delegate credentials normally used for AIK activation.
// Returns true on success.
virtual bool GetDelegateCredentials(brillo::SecureBlob* blob,
brillo::SecureBlob* secret,
bool* has_reset_lock_permissions);
// Provides cached |ek_public_key| and |ek_certificate| if they exist. If only
// the public key is found then it will be provided and the certificate will
// be empty. Returns false if the public key is not found (i.e. not cached).
virtual bool GetCachedEndorsementData(brillo::SecureBlob* ek_public_key,
brillo::SecureBlob* ek_certificate);
// Caches the endorsement public key if the TPM is not owned. If the TPM is
// owned or the public key is already known this will have no effect.
virtual void CacheEndorsementData();
// Sends a |request| to a Privacy CA and waits for the |reply|. This is a
// blocking call. Returns true on success.
virtual bool SendPCARequestAndBlock(PCAType pca_type,
PCARequestType request_type,
const brillo::SecureBlob& request,
brillo::SecureBlob* reply);
// Sets an alternative attestation database location. Useful in testing.
virtual void set_database_path(const char* path) {
database_path_ = base::FilePath(path);
}
// Sets an alternate key store.
virtual void set_key_store(KeyStore* key_store) {
key_store_ = key_store;
}
virtual void set_enterprise_test_key(RSA* enterprise_test_key) {
enterprise_test_key_ = enterprise_test_key;
}
virtual void set_http_transport(
std::shared_ptr<brillo::http::Transport> transport) {
http_transport_ = transport;
}
// PlatformThread::Delegate interface.
virtual void ThreadMain() { PrepareForEnrollment(); }
// InstallAttributes::Observer interface.
virtual void OnFinalized() { PrepareForEnrollmentAsync(); }
private:
typedef std::map<std::string, brillo::SecureBlob> CertRequestMap;
enum FirmwareType {
kUnknown,
kVerified,
kDeveloper
};
// So we can use std::unique_ptr with openssl types.
struct RSADeleter {
inline void operator()(void* ptr) const;
};
struct X509Deleter {
inline void operator()(void* ptr) const;
};
struct EVP_PKEYDeleter {
inline void operator()(void* ptr) const;
};
struct NETSCAPE_SPKIDeleter {
inline void operator()(void* ptr) const;
};
// Just enough CA information to do a basic verification.
struct CertificateAuthority {
const char* issuer;
const char* modulus; // In hex format.
};
static const size_t kQuoteExternalDataSize;
static const size_t kCipherKeySize;
static const size_t kNonceSize;
static const size_t kDigestSize;
static const size_t kChallengeSignatureNonceSize;
static const mode_t kDatabasePermissions;
static const char kDatabaseOwner[];
static const char kDefaultDatabasePath[];
static const char kDefaultPCAPublicKey[];
static const char kDefaultPCAPublicKeyID[];
static const char kDefaultPCAWebOrigin[];
static const char kEnterpriseSigningPublicKey[];
static const char kEnterpriseEncryptionPublicKey[];
static const char kEnterpriseEncryptionPublicKeyID[];
static const CertificateAuthority kKnownEndorsementCA[];
static const CertificateAuthority kKnownCrosCoreEndorsementCA[];
static const struct PCRValue {
bool developer_mode_enabled;
bool recovery_mode_enabled;
FirmwareType firmware_type;
} kKnownPCRValues[];
// ASN.1 DigestInfo header for SHA-256 (see PKCS #1 v2.1 section 9.2).
static const unsigned char kSha256DigestInfo[];
static const int kNumTemporalValues;
// Install attribute names for alternate PCA attributes.
static const char kAlternatePCAKeyAttributeName[];
static const char kAlternatePCAKeyIDAttributeName[];
static const char kAlternatePCAUrlAttributeName[];
Tpm* tpm_;
TpmInit* tpm_init_;
Platform* platform_;
Crypto* crypto_;
// A lock to protect |database_pb_| because PrepareForEnrollment may happen on
// a worker thread.
base::Lock lock_;
brillo::SecureBlob database_key_;
brillo::SecureBlob sealed_database_key_;
base::FilePath database_path_;
AttestationDatabase database_pb_;
base::PlatformThreadHandle thread_;
CertRequestMap pending_cert_requests_;
std::unique_ptr<Pkcs11KeyStore> pkcs11_key_store_;
KeyStore* key_store_;
// If set, this will be used to sign / encrypt enterprise challenge-response
// data instead of using kEnterprise*PublicKey.
RSA* enterprise_test_key_;
InstallAttributes* install_attributes_;
ScopedObserver<InstallAttributes, InstallAttributes::Observer>
install_attributes_observer_;
// Don't use directly, use IsTPMReady() instead.
bool is_tpm_ready_;
bool is_prepare_in_progress_;
bool retain_endorsement_data_;
// Can be used to override the default transport (e.g. during testing).
std::shared_ptr<brillo::http::Transport> http_transport_;
// User and group for ownership of the database file.
uid_t attestation_user_;
gid_t attestation_group_;
// Serializes and encrypts an attestation database.
bool EncryptDatabase(const AttestationDatabase& db,
std::string* serial_encrypted_db);
// Decrypts and parses an attestation database.
bool DecryptDatabase(const std::string& serial_encrypted_db,
AttestationDatabase* db);
// Writes an encrypted database to a persistent storage location.
bool StoreDatabase(const std::string& serial_encrypted_db);
// Reads a database from a persistent storage location.
bool LoadDatabase(std::string* serial_encrypted_db);
// Persists any changes made to database_pb_.
bool PersistDatabaseChanges();
// Ensures permissions of the database file are correct.
void CheckDatabasePermissions();
// Verifies an endorsement credential against known Chrome OS issuers. If
// |is_cros_core| is true, checks that the EK is endorsed for that
// configuration.
bool VerifyEndorsementCredential(const brillo::SecureBlob& credential,
const brillo::SecureBlob& public_key,
bool is_cros_core);
// Verifies identity key binding data.
bool VerifyIdentityBinding(const IdentityBinding& binding);
// Verifies a quote of PCR0.
bool VerifyPCR0Quote(const brillo::SecureBlob& aik_public_key,
const Quote& quote);
// Verifies a quote of PCR1.
bool VerifyPCR1Quote(const brillo::SecureBlob& aik_public_key,
const Quote& quote);
// Verifies that a quote signature is valid and matches the quoted data.
bool VerifyQuoteSignature(const brillo::SecureBlob& aik_public_key,
const Quote& quote,
int pcr_index);
// Verifies a certified key.
bool VerifyCertifiedKey(const brillo::SecureBlob& aik_public_key,
const brillo::SecureBlob& certified_public_key,
const brillo::SecureBlob& certified_key_info,
const brillo::SecureBlob& proof);
// Creates a public key based on a known credential issuer.
std::unique_ptr<EVP_PKEY, EVP_PKEYDeleter> GetAuthorityPublicKey(
const char* issuer_name,
bool is_cros_core);
// Verifies an RSA-PKCS1-SHA1 digital signature.
bool VerifySignature(const brillo::SecureBlob& public_key,
const brillo::SecureBlob& signed_data,
const brillo::SecureBlob& signature);
// Clears the memory of the database protobuf.
void ClearDatabase();
// Clears the memory of a Quote protobuf.
void ClearQuote(Quote* quote);
// Clears the memory of Identity protobufs.
void ClearIdentity(IdentityBinding* binding, IdentityKey* key);
// Clears the memory of a std::string.
void ClearString(std::string* s);
// Performs AIK activation with a fake credential.
bool VerifyActivateIdentity(const brillo::SecureBlob& delegate_blob,
const brillo::SecureBlob& delegate_secret,
const brillo::SecureBlob& identity_key_blob,
const brillo::SecureBlob& identity_public_key,
const brillo::SecureBlob& ek_public_key);
// Encrypts the endorsement credential with the Privacy CA public key.
bool EncryptEndorsementCredential(PCAType pca_type,
const brillo::SecureBlob& credential,
EncryptedData* encrypted_credential);
// Adds named device-wide key to the attestation database.
bool AddDeviceKey(const std::string& key_name, const CertifiedKey& key);
// Removes a device-wide key from the attestation database.
void RemoveDeviceKey(const std::string& key_name);
// Finds a key by name.
bool FindKeyByName(bool is_user_specific,
const std::string& username,
const std::string& key_name,
CertifiedKey* key);
// Saves a key either in the device or user key store.
bool SaveKey(bool is_user_specific,
const std::string& username,
const std::string& key_name,
const CertifiedKey& key);
// Deletes a key either from the device or user key store.
void DeleteKey(bool is_user_specific,
const std::string& username,
const std::string& key_name);
// Assembles a certificate chain in PEM format for a certified key. By
// convention, the leaf certificate will be first.
bool CreatePEMCertificateChain(const CertifiedKey& key,
brillo::SecureBlob* certificate_chain);
// Creates a certificate in PEM format from a DER encoded X.509 certificate.
std::string CreatePEMCertificate(const std::string& certificate);
// Signs data with a certified key using the scheme specified for challenges
// and outputs a serialized SignedData protobuf.
bool SignChallengeData(const CertifiedKey& key,
const brillo::SecureBlob& challenge,
brillo::SecureBlob* response);
// Validates incoming enterprise challenge data.
bool ValidateEnterpriseChallenge(const SignedData& signed_challenge);
// Encrypts a KeyInfo protobuf as required for an enterprise challenge
// response.
bool EncryptEnterpriseKeyInfo(const KeyInfo& key_info,
EncryptedData* encrypted_data);
// Encrypts data into an EncryptedData protobuf, wrapping the encryption key
// with |wrapping_key|.
bool EncryptData(const brillo::SecureBlob& input,
RSA* wrapping_key,
const std::string& wrapping_key_id,
EncryptedData* output);
// Creates an RSA* given a modulus in hex format. The exponent is always set
// to 65537. If an error occurs, NULL is returned.
std::unique_ptr<RSA, RSADeleter> CreateRSAFromHexModulus(
const std::string& hex_modulus);
// Creates a SignedPublicKeyAndChallenge with a random challenge.
bool CreateSignedPublicKey(const CertifiedKey& key,
brillo::SecureBlob* signed_public_key);
// Standard AES-256-CBC padded encryption.
bool AesEncrypt(const brillo::SecureBlob& plaintext,
const brillo::SecureBlob& key,
const brillo::SecureBlob& iv,
brillo::SecureBlob* ciphertext);
// Standard AES-256-CBC padded decryption.
bool AesDecrypt(const brillo::SecureBlob& ciphertext,
const brillo::SecureBlob& key,
const brillo::SecureBlob& iv,
brillo::SecureBlob* plaintext);
// Encrypts data in a TSS compatible way using AES-256-CBC.
//
// Parameters
// key - The AES key.
// input - The data to be encrypted.
// output - The encrypted data.
bool TssCompatibleEncrypt(const brillo::SecureBlob& key,
const brillo::SecureBlob& input,
brillo::SecureBlob* output);
// Encrypts data using the TPM_ES_RSAESOAEP_SHA1_MGF1 scheme.
//
// Parameters
// key - The RSA public key.
// input - The data to be encrypted.
// output - The encrypted data.
bool TpmCompatibleOAEPEncrypt(RSA* key,
const brillo::SecureBlob& input,
brillo::SecureBlob* output);
// Chooses a temporal index which will be used by the PCA to create a
// certificate. This decision factors in the currently signed-in |user| and
// the |origin| of the certificate request. The strategy is to find an index
// which has not already been used by another user for the same origin.
int ChooseTemporalIndex(const std::string& user, const std::string& origin);
// Returns true if the TPM is ready.
bool IsTPMReady();
// If PCR1 is clear (i.e. all 0 bytes), extends the PCR with the HWID. This is
// a fallback if the device firmware does not already do this.
void ExtendPCR1IfClear();
// Creates a PCA URL for the given |pca_type| and |request_type|.
std::string GetPCAURL(PCAType pca_type, PCARequestType request_type) const;
// Injects a TpmInit object to be used for RemoveTpmOwnerDependency
void set_tpm_init(TpmInit* value) { tpm_init_ = value; }
friend class AttestationTest;
DISALLOW_COPY_AND_ASSIGN(Attestation);
};
} // namespace cryptohome
#endif // CRYPTOHOME_ATTESTATION_H_