attestation: Implemented CreateGoogleAttestedKey.
Only mock dependencies currently exist so this code is limited to unit
tests.
BUG=brillo:737
TEST=unit
Change-Id: Ia23d0b6afdde9d7ce70a1363d143d562236f2fff
Reviewed-on: https://chromium-review.googlesource.com/265866
Reviewed-by: Darren Krahn <dkrahn@chromium.org>
Commit-Queue: Darren Krahn <dkrahn@chromium.org>
Tested-by: Darren Krahn <dkrahn@chromium.org>
diff --git a/attestation.gyp b/attestation.gyp
index 11e9f0e..c0e71c3 100644
--- a/attestation.gyp
+++ b/attestation.gyp
@@ -101,13 +101,19 @@
'variables': {
'deps': [
'libchrome-test-<(libbase_ver)',
+ 'libchromeos-test-<(libbase_ver)',
],
},
'sources': [
'attestation_testrunner.cc',
'client/dbus_proxy_test.cc',
+ 'server/attestation_service_test.cc',
'server/database_impl_test.cc',
'server/dbus_service_test.cc',
+ 'server/mock_crypto_utility.cc',
+ 'server/mock_database.cc',
+ 'server/mock_key_store.cc',
+ 'server/mock_tpm_utility.cc',
],
'dependencies': [
'client_library',
diff --git a/client/dbus_proxy.cc b/client/dbus_proxy.cc
index 9f507a2..bde48e9 100644
--- a/client/dbus_proxy.cc
+++ b/client/dbus_proxy.cc
@@ -41,7 +41,9 @@
KeyType key_type,
KeyUsage key_usage,
CertificateProfile certificate_profile,
- const base::Callback<CreateGoogleAttestedKeyCallback>& callback) {
+ const std::string& username,
+ const std::string& origin,
+ const CreateGoogleAttestedKeyCallback& callback) {
attestation::CreateGoogleAttestedKeyRequest request;
request.set_key_label(key_label);
request.set_key_type(key_type);
@@ -49,12 +51,12 @@
request.set_certificate_profile(certificate_profile);
auto on_success = [callback](
const attestation::CreateGoogleAttestedKeyReply& reply) {
- callback.Run(reply.status(),
- reply.certificate_chain(),
- reply.server_error());
+ callback.Run(reply.certificate_chain(),
+ reply.server_error(),
+ reply.status());
};
auto on_error = [callback](chromeos::Error* error) {
- callback.Run(NOT_AVAILABLE, std::string(), std::string());
+ callback.Run(std::string(), std::string(), STATUS_NOT_AVAILABLE);
};
chromeos::dbus_utils::CallMethodWithTimeout(
kDBusTimeoutMS,
diff --git a/client/dbus_proxy.h b/client/dbus_proxy.h
index 7d3d135..c8197ba 100644
--- a/client/dbus_proxy.h
+++ b/client/dbus_proxy.h
@@ -33,7 +33,9 @@
KeyType key_type,
KeyUsage key_usage,
CertificateProfile certificate_profile,
- const base::Callback<CreateGoogleAttestedKeyCallback>& callback) override;
+ const std::string& username,
+ const std::string& origin,
+ const CreateGoogleAttestedKeyCallback& callback) override;
// Useful for testing.
void set_object_proxy(dbus::ObjectProxy* object_proxy) {
diff --git a/client/dbus_proxy_test.cc b/client/dbus_proxy_test.cc
index bc1920a..a48c1c5 100644
--- a/client/dbus_proxy_test.cc
+++ b/client/dbus_proxy_test.cc
@@ -49,7 +49,7 @@
dbus::Response::CreateEmpty();
dbus::MessageWriter writer(response.get());
CreateGoogleAttestedKeyReply reply_proto;
- reply_proto.set_status(SUCCESS);
+ reply_proto.set_status(STATUS_SUCCESS);
reply_proto.set_certificate_chain("certificate");
reply_proto.set_server_error("server_error");
writer.AppendProtoAsArrayOfBytes(reply_proto);
@@ -60,16 +60,19 @@
.WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
// Set expectations on the outputs.
- auto callback = [](AttestationStatus status,
- const std::string& certificate,
- const std::string& server_error) {
- EXPECT_EQ(SUCCESS, status);
+ int callback_count = 0;
+ auto callback = [&callback_count](const std::string& certificate,
+ const std::string& server_error,
+ AttestationStatus status) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, status);
EXPECT_EQ("certificate", certificate);
EXPECT_EQ("server_error", server_error);
};
proxy_.CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
- ENTERPRISE_MACHINE_CERTIFICATE,
+ ENTERPRISE_MACHINE_CERTIFICATE, "", "",
base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
}
} // namespace attestation
diff --git a/client/main.cc b/client/main.cc
index b97e9a6..a432455 100644
--- a/client/main.cc
+++ b/client/main.cc
@@ -73,10 +73,10 @@
}
void HandleCreateGoogleAttestedKeyReply(
- attestation::AttestationStatus status,
const std::string& certificate,
- const std::string& server_error_details) {
- if (status == attestation::SUCCESS) {
+ const std::string& server_error_details,
+ attestation::AttestationStatus status) {
+ if (status == attestation::STATUS_SUCCESS) {
printf("Success!\n");
} else {
printf("Error occurred: %d.\n", status);
@@ -93,6 +93,8 @@
attestation::KEY_TYPE_RSA,
attestation::KEY_USAGE_SIGN,
attestation::ENTERPRISE_MACHINE_CERTIFICATE,
+ "", // username
+ "", // origin
base::Bind(&ClientLoop::HandleCreateGoogleAttestedKeyReply,
weak_factory_.GetWeakPtr()));
}
diff --git a/common/attestation_interface.h b/common/attestation_interface.h
index 58a52db..bf68121 100644
--- a/common/attestation_interface.h
+++ b/common/attestation_interface.h
@@ -35,17 +35,22 @@
// The Google Attestation CA root certificate is well-known and not included.
// If the CA refuses to issue a certificate, |status| will be
// REQUEST_DENIED_BY_CA and |server_error_details| will contain an error
- // message from the CA.
+ // message from the CA. On success both the key and certificate are associated
+ // with |username|, which must be a canonical email address or the empty
+ // string (in which case it will be associated to the device). The |origin|
+ // parameter is passed to the CA; it is required by some certificate profiles.
using CreateGoogleAttestedKeyCallback =
- void(AttestationStatus status,
- const std::string& certificate_chain,
- const std::string& server_error_details);
+ base::Callback<void(const std::string& certificate_chain,
+ const std::string& server_error_details,
+ AttestationStatus status)>;
virtual void CreateGoogleAttestedKey(
const std::string& key_label,
KeyType key_type,
KeyUsage key_usage,
CertificateProfile certificate_profile,
- const base::Callback<CreateGoogleAttestedKeyCallback>& callback) = 0;
+ const std::string& username,
+ const std::string& origin,
+ const CreateGoogleAttestedKeyCallback& callback) = 0;
};
} // namespace attestation
diff --git a/common/interface.proto b/common/interface.proto
index b7337f9..8d6b667 100644
--- a/common/interface.proto
+++ b/common/interface.proto
@@ -9,13 +9,14 @@
import "common.proto";
enum AttestationStatus {
- SUCCESS = 0;
- UNEXPECTED_DEVICE_ERROR = 1;
- NOT_AVAILABLE = 2;
- NOT_READY = 3;
- NOT_ALLOWED = 4;
- INVALID_PARAMETER = 5;
- REQUEST_DENIED_BY_CA = 6;
+ STATUS_SUCCESS = 0;
+ STATUS_UNEXPECTED_DEVICE_ERROR = 1;
+ STATUS_NOT_AVAILABLE = 2;
+ STATUS_NOT_READY = 3;
+ STATUS_NOT_ALLOWED = 4;
+ STATUS_INVALID_PARAMETER = 5;
+ STATUS_REQUEST_DENIED_BY_CA = 6;
+ STATUS_CA_NOT_AVAILABLE = 7;
}
// Describes key type.
@@ -37,6 +38,13 @@
optional KeyUsage key_usage = 3;
// Describes the certificate to be requested of the CA.
optional CertificateProfile certificate_profile = 4;
+ // Provided if the new key should be accessible only by a particular user. If
+ // this field is not set or is the empty string, the key will be accessible
+ // system-wide.
+ optional string username = 5;
+ // If the |certificate_profile| is intended to be bound to a particular origin
+ // this field specifies the origin. For most profiles this is not required.
+ optional string origin = 6;
}
message CreateGoogleAttestedKeyReply {
diff --git a/common/mock_attestation_interface.h b/common/mock_attestation_interface.h
index 2dcca1f..5b8c485 100644
--- a/common/mock_attestation_interface.h
+++ b/common/mock_attestation_interface.h
@@ -19,12 +19,14 @@
virtual ~MockAttestationInterface() = default;
MOCK_METHOD0(Initialize, bool());
- MOCK_METHOD5(CreateGoogleAttestedKey, void(
+ MOCK_METHOD7(CreateGoogleAttestedKey, void(
const std::string&,
KeyType,
KeyUsage,
CertificateProfile,
- const base::Callback<CreateGoogleAttestedKeyCallback>&));
+ const std::string&,
+ const std::string&,
+ const CreateGoogleAttestedKeyCallback&));
};
} // namespace attestation
diff --git a/server/attestation_service.cc b/server/attestation_service.cc
index 1ecea6b..3c65c2a 100644
--- a/server/attestation_service.cc
+++ b/server/attestation_service.cc
@@ -4,18 +4,48 @@
#include "attestation/server/attestation_service.h"
-#include <base/callback.h>
+#include <string>
+#include <base/callback.h>
+#include <chromeos/bind_lambda.h>
+#include <chromeos/data_encoding.h>
+#include <chromeos/http/http_utils.h>
+#include <chromeos/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_->Start();
if (!database_) {
- default_database_.reset(new DatabaseImpl(crypto_));
+ default_database_.reset(new DatabaseImpl(crypto_utility_));
+ if (!default_database_->Initialize()) {
+ LOG(WARNING) << "Creating new attestation database.";
+ }
database_ = default_database_.get();
}
- LOG(INFO) << "Attestation service started.";
return true;
}
@@ -24,8 +54,522 @@
KeyType key_type,
KeyUsage key_usage,
CertificateProfile certificate_profile,
- const base::Callback<CreateGoogleAttestedKeyCallback>& callback) {
- callback.Run(NOT_AVAILABLE, std::string(), std::string());
+ const std::string& username,
+ const std::string& origin,
+ const CreateGoogleAttestedKeyCallback& callback) {
+ auto result = std::make_shared<CreateGoogleAttestedKeyTaskResult>();
+ base::Closure task = base::Bind(
+ &AttestationService::CreateGoogleAttestedKeyTask,
+ base::Unretained(this),
+ key_label,
+ key_type,
+ key_usage,
+ certificate_profile,
+ username,
+ origin,
+ result);
+ base::Closure reply = base::Bind(
+ &AttestationService::CreateGoogleAttestedKeyTaskCallback,
+ GetWeakPtr(),
+ callback,
+ result);
+ worker_thread_->task_runner()->PostTaskAndReply(FROM_HERE, task, reply);
+}
+
+void AttestationService::CreateGoogleAttestedKeyTask(
+ const std::string& key_label,
+ KeyType key_type,
+ KeyUsage key_usage,
+ CertificateProfile certificate_profile,
+ const std::string& username,
+ const std::string& origin,
+ const std::shared_ptr<CreateGoogleAttestedKeyTaskResult>& result) {
+ LOG(INFO) << "Creating attested key: " << key_label;
+ if (!IsPreparedForEnrollment()) {
+ LOG(ERROR) << "Attestation: TPM is not ready.";
+ result->status = STATUS_NOT_READY;
+ return;
+ }
+ if (!IsEnrolled()) {
+ std::string enroll_request;
+ if (!CreateEnrollRequest(&enroll_request)) {
+ result->status = STATUS_UNEXPECTED_DEVICE_ERROR;
+ return;
+ }
+ std::string enroll_reply;
+ if (!SendACARequestAndBlock(kEnroll,
+ enroll_request,
+ &enroll_reply)) {
+ result->status = STATUS_CA_NOT_AVAILABLE;
+ return;
+ }
+ std::string server_error;
+ if (!FinishEnroll(enroll_reply, &result->server_error_details)) {
+ result->status = STATUS_REQUEST_DENIED_BY_CA;
+ return;
+ }
+ }
+ if (!FindKeyByLabel(username, key_label, nullptr)) {
+ if (!CreateKey(username, key_label, key_type, key_usage)) {
+ result->status = STATUS_UNEXPECTED_DEVICE_ERROR;
+ return;
+ }
+ }
+ std::string certificate_request;
+ std::string message_id;
+ if (!CreateCertificateRequest(username,
+ key_label,
+ certificate_profile,
+ origin,
+ &certificate_request,
+ &message_id)) {
+ result->status = STATUS_UNEXPECTED_DEVICE_ERROR;
+ return;
+ }
+ std::string certificate_reply;
+ if (!SendACARequestAndBlock(kGetCertificate,
+ certificate_request,
+ &certificate_reply)) {
+ result->status = STATUS_CA_NOT_AVAILABLE;
+ return;
+ }
+ std::string certificate_chain;
+ std::string server_error;
+ if (!FinishCertificateRequest(certificate_reply,
+ username,
+ key_label,
+ message_id,
+ &result->certificate_chain,
+ &result->server_error_details)) {
+ result->status = STATUS_REQUEST_DENIED_BY_CA;
+ return;
+ }
+}
+
+void AttestationService::CreateGoogleAttestedKeyTaskCallback(
+ const CreateGoogleAttestedKeyCallback& callback,
+ const std::shared_ptr<CreateGoogleAttestedKeyTaskResult>& result) {
+ callback.Run(result->certificate_chain,
+ result->server_error_details,
+ result->status);
+}
+
+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 std::string& key_label,
+ 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));
+ }
+ std::string public_key;
+ std::string key_info;
+ std::string proof;
+ if (!CertifyKey(username, key_label, &public_key, &key_info, &proof)) {
+ return false;
+ }
+ request_pb.set_certified_public_key(public_key);
+ request_pb.set_certified_key_info(key_info);
+ request_pb.set_certified_key_proof(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,
+ 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;
+ }
+ CertifiedKey certified_key_pb;
+ if (!FindKeyByLabel(username, key_label, &certified_key_pb)) {
+ LOG(ERROR) << __func__ << ": Key not found.";
+ return false;
+ }
+
+ // Finish populating the CertifiedKey protobuf and store it.
+ 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());
+ if (!SaveKey(username, key_label, certified_key_pb)) {
+ return false;
+ }
+ LOG(INFO) << "Attestation: Certified key credential received and stored.";
+ *certificate_chain = CreatePEMCertificateChain(certified_key_pb);
+ return true;
+}
+
+bool AttestationService::SendACARequestAndBlock(ACARequestType request_type,
+ const std::string& request,
+ std::string* reply) {
+ std::shared_ptr<chromeos::http::Transport> transport = http_transport_;
+ if (!transport) {
+ transport = chromeos::http::Transport::CreateDefault();
+ }
+ std::unique_ptr<chromeos::http::Response> response = PostBinaryAndBlock(
+ GetACAURL(request_type),
+ request.data(),
+ request.size(),
+ chromeos::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) {
+ std::string key_blob;
+ std::string public_key;
+ if (!tpm_utility_->GenerateKey(key_type, key_usage, &key_blob, &public_key)) {
+ return false;
+ }
+ CertifiedKey key;
+ key.set_key_blob(key_blob);
+ key.set_public_key(public_key);
+ key.set_key_name(key_label);
+ 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.";
+ }
+ }
+}
+
+bool AttestationService::CertifyKey(const std::string& username,
+ const std::string& key_label,
+ std::string* public_key_tpm_format,
+ std::string* key_info,
+ std::string* proof) {
+ CertifiedKey key;
+ if (!FindKeyByLabel(username, key_label, &key)) {
+ LOG(ERROR) << __func__ << ": Failed to certify key - key not found.";
+ return false;
+ }
+
+ std::string nonce;
+ if (!crypto_utility_->GetRandom(kNonceSize, &nonce)) {
+ LOG(ERROR) << __func__ << ": GetRandom(nonce) failed.";
+ return false;
+ }
+ auto database_pb = database_->GetProtobuf();
+ return tpm_utility_->CertifyKey(
+ key.key_blob(),
+ database_pb.identity_key().identity_key_blob(),
+ nonce,
+ public_key_tpm_format,
+ key_info,
+ proof);
+}
+
+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 += chromeos::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;
+}
+
+base::WeakPtr<AttestationService> AttestationService::GetWeakPtr() {
+ return weak_factory_.GetWeakPtr();
}
} // namespace attestation
diff --git a/server/attestation_service.h b/server/attestation_service.h
index 0bf2d61..04dffe1 100644
--- a/server/attestation_service.h
+++ b/server/attestation_service.h
@@ -10,22 +10,45 @@
#include <memory>
#include <string>
+#include <base/callback.h>
#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <base/threading/thread.h>
+#include <chromeos/bind_lambda.h>
+#include <chromeos/http/http_transport.h>
#include "attestation/server/crypto_utility.h"
#include "attestation/server/database.h"
+#include "attestation/server/database_impl.h"
+#include "attestation/server/key_store.h"
+#include "attestation/server/tpm_utility.h"
namespace attestation {
// An implementation of AttestationInterface for the core attestation service.
+// Access to TPM, network and local file-system resources occurs asynchronously
+// with the exception of Initialize(). All methods must be called on the same
+// thread that originally called Initialize().
// Usage:
// std::unique_ptr<AttestationInterface> attestation =
// new AttestationService();
// CHECK(attestation->Initialize());
// attestation->CreateGoogleAttestedKey(...);
+//
+// THREADING NOTES:
+// This class runs a worker thread and delegates all calls to it. This keeps the
+// public methods non-blocking while allowing complex implementation details
+// with dependencies on the TPM, network, and filesystem to be coded in a more
+// readable way. It also serves to serialize method execution which reduces
+// complexity with TPM state.
+//
+// Tasks that run on the worker thread are bound with base::Unretained which is
+// safe because the thread is owned by this class (so it is guaranteed not to
+// process a task after destruction). Weak pointers are used to post replies
+// back to the main thread.
class AttestationService : public AttestationInterface {
public:
- AttestationService() = default;
+ AttestationService();
~AttestationService() override = default;
// AttestationInterface methods.
@@ -35,17 +58,187 @@
KeyType key_type,
KeyUsage key_usage,
CertificateProfile certificate_profile,
- const base::Callback<CreateGoogleAttestedKeyCallback>& callback) override;
+ const std::string& username,
+ const std::string& origin,
+ const CreateGoogleAttestedKeyCallback& callback) override;
- // Useful for testing.
+ // Mutators useful for testing.
+ void set_crypto_utility(CryptoUtility* crypto_utility) {
+ crypto_utility_ = crypto_utility;
+ }
+
void set_database(Database* database) {
database_ = database;
}
+ void set_http_transport(
+ const std::shared_ptr<chromeos::http::Transport>& transport) {
+ http_transport_ = transport;
+ }
+
+ void set_key_store(KeyStore* key_store) {
+ key_store_ = key_store;
+ }
+
+ void set_tpm_utility(TpmUtility* tpm_utility) {
+ tpm_utility_ = tpm_utility;
+ }
+
+ // So tests don't need to duplicate URL decisions.
+ const std::string& attestation_ca_origin() {
+ return attestation_ca_origin_;
+ }
+
private:
- CryptoUtility* crypto_;
+ enum ACARequestType {
+ kEnroll, // Enrolls a device, certifying an identity key.
+ kGetCertificate, // Issues a certificate for a TPM-backed key.
+ };
+
+ struct CreateGoogleAttestedKeyTaskResult {
+ AttestationStatus status{STATUS_SUCCESS};
+ std::string certificate_chain;
+ std::string server_error_details;
+ };
+
+ // A synchronous implementation of CreateGoogleAttestedKey appropriate to run
+ // on the worker thread.
+ void CreateGoogleAttestedKeyTask(
+ const std::string& key_label,
+ KeyType key_type,
+ KeyUsage key_usage,
+ CertificateProfile certificate_profile,
+ const std::string& username,
+ const std::string& origin,
+ const std::shared_ptr<CreateGoogleAttestedKeyTaskResult>& result);
+
+ // A callback for CreateGoogleAttestedKeyTask that invokes the original
+ // |callback| with the given |result|. Having this relay allows us to use weak
+ // pointer semantics to cancel callbacks.
+ void CreateGoogleAttestedKeyTaskCallback(
+ const CreateGoogleAttestedKeyCallback& callback,
+ const std::shared_ptr<CreateGoogleAttestedKeyTaskResult>& result);
+
+ // Returns true iff all information required for enrollment with the Google
+ // Attestation CA is available.
+ bool IsPreparedForEnrollment();
+
+ // Returns true iff enrollment with the Google Attestation CA has been
+ // completed.
+ bool IsEnrolled();
+
+ // Creates an enrollment request compatible with the Google Attestation CA.
+ // Returns true on success.
+ bool CreateEnrollRequest(std::string* enroll_request);
+
+ // Finishes enrollment given an |enroll_response| from the Google Attestation
+ // CA. Returns true on success. On failure, returns false and sets
+ // |server_error| to the error string from the CA.
+ bool FinishEnroll(const std::string& enroll_response,
+ std::string* server_error);
+
+ // Creates a |certificate_request| compatible with the Google Attestation CA
+ // for the key identified by |username| and |key_label|, according to the
+ // given |profile| and |origin.
+ bool CreateCertificateRequest(const std::string& username,
+ const std::string& key_label,
+ CertificateProfile profile,
+ const std::string& origin,
+ std::string* certificate_request,
+ std::string* message_id);
+
+ // Finishes a certificate request by decoding the |certificate_response| to
+ // recover the |certificate_chain| and storing it in association with the key
+ // identified by |username| and |key_label|. Returns true on success. On
+ // failure, returns false and sets |server_error| to the error string from the
+ // CA.
+ bool FinishCertificateRequest(const std::string& certificate_response,
+ const std::string& username,
+ const std::string& key_label,
+ const std::string& message_id,
+ std::string* certficate_chain,
+ std::string* server_error);
+
+ // Sends a |request_type| |request| to the Google Attestation CA and waits for
+ // the |reply|. Returns true on success.
+ bool SendACARequestAndBlock(ACARequestType request_type,
+ const std::string& request,
+ std::string* reply);
+
+ // Creates a new certifiable key for |username| with the given |key_label|,
+ // |key_type|, and |key_usage|.
+ bool CreateKey(const std::string& username,
+ const std::string& key_label,
+ KeyType key_type,
+ KeyUsage key_usage);
+
+ // Finds the |key| associated with |username| and |key_label|. Returns false
+ // if such a key does not exist.
+ bool FindKeyByLabel(const std::string& username,
+ const std::string& key_label,
+ CertifiedKey* key);
+
+ // Saves the |key| associated with |username| and |key_label|. Returns true on
+ // success.
+ bool SaveKey(const std::string& username,
+ const std::string& key_label,
+ const CertifiedKey& key);
+
+ // Deletes the key associated with |username| and |key_label|.
+ void DeleteKey(const std::string& username,
+ const std::string& key_label);
+
+ // Adds named device-wide key to the attestation database.
+ bool AddDeviceKey(const std::string& key_label, const CertifiedKey& key);
+
+ // Removes a device-wide key from the attestation database.
+ void RemoveDeviceKey(const std::string& key_label);
+
+ // Certifies the key associated with |username| and |key_label| with the
+ // Attestation Identity Key (AIK). On success returns true and populates
+ // outputs as required by the CA (see AttestationCertificateRequest).
+ bool CertifyKey(const std::string& username,
+ const std::string& key_label,
+ std::string* public_key_tpm_format,
+ std::string* key_info,
+ std::string* proof);
+
+ // Creates a PEM certificate chain from the credential fields of a |key|.
+ std::string CreatePEMCertificateChain(const CertifiedKey& key);
+
+ // Creates a certificate in PEM format from a DER encoded X.509 certificate.
+ std::string CreatePEMCertificate(const std::string& certificate);
+
+ // Chooses a temporal index which will be used by the ACA 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);
+
+ // Creates a Google Attestation CA URL for the given |request_type|.
+ std::string GetACAURL(ACARequestType request_type) const;
+
+ base::WeakPtr<AttestationService> GetWeakPtr();
+
+
+ const std::string attestation_ca_origin_;
+
+ // Other than initialization and destruction, these are used only by the
+ // worker thread.
+ CryptoUtility* crypto_utility_;
Database* database_;
- std::unique_ptr<Database> default_database_;
+ std::unique_ptr<DatabaseImpl> default_database_;
+ std::shared_ptr<chromeos::http::Transport> http_transport_;
+ KeyStore* key_store_;
+ TpmUtility* tpm_utility_;
+
+ // All work is done in the background. This serves to serialize requests and
+ // allow synchronous implementation of complex methods. This is intentionally
+ // declared after the thread-owned members.
+ std::unique_ptr<base::Thread> worker_thread_;
+
+ // Declared last so any weak pointers are destroyed first.
+ base::WeakPtrFactory<AttestationService> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(AttestationService);
};
diff --git a/server/attestation_service_test.cc b/server/attestation_service_test.cc
new file mode 100644
index 0000000..9447e86
--- /dev/null
+++ b/server/attestation_service_test.cc
@@ -0,0 +1,548 @@
+// Copyright 2015 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 <string>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/message_loop/message_loop.h>
+#include <base/run_loop.h>
+#include <chromeos/bind_lambda.h>
+#include <chromeos/data_encoding.h>
+#include <chromeos/http/http_transport_fake.h>
+#include <chromeos/mime_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "attestation/common/attestation_ca.pb.h"
+#include "attestation/server/attestation_service.h"
+#include "attestation/server/mock_crypto_utility.h"
+#include "attestation/server/mock_database.h"
+#include "attestation/server/mock_key_store.h"
+#include "attestation/server/mock_tpm_utility.h"
+
+using chromeos::http::fake::ServerRequest;
+using chromeos::http::fake::ServerResponse;
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+namespace attestation {
+
+class AttestationServiceTest : public testing::Test {
+ public:
+ enum FakeCAState {
+ kSuccess, // Valid successful response.
+ kCommandFailure, // Valid error response.
+ kHttpFailure, // Responds with an HTTP error.
+ kBadMessageID, // Valid successful response but a message ID mismatch.
+ };
+
+ ~AttestationServiceTest() override = default;
+ void SetUp() override {
+ service_.reset(new AttestationService);
+ service_->set_database(&mock_database_);
+ service_->set_crypto_utility(&mock_crypto_utility_);
+ fake_http_transport_ = std::make_shared<chromeos::http::fake::Transport>();
+ service_->set_http_transport(fake_http_transport_);
+ service_->set_key_store(&mock_key_store_);
+ service_->set_tpm_utility(&mock_tpm_utility_);
+ // Setup a fake EK certificate by default.
+ mock_database_.GetMutableProtobuf()->mutable_credentials()->
+ set_endorsement_credential("ek_cert");
+ // Setup a fake Attestation CA for success by default.
+ SetupFakeCAEnroll(kSuccess);
+ SetupFakeCASign(kSuccess);
+ CHECK(service_->Initialize());
+ }
+
+ void SetupFakeCAEnroll(FakeCAState state) {
+ fake_http_transport_->AddHandler(
+ service_->attestation_ca_origin() + "/enroll",
+ chromeos::http::request_type::kPost,
+ base::Bind(&AttestationServiceTest::FakeCAEnroll,
+ base::Unretained(this),
+ state));
+ }
+
+ void SetupFakeCASign(FakeCAState state) {
+ fake_http_transport_->AddHandler(
+ service_->attestation_ca_origin() + "/sign",
+ chromeos::http::request_type::kPost,
+ base::Bind(&AttestationServiceTest::FakeCASign,
+ base::Unretained(this),
+ state));
+ }
+
+ std::string GetFakeCertificateChain() {
+ const std::string kBeginCertificate = "-----BEGIN CERTIFICATE-----\n";
+ const std::string kEndCertificate = "-----END CERTIFICATE-----";
+ std::string pem = kBeginCertificate;
+ pem += chromeos::data_encoding::Base64EncodeWrapLines("fake_cert");
+ pem += kEndCertificate + "\n" + kBeginCertificate;
+ pem += chromeos::data_encoding::Base64EncodeWrapLines("fake_ca_cert");
+ pem += kEndCertificate;
+ return pem;
+ }
+
+ void Run() {
+ run_loop_.Run();
+ }
+
+ void RunUntilIdle() {
+ run_loop_.RunUntilIdle();
+ }
+
+ void Quit() {
+ run_loop_.Quit();
+ }
+
+ protected:
+ std::shared_ptr<chromeos::http::fake::Transport> fake_http_transport_;
+ NiceMock<MockCryptoUtility> mock_crypto_utility_;
+ NiceMock<MockDatabase> mock_database_;
+ NiceMock<MockKeyStore> mock_key_store_;
+ NiceMock<MockTpmUtility> mock_tpm_utility_;
+ std::unique_ptr<AttestationService> service_;
+
+ private:
+ void FakeCAEnroll(FakeCAState state,
+ const ServerRequest& request,
+ ServerResponse* response) {
+ AttestationEnrollmentRequest request_pb;
+ EXPECT_TRUE(request_pb.ParseFromString(request.GetDataAsString()));
+ if (state == kHttpFailure) {
+ response->ReplyText(chromeos::http::status_code::NotFound, std::string(),
+ chromeos::mime::application::kOctet_stream);
+ return;
+ }
+ AttestationEnrollmentResponse response_pb;
+ if (state == kCommandFailure) {
+ response_pb.set_status(SERVER_ERROR);
+ response_pb.set_detail("fake_enroll_error");
+ } else if (state == kSuccess) {
+ response_pb.set_status(OK);
+ response_pb.set_detail("");
+ response_pb.mutable_encrypted_identity_credential()->
+ set_asym_ca_contents("1234");
+ response_pb.mutable_encrypted_identity_credential()->
+ set_sym_ca_attestation("5678");
+ } else {
+ NOTREACHED();
+ }
+ std::string tmp;
+ response_pb.SerializeToString(&tmp);
+ response->ReplyText(chromeos::http::status_code::Ok, tmp,
+ chromeos::mime::application::kOctet_stream);
+ }
+
+ void FakeCASign(FakeCAState state,
+ const ServerRequest& request,
+ ServerResponse* response) {
+ AttestationCertificateRequest request_pb;
+ EXPECT_TRUE(request_pb.ParseFromString(request.GetDataAsString()));
+ if (state == kHttpFailure) {
+ response->ReplyText(chromeos::http::status_code::NotFound, std::string(),
+ chromeos::mime::application::kOctet_stream);
+ return;
+ }
+ AttestationCertificateResponse response_pb;
+ if (state == kCommandFailure) {
+ response_pb.set_status(SERVER_ERROR);
+ response_pb.set_detail("fake_sign_error");
+ } else if (state == kSuccess || state == kBadMessageID) {
+ response_pb.set_status(OK);
+ response_pb.set_detail("");
+ if (state == kSuccess) {
+ response_pb.set_message_id(request_pb.message_id());
+ }
+ response_pb.set_certified_key_credential("fake_cert");
+ response_pb.set_intermediate_ca_cert("fake_ca_cert");
+ }
+ std::string tmp;
+ response_pb.SerializeToString(&tmp);
+ response->ReplyText(chromeos::http::status_code::Ok, tmp,
+ chromeos::mime::application::kOctet_stream);
+ }
+
+ base::MessageLoop message_loop_;
+ base::RunLoop run_loop_;
+};
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeySuccess) {
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_EQ(STATUS_SUCCESS, status);
+ EXPECT_EQ(GetFakeCertificateChain(), certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeySuccessNoUser) {
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_EQ(STATUS_SUCCESS, status);
+ EXPECT_EQ(GetFakeCertificateChain(), certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithEnrollHttpError) {
+ SetupFakeCAEnroll(kHttpFailure);
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_EQ(STATUS_CA_NOT_AVAILABLE, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithSignHttpError) {
+ SetupFakeCASign(kHttpFailure);
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_EQ(STATUS_CA_NOT_AVAILABLE, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithCAEnrollFailure) {
+ SetupFakeCAEnroll(kCommandFailure);
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_EQ(STATUS_REQUEST_DENIED_BY_CA, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("fake_enroll_error", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithCASignFailure) {
+ SetupFakeCASign(kCommandFailure);
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_EQ(STATUS_REQUEST_DENIED_BY_CA, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("fake_sign_error", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithBadCAMessageID) {
+ SetupFakeCASign(kBadMessageID);
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_EQ(STATUS_REQUEST_DENIED_BY_CA, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithNoEKCertificate) {
+ // Remove the fake EK certificate.
+ mock_database_.GetMutableProtobuf()->mutable_credentials()->
+ clear_endorsement_credential();
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithRNGFailure) {
+ EXPECT_CALL(mock_crypto_utility_, GetRandom(_, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithRNGFailure2) {
+ EXPECT_CALL(mock_crypto_utility_, GetRandom(_, _))
+ .WillOnce(Return(true))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithDBFailure) {
+ EXPECT_CALL(mock_database_, SaveChanges())
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithDBFailureNoUser) {
+ EXPECT_CALL(mock_database_, SaveChanges())
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithKeyReadFailure) {
+ EXPECT_CALL(mock_key_store_, Read(_, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithKeyWriteFailure) {
+ EXPECT_CALL(mock_key_store_, Write(_, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithTpmNotReady) {
+ EXPECT_CALL(mock_tpm_utility_, IsTpmReady())
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithTpmActivateFailure) {
+ EXPECT_CALL(mock_tpm_utility_, ActivateIdentity(_, _, _, _, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithTpmGenerateFailure) {
+ // Return false once to simulate that a key does not initially exist.
+ EXPECT_CALL(mock_key_store_, Read(_, _, _))
+ .WillOnce(Return(false))
+ .WillRepeatedly(Return(true));
+ EXPECT_CALL(mock_tpm_utility_, GenerateKey(_, _, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithTpmGenerateFailure2) {
+ EXPECT_CALL(mock_tpm_utility_, GenerateKey(_, _, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithTpmCertifyFailure) {
+ EXPECT_CALL(mock_tpm_utility_, CertifyKey(_, _, _, _, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ EXPECT_NE(STATUS_SUCCESS, status);
+ EXPECT_EQ("", certificate_chain);
+ EXPECT_EQ("", server_error);
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyAndCancel) {
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ callback_count++;
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ // Bring down the service, which should cancel any callbacks.
+ service_.reset();
+ EXPECT_EQ(0, callback_count);
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyAndCancel2) {
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const std::string& certificate_chain,
+ const std::string& server_error,
+ AttestationStatus status) {
+ callback_count++;
+ };
+ service_->CreateGoogleAttestedKey("label", KEY_TYPE_ECC, KEY_USAGE_SIGN,
+ ENTERPRISE_MACHINE_CERTIFICATE, "user",
+ "origin", base::Bind(callback));
+ // Give threads a chance to run.
+ base::PlatformThread::Sleep(base::TimeDelta::FromMilliseconds(10));
+ // Bring down the service, which should cancel any callbacks.
+ service_.reset();
+ // Pump the loop to make sure no callbacks were posted.
+ RunUntilIdle();
+ EXPECT_EQ(0, callback_count);
+}
+
+} // namespace attestation
diff --git a/server/crypto_utility.h b/server/crypto_utility.h
index 751632b..0048452 100644
--- a/server/crypto_utility.h
+++ b/server/crypto_utility.h
@@ -14,6 +14,9 @@
public:
virtual ~CryptoUtility() = default;
+ // Generates |num_bytes| of |random_data|. Returns true on success.
+ virtual bool GetRandom(size_t num_bytes, std::string* random_data) const = 0;
+
// Creates a random |aes_key| and seals it to the TPM's PCR0, producing a
// |sealed_key|. Returns true on success.
virtual bool CreateSealedKey(std::string* aes_key,
diff --git a/server/database_impl_test.cc b/server/database_impl_test.cc
index aeb012a..d234c6d 100644
--- a/server/database_impl_test.cc
+++ b/server/database_impl_test.cc
@@ -21,12 +21,6 @@
const char kFakeCredential[] = "1234";
-// For faking encrypt / decrypt operations.
-bool CopyString(const std::string& s1, std::string* s2) {
- *s2 = s1;
- return true;
-}
-
} // namespace
namespace attestation {
@@ -39,14 +33,6 @@
database_.reset(new DatabaseImpl(&mock_crypto_utility_));
database_->set_io(this);
InitializeFakeData();
- ON_CALL(mock_crypto_utility_, CreateSealedKey(_, _))
- .WillByDefault(Return(true));
- ON_CALL(mock_crypto_utility_, EncryptData(_, _, _, _))
- .WillByDefault(WithArgs<0, 3>(Invoke(CopyString)));
- ON_CALL(mock_crypto_utility_, UnsealKey(_, _, _))
- .WillByDefault(Return(true));
- ON_CALL(mock_crypto_utility_, DecryptData(_, _, _))
- .WillByDefault(WithArgs<0, 2>(Invoke(CopyString)));
}
// Fake DatabaseIO::Read.
diff --git a/server/dbus_service.cc b/server/dbus_service.cc
index 45db9b7..7e70731 100644
--- a/server/dbus_service.cc
+++ b/server/dbus_service.cc
@@ -46,14 +46,14 @@
DBusMethodResponse<const CreateGoogleAttestedKeyReply&>>;
// A callback that fills the reply protobuf and sends it.
auto callback = [](const SharedResponsePointer& response,
- AttestationStatus status,
const std::string& certificate_chain,
- const std::string& server_error_details) {
+ const std::string& server_error_details,
+ AttestationStatus status) {
CreateGoogleAttestedKeyReply reply;
reply.set_status(status);
- if (status == SUCCESS) {
+ if (status == STATUS_SUCCESS) {
reply.set_certificate_chain(certificate_chain);
- } else if (status == REQUEST_DENIED_BY_CA) {
+ } else if (status == STATUS_REQUEST_DENIED_BY_CA) {
reply.set_server_error(server_error_details);
}
response->Return(reply);
@@ -63,6 +63,8 @@
request.key_type(),
request.key_usage(),
request.certificate_profile(),
+ request.username(),
+ request.origin(),
base::Bind(callback, SharedResponsePointer(response.release())));
}
diff --git a/server/dbus_service_test.cc b/server/dbus_service_test.cc
index ab3e4e0..9e253b7 100644
--- a/server/dbus_service_test.cc
+++ b/server/dbus_service_test.cc
@@ -62,10 +62,11 @@
TEST_F(DBusServiceTest, CreateGoogleAttestedKeySuccess) {
EXPECT_CALL(mock_service_, CreateGoogleAttestedKey(
- "label", KEY_TYPE_ECC, KEY_USAGE_SIGN, ENTERPRISE_MACHINE_CERTIFICATE, _))
- .WillOnce(WithArgs<4>(Invoke([](const base::Callback<
- AttestationInterface::CreateGoogleAttestedKeyCallback>& callback) {
- callback.Run(SUCCESS, "certificate", "server_error");
+ "label", KEY_TYPE_ECC, KEY_USAGE_SIGN, ENTERPRISE_MACHINE_CERTIFICATE,
+ "username", "origin", _))
+ .WillOnce(WithArgs<6>(Invoke([](const AttestationInterface::
+ CreateGoogleAttestedKeyCallback& callback) {
+ callback.Run("certificate", "server_error", STATUS_SUCCESS);
})));
std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(
kCreateGoogleAttestedKey);
@@ -74,22 +75,25 @@
request_proto.set_key_type(KEY_TYPE_ECC);
request_proto.set_key_usage(KEY_USAGE_SIGN);
request_proto.set_certificate_profile(ENTERPRISE_MACHINE_CERTIFICATE);
+ request_proto.set_username("username");
+ request_proto.set_origin("origin");
dbus::MessageWriter writer(call.get());
writer.AppendProtoAsArrayOfBytes(request_proto);
auto response = CallMethod(call.get());
dbus::MessageReader reader(response.get());
CreateGoogleAttestedKeyReply reply_proto;
EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply_proto));
- EXPECT_EQ(SUCCESS, reply_proto.status());
+ EXPECT_EQ(STATUS_SUCCESS, reply_proto.status());
EXPECT_EQ("certificate", reply_proto.certificate_chain());
EXPECT_EQ("", reply_proto.server_error());
}
TEST_F(DBusServiceTest, CreateGoogleAttestedKeyServerError) {
- EXPECT_CALL(mock_service_, CreateGoogleAttestedKey(_, _, _, _, _))
- .WillOnce(WithArgs<4>(Invoke([](const base::Callback<
- AttestationInterface::CreateGoogleAttestedKeyCallback>& callback) {
- callback.Run(REQUEST_DENIED_BY_CA, "certificate", "server_error");
+ EXPECT_CALL(mock_service_, CreateGoogleAttestedKey(_, _, _, _, _, _, _))
+ .WillOnce(WithArgs<6>(Invoke([](const AttestationInterface::
+ CreateGoogleAttestedKeyCallback& callback) {
+ callback.Run("certificate", "server_error",
+ STATUS_REQUEST_DENIED_BY_CA);
})));
std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(
kCreateGoogleAttestedKey);
@@ -100,18 +104,18 @@
dbus::MessageReader reader(response.get());
CreateGoogleAttestedKeyReply reply_proto;
EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply_proto));
- EXPECT_EQ(REQUEST_DENIED_BY_CA, reply_proto.status());
+ EXPECT_EQ(STATUS_REQUEST_DENIED_BY_CA, reply_proto.status());
EXPECT_EQ("", reply_proto.certificate_chain());
EXPECT_EQ("server_error", reply_proto.server_error());
}
TEST_F(DBusServiceTest, CopyableCallback) {
- EXPECT_CALL(mock_service_, CreateGoogleAttestedKey(_, _, _, _, _))
- .WillOnce(WithArgs<4>(Invoke([](const base::Callback<
- AttestationInterface::CreateGoogleAttestedKeyCallback>& callback) {
+ EXPECT_CALL(mock_service_, CreateGoogleAttestedKey(_, _, _, _, _, _, _))
+ .WillOnce(WithArgs<6>(Invoke([](const AttestationInterface::
+ CreateGoogleAttestedKeyCallback& callback) {
// Copy the callback, then call the original.
- base::Closure copy = base::Bind(callback, SUCCESS, "", "");
- callback.Run(SUCCESS, "", "");
+ base::Closure copy = base::Bind(callback, "", "", STATUS_SUCCESS);
+ callback.Run("", "", STATUS_SUCCESS);
})));
std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(
kCreateGoogleAttestedKey);
diff --git a/server/key_store.h b/server/key_store.h
new file mode 100644
index 0000000..6fc4421
--- /dev/null
+++ b/server/key_store.h
@@ -0,0 +1,66 @@
+// Copyright 2015 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 ATTESTATION_SERVER_KEY_STORE_H_
+#define ATTESTATION_SERVER_KEY_STORE_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+namespace attestation {
+
+// A mock-able key storage interface.
+class KeyStore {
+ public:
+ KeyStore() {}
+ virtual ~KeyStore() {}
+
+ // Reads key data from the store for the key identified by |key_label| and by
+ // |username|. On success true is returned and |key_data| is populated.
+ virtual bool Read(const std::string& username,
+ const std::string& key_label,
+ std::string* key_data) = 0;
+
+ // Writes key data to the store for the key identified by |key_label| and by
+ // |username|. If such a key already exists the existing data will be
+ // overwritten.
+ virtual bool Write(const std::string& username,
+ const std::string& key_label,
+ const std::string& key_data) = 0;
+
+ // Deletes key data for the key identified by |key_label| and by |username|.
+ // Returns false if key data exists but could not be deleted.
+ virtual bool Delete(const std::string& username,
+ const std::string& key_label) = 0;
+
+ // Deletes key data for all keys identified by |key_prefix| and by |username|
+ // Returns false if key data exists but could not be deleted.
+ virtual bool DeleteByPrefix(const std::string& username,
+ const std::string& key_prefix) = 0;
+
+ // Registers a key to be associated with |username|.
+ // The provided |label| will be associated with all registered objects.
+ // |private_key_blob| holds the private key in some opaque format and
+ // |public_key_der| holds the public key in PKCS #1 RSAPublicKey format.
+ // If a non-empty |certificate| is provided it will be registered along with
+ // the key. Returns true on success.
+ virtual bool Register(const std::string& username,
+ const std::string& label,
+ const std::string& private_key_blob,
+ const std::string& public_key_der,
+ const std::string& certificate) = 0;
+
+ // Registers a |certificate| that is not associated to a registered key. The
+ // certificate will be associated with |username|.
+ virtual bool RegisterCertificate(const std::string& username,
+ const std::string& certificate) = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(KeyStore);
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_KEY_STORE_H_
diff --git a/server/mock_crypto_utility.cc b/server/mock_crypto_utility.cc
new file mode 100644
index 0000000..e3655c7
--- /dev/null
+++ b/server/mock_crypto_utility.cc
@@ -0,0 +1,40 @@
+// Copyright 2015 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/server/mock_crypto_utility.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArgs;
+
+namespace {
+
+bool FakeRandom(size_t num_bytes, std::string* output) {
+ *output = std::string(num_bytes, 'A');
+ return true;
+}
+
+bool CopyString(const std::string& s1, std::string* s2) {
+ *s2 = s1;
+ return true;
+}
+
+} // namespace
+
+namespace attestation {
+
+MockCryptoUtility::MockCryptoUtility() {
+ ON_CALL(*this, GetRandom(_, _)).WillByDefault(Invoke(FakeRandom));
+ ON_CALL(*this, CreateSealedKey(_, _)).WillByDefault(Return(true));
+ ON_CALL(*this, UnsealKey(_, _, _)).WillByDefault(Return(true));
+ ON_CALL(*this, EncryptData(_, _, _, _))
+ .WillByDefault(WithArgs<0, 3>(Invoke(CopyString)));
+ ON_CALL(*this, DecryptData(_, _, _))
+ .WillByDefault(WithArgs<0, 2>(Invoke(CopyString)));
+}
+
+MockCryptoUtility::~MockCryptoUtility() {}
+
+} // namespace attestation
diff --git a/server/mock_crypto_utility.h b/server/mock_crypto_utility.h
index bee86dc..98ea6d3 100644
--- a/server/mock_crypto_utility.h
+++ b/server/mock_crypto_utility.h
@@ -15,8 +15,10 @@
class MockCryptoUtility : public CryptoUtility {
public:
- MockCryptoUtility() = default;
- ~MockCryptoUtility() override = default;
+ MockCryptoUtility();
+ ~MockCryptoUtility() override;
+
+ MOCK_CONST_METHOD2(GetRandom, bool(size_t, std::string*));
MOCK_CONST_METHOD2(CreateSealedKey, bool(std::string* aes_key,
std::string* sealed_key));
diff --git a/server/mock_database.cc b/server/mock_database.cc
new file mode 100644
index 0000000..5215c2c
--- /dev/null
+++ b/server/mock_database.cc
@@ -0,0 +1,20 @@
+// Copyright 2015 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/server/mock_database.h"
+
+using testing::Return;
+using testing::ReturnRef;
+
+namespace attestation {
+
+MockDatabase::MockDatabase() {
+ ON_CALL(*this, GetProtobuf()).WillByDefault(ReturnRef(fake_));
+ ON_CALL(*this, GetMutableProtobuf()).WillByDefault(Return(&fake_));
+ ON_CALL(*this, SaveChanges()).WillByDefault(Return(true));
+}
+
+MockDatabase::~MockDatabase() {}
+
+} // namespace attestation
diff --git a/server/mock_database.h b/server/mock_database.h
new file mode 100644
index 0000000..d9ab985
--- /dev/null
+++ b/server/mock_database.h
@@ -0,0 +1,29 @@
+// Copyright 2015 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 ATTESTATION_SERVER_MOCK_DATABASE_H_
+#define ATTESTATION_SERVER_MOCK_DATABASE_H_
+
+#include "attestation/server/database.h"
+
+#include <gmock/gmock.h>
+
+namespace attestation {
+
+class MockDatabase : public Database {
+ public:
+ MockDatabase();
+ ~MockDatabase() override;
+
+ MOCK_CONST_METHOD0(GetProtobuf, const AttestationDatabase&());
+ MOCK_METHOD0(GetMutableProtobuf, AttestationDatabase*());
+ MOCK_METHOD0(SaveChanges, bool());
+
+ private:
+ AttestationDatabase fake_;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_MOCK_DATABASE_H_
diff --git a/server/mock_key_store.cc b/server/mock_key_store.cc
new file mode 100644
index 0000000..2558f35
--- /dev/null
+++ b/server/mock_key_store.cc
@@ -0,0 +1,23 @@
+// Copyright 2015 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/server/mock_key_store.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace attestation {
+
+MockKeyStore::MockKeyStore() {
+ ON_CALL(*this, Read(_, _, _)).WillByDefault(Return(true));
+ ON_CALL(*this, Write(_, _, _)).WillByDefault(Return(true));
+ ON_CALL(*this, Delete(_, _)).WillByDefault(Return(true));
+ ON_CALL(*this, DeleteByPrefix(_, _)).WillByDefault(Return(true));
+ ON_CALL(*this, Register(_, _, _, _, _)).WillByDefault(Return(true));
+ ON_CALL(*this, RegisterCertificate(_, _)).WillByDefault(Return(true));
+}
+
+MockKeyStore::~MockKeyStore() {}
+
+} // namespace attestation
diff --git a/server/mock_key_store.h b/server/mock_key_store.h
new file mode 100644
index 0000000..40d04f1
--- /dev/null
+++ b/server/mock_key_store.h
@@ -0,0 +1,46 @@
+// Copyright 2015 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 ATTESTATION_SERVER_MOCK_KEY_STORE_H_
+#define ATTESTATION_SERVER_MOCK_KEY_STORE_H_
+
+#include "attestation/server/key_store.h"
+
+#include <string>
+
+#include <base/macros.h>
+#include <gmock/gmock.h>
+
+namespace attestation {
+
+class MockKeyStore : public KeyStore {
+ public:
+ MockKeyStore();
+ virtual ~MockKeyStore();
+
+ MOCK_METHOD3(Read, bool(const std::string& username,
+ const std::string& name,
+ std::string* key_data));
+ MOCK_METHOD3(Write, bool(const std::string& username,
+ const std::string& name,
+ const std::string& key_data));
+ MOCK_METHOD2(Delete, bool(const std::string& username,
+ const std::string& name));
+ MOCK_METHOD2(DeleteByPrefix, bool(const std::string& username,
+ const std::string& key_prefix));
+ MOCK_METHOD5(Register, bool(const std::string& username,
+ const std::string& label,
+ const std::string& private_key_blob,
+ const std::string& public_key_der,
+ const std::string& certificate));
+ MOCK_METHOD2(RegisterCertificate, bool(const std::string& username,
+ const std::string& certificate));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockKeyStore);
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_MOCK_KEY_STORE_H_
diff --git a/server/mock_tpm_utility.cc b/server/mock_tpm_utility.cc
new file mode 100644
index 0000000..80692bd
--- /dev/null
+++ b/server/mock_tpm_utility.cc
@@ -0,0 +1,22 @@
+// Copyright 2015 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/server/mock_tpm_utility.h"
+
+using ::testing::_;
+using ::testing::Return;
+
+namespace attestation {
+
+MockTpmUtility::MockTpmUtility() {
+ ON_CALL(*this, IsTpmReady()).WillByDefault(Return(true));
+ ON_CALL(*this, ActivateIdentity(_, _, _, _, _, _))
+ .WillByDefault(Return(true));
+ ON_CALL(*this, GenerateKey(_, _, _, _)).WillByDefault(Return(true));
+ ON_CALL(*this, CertifyKey(_, _, _, _, _, _)).WillByDefault(Return(true));
+}
+
+MockTpmUtility::~MockTpmUtility() {}
+
+} // namespace attestation
diff --git a/server/mock_tpm_utility.h b/server/mock_tpm_utility.h
new file mode 100644
index 0000000..3f2b616
--- /dev/null
+++ b/server/mock_tpm_utility.h
@@ -0,0 +1,40 @@
+// Copyright 2015 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 ATTESTATION_SERVER_MOCK_TPM_UTILITY_H_
+#define ATTESTATION_SERVER_MOCK_TPM_UTILITY_H_
+
+#include "attestation/server/tpm_utility.h"
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+namespace attestation {
+
+class MockTpmUtility : public TpmUtility {
+ public:
+ MockTpmUtility();
+ ~MockTpmUtility() override;
+
+ MOCK_METHOD0(IsTpmReady, bool());
+ MOCK_METHOD6(ActivateIdentity, bool(const std::string&,
+ const std::string&,
+ const std::string&,
+ const std::string&,
+ const std::string&,
+ std::string*));
+ MOCK_METHOD4(GenerateKey, bool(KeyType, KeyUsage, std::string*,
+ std::string*));
+ MOCK_METHOD6(CertifyKey, bool(const std::string&,
+ const std::string&,
+ const std::string&,
+ std::string*,
+ std::string*,
+ std::string*));
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_MOCK_TPM_UTILITY_H_
diff --git a/server/tpm_utility.h b/server/tpm_utility.h
new file mode 100644
index 0000000..bd447a2
--- /dev/null
+++ b/server/tpm_utility.h
@@ -0,0 +1,61 @@
+// Copyright 2015 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 ATTESTATION_SERVER_TPM_UTILITY_H_
+#define ATTESTATION_SERVER_TPM_UTILITY_H_
+
+#include <string>
+
+#include "attestation/common/interface.pb.h"
+
+namespace attestation {
+
+// A class which provides helpers for TPM-related tasks.
+class TpmUtility {
+ public:
+ virtual ~TpmUtility() = default;
+
+ // Returns true iff the TPM is enabled, owned, and ready for attestation.
+ virtual bool IsTpmReady() = 0;
+
+ // Activates an attestation identity key. Effectively this decrypts a
+ // certificate or some other type of credential with the endorsement key. The
+ // |delegate_blob| and |delegate_secret| must be authorized to activate with
+ // owner privilege. The |identity_key_blob| is the key to which the credential
+ // is bound. The |asym_ca_contents| and |sym_ca_attestation| parameters are
+ // encrypted TPM structures, typically created by a CA (TPM_ASYM_CA_CONTENTS
+ // and TPM_SYM_CA_ATTESTATION respectively). On success returns true and
+ // populates the decrypted |credential|.
+ virtual bool ActivateIdentity(const std::string& delegate_blob,
+ const std::string& delegate_secret,
+ const std::string& identity_key_blob,
+ const std::string& asym_ca_contents,
+ const std::string& sym_ca_attestation,
+ std::string* credential) = 0;
+
+ // Generates a non-migratable key in the TPM corresponding to |key_type| and
+ // |key_usage|. The parent key will be the storage root key. The new key will
+ // always be certifiable.
+ virtual bool GenerateKey(KeyType key_type,
+ KeyUsage key_usage,
+ std::string* key_blob,
+ std::string* public_key) = 0;
+
+ // Certifies the key represented by |key_blob| with the attestation identity
+ // key represented by |identity_key_blob|. The |external_data| will be
+ // included in the |key_info|. On success, returns true and populates
+ // |public_key_tpm_format| with the public key of |key_blob| in TPM_PUBKEY
+ // format, |key_info| with the TPM_CERTIFY_INFO that was signed, and |proof|
+ // with the signature of |key_info| by the identity key.
+ virtual bool CertifyKey(const std::string& key_blob,
+ const std::string& identity_key_blob,
+ const std::string& external_data,
+ std::string* public_key_tpm_format,
+ std::string* key_info,
+ std::string* proof) = 0;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_TPM_UTILITY_H_