// // Copyright (C) 2015 The Android Open Source Project // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. // #include "attestation/server/attestation_service.h" #include <string> #include <base/callback.h> #include <brillo/bind_lambda.h> #include <brillo/data_encoding.h> #include <brillo/http/http_utils.h> #include <brillo/mime_utils.h> #include <crypto/sha2.h> #include "attestation/common/attestation_ca.pb.h" #include "attestation/common/database.pb.h" #include "attestation/server/database_impl.h" namespace { #ifndef USE_TEST_ACA const char kACAWebOrigin[] = "https://chromeos-ca.gstatic.com"; #else const char kACAWebOrigin[] = "https://asbestos-qa.corp.google.com"; #endif const size_t kNonceSize = 20; // As per TPM_NONCE definition. const int kNumTemporalValues = 5; } // namespace namespace attestation { AttestationService::AttestationService() : attestation_ca_origin_(kACAWebOrigin), weak_factory_(this) {} bool AttestationService::Initialize() { LOG(INFO) << "Attestation service started."; worker_thread_.reset(new base::Thread("Attestation Service Worker")); worker_thread_->StartWithOptions( base::Thread::Options(base::MessageLoop::TYPE_IO, 0)); if (!tpm_utility_) { default_tpm_utility_.reset(new TpmUtilityV1()); if (!default_tpm_utility_->Initialize()) { return false; } tpm_utility_ = default_tpm_utility_.get(); } if (!crypto_utility_) { default_crypto_utility_.reset(new CryptoUtilityImpl(tpm_utility_)); crypto_utility_ = default_crypto_utility_.get(); } if (!database_) { default_database_.reset(new DatabaseImpl(crypto_utility_)); worker_thread_->task_runner()->PostTask( FROM_HERE, base::Bind(&DatabaseImpl::Initialize, base::Unretained(default_database_.get()))); database_ = default_database_.get(); } if (!key_store_) { pkcs11_token_manager_.reset(new chaps::TokenManagerClient()); default_key_store_.reset(new Pkcs11KeyStore(pkcs11_token_manager_.get())); key_store_ = default_key_store_.get(); } return true; } void AttestationService::CreateGoogleAttestedKey( const CreateGoogleAttestedKeyRequest& request, const CreateGoogleAttestedKeyCallback& callback) { auto result = std::make_shared<CreateGoogleAttestedKeyReply>(); base::Closure task = base::Bind(&AttestationService::CreateGoogleAttestedKeyTask, base::Unretained(this), request, result); base::Closure reply = base::Bind( &AttestationService::TaskRelayCallback<CreateGoogleAttestedKeyReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::CreateGoogleAttestedKeyTask( const CreateGoogleAttestedKeyRequest& request, const std::shared_ptr<CreateGoogleAttestedKeyReply>& result) { LOG(INFO) << "Creating attested key: " << request.key_label(); if (!IsPreparedForEnrollment()) { LOG(ERROR) << "Attestation: TPM is not ready."; result->set_status(STATUS_NOT_READY); return; } if (!IsEnrolled()) { std::string enroll_request; if (!CreateEnrollRequest(&enroll_request)) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } std::string enroll_reply; if (!SendACARequestAndBlock(kEnroll, enroll_request, &enroll_reply)) { result->set_status(STATUS_CA_NOT_AVAILABLE); return; } std::string server_error; if (!FinishEnroll(enroll_reply, &server_error)) { if (server_error.empty()) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } result->set_status(STATUS_REQUEST_DENIED_BY_CA); result->set_server_error(server_error); return; } } CertifiedKey key; if (!CreateKey(request.username(), request.key_label(), request.key_type(), request.key_usage(), &key)) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } std::string certificate_request; std::string message_id; if (!CreateCertificateRequest(request.username(), key, request.certificate_profile(), request.origin(), &certificate_request, &message_id)) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } std::string certificate_reply; if (!SendACARequestAndBlock(kGetCertificate, certificate_request, &certificate_reply)) { result->set_status(STATUS_CA_NOT_AVAILABLE); return; } std::string certificate_chain; std::string server_error; if (!FinishCertificateRequest(certificate_reply, request.username(), request.key_label(), message_id, &key, &certificate_chain, &server_error)) { if (server_error.empty()) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } result->set_status(STATUS_REQUEST_DENIED_BY_CA); result->set_server_error(server_error); return; } result->set_certificate_chain(certificate_chain); } void AttestationService::GetKeyInfo(const GetKeyInfoRequest& request, const GetKeyInfoCallback& callback) { auto result = std::make_shared<GetKeyInfoReply>(); base::Closure task = base::Bind(&AttestationService::GetKeyInfoTask, base::Unretained(this), request, result); base::Closure reply = base::Bind(&AttestationService::TaskRelayCallback<GetKeyInfoReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::GetKeyInfoTask( const GetKeyInfoRequest& request, const std::shared_ptr<GetKeyInfoReply>& result) { CertifiedKey key; if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { result->set_status(STATUS_INVALID_PARAMETER); return; } std::string public_key_info; if (!GetSubjectPublicKeyInfo(key.key_type(), key.public_key(), &public_key_info)) { LOG(ERROR) << __func__ << ": Bad public key."; result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } result->set_key_type(key.key_type()); result->set_key_usage(key.key_usage()); result->set_public_key(public_key_info); result->set_certify_info(key.certified_key_info()); result->set_certify_info_signature(key.certified_key_proof()); if (key.has_intermediate_ca_cert()) { result->set_certificate(CreatePEMCertificateChain(key)); } else { result->set_certificate(key.certified_key_credential()); } } void AttestationService::GetEndorsementInfo( const GetEndorsementInfoRequest& request, const GetEndorsementInfoCallback& callback) { auto result = std::make_shared<GetEndorsementInfoReply>(); base::Closure task = base::Bind(&AttestationService::GetEndorsementInfoTask, base::Unretained(this), request, result); base::Closure reply = base::Bind( &AttestationService::TaskRelayCallback<GetEndorsementInfoReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::GetEndorsementInfoTask( const GetEndorsementInfoRequest& request, const std::shared_ptr<GetEndorsementInfoReply>& result) { if (request.key_type() != KEY_TYPE_RSA) { result->set_status(STATUS_INVALID_PARAMETER); return; } auto database_pb = database_->GetProtobuf(); if (!database_pb.has_credentials() || !database_pb.credentials().has_endorsement_public_key()) { // Try to read the public key directly. std::string public_key; if (!tpm_utility_->GetEndorsementPublicKey(&public_key)) { result->set_status(STATUS_NOT_AVAILABLE); return; } database_pb.mutable_credentials()->set_endorsement_public_key(public_key); } std::string public_key_info; if (!GetSubjectPublicKeyInfo( request.key_type(), database_pb.credentials().endorsement_public_key(), &public_key_info)) { LOG(ERROR) << __func__ << ": Bad public key."; result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } result->set_ek_public_key(public_key_info); if (database_pb.credentials().has_endorsement_credential()) { result->set_ek_certificate( database_pb.credentials().endorsement_credential()); } } void AttestationService::GetAttestationKeyInfo( const GetAttestationKeyInfoRequest& request, const GetAttestationKeyInfoCallback& callback) { auto result = std::make_shared<GetAttestationKeyInfoReply>(); base::Closure task = base::Bind(&AttestationService::GetAttestationKeyInfoTask, base::Unretained(this), request, result); base::Closure reply = base::Bind( &AttestationService::TaskRelayCallback<GetAttestationKeyInfoReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::GetAttestationKeyInfoTask( const GetAttestationKeyInfoRequest& request, const std::shared_ptr<GetAttestationKeyInfoReply>& result) { if (request.key_type() != KEY_TYPE_RSA) { result->set_status(STATUS_INVALID_PARAMETER); return; } auto database_pb = database_->GetProtobuf(); if (!IsPreparedForEnrollment() || !database_pb.has_identity_key()) { result->set_status(STATUS_NOT_AVAILABLE); return; } if (database_pb.identity_key().has_identity_public_key()) { std::string public_key_info; if (!GetSubjectPublicKeyInfo( request.key_type(), database_pb.identity_key().identity_public_key(), &public_key_info)) { LOG(ERROR) << __func__ << ": Bad public key."; result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } result->set_public_key(public_key_info); } if (database_pb.has_identity_binding() && database_pb.identity_binding().has_identity_public_key()) { result->set_public_key_tpm_format( database_pb.identity_binding().identity_public_key()); } if (database_pb.identity_key().has_identity_credential()) { result->set_certificate(database_pb.identity_key().identity_credential()); } if (database_pb.has_pcr0_quote()) { *result->mutable_pcr0_quote() = database_pb.pcr0_quote(); } if (database_pb.has_pcr1_quote()) { *result->mutable_pcr1_quote() = database_pb.pcr1_quote(); } } void AttestationService::ActivateAttestationKey( const ActivateAttestationKeyRequest& request, const ActivateAttestationKeyCallback& callback) { auto result = std::make_shared<ActivateAttestationKeyReply>(); base::Closure task = base::Bind(&AttestationService::ActivateAttestationKeyTask, base::Unretained(this), request, result); base::Closure reply = base::Bind( &AttestationService::TaskRelayCallback<ActivateAttestationKeyReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::ActivateAttestationKeyTask( const ActivateAttestationKeyRequest& request, const std::shared_ptr<ActivateAttestationKeyReply>& result) { if (request.key_type() != KEY_TYPE_RSA) { result->set_status(STATUS_INVALID_PARAMETER); return; } std::string certificate; auto database_pb = database_->GetProtobuf(); if (!tpm_utility_->ActivateIdentity( database_pb.delegate().blob(), database_pb.delegate().secret(), database_pb.identity_key().identity_key_blob(), request.encrypted_certificate().asym_ca_contents(), request.encrypted_certificate().sym_ca_attestation(), &certificate)) { LOG(ERROR) << __func__ << ": Failed to activate identity."; result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } if (request.save_certificate()) { database_->GetMutableProtobuf() ->mutable_identity_key() ->set_identity_credential(certificate); if (!database_->SaveChanges()) { LOG(ERROR) << __func__ << ": Failed to persist database changes."; result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); } } result->set_certificate(certificate); } void AttestationService::CreateCertifiableKey( const CreateCertifiableKeyRequest& request, const CreateCertifiableKeyCallback& callback) { auto result = std::make_shared<CreateCertifiableKeyReply>(); base::Closure task = base::Bind(&AttestationService::CreateCertifiableKeyTask, base::Unretained(this), request, result); base::Closure reply = base::Bind( &AttestationService::TaskRelayCallback<CreateCertifiableKeyReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::CreateCertifiableKeyTask( const CreateCertifiableKeyRequest& request, const std::shared_ptr<CreateCertifiableKeyReply>& result) { CertifiedKey key; if (!CreateKey(request.username(), request.key_label(), request.key_type(), request.key_usage(), &key)) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } std::string public_key_info; if (!GetSubjectPublicKeyInfo(key.key_type(), key.public_key(), &public_key_info)) { LOG(ERROR) << __func__ << ": Bad public key."; result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } result->set_public_key(public_key_info); result->set_certify_info(key.certified_key_info()); result->set_certify_info_signature(key.certified_key_proof()); } void AttestationService::Decrypt(const DecryptRequest& request, const DecryptCallback& callback) { auto result = std::make_shared<DecryptReply>(); base::Closure task = base::Bind(&AttestationService::DecryptTask, base::Unretained(this), request, result); base::Closure reply = base::Bind(&AttestationService::TaskRelayCallback<DecryptReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::DecryptTask( const DecryptRequest& request, const std::shared_ptr<DecryptReply>& result) { CertifiedKey key; if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { result->set_status(STATUS_INVALID_PARAMETER); return; } std::string data; if (!tpm_utility_->Unbind(key.key_blob(), request.encrypted_data(), &data)) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } result->set_decrypted_data(data); } void AttestationService::Sign(const SignRequest& request, const SignCallback& callback) { auto result = std::make_shared<SignReply>(); base::Closure task = base::Bind(&AttestationService::SignTask, base::Unretained(this), request, result); base::Closure reply = base::Bind(&AttestationService::TaskRelayCallback<SignReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::SignTask(const SignRequest& request, const std::shared_ptr<SignReply>& result) { CertifiedKey key; if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { result->set_status(STATUS_INVALID_PARAMETER); return; } std::string signature; if (!tpm_utility_->Sign(key.key_blob(), request.data_to_sign(), &signature)) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } result->set_signature(signature); } void AttestationService::RegisterKeyWithChapsToken( const RegisterKeyWithChapsTokenRequest& request, const RegisterKeyWithChapsTokenCallback& callback) { auto result = std::make_shared<RegisterKeyWithChapsTokenReply>(); base::Closure task = base::Bind(&AttestationService::RegisterKeyWithChapsTokenTask, base::Unretained(this), request, result); base::Closure reply = base::Bind( &AttestationService::TaskRelayCallback<RegisterKeyWithChapsTokenReply>, GetWeakPtr(), callback, result); worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply); } void AttestationService::RegisterKeyWithChapsTokenTask( const RegisterKeyWithChapsTokenRequest& request, const std::shared_ptr<RegisterKeyWithChapsTokenReply>& result) { CertifiedKey key; if (!FindKeyByLabel(request.username(), request.key_label(), &key)) { result->set_status(STATUS_INVALID_PARAMETER); return; } if (!key_store_->Register(request.username(), request.key_label(), key.key_type(), key.key_usage(), key.key_blob(), key.public_key(), key.certified_key_credential())) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } if (key.has_intermediate_ca_cert() && !key_store_->RegisterCertificate(request.username(), key.intermediate_ca_cert())) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } for (int i = 0; i < key.additional_intermediate_ca_cert_size(); ++i) { if (!key_store_->RegisterCertificate( request.username(), key.additional_intermediate_ca_cert(i))) { result->set_status(STATUS_UNEXPECTED_DEVICE_ERROR); return; } } DeleteKey(request.username(), request.key_label()); } bool AttestationService::IsPreparedForEnrollment() { if (!tpm_utility_->IsTpmReady()) { return false; } auto database_pb = database_->GetProtobuf(); if (!database_pb.has_credentials()) { return false; } return ( database_pb.credentials().has_endorsement_credential() || database_pb.credentials().has_default_encrypted_endorsement_credential()); } bool AttestationService::IsEnrolled() { auto database_pb = database_->GetProtobuf(); return database_pb.has_identity_key() && database_pb.identity_key().has_identity_credential(); } bool AttestationService::CreateEnrollRequest(std::string* enroll_request) { if (!IsPreparedForEnrollment()) { LOG(ERROR) << __func__ << ": Enrollment is not possible, attestation data " << "does not exist."; return false; } auto database_pb = database_->GetProtobuf(); AttestationEnrollmentRequest request_pb; *request_pb.mutable_encrypted_endorsement_credential() = database_pb.credentials().default_encrypted_endorsement_credential(); request_pb.set_identity_public_key( database_pb.identity_binding().identity_public_key()); *request_pb.mutable_pcr0_quote() = database_pb.pcr0_quote(); *request_pb.mutable_pcr1_quote() = database_pb.pcr1_quote(); if (!request_pb.SerializeToString(enroll_request)) { LOG(ERROR) << __func__ << ": Failed to serialize protobuf."; return false; } return true; } bool AttestationService::FinishEnroll(const std::string& enroll_response, std::string* server_error) { if (!tpm_utility_->IsTpmReady()) { return false; } AttestationEnrollmentResponse response_pb; if (!response_pb.ParseFromString(enroll_response)) { LOG(ERROR) << __func__ << ": Failed to parse response from CA."; return false; } if (response_pb.status() != OK) { *server_error = response_pb.detail(); LOG(ERROR) << __func__ << ": Error received from CA: " << response_pb.detail(); return false; } std::string credential; auto database_pb = database_->GetProtobuf(); if (!tpm_utility_->ActivateIdentity( database_pb.delegate().blob(), database_pb.delegate().secret(), database_pb.identity_key().identity_key_blob(), response_pb.encrypted_identity_credential().asym_ca_contents(), response_pb.encrypted_identity_credential().sym_ca_attestation(), &credential)) { LOG(ERROR) << __func__ << ": Failed to activate identity."; return false; } database_->GetMutableProtobuf() ->mutable_identity_key() ->set_identity_credential(credential); if (!database_->SaveChanges()) { LOG(ERROR) << __func__ << ": Failed to persist database changes."; return false; } LOG(INFO) << "Attestation: Enrollment complete."; return true; } bool AttestationService::CreateCertificateRequest( const std::string& username, const CertifiedKey& key, CertificateProfile profile, const std::string& origin, std::string* certificate_request, std::string* message_id) { if (!tpm_utility_->IsTpmReady()) { return false; } if (!IsEnrolled()) { LOG(ERROR) << __func__ << ": Device is not enrolled for attestation."; return false; } AttestationCertificateRequest request_pb; if (!crypto_utility_->GetRandom(kNonceSize, message_id)) { LOG(ERROR) << __func__ << ": GetRandom(message_id) failed."; return false; } request_pb.set_message_id(*message_id); auto database_pb = database_->GetProtobuf(); request_pb.set_identity_credential( database_pb.identity_key().identity_credential()); request_pb.set_profile(profile); if (!origin.empty() && (profile == CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID)) { request_pb.set_origin(origin); request_pb.set_temporal_index(ChooseTemporalIndex(username, origin)); } request_pb.set_certified_public_key(key.public_key_tpm_format()); request_pb.set_certified_key_info(key.certified_key_info()); request_pb.set_certified_key_proof(key.certified_key_proof()); if (!request_pb.SerializeToString(certificate_request)) { LOG(ERROR) << __func__ << ": Failed to serialize protobuf."; return false; } return true; } bool AttestationService::FinishCertificateRequest( const std::string& certificate_response, const std::string& username, const std::string& key_label, const std::string& message_id, CertifiedKey* key, std::string* certificate_chain, std::string* server_error) { if (!tpm_utility_->IsTpmReady()) { return false; } AttestationCertificateResponse response_pb; if (!response_pb.ParseFromString(certificate_response)) { LOG(ERROR) << __func__ << ": Failed to parse response from Privacy CA."; return false; } if (response_pb.status() != OK) { *server_error = response_pb.detail(); LOG(ERROR) << __func__ << ": Error received from Privacy CA: " << response_pb.detail(); return false; } if (message_id != response_pb.message_id()) { LOG(ERROR) << __func__ << ": Message ID mismatch."; return false; } // Finish populating the CertifiedKey protobuf and store it. key->set_certified_key_credential(response_pb.certified_key_credential()); key->set_intermediate_ca_cert(response_pb.intermediate_ca_cert()); key->mutable_additional_intermediate_ca_cert()->MergeFrom( response_pb.additional_intermediate_ca_cert()); if (!SaveKey(username, key_label, *key)) { return false; } LOG(INFO) << "Attestation: Certified key credential received and stored."; *certificate_chain = CreatePEMCertificateChain(*key); return true; } bool AttestationService::SendACARequestAndBlock(ACARequestType request_type, const std::string& request, std::string* reply) { std::shared_ptr<brillo::http::Transport> transport = http_transport_; if (!transport) { transport = brillo::http::Transport::CreateDefault(); } std::unique_ptr<brillo::http::Response> response = PostBinaryAndBlock( GetACAURL(request_type), request.data(), request.size(), brillo::mime::application::kOctet_stream, {}, // headers transport, nullptr); // error if (!response || !response->IsSuccessful()) { LOG(ERROR) << "HTTP request to Attestation CA failed."; return false; } *reply = response->ExtractDataAsString(); return true; } bool AttestationService::FindKeyByLabel(const std::string& username, const std::string& key_label, CertifiedKey* key) { if (!username.empty()) { std::string key_data; if (!key_store_->Read(username, key_label, &key_data)) { LOG(INFO) << "Key not found: " << key_label; return false; } if (key && !key->ParseFromString(key_data)) { LOG(ERROR) << "Failed to parse key: " << key_label; return false; } return true; } auto database_pb = database_->GetProtobuf(); for (int i = 0; i < database_pb.device_keys_size(); ++i) { if (database_pb.device_keys(i).key_name() == key_label) { *key = database_pb.device_keys(i); return true; } } LOG(INFO) << "Key not found: " << key_label; return false; } bool AttestationService::CreateKey(const std::string& username, const std::string& key_label, KeyType key_type, KeyUsage key_usage, CertifiedKey* key) { std::string nonce; if (!crypto_utility_->GetRandom(kNonceSize, &nonce)) { LOG(ERROR) << __func__ << ": GetRandom(nonce) failed."; return false; } std::string key_blob; std::string public_key; std::string public_key_tpm_format; std::string key_info; std::string proof; auto database_pb = database_->GetProtobuf(); if (!tpm_utility_->CreateCertifiedKey( key_type, key_usage, database_pb.identity_key().identity_key_blob(), nonce, &key_blob, &public_key, &public_key_tpm_format, &key_info, &proof)) { return false; } key->set_key_blob(key_blob); key->set_public_key(public_key); key->set_key_name(key_label); key->set_public_key_tpm_format(public_key_tpm_format); key->set_certified_key_info(key_info); key->set_certified_key_proof(proof); return SaveKey(username, key_label, *key); } bool AttestationService::SaveKey(const std::string& username, const std::string& key_label, const CertifiedKey& key) { if (!username.empty()) { std::string key_data; if (!key.SerializeToString(&key_data)) { LOG(ERROR) << __func__ << ": Failed to serialize protobuf."; return false; } if (!key_store_->Write(username, key_label, key_data)) { LOG(ERROR) << __func__ << ": Failed to store certified key for user."; return false; } } else { if (!AddDeviceKey(key_label, key)) { LOG(ERROR) << __func__ << ": Failed to store certified key for device."; return false; } } return true; } void AttestationService::DeleteKey(const std::string& username, const std::string& key_label) { if (!username.empty()) { key_store_->Delete(username, key_label); } else { RemoveDeviceKey(key_label); } } bool AttestationService::AddDeviceKey(const std::string& key_label, const CertifiedKey& key) { // If a key by this name already exists, reuse the field. auto* database_pb = database_->GetMutableProtobuf(); bool found = false; for (int i = 0; i < database_pb->device_keys_size(); ++i) { if (database_pb->device_keys(i).key_name() == key_label) { found = true; *database_pb->mutable_device_keys(i) = key; break; } } if (!found) *database_pb->add_device_keys() = key; return database_->SaveChanges(); } void AttestationService::RemoveDeviceKey(const std::string& key_label) { auto* database_pb = database_->GetMutableProtobuf(); bool found = false; for (int i = 0; i < database_pb->device_keys_size(); ++i) { if (database_pb->device_keys(i).key_name() == key_label) { found = true; int last = database_pb->device_keys_size() - 1; if (i < last) { database_pb->mutable_device_keys()->SwapElements(i, last); } database_pb->mutable_device_keys()->RemoveLast(); break; } } if (found) { if (!database_->SaveChanges()) { LOG(WARNING) << __func__ << ": Failed to persist key deletion."; } } } std::string AttestationService::CreatePEMCertificateChain( const CertifiedKey& key) { if (key.certified_key_credential().empty()) { LOG(WARNING) << "Certificate is empty."; return std::string(); } std::string pem = CreatePEMCertificate(key.certified_key_credential()); if (!key.intermediate_ca_cert().empty()) { pem += "\n"; pem += CreatePEMCertificate(key.intermediate_ca_cert()); } for (int i = 0; i < key.additional_intermediate_ca_cert_size(); ++i) { pem += "\n"; pem += CreatePEMCertificate(key.additional_intermediate_ca_cert(i)); } return pem; } std::string AttestationService::CreatePEMCertificate( const std::string& certificate) { const char kBeginCertificate[] = "-----BEGIN CERTIFICATE-----\n"; const char kEndCertificate[] = "-----END CERTIFICATE-----"; std::string pem = kBeginCertificate; pem += brillo::data_encoding::Base64EncodeWrapLines(certificate); pem += kEndCertificate; return pem; } int AttestationService::ChooseTemporalIndex(const std::string& user, const std::string& origin) { std::string user_hash = crypto::SHA256HashString(user); std::string origin_hash = crypto::SHA256HashString(origin); int histogram[kNumTemporalValues] = {}; auto database_pb = database_->GetProtobuf(); for (int i = 0; i < database_pb.temporal_index_record_size(); ++i) { const AttestationDatabase::TemporalIndexRecord& record = database_pb.temporal_index_record(i); // Ignore out-of-range index values. if (record.temporal_index() < 0 || record.temporal_index() >= kNumTemporalValues) continue; if (record.origin_hash() == origin_hash) { if (record.user_hash() == user_hash) { // We've previously chosen this index for this user, reuse it. return record.temporal_index(); } else { // We've previously chosen this index for another user. ++histogram[record.temporal_index()]; } } } int least_used_index = 0; for (int i = 1; i < kNumTemporalValues; ++i) { if (histogram[i] < histogram[least_used_index]) least_used_index = i; } if (histogram[least_used_index] > 0) { LOG(WARNING) << "Unique origin-specific identifiers have been exhausted."; } // Record our choice for later reference. AttestationDatabase::TemporalIndexRecord* new_record = database_pb.add_temporal_index_record(); new_record->set_origin_hash(origin_hash); new_record->set_user_hash(user_hash); new_record->set_temporal_index(least_used_index); database_->SaveChanges(); return least_used_index; } std::string AttestationService::GetACAURL(ACARequestType request_type) const { std::string url = attestation_ca_origin_; switch (request_type) { case kEnroll: url += "/enroll"; break; case kGetCertificate: url += "/sign"; break; default: NOTREACHED(); } return url; } bool AttestationService::GetSubjectPublicKeyInfo( KeyType key_type, const std::string& public_key, std::string* public_key_info) const { // Only RSA is supported currently. if (key_type != KEY_TYPE_RSA) { return false; } return crypto_utility_->GetRSASubjectPublicKeyInfo(public_key, public_key_info); } base::WeakPtr<AttestationService> AttestationService::GetWeakPtr() { return weak_factory_.GetWeakPtr(); } } // namespace attestation