summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBill Yi <byi@google.com>2015-11-20 16:46:49 -0800
committerBill Yi <byi@google.com>2015-11-20 16:46:49 -0800
commitff2783e1bf1f26cb1173c821cbfa70c5f1cee954 (patch)
tree3fff41289b3402dbf046df39454e4119167b3ea0
parent932dca120584df2887e105c7dfe4a64e7849751a (diff)
parentb3c9cdcef463064d7ac8f1e6b4b88e62433f9d5d (diff)
downloadtpm-ff2783e1bf1f26cb1173c821cbfa70c5f1cee954.tar.gz
Add 'attestation/' from commit 'b3c9cdcef463064d7ac8f1e6b4b88e62433f9d5d'
git-subtree-dir: attestation git-subtree-mainline: 932dca120584df2887e105c7dfe4a64e7849751a git-subtree-split: b3c9cdcef463064d7ac8f1e6b4b88e62433f9d5d
-rw-r--r--attestation/MODULE_LICENSE_APACHE20
-rw-r--r--attestation/NOTICE190
-rw-r--r--attestation/OWNERS3
-rw-r--r--attestation/PRESUBMIT.cfg3
-rw-r--r--attestation/attestation.gyp195
-rw-r--r--attestation/attestation_testrunner.cc31
-rw-r--r--attestation/client/dbus_proxy.cc208
-rw-r--r--attestation/client/dbus_proxy.h77
-rw-r--r--attestation/client/dbus_proxy_test.cc414
-rw-r--r--attestation/client/main.cc503
-rw-r--r--attestation/common/attestation_ca.proto193
-rw-r--r--attestation/common/attestation_interface.h106
-rw-r--r--attestation/common/common.proto96
-rw-r--r--attestation/common/crypto_utility.h98
-rw-r--r--attestation/common/crypto_utility_impl.cc472
-rw-r--r--attestation/common/crypto_utility_impl.h100
-rw-r--r--attestation/common/crypto_utility_impl_test.cc241
-rw-r--r--attestation/common/database.proto113
-rw-r--r--attestation/common/dbus_interface.h40
-rw-r--r--attestation/common/interface.proto173
-rw-r--r--attestation/common/mock_attestation_interface.h59
-rw-r--r--attestation/common/mock_crypto_utility.cc54
-rw-r--r--attestation/common/mock_crypto_utility.h67
-rw-r--r--attestation/common/mock_tpm_utility.cc83
-rw-r--r--attestation/common/mock_tpm_utility.h66
-rw-r--r--attestation/common/print_common_proto.cc233
-rw-r--r--attestation/common/print_common_proto.h50
-rw-r--r--attestation/common/print_interface_proto.cc683
-rw-r--r--attestation/common/print_interface_proto.h99
-rwxr-xr-xattestation/common/proto_print.py417
-rw-r--r--attestation/common/tpm_utility.h97
-rw-r--r--attestation/common/tpm_utility_v1.cc720
-rw-r--r--attestation/common/tpm_utility_v1.h120
-rw-r--r--attestation/server/attestation_service.cc937
-rw-r--r--attestation/server/attestation_service.h313
-rw-r--r--attestation/server/attestation_service_test.cc1190
-rw-r--r--attestation/server/attestationd-seccomp-amd64.policy80
-rw-r--r--attestation/server/attestationd-seccomp-arm.policy50
-rw-r--r--attestation/server/attestationd-seccomp-x86.policy45
-rw-r--r--attestation/server/attestationd.conf24
-rw-r--r--attestation/server/database.h46
-rw-r--r--attestation/server/database_impl.cc199
-rw-r--r--attestation/server/database_impl.h91
-rw-r--r--attestation/server/database_impl_test.cc163
-rw-r--r--attestation/server/dbus_service.cc242
-rw-r--r--attestation/server/dbus_service.h115
-rw-r--r--attestation/server/dbus_service_test.cc382
-rw-r--r--attestation/server/key_store.h82
-rw-r--r--attestation/server/main.cc113
-rw-r--r--attestation/server/mock_database.cc33
-rw-r--r--attestation/server/mock_database.h42
-rw-r--r--attestation/server/mock_key_store.cc35
-rw-r--r--attestation/server/mock_key_store.h60
-rw-r--r--attestation/server/org.chromium.Attestation.conf15
-rw-r--r--attestation/server/pkcs11_key_store.cc685
-rw-r--r--attestation/server/pkcs11_key_store.h123
-rw-r--r--attestation/server/pkcs11_key_store_test.cc598
57 files changed, 11667 insertions, 0 deletions
diff --git a/attestation/MODULE_LICENSE_APACHE2 b/attestation/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/attestation/MODULE_LICENSE_APACHE2
diff --git a/attestation/NOTICE b/attestation/NOTICE
new file mode 100644
index 0000000..a849a94
--- /dev/null
+++ b/attestation/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2014-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.
+
+ 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.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/attestation/OWNERS b/attestation/OWNERS
new file mode 100644
index 0000000..699d270
--- /dev/null
+++ b/attestation/OWNERS
@@ -0,0 +1,3 @@
+set noparent
+dkrahn@chromium.org
+namnguyen@chromium.org
diff --git a/attestation/PRESUBMIT.cfg b/attestation/PRESUBMIT.cfg
new file mode 100644
index 0000000..087dfa3
--- /dev/null
+++ b/attestation/PRESUBMIT.cfg
@@ -0,0 +1,3 @@
+[Hook Overrides]
+cros_license_check: false
+aosp_license_check: true
diff --git a/attestation/attestation.gyp b/attestation/attestation.gyp
new file mode 100644
index 0000000..0f691f8
--- /dev/null
+++ b/attestation/attestation.gyp
@@ -0,0 +1,195 @@
+#
+# Copyright (C) 2014 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.
+#
+
+{
+ 'target_defaults': {
+ 'variables': {
+ 'deps': [ # This is a list of pkg-config dependencies
+ 'libbrillo-<(libbase_ver)',
+ 'libchrome-<(libbase_ver)',
+ 'protobuf-lite',
+ ],
+ },
+ 'include_dirs': [
+ # We need this include dir because we include all the local code as
+ # "attestation/...".
+ '<(platform2_root)/../aosp/system/',
+ ],
+ },
+ 'targets': [
+ # A library for just the protobufs.
+ {
+ 'target_name': 'proto_library',
+ 'type': 'static_library',
+ # Use -fPIC so this code can be linked into a shared library.
+ 'cflags!': ['-fPIE'],
+ 'cflags': [
+ '-fPIC',
+ '-fvisibility=default',
+ ],
+ 'variables': {
+ 'proto_in_dir': 'common',
+ 'proto_out_dir': 'include/attestation/common',
+ },
+ 'sources': [
+ '<(proto_in_dir)/attestation_ca.proto',
+ '<(proto_in_dir)/common.proto',
+ '<(proto_in_dir)/database.proto',
+ '<(proto_in_dir)/interface.proto',
+ 'common/print_common_proto.cc',
+ 'common/print_interface_proto.cc',
+ ],
+ 'includes': ['../../../platform2/common-mk/protoc.gypi'],
+ },
+ # A library for common code.
+ {
+ 'target_name': 'common_library',
+ 'type': 'static_library',
+ 'sources': [
+ 'common/crypto_utility_impl.cc',
+ 'common/tpm_utility_v1.cc',
+ ],
+ 'all_dependent_settings': {
+ 'variables': {
+ 'deps': [
+ 'openssl',
+ ],
+ },
+ 'libraries': [
+ '-ltspi',
+ ],
+ },
+ 'dependencies': [
+ 'proto_library',
+ ],
+ },
+ # A library for client code.
+ {
+ 'target_name': 'client_library',
+ 'type': 'static_library',
+ # Use -fPIC so this code can be linked into a shared library.
+ 'cflags!': ['-fPIE'],
+ 'cflags': [
+ '-fPIC',
+ '-fvisibility=default',
+ ],
+ 'sources': [
+ 'client/dbus_proxy.cc',
+ ],
+ 'dependencies': [
+ 'proto_library',
+ ],
+ },
+ # A shared library for clients.
+ {
+ 'target_name': 'libattestation',
+ 'type': 'shared_library',
+ 'cflags': ['-fvisibility=default'],
+ 'sources': [
+ ],
+ 'dependencies': [
+ 'client_library',
+ 'proto_library',
+ ],
+ },
+ # A client command line utility.
+ {
+ 'target_name': 'attestation_client',
+ 'type': 'executable',
+ 'sources': [
+ 'client/main.cc',
+ ],
+ 'dependencies': [
+ 'client_library',
+ 'common_library',
+ 'proto_library',
+ ]
+ },
+ # A library for server code.
+ {
+ 'target_name': 'server_library',
+ 'type': 'static_library',
+ 'sources': [
+ 'server/attestation_service.cc',
+ 'server/dbus_service.cc',
+ 'server/database_impl.cc',
+ 'server/pkcs11_key_store.cc',
+ ],
+ 'all_dependent_settings': {
+ 'libraries': [
+ '-lchaps',
+ ],
+ },
+ 'dependencies': [
+ 'proto_library',
+ ],
+ },
+ # The attestation daemon.
+ {
+ 'target_name': 'attestationd',
+ 'type': 'executable',
+ 'sources': [
+ 'server/main.cc',
+ ],
+ 'variables': {
+ 'deps': [
+ 'libminijail',
+ ],
+ },
+ 'dependencies': [
+ 'common_library',
+ 'proto_library',
+ 'server_library',
+ ],
+ },
+ ],
+ 'conditions': [
+ ['USE_test == 1', {
+ 'targets': [
+ {
+ 'target_name': 'attestation_testrunner',
+ 'type': 'executable',
+ 'includes': ['../../../platform2/common-mk/common_test.gypi'],
+ 'variables': {
+ 'deps': [
+ 'libbrillo-test-<(libbase_ver)',
+ 'libchrome-test-<(libbase_ver)',
+ ],
+ },
+ 'sources': [
+ 'attestation_testrunner.cc',
+ 'client/dbus_proxy_test.cc',
+ 'common/crypto_utility_impl_test.cc',
+ 'common/mock_crypto_utility.cc',
+ 'common/mock_tpm_utility.cc',
+ 'server/attestation_service_test.cc',
+ 'server/database_impl_test.cc',
+ 'server/dbus_service_test.cc',
+ 'server/mock_database.cc',
+ 'server/mock_key_store.cc',
+ 'server/pkcs11_key_store_test.cc',
+ ],
+ 'dependencies': [
+ 'common_library',
+ 'client_library',
+ 'proto_library',
+ 'server_library',
+ ],
+ },
+ ],
+ }],
+ ],
+}
diff --git a/attestation/attestation_testrunner.cc b/attestation/attestation_testrunner.cc
new file mode 100644
index 0000000..2aac479
--- /dev/null
+++ b/attestation/attestation_testrunner.cc
@@ -0,0 +1,31 @@
+//
+// 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 <base/at_exit.h>
+#include <base/command_line.h>
+#include <base/logging.h>
+#include <brillo/syslog_logging.h>
+#include <gtest/gtest.h>
+
+int main(int argc, char **argv) {
+ base::CommandLine::Init(argc, argv);
+ brillo::InitLog(brillo::kLogToStderr);
+ // Enable verbose logging while running unit tests.
+ logging::SetMinLogLevel(logging::LOG_VERBOSE);
+ base::AtExitManager exit_manager;
+ ::testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
+}
diff --git a/attestation/client/dbus_proxy.cc b/attestation/client/dbus_proxy.cc
new file mode 100644
index 0000000..bf37d0a
--- /dev/null
+++ b/attestation/client/dbus_proxy.cc
@@ -0,0 +1,208 @@
+//
+// 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/client/dbus_proxy.h"
+
+#include <brillo/bind_lambda.h>
+#include <brillo/dbus/dbus_method_invoker.h>
+
+#include "attestation/common/dbus_interface.h"
+
+namespace {
+
+// Use a two minute timeout because TPM operations can take a long time and
+// there may be a few of them queued up.
+const int kDBusTimeoutMS = 120000;
+
+} // namespace
+
+namespace attestation {
+
+DBusProxy::DBusProxy() {}
+DBusProxy::~DBusProxy() {
+ if (bus_) {
+ bus_->ShutdownAndBlock();
+ }
+}
+
+bool DBusProxy::Initialize() {
+ dbus::Bus::Options options;
+ options.bus_type = dbus::Bus::SYSTEM;
+ bus_ = new dbus::Bus(options);
+ object_proxy_ = bus_->GetObjectProxy(
+ attestation::kAttestationServiceName,
+ dbus::ObjectPath(attestation::kAttestationServicePath));
+ return (object_proxy_ != nullptr);
+}
+
+void DBusProxy::CreateGoogleAttestedKey(
+ const CreateGoogleAttestedKeyRequest& request,
+ const CreateGoogleAttestedKeyCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ CreateGoogleAttestedKeyReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kCreateGoogleAttestedKey,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+void DBusProxy::GetKeyInfo(const GetKeyInfoRequest& request,
+ const GetKeyInfoCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ GetKeyInfoReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kGetKeyInfo,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+void DBusProxy::GetEndorsementInfo(const GetEndorsementInfoRequest& request,
+ const GetEndorsementInfoCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ GetEndorsementInfoReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kGetEndorsementInfo,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+void DBusProxy::GetAttestationKeyInfo(
+ const GetAttestationKeyInfoRequest& request,
+ const GetAttestationKeyInfoCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ GetAttestationKeyInfoReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kGetAttestationKeyInfo,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+void DBusProxy::ActivateAttestationKey(
+ const ActivateAttestationKeyRequest& request,
+ const ActivateAttestationKeyCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ ActivateAttestationKeyReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kActivateAttestationKey,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+void DBusProxy::CreateCertifiableKey(
+ const CreateCertifiableKeyRequest& request,
+ const CreateCertifiableKeyCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ CreateCertifiableKeyReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kCreateCertifiableKey,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+void DBusProxy::Decrypt(const DecryptRequest& request,
+ const DecryptCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ DecryptReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kDecrypt,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+void DBusProxy::Sign(const SignRequest& request, const SignCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ SignReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kSign,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+void DBusProxy::RegisterKeyWithChapsToken(
+ const RegisterKeyWithChapsTokenRequest& request,
+ const RegisterKeyWithChapsTokenCallback& callback) {
+ auto on_error = [callback](brillo::Error* error) {
+ RegisterKeyWithChapsTokenReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ brillo::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kRegisterKeyWithChapsToken,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
+} // namespace attestation
diff --git a/attestation/client/dbus_proxy.h b/attestation/client/dbus_proxy.h
new file mode 100644
index 0000000..9ecd527
--- /dev/null
+++ b/attestation/client/dbus_proxy.h
@@ -0,0 +1,77 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_CLIENT_DBUS_PROXY_H_
+#define ATTESTATION_CLIENT_DBUS_PROXY_H_
+
+#include "attestation/common/attestation_interface.h"
+
+#include <string>
+
+#include <base/memory/ref_counted.h>
+#include <dbus/bus.h>
+#include <dbus/object_proxy.h>
+
+namespace attestation {
+
+// An implementation of AttestationInterface that forwards requests over D-Bus.
+// Usage:
+// std::unique_ptr<AttestationInterface> attestation = new DBusProxy();
+// attestation->Initialize();
+// attestation->CreateGoogleAttestedKey(...);
+class DBusProxy : public AttestationInterface {
+ public:
+ DBusProxy();
+ virtual ~DBusProxy();
+
+ // AttestationInterface methods.
+ bool Initialize() override;
+ void CreateGoogleAttestedKey(
+ const CreateGoogleAttestedKeyRequest& request,
+ const CreateGoogleAttestedKeyCallback& callback) override;
+ void GetKeyInfo(const GetKeyInfoRequest& request,
+ const GetKeyInfoCallback& callback) override;
+ void GetEndorsementInfo(const GetEndorsementInfoRequest& request,
+ const GetEndorsementInfoCallback& callback) override;
+ void GetAttestationKeyInfo(
+ const GetAttestationKeyInfoRequest& request,
+ const GetAttestationKeyInfoCallback& callback) override;
+ void ActivateAttestationKey(
+ const ActivateAttestationKeyRequest& request,
+ const ActivateAttestationKeyCallback& callback) override;
+ void CreateCertifiableKey(
+ const CreateCertifiableKeyRequest& request,
+ const CreateCertifiableKeyCallback& callback) override;
+ void Decrypt(const DecryptRequest& request,
+ const DecryptCallback& callback) override;
+ void Sign(const SignRequest& request, const SignCallback& callback) override;
+ void RegisterKeyWithChapsToken(
+ const RegisterKeyWithChapsTokenRequest& request,
+ const RegisterKeyWithChapsTokenCallback& callback) override;
+
+ // Useful for testing.
+ void set_object_proxy(dbus::ObjectProxy* object_proxy) {
+ object_proxy_ = object_proxy;
+ }
+
+ private:
+ scoped_refptr<dbus::Bus> bus_;
+ dbus::ObjectProxy* object_proxy_;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_CLIENT_DBUS_PROXY_H_
diff --git a/attestation/client/dbus_proxy_test.cc b/attestation/client/dbus_proxy_test.cc
new file mode 100644
index 0000000..7170a28
--- /dev/null
+++ b/attestation/client/dbus_proxy_test.cc
@@ -0,0 +1,414 @@
+//
+// 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 <string>
+
+#include <brillo/bind_lambda.h>
+#include <dbus/mock_object_proxy.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "attestation/client/dbus_proxy.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace attestation {
+
+class DBusProxyTest : public testing::Test {
+ public:
+ ~DBusProxyTest() override = default;
+ void SetUp() override {
+ mock_object_proxy_ = new StrictMock<dbus::MockObjectProxy>(
+ nullptr, "", dbus::ObjectPath(""));
+ proxy_.set_object_proxy(mock_object_proxy_.get());
+ }
+ protected:
+ scoped_refptr<StrictMock<dbus::MockObjectProxy>> mock_object_proxy_;
+ DBusProxy proxy_;
+};
+
+TEST_F(DBusProxyTest, CreateGoogleAttestedKey) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ CreateGoogleAttestedKeyRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ("label", request_proto.key_label());
+ EXPECT_EQ(KEY_TYPE_ECC, request_proto.key_type());
+ EXPECT_EQ(KEY_USAGE_SIGN, request_proto.key_usage());
+ EXPECT_EQ(ENTERPRISE_MACHINE_CERTIFICATE,
+ request_proto.certificate_profile());
+ EXPECT_EQ("user", request_proto.username());
+ EXPECT_EQ("origin", request_proto.origin());
+ // Create reply protobuf.
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ CreateGoogleAttestedKeyReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ reply_proto.set_certificate_chain("certificate");
+ reply_proto.set_server_error("server_error");
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const CreateGoogleAttestedKeyReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("certificate", reply.certificate_chain());
+ EXPECT_EQ("server_error", reply.server_error());
+ };
+ CreateGoogleAttestedKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ request.set_certificate_profile(ENTERPRISE_MACHINE_CERTIFICATE);
+ request.set_username("user");
+ request.set_origin("origin");
+ proxy_.CreateGoogleAttestedKey(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+TEST_F(DBusProxyTest, GetKeyInfo) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ GetKeyInfoRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ("label", request_proto.key_label());
+ EXPECT_EQ("username", request_proto.username());
+ // Create reply protobuf.
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ GetKeyInfoReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ reply_proto.set_key_type(KEY_TYPE_ECC);
+ reply_proto.set_key_usage(KEY_USAGE_SIGN);
+ reply_proto.set_public_key("public_key");
+ reply_proto.set_certify_info("certify_info");
+ reply_proto.set_certify_info_signature("signature");
+ reply_proto.set_certificate("certificate");
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const GetKeyInfoReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(KEY_TYPE_ECC, reply.key_type());
+ EXPECT_EQ(KEY_USAGE_SIGN, reply.key_usage());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("certify_info", reply.certify_info());
+ EXPECT_EQ("signature", reply.certify_info_signature());
+ EXPECT_EQ("certificate", reply.certificate());
+ };
+ GetKeyInfoRequest request;
+ request.set_key_label("label");
+ request.set_username("username");
+ proxy_.GetKeyInfo(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+TEST_F(DBusProxyTest, GetEndorsementInfo) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ GetEndorsementInfoRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ(KEY_TYPE_ECC, request_proto.key_type());
+ // Create reply protobuf.
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ GetEndorsementInfoReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ reply_proto.set_ek_public_key("public_key");
+ reply_proto.set_ek_certificate("certificate");
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const GetEndorsementInfoReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.ek_public_key());
+ EXPECT_EQ("certificate", reply.ek_certificate());
+ };
+ GetEndorsementInfoRequest request;
+ request.set_key_type(KEY_TYPE_ECC);
+ proxy_.GetEndorsementInfo(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+TEST_F(DBusProxyTest, GetAttestationKeyInfo) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ GetAttestationKeyInfoRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ(KEY_TYPE_ECC, request_proto.key_type());
+ // Create reply protobuf.
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ GetAttestationKeyInfoReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ reply_proto.set_public_key("public_key");
+ reply_proto.set_public_key_tpm_format("public_key_tpm_format");
+ reply_proto.set_certificate("certificate");
+ reply_proto.mutable_pcr0_quote()->set_quote("pcr0");
+ reply_proto.mutable_pcr1_quote()->set_quote("pcr1");
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const GetAttestationKeyInfoReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("public_key_tpm_format", reply.public_key_tpm_format());
+ EXPECT_EQ("certificate", reply.certificate());
+ EXPECT_EQ("pcr0", reply.pcr0_quote().quote());
+ EXPECT_EQ("pcr1", reply.pcr1_quote().quote());
+ };
+ GetAttestationKeyInfoRequest request;
+ request.set_key_type(KEY_TYPE_ECC);
+ proxy_.GetAttestationKeyInfo(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+TEST_F(DBusProxyTest, ActivateAttestationKey) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ ActivateAttestationKeyRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ(KEY_TYPE_ECC, request_proto.key_type());
+ EXPECT_EQ("encrypted1",
+ request_proto.encrypted_certificate().asym_ca_contents());
+ EXPECT_EQ("encrypted2",
+ request_proto.encrypted_certificate().sym_ca_attestation());
+ EXPECT_TRUE(request_proto.save_certificate());
+ // Create reply protobuf.
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ ActivateAttestationKeyReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ reply_proto.set_certificate("certificate");
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const ActivateAttestationKeyReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("certificate", reply.certificate());
+ };
+ ActivateAttestationKeyRequest request;
+ request.set_key_type(KEY_TYPE_ECC);
+ request.mutable_encrypted_certificate()->set_asym_ca_contents("encrypted1");
+ request.mutable_encrypted_certificate()->set_sym_ca_attestation("encrypted2");
+ request.set_save_certificate(true);
+ proxy_.ActivateAttestationKey(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+TEST_F(DBusProxyTest, CreateCertifiableKey) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ CreateCertifiableKeyRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ("label", request_proto.key_label());
+ EXPECT_EQ(KEY_TYPE_ECC, request_proto.key_type());
+ EXPECT_EQ(KEY_USAGE_SIGN, request_proto.key_usage());
+ EXPECT_EQ("user", request_proto.username());
+ // Create reply protobuf.
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ CreateCertifiableKeyReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ reply_proto.set_public_key("public_key");
+ reply_proto.set_certify_info("certify_info");
+ reply_proto.set_certify_info_signature("signature");
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const CreateCertifiableKeyReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("certify_info", reply.certify_info());
+ EXPECT_EQ("signature", reply.certify_info_signature());
+ };
+ CreateCertifiableKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ request.set_username("user");
+ proxy_.CreateCertifiableKey(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+TEST_F(DBusProxyTest, Decrypt) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ DecryptRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ("label", request_proto.key_label());
+ EXPECT_EQ("user", request_proto.username());
+ EXPECT_EQ("data", request_proto.encrypted_data());
+ // Create reply protobuf.
+ scoped_ptr<dbus::Response> response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ DecryptReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ reply_proto.set_decrypted_data("data");
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const DecryptReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("data", reply.decrypted_data());
+ };
+ DecryptRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_encrypted_data("data");
+ proxy_.Decrypt(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+TEST_F(DBusProxyTest, Sign) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ SignRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ("label", request_proto.key_label());
+ EXPECT_EQ("user", request_proto.username());
+ EXPECT_EQ("data", request_proto.data_to_sign());
+ // Create reply protobuf.
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ SignReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ reply_proto.set_signature("signature");
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const SignReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("signature", reply.signature());
+ };
+ SignRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_data_to_sign("data");
+ proxy_.Sign(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+TEST_F(DBusProxyTest, RegisterKeyWithChapsToken) {
+ auto fake_dbus_call = [](
+ dbus::MethodCall* method_call,
+ const dbus::MockObjectProxy::ResponseCallback& response_callback) {
+ // Verify request protobuf.
+ dbus::MessageReader reader(method_call);
+ RegisterKeyWithChapsTokenRequest request_proto;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&request_proto));
+ EXPECT_EQ("label", request_proto.key_label());
+ EXPECT_EQ("user", request_proto.username());
+ // Create reply protobuf.
+ auto response = dbus::Response::CreateEmpty();
+ dbus::MessageWriter writer(response.get());
+ RegisterKeyWithChapsTokenReply reply_proto;
+ reply_proto.set_status(STATUS_SUCCESS);
+ writer.AppendProtoAsArrayOfBytes(reply_proto);
+ response_callback.Run(response.release());
+ };
+ EXPECT_CALL(*mock_object_proxy_, CallMethodWithErrorCallback(_, _, _, _))
+ .WillOnce(WithArgs<0, 2>(Invoke(fake_dbus_call)));
+
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](
+ const RegisterKeyWithChapsTokenReply& reply) {
+ callback_count++;
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ };
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ proxy_.RegisterKeyWithChapsToken(request, base::Bind(callback));
+ EXPECT_EQ(1, callback_count);
+}
+
+} // namespace attestation
diff --git a/attestation/client/main.cc b/attestation/client/main.cc
new file mode 100644
index 0000000..868b42c
--- /dev/null
+++ b/attestation/client/main.cc
@@ -0,0 +1,503 @@
+//
+// Copyright (C) 2014 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 <stdio.h>
+#include <sysexits.h>
+
+#include <memory>
+#include <string>
+
+#include <base/command_line.h>
+#include <base/files/file_util.h>
+#include <base/message_loop/message_loop.h>
+#include <brillo/bind_lambda.h>
+#include <brillo/daemons/daemon.h>
+#include <brillo/syslog_logging.h>
+
+#include "attestation/client/dbus_proxy.h"
+#include "attestation/common/attestation_ca.pb.h"
+#include "attestation/common/crypto_utility_impl.h"
+#include "attestation/common/interface.pb.h"
+#include "attestation/common/print_interface_proto.h"
+
+namespace attestation {
+
+const char kCreateAndCertifyCommand[] = "create_and_certify";
+const char kCreateCommand[] = "create";
+const char kInfoCommand[] = "info";
+const char kEndorsementCommand[] = "endorsement";
+const char kAttestationKeyCommand[] = "attestation_key";
+const char kActivateCommand[] = "activate";
+const char kEncryptForActivateCommand[] = "encrypt_for_activate";
+const char kEncryptCommand[] = "encrypt";
+const char kDecryptCommand[] = "decrypt";
+const char kSignCommand[] = "sign";
+const char kVerifyCommand[] = "verify";
+const char kRegisterCommand[] = "register";
+const char kUsage[] = R"(
+Usage: attestation_client <command> [<args>]
+Commands:
+ create_and_certify [--user=<email>] [--label=<keylabel>]
+ Creates a key and requests certification by the Google Attestation CA.
+ This is the default command.
+ create [--user=<email>] [--label=<keylabel] [--usage=sign|decrypt]
+ Creates a certifiable key.
+
+ info [--user=<email>] [--label=<keylabel>]
+ Prints info about a key.
+ endorsement
+ Prints info about the TPM endorsement.
+ attestation_key
+ Prints info about the TPM attestation key.
+
+ activate --input=<input_file>
+ Activates an attestation key using the encrypted credential in
+ |input_file|.
+ encrypt_for_activate --input=<input_file> --output=<output_file>
+ Encrypts the content of |input_file| as required by the TPM for activating
+ an attestation key. The result is written to |output_file|.
+
+ encrypt [--user=<email>] [--label=<keylabel>] --input=<input_file>
+ --output=<output_file>
+ Encrypts the contents of |input_file| as required by the TPM for a decrypt
+ operation. The result is written to |output_file|.
+ decrypt [--user=<email>] [--label=<keylabel>] --input=<input_file>
+ Decrypts the contents of |input_file|.
+
+ sign [--user=<email>] [--label=<keylabel>] --input=<input_file>
+ [--output=<output_file>]
+ Signs the contents of |input_file|.
+ verify [--user=<email>] [--label=<keylabel] --input=<signed_data_file>
+ --signature=<signature_file>
+ Verifies the signature in |signature_file| against the contents of
+ |input_file|.
+
+ register [--user=<email>] [--label=<keylabel]
+ Registers a key with a PKCS #11 token.
+)";
+
+// The Daemon class works well as a client loop as well.
+using ClientLoopBase = brillo::Daemon;
+
+class ClientLoop : public ClientLoopBase {
+ public:
+ ClientLoop() = default;
+ ~ClientLoop() override = default;
+
+ protected:
+ int OnInit() override {
+ int exit_code = ClientLoopBase::OnInit();
+ if (exit_code != EX_OK) {
+ return exit_code;
+ }
+ attestation_.reset(new attestation::DBusProxy());
+ if (!attestation_->Initialize()) {
+ return EX_UNAVAILABLE;
+ }
+ exit_code = ScheduleCommand();
+ if (exit_code == EX_USAGE) {
+ printf("%s", kUsage);
+ }
+ return exit_code;
+ }
+
+ void OnShutdown(int* exit_code) override {
+ attestation_.reset();
+ ClientLoopBase::OnShutdown(exit_code);
+ }
+
+ private:
+ // Posts tasks according to the command line options.
+ int ScheduleCommand() {
+ base::Closure task;
+ base::CommandLine* command_line = base::CommandLine::ForCurrentProcess();
+ const auto& args = command_line->GetArgs();
+ if (command_line->HasSwitch("help") || command_line->HasSwitch("h") ||
+ (!args.empty() && args.front() == "help")) {
+ return EX_USAGE;
+ }
+ if (args.empty() || args.front() == kCreateAndCertifyCommand) {
+ task = base::Bind(&ClientLoop::CallCreateGoogleAttestedKey,
+ weak_factory_.GetWeakPtr(),
+ command_line->GetSwitchValueASCII("label"),
+ command_line->GetSwitchValueASCII("user"));
+ } else if (args.front() == kCreateCommand) {
+ std::string usage_str = command_line->GetSwitchValueASCII("usage");
+ KeyUsage usage;
+ if (usage_str.empty() || usage_str == "sign") {
+ usage = KEY_USAGE_SIGN;
+ } else if (usage_str == "decrypt") {
+ usage = KEY_USAGE_DECRYPT;
+ } else {
+ return EX_USAGE;
+ }
+ task = base::Bind(&ClientLoop::CallCreateCertifiableKey,
+ weak_factory_.GetWeakPtr(),
+ command_line->GetSwitchValueASCII("label"),
+ command_line->GetSwitchValueASCII("user"),
+ usage);
+ } else if (args.front() == kInfoCommand) {
+ task = base::Bind(&ClientLoop::CallGetKeyInfo,
+ weak_factory_.GetWeakPtr(),
+ command_line->GetSwitchValueASCII("label"),
+ command_line->GetSwitchValueASCII("user"));
+ } else if (args.front() == kEndorsementCommand) {
+ task = base::Bind(&ClientLoop::CallGetEndorsementInfo,
+ weak_factory_.GetWeakPtr());
+ } else if (args.front() == kAttestationKeyCommand) {
+ task = base::Bind(&ClientLoop::CallGetAttestationKeyInfo,
+ weak_factory_.GetWeakPtr());
+ } else if (args.front() == kActivateCommand) {
+ if (!command_line->HasSwitch("input")) {
+ return EX_USAGE;
+ }
+ std::string input;
+ base::FilePath filename(command_line->GetSwitchValueASCII("input"));
+ if (!base::ReadFileToString(filename, &input)) {
+ LOG(ERROR) << "Failed to read file: " << filename.value();
+ return EX_NOINPUT;
+ }
+ task = base::Bind(&ClientLoop::CallActivateAttestationKey,
+ weak_factory_.GetWeakPtr(),
+ input);
+ } else if (args.front() == kEncryptForActivateCommand) {
+ if (!command_line->HasSwitch("input") ||
+ !command_line->HasSwitch("output")) {
+ return EX_USAGE;
+ }
+ std::string input;
+ base::FilePath filename(command_line->GetSwitchValueASCII("input"));
+ if (!base::ReadFileToString(filename, &input)) {
+ LOG(ERROR) << "Failed to read file: " << filename.value();
+ return EX_NOINPUT;
+ }
+ task = base::Bind(&ClientLoop::EncryptForActivate,
+ weak_factory_.GetWeakPtr(),
+ input);
+ } else if (args.front() == kEncryptCommand) {
+ if (!command_line->HasSwitch("input") ||
+ !command_line->HasSwitch("output")) {
+ return EX_USAGE;
+ }
+ std::string input;
+ base::FilePath filename(command_line->GetSwitchValueASCII("input"));
+ if (!base::ReadFileToString(filename, &input)) {
+ LOG(ERROR) << "Failed to read file: " << filename.value();
+ return EX_NOINPUT;
+ }
+ task = base::Bind(&ClientLoop::Encrypt,
+ weak_factory_.GetWeakPtr(),
+ command_line->GetSwitchValueASCII("label"),
+ command_line->GetSwitchValueASCII("user"),
+ input);
+ } else if (args.front() == kDecryptCommand) {
+ if (!command_line->HasSwitch("input")) {
+ return EX_USAGE;
+ }
+ std::string input;
+ base::FilePath filename(command_line->GetSwitchValueASCII("input"));
+ if (!base::ReadFileToString(filename, &input)) {
+ LOG(ERROR) << "Failed to read file: " << filename.value();
+ return EX_NOINPUT;
+ }
+ task = base::Bind(&ClientLoop::CallDecrypt,
+ weak_factory_.GetWeakPtr(),
+ command_line->GetSwitchValueASCII("label"),
+ command_line->GetSwitchValueASCII("user"),
+ input);
+ } else if (args.front() == kSignCommand) {
+ if (!command_line->HasSwitch("input")) {
+ return EX_USAGE;
+ }
+ std::string input;
+ base::FilePath filename(command_line->GetSwitchValueASCII("input"));
+ if (!base::ReadFileToString(filename, &input)) {
+ LOG(ERROR) << "Failed to read file: " << filename.value();
+ return EX_NOINPUT;
+ }
+ task = base::Bind(&ClientLoop::CallSign,
+ weak_factory_.GetWeakPtr(),
+ command_line->GetSwitchValueASCII("label"),
+ command_line->GetSwitchValueASCII("user"),
+ input);
+ } else if (args.front() == kVerifyCommand) {
+ if (!command_line->HasSwitch("input") ||
+ !command_line->HasSwitch("signature")) {
+ return EX_USAGE;
+ }
+ std::string input;
+ base::FilePath filename(command_line->GetSwitchValueASCII("input"));
+ if (!base::ReadFileToString(filename, &input)) {
+ LOG(ERROR) << "Failed to read file: " << filename.value();
+ return EX_NOINPUT;
+ }
+ std::string signature;
+ base::FilePath filename2(command_line->GetSwitchValueASCII("signature"));
+ if (!base::ReadFileToString(filename2, &signature)) {
+ LOG(ERROR) << "Failed to read file: " << filename2.value();
+ return EX_NOINPUT;
+ }
+ task = base::Bind(&ClientLoop::VerifySignature,
+ weak_factory_.GetWeakPtr(),
+ command_line->GetSwitchValueASCII("label"),
+ command_line->GetSwitchValueASCII("user"),
+ input,
+ signature);
+ } else if (args.front() == kRegisterCommand) {
+ task = base::Bind(&ClientLoop::CallRegister,
+ weak_factory_.GetWeakPtr(),
+ command_line->GetSwitchValueASCII("label"),
+ command_line->GetSwitchValueASCII("user"));
+ } else {
+ return EX_USAGE;
+ }
+ base::MessageLoop::current()->PostTask(FROM_HERE, task);
+ return EX_OK;
+ }
+
+ template <typename ProtobufType>
+ void PrintReplyAndQuit(const ProtobufType& reply) {
+ printf("%s\n", GetProtoDebugString(reply).c_str());
+ Quit();
+ }
+
+ void WriteOutput(const std::string& output) {
+ base::FilePath filename(base::CommandLine::ForCurrentProcess()->
+ GetSwitchValueASCII("output"));
+ if (base::WriteFile(filename, output.data(), output.size()) !=
+ static_cast<int>(output.size())) {
+ LOG(ERROR) << "Failed to write file: " << filename.value();
+ QuitWithExitCode(EX_IOERR);
+ }
+ }
+
+ void CallCreateGoogleAttestedKey(const std::string& label,
+ const std::string& username) {
+ CreateGoogleAttestedKeyRequest request;
+ request.set_key_label(label);
+ request.set_key_type(KEY_TYPE_RSA);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ request.set_certificate_profile(ENTERPRISE_MACHINE_CERTIFICATE);
+ request.set_username(username);
+ attestation_->CreateGoogleAttestedKey(
+ request,
+ base::Bind(&ClientLoop::PrintReplyAndQuit<CreateGoogleAttestedKeyReply>,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void CallGetKeyInfo(const std::string& label, const std::string& username) {
+ GetKeyInfoRequest request;
+ request.set_key_label(label);
+ request.set_username(username);
+ attestation_->GetKeyInfo(
+ request,
+ base::Bind(&ClientLoop::PrintReplyAndQuit<GetKeyInfoReply>,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void CallGetEndorsementInfo() {
+ GetEndorsementInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ attestation_->GetEndorsementInfo(
+ request,
+ base::Bind(&ClientLoop::PrintReplyAndQuit<GetEndorsementInfoReply>,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void CallGetAttestationKeyInfo() {
+ GetAttestationKeyInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ attestation_->GetAttestationKeyInfo(
+ request,
+ base::Bind(&ClientLoop::PrintReplyAndQuit<GetAttestationKeyInfoReply>,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void CallActivateAttestationKey(const std::string& input) {
+ ActivateAttestationKeyRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ request.mutable_encrypted_certificate()->ParseFromString(input);
+ request.set_save_certificate(true);
+ attestation_->ActivateAttestationKey(
+ request,
+ base::Bind(&ClientLoop::PrintReplyAndQuit<ActivateAttestationKeyReply>,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void EncryptForActivate(const std::string& input) {
+ GetEndorsementInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ attestation_->GetEndorsementInfo(
+ request,
+ base::Bind(&ClientLoop::EncryptForActivate2,
+ weak_factory_.GetWeakPtr(),
+ input));
+ }
+
+ void EncryptForActivate2(const std::string& input,
+ const GetEndorsementInfoReply& endorsement_info) {
+ if (endorsement_info.status() != STATUS_SUCCESS) {
+ PrintReplyAndQuit(endorsement_info);
+ }
+ GetAttestationKeyInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ attestation_->GetAttestationKeyInfo(
+ request,
+ base::Bind(&ClientLoop::EncryptForActivate3,
+ weak_factory_.GetWeakPtr(),
+ input,
+ endorsement_info));
+ }
+
+ void EncryptForActivate3(
+ const std::string& input,
+ const GetEndorsementInfoReply& endorsement_info,
+ const GetAttestationKeyInfoReply& attestation_key_info) {
+ if (attestation_key_info.status() != STATUS_SUCCESS) {
+ PrintReplyAndQuit(attestation_key_info);
+ }
+ CryptoUtilityImpl crypto(nullptr);
+ EncryptedIdentityCredential encrypted;
+ if (!crypto.EncryptIdentityCredential(
+ input,
+ endorsement_info.ek_public_key(),
+ attestation_key_info.public_key_tpm_format(),
+ &encrypted)) {
+ QuitWithExitCode(EX_SOFTWARE);
+ }
+ std::string output;
+ encrypted.SerializeToString(&output);
+ WriteOutput(output);
+ Quit();
+ }
+
+ void CallCreateCertifiableKey(const std::string& label,
+ const std::string& username,
+ KeyUsage usage) {
+ CreateCertifiableKeyRequest request;
+ request.set_key_label(label);
+ request.set_username(username);
+ request.set_key_type(KEY_TYPE_RSA);
+ request.set_key_usage(usage);
+ attestation_->CreateCertifiableKey(
+ request,
+ base::Bind(&ClientLoop::PrintReplyAndQuit<CreateCertifiableKeyReply>,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void Encrypt(const std::string& label,
+ const std::string& username,
+ const std::string& input) {
+ GetKeyInfoRequest request;
+ request.set_key_label(label);
+ request.set_username(username);
+ attestation_->GetKeyInfo(request, base::Bind(&ClientLoop::Encrypt2,
+ weak_factory_.GetWeakPtr(),
+ input));
+ }
+
+ void Encrypt2(const std::string& input,
+ const GetKeyInfoReply& key_info) {
+ CryptoUtilityImpl crypto(nullptr);
+ std::string output;
+ if (!crypto.EncryptForUnbind(key_info.public_key(), input, &output)) {
+ QuitWithExitCode(EX_SOFTWARE);
+ }
+ WriteOutput(output);
+ Quit();
+ }
+
+ void CallDecrypt(const std::string& label,
+ const std::string& username,
+ const std::string& input) {
+ DecryptRequest request;
+ request.set_key_label(label);
+ request.set_username(username);
+ request.set_encrypted_data(input);
+ attestation_->Decrypt(
+ request,
+ base::Bind(&ClientLoop::PrintReplyAndQuit<DecryptReply>,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void CallSign(const std::string& label,
+ const std::string& username,
+ const std::string& input) {
+ SignRequest request;
+ request.set_key_label(label);
+ request.set_username(username);
+ request.set_data_to_sign(input);
+ attestation_->Sign(request, base::Bind(&ClientLoop::OnSignComplete,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ void OnSignComplete(const SignReply& reply) {
+ if (reply.status() == STATUS_SUCCESS &&
+ base::CommandLine::ForCurrentProcess()->HasSwitch("output")) {
+ WriteOutput(reply.signature());
+ }
+ PrintReplyAndQuit<SignReply>(reply);
+ }
+
+ void VerifySignature(const std::string& label,
+ const std::string& username,
+ const std::string& input,
+ const std::string& signature) {
+ GetKeyInfoRequest request;
+ request.set_key_label(label);
+ request.set_username(username);
+ attestation_->GetKeyInfo(request, base::Bind(&ClientLoop::VerifySignature2,
+ weak_factory_.GetWeakPtr(),
+ input, signature));
+ }
+
+ void VerifySignature2(const std::string& input,
+ const std::string& signature,
+ const GetKeyInfoReply& key_info) {
+ CryptoUtilityImpl crypto(nullptr);
+ if (crypto.VerifySignature(key_info.public_key(), input, signature)) {
+ printf("Signature is OK!\n");
+ } else {
+ printf("Signature is BAD!\n");
+ }
+ Quit();
+ }
+
+ void CallRegister(const std::string& label, const std::string& username) {
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label(label);
+ request.set_username(username);
+ attestation_->RegisterKeyWithChapsToken(request, base::Bind(
+ &ClientLoop::PrintReplyAndQuit<RegisterKeyWithChapsTokenReply>,
+ weak_factory_.GetWeakPtr()));
+ }
+
+ std::unique_ptr<attestation::AttestationInterface> attestation_;
+
+ // Declare this last so weak pointers will be destroyed first.
+ base::WeakPtrFactory<ClientLoop> weak_factory_{this};
+
+ DISALLOW_COPY_AND_ASSIGN(ClientLoop);
+};
+
+} // namespace attestation
+
+int main(int argc, char* argv[]) {
+ base::CommandLine::Init(argc, argv);
+ brillo::InitLog(brillo::kLogToStderr);
+ attestation::ClientLoop loop;
+ return loop.Run();
+}
diff --git a/attestation/common/attestation_ca.proto b/attestation/common/attestation_ca.proto
new file mode 100644
index 0000000..b2cd3a3
--- /dev/null
+++ b/attestation/common/attestation_ca.proto
@@ -0,0 +1,193 @@
+//
+// 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.
+//
+
+option optimize_for = LITE_RUNTIME;
+
+import "common.proto";
+
+package attestation;
+
+// This message holds all information to be sent to the attestation server in
+// order to complete enrollment.
+message AttestationEnrollmentRequest {
+ // The EK cert, in X.509 form, encrypted using the server's public key with
+ // the following parameters:
+ // Key encryption: RSA-OAEP with no custom parameters.
+ // Data encryption: 256-bit key, AES-CBC with PKCS5 padding.
+ // MAC: HMAC-SHA-512 using the AES key.
+ optional EncryptedData encrypted_endorsement_credential = 1;
+ // The AIK public key, in TPM_PUBKEY form.
+ optional bytes identity_public_key = 2;
+ // PCR0 quoted by AIK.
+ optional Quote pcr0_quote = 3;
+ // PCR1 quoted by AIK.
+ optional Quote pcr1_quote = 4;
+}
+
+enum ResponseStatus {
+ OK = 0;
+ // Internal server error.
+ SERVER_ERROR = 1;
+ // The server cannot parse the request.
+ BAD_REQUEST = 2;
+ // The server rejects the request.
+ REJECT = 3;
+ // Only appears in enrollment response. The server returns the same generated
+ // id and reports the quota limit exceeded status when the number of reset
+ // action in a specified time window is more than self reset limitation.
+ QUOTA_LIMIT_EXCEEDED = 4;
+}
+
+// The response from the attestation server for the enrollment request.
+message AttestationEnrollmentResponse {
+ optional ResponseStatus status = 1;
+ // Detail response message. Included when the result is not OK.
+ optional string detail = 2;
+ optional EncryptedIdentityCredential encrypted_identity_credential = 3;
+}
+
+// The certificate request to be sent to the attestation server.
+message AttestationCertificateRequest {
+ // The AIK cert in X.509 format.
+ optional bytes identity_credential = 1;
+ // A certified public key in TPM_PUBKEY.
+ optional bytes certified_public_key = 3;
+ // The serialized TPM_CERTIFY_INFO for the certified key.
+ optional bytes certified_key_info = 4;
+ // The signature of the TPM_CERTIFY_INFO by the AIK.
+ optional bytes certified_key_proof = 5;
+ // A message identifier to be included in the response.
+ optional bytes message_id = 10;
+ // The certificate profile defines the type of certificate to issue.
+ optional CertificateProfile profile = 11;
+ // Information about the origin of the request which may be used depending on
+ // the certificate profile.
+ optional string origin = 12;
+ // The index of a temporal value. This may be used or ignored depending on
+ // the certificate profile.
+ optional int32 temporal_index = 13;
+}
+
+// The response from the attestation server for the certificate request.
+message AttestationCertificateResponse {
+ optional ResponseStatus status = 1;
+ // Detail response message. Included when the result is not OK.
+ optional string detail = 2;
+ // The credential of the certified key in X.509 format.
+ optional bytes certified_key_credential = 3;
+ // The issuer intermediate CA certificate in X.509 format.
+ optional bytes intermediate_ca_cert = 5;
+ // A message identifier from the request this message is responding to.
+ optional bytes message_id = 6;
+ // Additional intermediate CA certificates that can help in validation.
+ // Certificate chaining order is from the leaf to the root. That is,
+ // |certified_key_credential| is signed by
+ // |intermediate_ca_cert|, which is signed by
+ // |additional_intermediate_ca_cert(0)|, which is signed by
+ // |additional_intermediate_ca_cert(1)|, ... and so on.
+ repeated bytes additional_intermediate_ca_cert = 7;
+}
+
+// The reset request to be sent to the attestation server.
+message AttestationResetRequest {
+ // The AIK cert, in X.509 form, encrypted using the server's public key with
+ // the following parameters:
+ // Key encryption: RSA-OAEP with no custom parameters.
+ // Data encryption: 256-bit key, AES-CBC with PKCS5 padding.
+ // MAC: HMAC-SHA-512 using the AES key.
+ optional EncryptedData encrypted_identity_credential = 1;
+
+ // The one time token to make sure the reset process can be triggered only once.
+ optional bytes token = 2;
+
+ // The EK cert, in X.509 form, encrypted using the server's public key with
+ // the following parameters:
+ // Key encryption: RSA-OAEP with no custom parameters.
+ // Data encryption: 256-bit key, AES-CBC with PKCS5 padding.
+ // MAC: HMAC-SHA-512 using the AES key.
+ optional EncryptedData encrypted_endorsement_credential = 3;
+}
+
+// The response from the attestation server for the reset request.
+message AttestationResetResponse {
+ // The response status.
+ optional ResponseStatus status = 1;
+ // Detail response message. Included when the result is not OK.
+ optional string detail = 2;
+}
+
+// The challenge data (as in challenge-response) generated by the server.
+// Before transmitted to the client, this message will be wrapped as a
+// SignedData message, in which the data field is the serialized Challenge
+// message, and the signature field is the signature of the data field signed
+// by the enterprise server using a hard-coded key. The signature algorithm is
+// RSASSA-PKCS1-v1_5-SHA256.
+message Challenge {
+ // A string for the client to sanity check a legitimate challenge.
+ optional string prefix = 1;
+ // A 256-bit random value generated by the server.
+ optional bytes nonce = 2;
+ // A timestamp for a stateless server to limit the timeframe during which the
+ // challenge may be replayed.
+ optional int64 timestamp = 3;
+}
+
+// The response data (as in challenge-response) generated by the client.
+// Before transmitted to the server, this message will be wrapped as a
+// SignedData message, in which the data field is the serialized
+// ChallengeResponse message, and the signature field is the signature of the
+// data field signed by the client using the key being challenged. The
+// signature algorithm is RSASSA-PKCS1-v1_5-SHA256.
+message ChallengeResponse {
+ // The original challenge data.
+ optional SignedData challenge = 1;
+ // A 256-bit random value generated by the client. Mixing in this nonce
+ // prevents a caller from using a challenge to sign arbitrary data.
+ optional bytes nonce = 2;
+ // The KeyInfo message encrypted using a public encryption key, pushed via
+ // policy with the following parameters:
+ // Key encryption: RSA-OAEP with no custom parameters.
+ // Data encryption: 256-bit key, AES-CBC with PKCS5 padding.
+ // MAC: HMAC-SHA-512 using the AES key.
+ optional EncryptedData encrypted_key_info = 3;
+}
+
+// The data type of the message decrypted from
+// ChallengeResponse.encrypted_key_info.encrypted_data field. This message holds
+// information required by enterprise server to complete the verification.
+message KeyInfo {
+ // Indicates whether the key is an EMK or EUK.
+ optional KeyProfile key_type = 1;
+ // Domain information about the device or user associated with the key. For an
+ // EMK, this value is the enrolled domain. For an EUK, this value is the
+ // user's email address.
+ optional string domain = 2;
+ // The virtual device ID associated with the device or user.
+ optional bytes device_id = 3;
+ // If the key is an EUK, this value is the PCA-issued certificate for the key.
+ optional bytes certificate = 4;
+ // If the key is an EUK, this value may hold a SignedPublicKeyAndChallenge
+ // with a random challenge. The SignedPublicKeyAndChallenge specification is
+ // here: https://developer.mozilla.org/en-US/docs/HTML/Element/keygen.
+ optional bytes signed_public_key_and_challenge = 5;
+}
+
+enum KeyProfile {
+ // Enterprise machine key.
+ EMK = 0;
+ // Enterprise user key.
+ EUK = 1;
+}
diff --git a/attestation/common/attestation_interface.h b/attestation/common/attestation_interface.h
new file mode 100644
index 0000000..ba9f7ed
--- /dev/null
+++ b/attestation/common/attestation_interface.h
@@ -0,0 +1,106 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_COMMON_ATTESTATION_INTERFACE_H_
+#define ATTESTATION_COMMON_ATTESTATION_INTERFACE_H_
+
+#include <string>
+
+#include <base/callback_forward.h>
+
+#include "attestation/common/interface.pb.h"
+
+namespace attestation {
+
+// The main attestation interface implemented by proxies and services. The
+// anticipated flow looks like this:
+// [APP] -> AttestationInterface -> [IPC] -> AttestationInterface
+class AttestationInterface {
+ public:
+ virtual ~AttestationInterface() = default;
+
+ // Performs initialization tasks that may take a long time. This method must
+ // be successfully called before calling any other method. Returns true on
+ // success.
+ virtual bool Initialize() = 0;
+
+ // Processes a CreateGoogleAttestedKeyRequest and responds with a
+ // CreateGoogleAttestedKeyReply.
+ using CreateGoogleAttestedKeyCallback =
+ base::Callback<void(const CreateGoogleAttestedKeyReply&)>;
+ virtual void CreateGoogleAttestedKey(
+ const CreateGoogleAttestedKeyRequest& request,
+ const CreateGoogleAttestedKeyCallback& callback) = 0;
+
+ // Processes a GetKeyInfoRequest and responds with a GetKeyInfoReply.
+ using GetKeyInfoCallback = base::Callback<void(const GetKeyInfoReply&)>;
+ virtual void GetKeyInfo(const GetKeyInfoRequest& request,
+ const GetKeyInfoCallback& callback) = 0;
+
+ // Processes a GetEndorsementInfoRequest and responds with a
+ // GetEndorsementInfoReply.
+ using GetEndorsementInfoCallback =
+ base::Callback<void(const GetEndorsementInfoReply&)>;
+ virtual void GetEndorsementInfo(
+ const GetEndorsementInfoRequest& request,
+ const GetEndorsementInfoCallback& callback) = 0;
+
+ // Processes a GetAttestationKeyInfoRequest and responds with a
+ // GetAttestationKeyInfoReply.
+ using GetAttestationKeyInfoCallback =
+ base::Callback<void(const GetAttestationKeyInfoReply&)>;
+ virtual void GetAttestationKeyInfo(
+ const GetAttestationKeyInfoRequest& request,
+ const GetAttestationKeyInfoCallback& callback) = 0;
+
+ // Processes a ActivateAttestationKeyRequest and responds with a
+ // ActivateAttestationKeyReply.
+ using ActivateAttestationKeyCallback =
+ base::Callback<void(const ActivateAttestationKeyReply&)>;
+ virtual void ActivateAttestationKey(
+ const ActivateAttestationKeyRequest& request,
+ const ActivateAttestationKeyCallback& callback) = 0;
+
+ // Processes a CreateCertifiableKeyRequest and responds with a
+ // CreateCertifiableKeyReply.
+ using CreateCertifiableKeyCallback =
+ base::Callback<void(const CreateCertifiableKeyReply&)>;
+ virtual void CreateCertifiableKey(
+ const CreateCertifiableKeyRequest& request,
+ const CreateCertifiableKeyCallback& callback) = 0;
+
+ // Processes a DecryptRequest and responds with a DecryptReply.
+ using DecryptCallback = base::Callback<void(const DecryptReply&)>;
+ virtual void Decrypt(const DecryptRequest& request,
+ const DecryptCallback& callback) = 0;
+
+ // Processes a SignRequest and responds with a SignReply.
+ using SignCallback = base::Callback<void(const SignReply&)>;
+ virtual void Sign(const SignRequest& request,
+ const SignCallback& callback) = 0;
+
+ // Processes a RegisterKeyWithChapsTokenRequest and responds with a
+ // RegisterKeyWithChapsTokenReply.
+ using RegisterKeyWithChapsTokenCallback =
+ base::Callback<void(const RegisterKeyWithChapsTokenReply&)>;
+ virtual void RegisterKeyWithChapsToken(
+ const RegisterKeyWithChapsTokenRequest& request,
+ const RegisterKeyWithChapsTokenCallback& callback) = 0;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_ATTESTATION_INTERFACE_H_
diff --git a/attestation/common/common.proto b/attestation/common/common.proto
new file mode 100644
index 0000000..bdb2775
--- /dev/null
+++ b/attestation/common/common.proto
@@ -0,0 +1,96 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+option optimize_for = LITE_RUNTIME;
+
+package attestation;
+
+// Describes key type.
+enum KeyType {
+ KEY_TYPE_RSA = 1;
+ KEY_TYPE_ECC = 2;
+}
+
+// Describes allowed key usage.
+enum KeyUsage {
+ KEY_USAGE_SIGN = 1;
+ KEY_USAGE_DECRYPT = 2;
+}
+
+// Enumerates various certificate profiles supported by the Attestation CA.
+enum CertificateProfile {
+ // A certificate intended for enterprise-owned devices. It has the following
+ // subjectName fields:
+ // CN=<stable device identifier>
+ // OU=state:[verified|developer]
+ // O=Chrome Device Enterprise
+ ENTERPRISE_MACHINE_CERTIFICATE = 0;
+
+ // A certificate intended for enterprise-owned user accounts. It has the
+ // following subjectName fields:
+ // OU=state:[verified|developer]
+ // O=Chrome Device Enterprise
+ ENTERPRISE_USER_CERTIFICATE = 1;
+
+ // A certificate intended for platform verification by providers of protected
+ // content. It has the following subjectName fields:
+ // O=Chrome Device Content Protection
+ CONTENT_PROTECTION_CERTIFICATE = 2;
+
+ // Like above, but it also includes a stable ID and origin.
+ // CN=<origin-specific device identifier>
+ // OU=<origin>
+ // O=Chrome Device Content Protection
+ CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID = 3;
+
+ // A certificate intended for cast devices.
+ CAST_CERTIFICATE = 4;
+
+ GFSC_CERTIFICATE = 5;
+}
+
+// Holds information about a quote generated by the TPM.
+message Quote {
+ // The quote; a signature generated with the AIK.
+ optional bytes quote = 1;
+ // The serialized data that was quoted; this assists in verifying the quote.
+ optional bytes quoted_data = 2;
+ // The value of the PCR(s) at the time the quote was generated.
+ optional bytes quoted_pcr_value = 3;
+ // Source data which was originally used to extend the PCR. If this field
+ // exists it can be expected that SHA1(pcr_source_hint) was extended into the
+ // PCR.
+ optional bytes pcr_source_hint = 4;
+}
+
+// Holds encrypted data and information required to decrypt it.
+message EncryptedData {
+ // A key that has been sealed to the TPM or wrapped by another key.
+ optional bytes wrapped_key = 2;
+ // The initialization vector used during encryption.
+ optional bytes iv = 3;
+ // MAC of (iv || encrypted_data).
+ optional bytes mac = 4;
+ optional bytes encrypted_data = 5;
+ // An identifier for the wrapping key to assist in decryption.
+ optional bytes wrapping_key_id = 6;
+}
+
+// The wrapper message of any data and its signature.
+message SignedData {
+ // The data to be signed.
+ optional bytes data = 1;
+ // The signature of the data field.
+ optional bytes signature = 2;
+}
+
+// These two fields are suitable for passing to Tspi_TPM_ActivateIdentity()
+// directly.
+message EncryptedIdentityCredential {
+ // TPM_ASYM_CA_CONTENTS, encrypted with EK public key.
+ optional bytes asym_ca_contents = 1;
+ // TPM_SYM_CA_ATTESTATION, encrypted with the key in aysm_ca_contents.
+ optional bytes sym_ca_attestation = 2;
+}
+
diff --git a/attestation/common/crypto_utility.h b/attestation/common/crypto_utility.h
new file mode 100644
index 0000000..1b9e708
--- /dev/null
+++ b/attestation/common/crypto_utility.h
@@ -0,0 +1,98 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_COMMON_CRYPTO_UTILITY_H_
+#define ATTESTATION_COMMON_CRYPTO_UTILITY_H_
+
+#include <string>
+
+#include "attestation/common/common.pb.h"
+
+namespace attestation {
+
+// A class which provides helpers for cryptography-related tasks.
+class CryptoUtility {
+ 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,
+ std::string* sealed_key) = 0;
+
+ // Encrypts the given |data| using the |aes_key|. The |sealed_key| will be
+ // embedded in the |encrypted_data| to assist with decryption. It can be
+ // extracted from the |encrypted_data| using UnsealKey(). Returns true on
+ // success.
+ virtual bool EncryptData(const std::string& data,
+ const std::string& aes_key,
+ const std::string& sealed_key,
+ std::string* encrypted_data) = 0;
+
+ // Extracts and unseals the |aes_key| from the |sealed_key| embedded in
+ // the given |encrypted_data|. The |sealed_key| is also provided as an output
+ // so callers can make subsequent calls to EncryptData() with the same key.
+ // Returns true on success.
+ virtual bool UnsealKey(const std::string& encrypted_data,
+ std::string* aes_key,
+ std::string* sealed_key) = 0;
+
+ // Decrypts |encrypted_data| using |aes_key|, producing the decrypted |data|.
+ // Returns true on success.
+ virtual bool DecryptData(const std::string& encrypted_data,
+ const std::string& aes_key,
+ std::string* data) = 0;
+
+ // Convert |public_key| from PKCS #1 RSAPublicKey to X.509
+ // SubjectPublicKeyInfo. On success returns true and provides the
+ // |public_key_info|.
+ virtual bool GetRSASubjectPublicKeyInfo(const std::string& public_key,
+ std::string* public_key_info) = 0;
+
+ // Convert |public_key_info| from X.509 SubjectPublicKeyInfo to PKCS #1
+ // RSAPublicKey. On success returns true and provides the |public_key|.
+ virtual bool GetRSAPublicKey(const std::string& public_key_info,
+ std::string* public_key) = 0;
+
+ // Encrypts a |credential| in a format compatible with TPM attestation key
+ // activation. The |ek_public_key_info| must be provided in X.509
+ // SubjectPublicKeyInfo format and the |aik_public_key| must be provided in
+ // TPM_PUBKEY format.
+ virtual bool EncryptIdentityCredential(
+ const std::string& credential,
+ const std::string& ek_public_key_info,
+ const std::string& aik_public_key,
+ EncryptedIdentityCredential* encrypted) = 0;
+
+ // Encrypts |data| in a format compatible with the TPM unbind operation. The
+ // |public_key| must be provided in X.509 SubjectPublicKeyInfo format.
+ virtual bool EncryptForUnbind(const std::string& public_key,
+ const std::string& data,
+ std::string* encrypted_data) = 0;
+
+ // Verifies a PKCS #1 v1.5 SHA-256 |signature| over |data|. The |public_key|
+ // must be provided in X.509 SubjectPublicKeyInfo format.
+ virtual bool VerifySignature(const std::string& public_key,
+ const std::string& data,
+ const std::string& signature) = 0;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_CRYPTO_UTILITY_H_
diff --git a/attestation/common/crypto_utility_impl.cc b/attestation/common/crypto_utility_impl.cc
new file mode 100644
index 0000000..c506fcb
--- /dev/null
+++ b/attestation/common/crypto_utility_impl.cc
@@ -0,0 +1,472 @@
+//
+// 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/common/crypto_utility_impl.h"
+
+#include <limits>
+#include <string>
+
+#include <arpa/inet.h>
+#include <base/sha1.h>
+#include <base/stl_util.h>
+#include <crypto/scoped_openssl_types.h>
+#include <crypto/secure_util.h>
+#include <crypto/sha2.h>
+#include <openssl/bio.h>
+#include <openssl/err.h>
+#include <openssl/evp.h>
+#include <openssl/hmac.h>
+#include <openssl/rand.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <openssl/x509.h>
+
+namespace {
+
+const size_t kAesKeySize = 32;
+const size_t kAesBlockSize = 16;
+
+std::string GetOpenSSLError() {
+ BIO* bio = BIO_new(BIO_s_mem());
+ ERR_print_errors(bio);
+ char* data = nullptr;
+ int data_len = BIO_get_mem_data(bio, &data);
+ std::string error_string(data, data_len);
+ BIO_free(bio);
+ return error_string;
+}
+
+unsigned char* StringAsOpenSSLBuffer(std::string* s) {
+ return reinterpret_cast<unsigned char*>(string_as_array(s));
+}
+
+} // namespace
+
+namespace attestation {
+
+CryptoUtilityImpl::CryptoUtilityImpl(TpmUtility* tpm_utility)
+ : tpm_utility_(tpm_utility) {
+ OpenSSL_add_all_algorithms();
+ ERR_load_crypto_strings();
+}
+
+CryptoUtilityImpl::~CryptoUtilityImpl() {
+ EVP_cleanup();
+ ERR_free_strings();
+}
+
+bool CryptoUtilityImpl::GetRandom(size_t num_bytes,
+ std::string* random_data) const {
+ // OpenSSL takes a signed integer.
+ if (num_bytes > static_cast<size_t>(std::numeric_limits<int>::max())) {
+ return false;
+ }
+ random_data->resize(num_bytes);
+ unsigned char* buffer = StringAsOpenSSLBuffer(random_data);
+ return (RAND_bytes(buffer, num_bytes) == 1);
+}
+
+bool CryptoUtilityImpl::CreateSealedKey(std::string* aes_key,
+ std::string* sealed_key) {
+ if (!GetRandom(kAesKeySize, aes_key)) {
+ LOG(ERROR) << __func__ << ": GetRandom failed.";
+ return false;
+ }
+ if (!tpm_utility_->SealToPCR0(*aes_key, sealed_key)) {
+ LOG(ERROR) << __func__ << ": Failed to seal cipher key.";
+ return false;
+ }
+ return true;
+}
+
+bool CryptoUtilityImpl::EncryptData(const std::string& data,
+ const std::string& aes_key,
+ const std::string& sealed_key,
+ std::string* encrypted_data) {
+ std::string iv;
+ if (!GetRandom(kAesBlockSize, &iv)) {
+ LOG(ERROR) << __func__ << ": GetRandom failed.";
+ return false;
+ }
+ std::string raw_encrypted_data;
+ if (!AesEncrypt(data, aes_key, iv, &raw_encrypted_data)) {
+ LOG(ERROR) << __func__ << ": AES encryption failed.";
+ return false;
+ }
+ EncryptedData encrypted_pb;
+ encrypted_pb.set_wrapped_key(sealed_key);
+ encrypted_pb.set_iv(iv);
+ encrypted_pb.set_encrypted_data(raw_encrypted_data);
+ encrypted_pb.set_mac(HmacSha512(iv + raw_encrypted_data, aes_key));
+ if (!encrypted_pb.SerializeToString(encrypted_data)) {
+ LOG(ERROR) << __func__ << ": Failed to serialize protobuf.";
+ return false;
+ }
+ return true;
+}
+
+bool CryptoUtilityImpl::UnsealKey(const std::string& encrypted_data,
+ std::string* aes_key,
+ std::string* sealed_key) {
+ EncryptedData encrypted_pb;
+ if (!encrypted_pb.ParseFromString(encrypted_data)) {
+ LOG(ERROR) << __func__ << ": Failed to parse protobuf.";
+ return false;
+ }
+ *sealed_key = encrypted_pb.wrapped_key();
+ if (!tpm_utility_->Unseal(*sealed_key, aes_key)) {
+ LOG(ERROR) << __func__ << ": Cannot unseal aes key.";
+ return false;
+ }
+ return true;
+}
+
+bool CryptoUtilityImpl::DecryptData(const std::string& encrypted_data,
+ const std::string& aes_key,
+ std::string* data) {
+ EncryptedData encrypted_pb;
+ if (!encrypted_pb.ParseFromString(encrypted_data)) {
+ LOG(ERROR) << __func__ << ": Failed to parse protobuf.";
+ return false;
+ }
+ std::string mac = HmacSha512(
+ encrypted_pb.iv() + encrypted_pb.encrypted_data(),
+ aes_key);
+ if (mac.length() != encrypted_pb.mac().length()) {
+ LOG(ERROR) << __func__ << ": Corrupted data in encrypted pb.";
+ return false;
+ }
+ if (!crypto::SecureMemEqual(mac.data(), encrypted_pb.mac().data(),
+ mac.length())) {
+ LOG(ERROR) << __func__ << ": Corrupted data in encrypted pb.";
+ return false;
+ }
+ if (!AesDecrypt(encrypted_pb.encrypted_data(), aes_key, encrypted_pb.iv(),
+ data)) {
+ LOG(ERROR) << __func__ << ": AES decryption failed.";
+ return false;
+ }
+ return true;
+}
+
+bool CryptoUtilityImpl::GetRSASubjectPublicKeyInfo(
+ const std::string& public_key,
+ std::string* public_key_info) {
+ auto asn1_ptr = reinterpret_cast<const unsigned char*>(public_key.data());
+ crypto::ScopedRSA rsa(d2i_RSAPublicKey(nullptr, &asn1_ptr,
+ public_key.size()));
+ if (!rsa.get()) {
+ LOG(ERROR) << __func__ << ": Failed to decode public key: "
+ << GetOpenSSLError();
+ return false;
+ }
+ unsigned char* buffer = nullptr;
+ int length = i2d_RSA_PUBKEY(rsa.get(), &buffer);
+ if (length <= 0) {
+ LOG(ERROR) << __func__ << ": Failed to encode public key: "
+ << GetOpenSSLError();
+ return false;
+ }
+ crypto::ScopedOpenSSLBytes scoped_buffer(buffer);
+ public_key_info->assign(reinterpret_cast<char*>(buffer), length);
+ return true;
+}
+
+bool CryptoUtilityImpl::GetRSAPublicKey(const std::string& public_key_info,
+ std::string* public_key) {
+ auto asn1_ptr = reinterpret_cast<const unsigned char*>(
+ public_key_info.data());
+ crypto::ScopedRSA rsa(d2i_RSA_PUBKEY(NULL, &asn1_ptr,
+ public_key_info.size()));
+ if (!rsa.get()) {
+ LOG(ERROR) << __func__ << ": Failed to decode public key: "
+ << GetOpenSSLError();
+ return false;
+ }
+ unsigned char* buffer = NULL;
+ int length = i2d_RSAPublicKey(rsa.get(), &buffer);
+ if (length <= 0) {
+ LOG(ERROR) << __func__ << ": Failed to encode public key: "
+ << GetOpenSSLError();
+ return false;
+ }
+ crypto::ScopedOpenSSLBytes scoped_buffer(buffer);
+ public_key->assign(reinterpret_cast<char*>(buffer), length);
+ return true;
+}
+
+bool CryptoUtilityImpl::EncryptIdentityCredential(
+ const std::string& credential,
+ const std::string& ek_public_key_info,
+ const std::string& aik_public_key,
+ EncryptedIdentityCredential* encrypted) {
+ const char kAlgAES256 = 9; // This comes from TPM_ALG_AES256.
+ const char kEncModeCBC = 2; // This comes from TPM_SYM_MODE_CBC.
+ const char kAsymContentHeader[] =
+ {0, 0, 0, kAlgAES256, 0, kEncModeCBC, 0, kAesKeySize};
+ const char kSymContentHeader[12] = {};
+
+ // Generate an AES key and encrypt the credential.
+ std::string aes_key;
+ if (!GetRandom(kAesKeySize, &aes_key)) {
+ LOG(ERROR) << __func__ << ": GetRandom failed.";
+ return false;
+ }
+ std::string encrypted_credential;
+ if (!TssCompatibleEncrypt(credential, aes_key, &encrypted_credential)) {
+ LOG(ERROR) << __func__ << ": Failed to encrypt credential.";
+ return false;
+ }
+
+ // Construct a TPM_ASYM_CA_CONTENTS structure.
+ std::string asym_header(std::begin(kAsymContentHeader),
+ std::end(kAsymContentHeader));
+ std::string asym_content = asym_header + aes_key +
+ base::SHA1HashString(aik_public_key);
+
+ // Encrypt the TPM_ASYM_CA_CONTENTS with the EK public key.
+ auto asn1_ptr = reinterpret_cast<const unsigned char*>(
+ ek_public_key_info.data());
+ crypto::ScopedRSA rsa(d2i_RSA_PUBKEY(NULL, &asn1_ptr,
+ ek_public_key_info.size()));
+ if (!rsa.get()) {
+ LOG(ERROR) << __func__ << ": Failed to decode EK public key: "
+ << GetOpenSSLError();
+ return false;
+ }
+ std::string encrypted_asym_content;
+ if (!TpmCompatibleOAEPEncrypt(asym_content, rsa.get(),
+ &encrypted_asym_content)) {
+ LOG(ERROR) << __func__ << ": Failed to encrypt with EK public key.";
+ return false;
+ }
+
+ // Construct a TPM_SYM_CA_ATTESTATION structure.
+ uint32_t length = htonl(encrypted_credential.size());
+ auto length_bytes = reinterpret_cast<const char*>(&length);
+ std::string length_blob(length_bytes, sizeof(uint32_t));
+ std::string sym_header(std::begin(kSymContentHeader),
+ std::end(kSymContentHeader));
+ std::string sym_content = length_blob + sym_header + encrypted_credential;
+
+ encrypted->set_asym_ca_contents(encrypted_asym_content);
+ encrypted->set_sym_ca_attestation(sym_content);
+ return true;
+}
+
+bool CryptoUtilityImpl::EncryptForUnbind(const std::string& public_key,
+ const std::string& data,
+ std::string* encrypted_data) {
+ // Construct a TPM_BOUND_DATA structure.
+ const char kBoundDataHeader[] = {1, 1, 0, 0, 2 /* TPM_PT_BIND */};
+ std::string header(std::begin(kBoundDataHeader), std::end(kBoundDataHeader));
+ std::string bound_data = header + data;
+
+ // Encrypt using the TPM_ES_RSAESOAEP_SHA1_MGF1 scheme.
+ auto asn1_ptr = reinterpret_cast<const unsigned char*>(public_key.data());
+ crypto::ScopedRSA rsa(d2i_RSA_PUBKEY(NULL, &asn1_ptr, public_key.size()));
+ if (!rsa.get()) {
+ LOG(ERROR) << __func__ << ": Failed to decode public key: "
+ << GetOpenSSLError();
+ return false;
+ }
+ if (!TpmCompatibleOAEPEncrypt(bound_data, rsa.get(), encrypted_data)) {
+ LOG(ERROR) << __func__ << ": Failed to encrypt with public key.";
+ return false;
+ }
+ return true;
+}
+
+bool CryptoUtilityImpl::VerifySignature(const std::string& public_key,
+ const std::string& data,
+ const std::string& signature) {
+ auto asn1_ptr = reinterpret_cast<const unsigned char*>(public_key.data());
+ crypto::ScopedRSA rsa(d2i_RSA_PUBKEY(NULL, &asn1_ptr, public_key.size()));
+ if (!rsa.get()) {
+ LOG(ERROR) << __func__ << ": Failed to decode public key: "
+ << GetOpenSSLError();
+ return false;
+ }
+ std::string digest = crypto::SHA256HashString(data);
+ auto digest_buffer = reinterpret_cast<const unsigned char*>(digest.data());
+ std::string mutable_signature(signature);
+ unsigned char* signature_buffer = StringAsOpenSSLBuffer(&mutable_signature);
+ return (RSA_verify(NID_sha256, digest_buffer, digest.size(),
+ signature_buffer, signature.size(), rsa.get()) == 1);
+}
+
+bool CryptoUtilityImpl::AesEncrypt(const std::string& data,
+ const std::string& key,
+ const std::string& iv,
+ std::string* encrypted_data) {
+ if (key.size() != kAesKeySize || iv.size() != kAesBlockSize) {
+ return false;
+ }
+ if (data.size() > static_cast<size_t>(std::numeric_limits<int>::max())) {
+ // EVP_EncryptUpdate takes a signed int.
+ return false;
+ }
+ std::string mutable_data(data);
+ unsigned char* input_buffer = StringAsOpenSSLBuffer(&mutable_data);
+ std::string mutable_key(key);
+ unsigned char* key_buffer = StringAsOpenSSLBuffer(&mutable_key);
+ std::string mutable_iv(iv);
+ unsigned char* iv_buffer = StringAsOpenSSLBuffer(&mutable_iv);
+ // Allocate enough space for the output (including padding).
+ encrypted_data->resize(data.size() + kAesBlockSize);
+ auto output_buffer = reinterpret_cast<unsigned char*>(
+ string_as_array(encrypted_data));
+ int output_size = 0;
+ const EVP_CIPHER* cipher = EVP_aes_256_cbc();
+ EVP_CIPHER_CTX encryption_context;
+ EVP_CIPHER_CTX_init(&encryption_context);
+ if (!EVP_EncryptInit_ex(&encryption_context, cipher, nullptr, key_buffer,
+ iv_buffer)) {
+ LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
+ return false;
+ }
+ if (!EVP_EncryptUpdate(&encryption_context, output_buffer, &output_size,
+ input_buffer, data.size())) {
+ LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
+ EVP_CIPHER_CTX_cleanup(&encryption_context);
+ return false;
+ }
+ size_t total_size = output_size;
+ output_buffer += output_size;
+ output_size = 0;
+ if (!EVP_EncryptFinal_ex(&encryption_context, output_buffer, &output_size)) {
+ LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
+ EVP_CIPHER_CTX_cleanup(&encryption_context);
+ return false;
+ }
+ total_size += output_size;
+ encrypted_data->resize(total_size);
+ EVP_CIPHER_CTX_cleanup(&encryption_context);
+ return true;
+}
+
+bool CryptoUtilityImpl::AesDecrypt(const std::string& encrypted_data,
+ const std::string& key,
+ const std::string& iv,
+ std::string* data) {
+ if (key.size() != kAesKeySize || iv.size() != kAesBlockSize) {
+ return false;
+ }
+ if (encrypted_data.size() >
+ static_cast<size_t>(std::numeric_limits<int>::max())) {
+ // EVP_DecryptUpdate takes a signed int.
+ return false;
+ }
+ std::string mutable_encrypted_data(encrypted_data);
+ unsigned char* input_buffer = StringAsOpenSSLBuffer(&mutable_encrypted_data);
+ std::string mutable_key(key);
+ unsigned char* key_buffer = StringAsOpenSSLBuffer(&mutable_key);
+ std::string mutable_iv(iv);
+ unsigned char* iv_buffer = StringAsOpenSSLBuffer(&mutable_iv);
+ // Allocate enough space for the output.
+ data->resize(encrypted_data.size());
+ unsigned char* output_buffer = StringAsOpenSSLBuffer(data);
+ int output_size = 0;
+ const EVP_CIPHER* cipher = EVP_aes_256_cbc();
+ EVP_CIPHER_CTX decryption_context;
+ EVP_CIPHER_CTX_init(&decryption_context);
+ if (!EVP_DecryptInit_ex(&decryption_context, cipher, nullptr, key_buffer,
+ iv_buffer)) {
+ LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
+ return false;
+ }
+ if (!EVP_DecryptUpdate(&decryption_context, output_buffer, &output_size,
+ input_buffer, encrypted_data.size())) {
+ LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
+ EVP_CIPHER_CTX_cleanup(&decryption_context);
+ return false;
+ }
+ size_t total_size = output_size;
+ output_buffer += output_size;
+ output_size = 0;
+ if (!EVP_DecryptFinal_ex(&decryption_context, output_buffer, &output_size)) {
+ LOG(ERROR) << __func__ << ": " << GetOpenSSLError();
+ EVP_CIPHER_CTX_cleanup(&decryption_context);
+ return false;
+ }
+ total_size += output_size;
+ data->resize(total_size);
+ EVP_CIPHER_CTX_cleanup(&decryption_context);
+ return true;
+}
+
+std::string CryptoUtilityImpl::HmacSha512(const std::string& data,
+ const std::string& key) {
+ unsigned char mac[SHA512_DIGEST_LENGTH];
+ std::string mutable_data(data);
+ unsigned char* data_buffer = StringAsOpenSSLBuffer(&mutable_data);
+ HMAC(EVP_sha512(), key.data(), key.size(), data_buffer, data.size(), mac,
+ nullptr);
+ return std::string(std::begin(mac), std::end(mac));
+}
+
+bool CryptoUtilityImpl::TssCompatibleEncrypt(const std::string& input,
+ const std::string& key,
+ std::string* output) {
+ CHECK(output);
+ CHECK_EQ(key.size(), kAesKeySize);
+ std::string iv;
+ if (!GetRandom(kAesBlockSize, &iv)) {
+ LOG(ERROR) << __func__ << ": GetRandom failed.";
+ return false;
+ }
+ std::string encrypted;
+ if (!AesEncrypt(input, key, iv, &encrypted)) {
+ LOG(ERROR) << __func__ << ": Encryption failed.";
+ return false;
+ }
+ *output = iv + encrypted;
+ return true;
+}
+
+bool CryptoUtilityImpl::TpmCompatibleOAEPEncrypt(const std::string& input,
+ RSA* key,
+ std::string* output) {
+ CHECK(output);
+ // The custom OAEP parameter as specified in TPM Main Part 1, Section 31.1.1.
+ const unsigned char oaep_param[4] = {'T', 'C', 'P', 'A'};
+ std::string padded_input;
+ padded_input.resize(RSA_size(key));
+ auto padded_buffer = reinterpret_cast<unsigned char*>(
+ string_as_array(&padded_input));
+ auto input_buffer = reinterpret_cast<const unsigned char*>(input.data());
+ int result = RSA_padding_add_PKCS1_OAEP(padded_buffer, padded_input.size(),
+ input_buffer, input.size(),
+ oaep_param, arraysize(oaep_param));
+ if (!result) {
+ LOG(ERROR) << __func__ << ": Failed to add OAEP padding: "
+ << GetOpenSSLError();
+ return false;
+ }
+ output->resize(padded_input.size());
+ auto output_buffer = reinterpret_cast<unsigned char*>(
+ string_as_array(output));
+ result = RSA_public_encrypt(padded_input.size(), padded_buffer,
+ output_buffer, key, RSA_NO_PADDING);
+ if (result == -1) {
+ LOG(ERROR) << __func__ << ": Failed to encrypt OAEP padded input: "
+ << GetOpenSSLError();
+ return false;
+ }
+ return true;
+}
+
+} // namespace attestation
diff --git a/attestation/common/crypto_utility_impl.h b/attestation/common/crypto_utility_impl.h
new file mode 100644
index 0000000..0c88df7
--- /dev/null
+++ b/attestation/common/crypto_utility_impl.h
@@ -0,0 +1,100 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_COMMON_CRYPTO_UTILITY_IMPL_H_
+#define ATTESTATION_COMMON_CRYPTO_UTILITY_IMPL_H_
+
+#include "attestation/common/crypto_utility.h"
+
+#include <string>
+
+#include <openssl/rsa.h>
+
+#include "attestation/common/tpm_utility.h"
+
+namespace attestation {
+
+// An implementation of CryptoUtility.
+class CryptoUtilityImpl : public CryptoUtility {
+ public:
+ // Does not take ownership of pointers.
+ explicit CryptoUtilityImpl(TpmUtility* tpm_utility);
+ ~CryptoUtilityImpl() override;
+
+ // CryptoUtility methods.
+ bool GetRandom(size_t num_bytes, std::string* random_data) const override;
+ bool CreateSealedKey(std::string* aes_key, std::string* sealed_key) override;
+ bool EncryptData(const std::string& data,
+ const std::string& aes_key,
+ const std::string& sealed_key,
+ std::string* encrypted_data) override;
+ bool UnsealKey(const std::string& encrypted_data,
+ std::string* aes_key,
+ std::string* sealed_key) override;
+ bool DecryptData(const std::string& encrypted_data,
+ const std::string& aes_key,
+ std::string* data) override;
+ bool GetRSASubjectPublicKeyInfo(const std::string& public_key,
+ std::string* spki) override;
+ bool GetRSAPublicKey(const std::string& public_key_info,
+ std::string* public_key) override;
+ bool EncryptIdentityCredential(
+ const std::string& credential,
+ const std::string& ek_public_key_info,
+ const std::string& aik_public_key,
+ EncryptedIdentityCredential* encrypted) override;
+ bool EncryptForUnbind(const std::string& public_key,
+ const std::string& data,
+ std::string* encrypted_data) override;
+ bool VerifySignature(const std::string& public_key,
+ const std::string& data,
+ const std::string& signature) override;
+
+ private:
+ // Encrypts |data| using |key| and |iv| for AES in CBC mode with PKCS #5
+ // padding and produces the |encrypted_data|. Returns true on success.
+ bool AesEncrypt(const std::string& data,
+ const std::string& key,
+ const std::string& iv,
+ std::string* encrypted_data);
+
+ // Decrypts |encrypted_data| using |key| and |iv| for AES in CBC mode with
+ // PKCS #5 padding and produces the decrypted |data|. Returns true on success.
+ bool AesDecrypt(const std::string& encrypted_data,
+ const std::string& key,
+ const std::string& iv,
+ std::string* data);
+
+ // Computes and returns an HMAC of |data| using |key| and SHA-512.
+ std::string HmacSha512(const std::string& data, const std::string& key);
+
+ // Encrypt like trousers does. This is like AesEncrypt but a random IV is
+ // included in the output.
+ bool TssCompatibleEncrypt(const std::string& input,
+ const std::string& key,
+ std::string* output);
+
+ // Encrypts using RSA-OAEP and the TPM-specific OAEP parameter.
+ bool TpmCompatibleOAEPEncrypt(const std::string& input,
+ RSA* key,
+ std::string* output);
+
+ TpmUtility* tpm_utility_;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_CRYPTO_UTILITY_IMPL_H_
diff --git a/attestation/common/crypto_utility_impl_test.cc b/attestation/common/crypto_utility_impl_test.cc
new file mode 100644
index 0000000..9d37583
--- /dev/null
+++ b/attestation/common/crypto_utility_impl_test.cc
@@ -0,0 +1,241 @@
+//
+// 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 <memory>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "attestation/common/crypto_utility_impl.h"
+#include "attestation/common/mock_tpm_utility.h"
+
+using testing::_;
+using testing::NiceMock;
+using testing::Return;
+
+namespace {
+
+const char kValidPublicKeyHex[] =
+ "3082010A0282010100"
+ "961037BC12D2A298BEBF06B2D5F8C9B64B832A2237F8CF27D5F96407A6041A4D"
+ "AD383CB5F88E625F412E8ACD5E9D69DF0F4FA81FCE7955829A38366CBBA5A2B1"
+ "CE3B48C14B59E9F094B51F0A39155874C8DE18A0C299EBF7A88114F806BE4F25"
+ "3C29A509B10E4B19E31675AFE3B2DA77077D94F43D8CE61C205781ED04D183B4"
+ "C349F61B1956C64B5398A3A98FAFF17D1B3D9120C832763EDFC8F4137F6EFBEF"
+ "46D8F6DE03BD00E49DEF987C10BDD5B6F8758B6A855C23C982DDA14D8F0F2B74"
+ "E6DEFA7EEE5A6FC717EB0FF103CB8049F693A2C8A5039EF1F5C025DC44BD8435"
+ "E8D8375DADE00E0C0F5C196E04B8483CC98B1D5B03DCD7E0048B2AB343FFC11F"
+ "0203"
+ "010001";
+
+std::string HexDecode(const std::string hex) {
+ std::vector<uint8_t> output;
+ CHECK(base::HexStringToBytes(hex, &output));
+ return std::string(reinterpret_cast<char*>(output.data()), output.size());
+}
+
+} // namespace
+
+namespace attestation {
+
+class CryptoUtilityImplTest : public testing::Test {
+ public:
+ ~CryptoUtilityImplTest() override = default;
+ void SetUp() override {
+ crypto_utility_.reset(new CryptoUtilityImpl(&mock_tpm_utility_));
+ }
+
+ protected:
+ NiceMock<MockTpmUtility> mock_tpm_utility_;
+ std::unique_ptr<CryptoUtilityImpl> crypto_utility_;
+};
+
+TEST_F(CryptoUtilityImplTest, GetRandomSuccess) {
+ std::string random1;
+ EXPECT_TRUE(crypto_utility_->GetRandom(20, &random1));
+ std::string random2;
+ EXPECT_TRUE(crypto_utility_->GetRandom(20, &random2));
+ EXPECT_NE(random1, random2);
+}
+
+TEST_F(CryptoUtilityImplTest, GetRandomIntOverflow) {
+ size_t num_bytes = -1;
+ std::string buffer;
+ EXPECT_FALSE(crypto_utility_->GetRandom(num_bytes, &buffer));
+}
+
+TEST_F(CryptoUtilityImplTest, PairwiseSealedEncryption) {
+ std::string key;
+ std::string sealed_key;
+ EXPECT_TRUE(crypto_utility_->CreateSealedKey(&key, &sealed_key));
+ std::string data("test");
+ std::string encrypted_data;
+ EXPECT_TRUE(crypto_utility_->EncryptData(data, key, sealed_key,
+ &encrypted_data));
+ key.clear();
+ sealed_key.clear();
+ data.clear();
+ EXPECT_TRUE(crypto_utility_->UnsealKey(encrypted_data, &key, &sealed_key));
+ EXPECT_TRUE(crypto_utility_->DecryptData(encrypted_data, key, &data));
+ EXPECT_EQ("test", data);
+}
+
+TEST_F(CryptoUtilityImplTest, SealFailure) {
+ EXPECT_CALL(mock_tpm_utility_, SealToPCR0(_, _))
+ .WillRepeatedly(Return(false));
+ std::string key;
+ std::string sealed_key;
+ EXPECT_FALSE(crypto_utility_->CreateSealedKey(&key, &sealed_key));
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptNoData) {
+ std::string key(32, 0);
+ std::string output;
+ EXPECT_TRUE(crypto_utility_->EncryptData(std::string(), key, key, &output));
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptInvalidKey) {
+ std::string key(12, 0);
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->EncryptData(std::string(), key, key, &output));
+}
+
+TEST_F(CryptoUtilityImplTest, UnsealInvalidData) {
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->UnsealKey("invalid", &output, &output));
+}
+
+TEST_F(CryptoUtilityImplTest, UnsealError) {
+ EXPECT_CALL(mock_tpm_utility_, Unseal(_, _))
+ .WillRepeatedly(Return(false));
+ std::string key(32, 0);
+ std::string data;
+ EXPECT_TRUE(crypto_utility_->EncryptData("data", key, key, &data));
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->UnsealKey(data, &output, &output));
+}
+
+TEST_F(CryptoUtilityImplTest, DecryptInvalidKey) {
+ std::string key(12, 0);
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->DecryptData(std::string(), key, &output));
+}
+
+TEST_F(CryptoUtilityImplTest, DecryptInvalidData) {
+ std::string key(32, 0);
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->DecryptData("invalid", key, &output));
+}
+
+TEST_F(CryptoUtilityImplTest, DecryptInvalidData2) {
+ std::string key(32, 0);
+ std::string output;
+ EncryptedData proto;
+ std::string input;
+ proto.SerializeToString(&input);
+ EXPECT_FALSE(crypto_utility_->DecryptData(input, key, &output));
+}
+
+TEST_F(CryptoUtilityImplTest, GetRSASubjectPublicKeyInfo) {
+ std::string public_key = HexDecode(kValidPublicKeyHex);
+ std::string output;
+ EXPECT_TRUE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key, &output));
+}
+
+TEST_F(CryptoUtilityImplTest, GetRSASubjectPublicKeyInfoBadInput) {
+ std::string public_key = "bad_public_key";
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key,
+ &output));
+}
+
+TEST_F(CryptoUtilityImplTest, GetRSASubjectPublicKeyInfoPairWise) {
+ std::string public_key = HexDecode(kValidPublicKeyHex);
+ std::string output;
+ EXPECT_TRUE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key, &output));
+ std::string public_key2;
+ EXPECT_TRUE(crypto_utility_->GetRSAPublicKey(output, &public_key2));
+ EXPECT_EQ(public_key, public_key2);
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptIdentityCredential) {
+ std::string public_key = HexDecode(kValidPublicKeyHex);
+ std::string public_key_info;
+ EXPECT_TRUE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key,
+ &public_key_info));
+ EncryptedIdentityCredential output;
+ EXPECT_TRUE(crypto_utility_->EncryptIdentityCredential("credential",
+ public_key_info,
+ "aik",
+ &output));
+ EXPECT_TRUE(output.has_asym_ca_contents());
+ EXPECT_TRUE(output.has_sym_ca_attestation());
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptIdentityCredentialBadEK) {
+ EncryptedIdentityCredential output;
+ EXPECT_FALSE(crypto_utility_->EncryptIdentityCredential("credential",
+ "bad_ek",
+ "aik",
+ &output));
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptForUnbind) {
+ std::string public_key = HexDecode(kValidPublicKeyHex);
+ std::string public_key_info;
+ EXPECT_TRUE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key,
+ &public_key_info));
+ std::string output;
+ EXPECT_TRUE(crypto_utility_->EncryptForUnbind(public_key_info, "input",
+ &output));
+ EXPECT_FALSE(output.empty());
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptForUnbindBadKey) {
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->EncryptForUnbind("bad_key", "input", &output));
+}
+
+TEST_F(CryptoUtilityImplTest, EncryptForUnbindLargeInput) {
+ std::string public_key = HexDecode(kValidPublicKeyHex);
+ std::string public_key_info;
+ EXPECT_TRUE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key,
+ &public_key_info));
+ std::string input(1000, 'A');
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->EncryptForUnbind(public_key_info, input,
+ &output));
+}
+
+TEST_F(CryptoUtilityImplTest, VerifySignatureBadSignature) {
+ std::string public_key = HexDecode(kValidPublicKeyHex);
+ std::string public_key_info;
+ EXPECT_TRUE(crypto_utility_->GetRSASubjectPublicKeyInfo(public_key,
+ &public_key_info));
+ std::string output;
+ EXPECT_FALSE(crypto_utility_->VerifySignature(public_key_info, "input",
+ "signature"));
+}
+
+TEST_F(CryptoUtilityImplTest, VerifySignatureBadKey) {
+ EXPECT_FALSE(crypto_utility_->VerifySignature("bad_key", "input", ""));
+}
+
+} // namespace attestation
diff --git a/attestation/common/database.proto b/attestation/common/database.proto
new file mode 100644
index 0000000..74d0f99
--- /dev/null
+++ b/attestation/common/database.proto
@@ -0,0 +1,113 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+option optimize_for = LITE_RUNTIME;
+
+import "common.proto";
+
+package attestation;
+
+// Holds TPM credentials that the attestation server will need to see. These
+// credentials must be cleared once the attestation server has certified the
+// AIK.
+message TPMCredentials {
+ optional bytes endorsement_public_key = 1;
+ optional bytes endorsement_credential = 2;
+ optional bytes platform_credential = 3;
+ optional bytes conformance_credential = 4;
+ // The |endorsement_credential| encrypted with a public key associated with
+ // the default Chrome OS Privacy CA.
+ optional EncryptedData default_encrypted_endorsement_credential = 5;
+ optional EncryptedData alternate_encrypted_endorsement_credential = 6;
+}
+
+// Holds information relevant to a particular AIK.
+message IdentityKey {
+ // The DER encoded public key.
+ optional bytes identity_public_key = 1;
+ // The TPM-specific key blob that can be loaded back into the TPM.
+ optional bytes identity_key_blob = 2;
+ // A credential issued by the attestation server.
+ optional bytes identity_credential = 3;
+}
+
+// Holds information required to verify the binding of an AIK to an EK. This
+// information should be cleared once the attestation server has certified the
+// AIK.
+message IdentityBinding {
+ // The binding data, as output by the TPM_MakeIdentity operation.
+ optional bytes identity_binding = 1;
+ // The AIK public key, DER encoded.
+ optional bytes identity_public_key_der = 2;
+ // The AIK public key, in TPM_PUBKEY form.
+ optional bytes identity_public_key = 3;
+ // The label used during AIK creation.
+ optional bytes identity_label = 4;
+ // The PCA public key used during AIK creation, in TPM_PUBKEY form.
+ optional bytes pca_public_key = 5;
+}
+
+// Holds owner delegation information.
+message Delegation {
+ // The delegate owner blob.
+ optional bytes blob = 1;
+ // The authorization secret.
+ optional bytes secret = 2;
+ // Whether this delegate has permissions to call TPM_ResetLockValue.
+ optional bool has_reset_lock_permissions = 3;
+}
+
+// Holds information about a certified key.
+message CertifiedKey {
+ // The TPM-wrapped key blob.
+ optional bytes key_blob = 1;
+ // The public key in ASN.1 DER form.
+ optional bytes public_key = 2;
+ // The credential of the certified key in X.509 format.
+ optional bytes certified_key_credential = 3;
+ // The issuer intermediate CA certificate in X.509 format.
+ optional bytes intermediate_ca_cert = 4;
+ // A key name. This is not necessarily a unique identifier.
+ optional bytes key_name = 5;
+ // An arbitrary payload associated with the key.
+ optional bytes payload = 6;
+ // Addtional intermediate CA certificates that helps chaining up to the root
+ // CA. See |AttestationCertificateResponse.additional_intermediate_ca_cert|
+ // for more detail.
+ repeated bytes additional_intermediate_ca_cert = 7;
+ // The public key in TPM_PUBKEY form.
+ optional bytes public_key_tpm_format = 8;
+ // The serialized TPM_CERTIFY_INFO for the certified key.
+ optional bytes certified_key_info = 9;
+ // The signature of the TPM_CERTIFY_INFO by the AIK.
+ optional bytes certified_key_proof = 10;
+ // The original key type specified when the key was created.
+ optional KeyType key_type = 11;
+ // The original key usage specified when the key was created.
+ optional KeyUsage key_usage = 12;
+}
+
+// Holds all information that a client stores locally.
+message AttestationDatabase {
+ optional TPMCredentials credentials = 2;
+ optional IdentityBinding identity_binding = 3;
+ optional IdentityKey identity_key = 4;
+ optional Quote pcr0_quote = 5;
+ optional Quote pcr1_quote = 12;
+ optional Delegation delegate = 6;
+ repeated CertifiedKey device_keys = 7;
+
+ message TemporalIndexRecord {
+ optional bytes user_hash = 1;
+ optional bytes origin_hash = 2;
+ optional int32 temporal_index = 3;
+ }
+ repeated TemporalIndexRecord temporal_index_record = 8;
+
+ optional IdentityBinding alternate_identity_binding = 9;
+ optional IdentityKey alternate_identity_key = 10;
+ optional Quote alternate_pcr0_quote = 11;
+ optional Quote alternate_pcr1_quote = 13;
+}
+
diff --git a/attestation/common/dbus_interface.h b/attestation/common/dbus_interface.h
new file mode 100644
index 0000000..06132b5
--- /dev/null
+++ b/attestation/common/dbus_interface.h
@@ -0,0 +1,40 @@
+//
+// Copyright (C) 2014 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.
+//
+
+#ifndef ATTESTATION_COMMON_DBUS_INTERFACE_H_
+#define ATTESTATION_COMMON_DBUS_INTERFACE_H_
+
+namespace attestation {
+
+// TODO(namnguyen): Move to brillo/system_api once we're ready.
+constexpr char kAttestationInterface[] = "org.chromium.Attestation";
+constexpr char kAttestationServicePath[] = "/org/chromium/Attestation";
+constexpr char kAttestationServiceName[] = "org.chromium.Attestation";
+
+// Methods exported by attestation.
+constexpr char kCreateGoogleAttestedKey[] = "CreateGoogleAttestedKey";
+constexpr char kGetKeyInfo[] = "GetKeyInfo";
+constexpr char kGetEndorsementInfo[] = "GetEndorsementInfo";
+constexpr char kGetAttestationKeyInfo[] = "GetAttestationKeyInfo";
+constexpr char kActivateAttestationKey[] = "ActivateAttestationKey";
+constexpr char kCreateCertifiableKey[] = "CreateCertifiableKey";
+constexpr char kDecrypt[] = "Decrypt";
+constexpr char kSign[] = "Sign";
+constexpr char kRegisterKeyWithChapsToken[] = "RegisterKeyWithChapsToken";
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_DBUS_INTERFACE_H_
diff --git a/attestation/common/interface.proto b/attestation/common/interface.proto
new file mode 100644
index 0000000..b8198bb
--- /dev/null
+++ b/attestation/common/interface.proto
@@ -0,0 +1,173 @@
+//
+// 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.
+//
+
+option optimize_for = LITE_RUNTIME;
+
+import "common.proto";
+
+package attestation;
+
+enum AttestationStatus {
+ 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;
+}
+
+message CreateGoogleAttestedKeyRequest {
+ // An arbitrary label which can be used to reference the key later.
+ optional string key_label = 1;
+ optional KeyType key_type = 2;
+ 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 {
+ optional AttestationStatus status = 1;
+ // More information about a server-side error. This only exists
+ // if status=REQUEST_DENIED_BY_CA.
+ optional string server_error = 2;
+ // A PEM-encoded list of X.509 certificates starting with the requested
+ // certificate issued by the CA and followed by certificates for any
+ // intermediate authorities, in order. The Google Attestation CA root
+ // certificate is well-known and not included.
+ optional string certificate_chain = 3;
+}
+
+message GetKeyInfoRequest {
+ optional string key_label = 1;
+ optional string username = 2;
+}
+
+message GetKeyInfoReply {
+ optional AttestationStatus status = 1;
+ optional KeyType key_type = 2;
+ optional KeyUsage key_usage = 3;
+ // The public key (X.509/DER SubjectPublicKeyInfo).
+ optional bytes public_key = 4;
+ // The serialized TPM_CERTIFY_INFO or TPM2B_ATTEST for the new key.
+ optional bytes certify_info = 5;
+ // The signature of certify_info by the Attestation Key.
+ optional bytes certify_info_signature = 6;
+ // The certificate data associated with the key (if any).
+ optional bytes certificate = 7;
+}
+
+message GetEndorsementInfoRequest {
+ optional KeyType key_type = 1;
+}
+
+message GetEndorsementInfoReply {
+ optional AttestationStatus status = 1;
+ // The endorsement public key (X.509/DER SubjectPublicKeyInfo).
+ optional bytes ek_public_key = 2;
+ // The endorsement certificate (X.509/DER).
+ optional bytes ek_certificate = 3;
+}
+
+message GetAttestationKeyInfoRequest {
+ optional KeyType key_type = 1;
+}
+
+message GetAttestationKeyInfoReply {
+ optional AttestationStatus status = 1;
+ // The attestation public key (X.509/DER SubjectPublicKeyInfo).
+ optional bytes public_key = 2;
+ // The attestation public key in TPM_PUBKEY form.
+ optional bytes public_key_tpm_format = 3;
+ // The attestation key certificate.
+ optional bytes certificate = 4;
+ // A quote of PCR0 at the time of attestation key creation.
+ optional Quote pcr0_quote = 5;
+ // A quote of PCR1 at the time of attestation key creation.
+ optional Quote pcr1_quote = 6;
+}
+
+message ActivateAttestationKeyRequest {
+ optional KeyType key_type = 1;
+ optional EncryptedIdentityCredential encrypted_certificate = 2;
+ optional bool save_certificate = 3;
+}
+
+message ActivateAttestationKeyReply {
+ optional AttestationStatus status = 1;
+ // The decrypted attestation key certificate.
+ optional bytes certificate = 2;
+}
+
+message CreateCertifiableKeyRequest {
+ // An arbitrary label which can be used to reference the key later.
+ optional string key_label = 1;
+ // 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 = 2;
+ optional KeyType key_type = 3;
+ optional KeyUsage key_usage = 4;
+}
+
+message CreateCertifiableKeyReply {
+ optional AttestationStatus status = 1;
+ // The new public key (X.509/DER SubjectPublicKeyInfo).
+ optional bytes public_key = 2;
+ // The serialized TPM_CERTIFY_INFO or TPM2B_ATTEST for the new key.
+ optional bytes certify_info = 3;
+ // The signature of certify_info by the Attestation Key.
+ optional bytes certify_info_signature = 4;
+}
+
+message DecryptRequest {
+ optional string key_label = 1;
+ optional string username = 2;
+ optional bytes encrypted_data = 3;
+}
+
+message DecryptReply {
+ optional AttestationStatus status = 1;
+ optional bytes decrypted_data = 2;
+}
+
+message SignRequest {
+ optional string key_label = 1;
+ optional string username = 2;
+ optional bytes data_to_sign = 3;
+}
+
+message SignReply {
+ optional AttestationStatus status = 1;
+ optional bytes signature = 2;
+}
+
+message RegisterKeyWithChapsTokenRequest {
+ optional string key_label = 1;
+ optional string username = 2;
+}
+
+message RegisterKeyWithChapsTokenReply {
+ optional AttestationStatus status = 1;
+}
diff --git a/attestation/common/mock_attestation_interface.h b/attestation/common/mock_attestation_interface.h
new file mode 100644
index 0000000..cff4809
--- /dev/null
+++ b/attestation/common/mock_attestation_interface.h
@@ -0,0 +1,59 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_COMMON_MOCK_ATTESTATION_INTERFACE_H_
+#define ATTESTATION_COMMON_MOCK_ATTESTATION_INTERFACE_H_
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+#include "attestation/common/attestation_interface.h"
+
+namespace attestation {
+
+class MockAttestationInterface : public AttestationInterface {
+ public:
+ MockAttestationInterface() = default;
+ virtual ~MockAttestationInterface() = default;
+
+ MOCK_METHOD0(Initialize, bool());
+ MOCK_METHOD2(CreateGoogleAttestedKey, void(
+ const CreateGoogleAttestedKeyRequest&,
+ const CreateGoogleAttestedKeyCallback&));
+ MOCK_METHOD2(GetKeyInfo, void(const GetKeyInfoRequest&,
+ const GetKeyInfoCallback&));
+ MOCK_METHOD2(GetEndorsementInfo, void(const GetEndorsementInfoRequest&,
+ const GetEndorsementInfoCallback&));
+ MOCK_METHOD2(GetAttestationKeyInfo,
+ void(const GetAttestationKeyInfoRequest&,
+ const GetAttestationKeyInfoCallback&));
+ MOCK_METHOD2(ActivateAttestationKey,
+ void(const ActivateAttestationKeyRequest&,
+ const ActivateAttestationKeyCallback&));
+ MOCK_METHOD2(CreateCertifiableKey, void(const CreateCertifiableKeyRequest&,
+ const CreateCertifiableKeyCallback&));
+ MOCK_METHOD2(Decrypt, void(const DecryptRequest&, const DecryptCallback&));
+ MOCK_METHOD2(Sign, void(const SignRequest&, const SignCallback&));
+ MOCK_METHOD2(RegisterKeyWithChapsToken,
+ void(const RegisterKeyWithChapsTokenRequest&,
+ const RegisterKeyWithChapsTokenCallback&));
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_MOCK_ATTESTATION_INTERFACE_H_
+
diff --git a/attestation/common/mock_crypto_utility.cc b/attestation/common/mock_crypto_utility.cc
new file mode 100644
index 0000000..fc251e0
--- /dev/null
+++ b/attestation/common/mock_crypto_utility.cc
@@ -0,0 +1,54 @@
+//
+// 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/common/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)));
+ ON_CALL(*this, GetRSASubjectPublicKeyInfo(_, _))
+ .WillByDefault(Invoke(CopyString));
+}
+
+MockCryptoUtility::~MockCryptoUtility() {}
+
+} // namespace attestation
diff --git a/attestation/common/mock_crypto_utility.h b/attestation/common/mock_crypto_utility.h
new file mode 100644
index 0000000..4327d55
--- /dev/null
+++ b/attestation/common/mock_crypto_utility.h
@@ -0,0 +1,67 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_COMMON_MOCK_CRYPTO_UTILITY_H_
+#define ATTESTATION_COMMON_MOCK_CRYPTO_UTILITY_H_
+
+#include "attestation/common/crypto_utility.h"
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+namespace attestation {
+
+class MockCryptoUtility : public CryptoUtility {
+ public:
+ MockCryptoUtility();
+ ~MockCryptoUtility() override;
+
+ MOCK_CONST_METHOD2(GetRandom, bool(size_t, std::string*));
+
+ MOCK_METHOD2(CreateSealedKey, bool(std::string* aes_key,
+ std::string* sealed_key));
+
+ MOCK_METHOD4(EncryptData, bool(const std::string& data,
+ const std::string& aes_key,
+ const std::string& sealed_key,
+ std::string* encrypted_data));
+
+ MOCK_METHOD3(UnsealKey, bool(const std::string& encrypted_data,
+ std::string* aes_key,
+ std::string* sealed_key));
+
+ MOCK_METHOD3(DecryptData, bool(const std::string& encrypted_data,
+ const std::string& aes_key,
+ std::string* data));
+ MOCK_METHOD2(GetRSASubjectPublicKeyInfo, bool(const std::string&,
+ std::string*));
+ MOCK_METHOD2(GetRSAPublicKey, bool(const std::string&, std::string*));
+ MOCK_METHOD4(EncryptIdentityCredential, bool(const std::string&,
+ const std::string&,
+ const std::string&,
+ EncryptedIdentityCredential*));
+ MOCK_METHOD3(EncryptForUnbind, bool(const std::string&,
+ const std::string&,
+ std::string*));
+ MOCK_METHOD3(VerifySignature, bool(const std::string&,
+ const std::string&,
+ const std::string&));
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_MOCK_CRYPTO_UTILITY_H_
diff --git a/attestation/common/mock_tpm_utility.cc b/attestation/common/mock_tpm_utility.cc
new file mode 100644
index 0000000..0dc4eee
--- /dev/null
+++ b/attestation/common/mock_tpm_utility.cc
@@ -0,0 +1,83 @@
+//
+// 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/common/mock_tpm_utility.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::Return;
+using ::testing::WithArgs;
+
+namespace {
+
+class TransformString {
+ public:
+ explicit TransformString(std::string method) : method_(method) {}
+ bool operator()(const std::string& in, std::string* out) {
+ *out = attestation::MockTpmUtility::Transform(method_, in);
+ return true;
+ }
+
+ private:
+ std::string method_;
+};
+
+class UntransformString {
+ public:
+ explicit UntransformString(std::string method) : method_(method) {}
+ bool operator()(const std::string& in, std::string* out) {
+ std::string suffix = "_fake_transform_" + method_;
+ auto position = in.find(suffix);
+ if (position == std::string::npos) {
+ return false;
+ }
+ *out = in.substr(0, position);
+ return true;
+ }
+
+ private:
+ std::string method_;
+};
+
+} // namespace
+
+namespace attestation {
+
+MockTpmUtility::MockTpmUtility() {
+ ON_CALL(*this, IsTpmReady()).WillByDefault(Return(true));
+ ON_CALL(*this, ActivateIdentity(_, _, _, _, _, _))
+ .WillByDefault(Return(true));
+ ON_CALL(*this, CreateCertifiedKey(_, _, _, _, _, _, _, _, _))
+ .WillByDefault(Return(true));
+ ON_CALL(*this, SealToPCR0(_, _))
+ .WillByDefault(Invoke(TransformString("SealToPCR0")));
+ ON_CALL(*this, Unseal(_, _))
+ .WillByDefault(Invoke(UntransformString("SealToPCR0")));
+ ON_CALL(*this, Unbind(_, _, _))
+ .WillByDefault(WithArgs<1, 2>(Invoke(TransformString("Unbind"))));
+ ON_CALL(*this, Sign(_, _, _))
+ .WillByDefault(WithArgs<1, 2>(Invoke(TransformString("Sign"))));
+}
+
+MockTpmUtility::~MockTpmUtility() {}
+
+// static
+std::string MockTpmUtility::Transform(const std::string& method,
+ const std::string& input) {
+ return input + "_fake_transform_" + method;
+}
+
+} // namespace attestation
diff --git a/attestation/common/mock_tpm_utility.h b/attestation/common/mock_tpm_utility.h
new file mode 100644
index 0000000..46ccfb9
--- /dev/null
+++ b/attestation/common/mock_tpm_utility.h
@@ -0,0 +1,66 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_COMMON_MOCK_TPM_UTILITY_H_
+#define ATTESTATION_COMMON_MOCK_TPM_UTILITY_H_
+
+#include "attestation/common/tpm_utility.h"
+
+#include <string>
+
+#include <gmock/gmock.h>
+
+namespace attestation {
+
+class MockTpmUtility : public TpmUtility {
+ public:
+ MockTpmUtility();
+ ~MockTpmUtility() override;
+ // By default this class will fake seal/unbind/sign operations by passing the
+ // input through Transform(<method>). E.g. The expected output of a fake Sign
+ // operation on "foo" can be computed by calling
+ // MockTpmUtility::Transform("Sign", "foo").
+ static std::string Transform(const std::string& method,
+ const std::string& input);
+
+ 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_METHOD9(CreateCertifiedKey, bool(KeyType,
+ KeyUsage,
+ const std::string&,
+ const std::string&,
+ std::string*,
+ std::string*,
+ std::string*,
+ std::string*,
+ std::string*));
+ MOCK_METHOD2(SealToPCR0, bool(const std::string&, std::string*));
+ MOCK_METHOD2(Unseal, bool(const std::string&, std::string*));
+ MOCK_METHOD1(GetEndorsementPublicKey, bool(std::string*));
+ MOCK_METHOD3(Unbind, bool(const std::string&, const std::string&,
+ std::string*));
+ MOCK_METHOD3(Sign, bool(const std::string&, const std::string&,
+ std::string*));
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_MOCK_TPM_UTILITY_H_
diff --git a/attestation/common/print_common_proto.cc b/attestation/common/print_common_proto.cc
new file mode 100644
index 0000000..077157e
--- /dev/null
+++ b/attestation/common/print_common_proto.cc
@@ -0,0 +1,233 @@
+//
+// 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.
+//
+
+// THIS CODE IS GENERATED.
+
+#include "attestation/common/print_common_proto.h"
+
+#include <string>
+
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
+
+namespace attestation {
+
+std::string GetProtoDebugString(KeyType value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(KeyType value, int indent_size) {
+ if (value == KEY_TYPE_RSA) {
+ return "KEY_TYPE_RSA";
+ }
+ if (value == KEY_TYPE_ECC) {
+ return "KEY_TYPE_ECC";
+ }
+ return "<unknown>";
+}
+
+std::string GetProtoDebugString(KeyUsage value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(KeyUsage value, int indent_size) {
+ if (value == KEY_USAGE_SIGN) {
+ return "KEY_USAGE_SIGN";
+ }
+ if (value == KEY_USAGE_DECRYPT) {
+ return "KEY_USAGE_DECRYPT";
+ }
+ return "<unknown>";
+}
+
+std::string GetProtoDebugString(CertificateProfile value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(CertificateProfile value,
+ int indent_size) {
+ if (value == ENTERPRISE_MACHINE_CERTIFICATE) {
+ return "ENTERPRISE_MACHINE_CERTIFICATE";
+ }
+ if (value == ENTERPRISE_USER_CERTIFICATE) {
+ return "ENTERPRISE_USER_CERTIFICATE";
+ }
+ if (value == CONTENT_PROTECTION_CERTIFICATE) {
+ return "CONTENT_PROTECTION_CERTIFICATE";
+ }
+ if (value == CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID) {
+ return "CONTENT_PROTECTION_CERTIFICATE_WITH_STABLE_ID";
+ }
+ if (value == CAST_CERTIFICATE) {
+ return "CAST_CERTIFICATE";
+ }
+ if (value == GFSC_CERTIFICATE) {
+ return "GFSC_CERTIFICATE";
+ }
+ return "<unknown>";
+}
+
+std::string GetProtoDebugString(const Quote& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const Quote& value, int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_quote()) {
+ output += indent + " quote: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.quote().data(), value.quote().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_quoted_data()) {
+ output += indent + " quoted_data: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.quoted_data().data(),
+ value.quoted_data().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_quoted_pcr_value()) {
+ output += indent + " quoted_pcr_value: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.quoted_pcr_value().data(),
+ value.quoted_pcr_value().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_pcr_source_hint()) {
+ output += indent + " pcr_source_hint: ";
+ base::StringAppendF(
+ &output, "%s", base::HexEncode(value.pcr_source_hint().data(),
+ value.pcr_source_hint().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const EncryptedData& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const EncryptedData& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_wrapped_key()) {
+ output += indent + " wrapped_key: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.wrapped_key().data(),
+ value.wrapped_key().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_iv()) {
+ output += indent + " iv: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.iv().data(), value.iv().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_mac()) {
+ output += indent + " mac: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.mac().data(), value.mac().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_encrypted_data()) {
+ output += indent + " encrypted_data: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.encrypted_data().data(),
+ value.encrypted_data().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_wrapping_key_id()) {
+ output += indent + " wrapping_key_id: ";
+ base::StringAppendF(
+ &output, "%s", base::HexEncode(value.wrapping_key_id().data(),
+ value.wrapping_key_id().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const SignedData& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const SignedData& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_data()) {
+ output += indent + " data: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.data().data(), value.data().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_signature()) {
+ output += indent + " signature: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.signature().data(),
+ value.signature().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const EncryptedIdentityCredential& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const EncryptedIdentityCredential& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_asym_ca_contents()) {
+ output += indent + " asym_ca_contents: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.asym_ca_contents().data(),
+ value.asym_ca_contents().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_sym_ca_attestation()) {
+ output += indent + " sym_ca_attestation: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.sym_ca_attestation().data(),
+ value.sym_ca_attestation().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+} // namespace attestation
diff --git a/attestation/common/print_common_proto.h b/attestation/common/print_common_proto.h
new file mode 100644
index 0000000..9a2511a
--- /dev/null
+++ b/attestation/common/print_common_proto.h
@@ -0,0 +1,50 @@
+//
+// 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.
+//
+
+// THIS CODE IS GENERATED.
+
+#ifndef ATTESTATION_COMMON_PRINT_COMMON_PROTO_H_
+#define ATTESTATION_COMMON_PRINT_COMMON_PROTO_H_
+
+#include <string>
+
+#include "attestation/common/common.pb.h"
+
+namespace attestation {
+
+std::string GetProtoDebugStringWithIndent(KeyType value, int indent_size);
+std::string GetProtoDebugString(KeyType value);
+std::string GetProtoDebugStringWithIndent(KeyUsage value, int indent_size);
+std::string GetProtoDebugString(KeyUsage value);
+std::string GetProtoDebugStringWithIndent(CertificateProfile value,
+ int indent_size);
+std::string GetProtoDebugString(CertificateProfile value);
+std::string GetProtoDebugStringWithIndent(const Quote& value, int indent_size);
+std::string GetProtoDebugString(const Quote& value);
+std::string GetProtoDebugStringWithIndent(const EncryptedData& value,
+ int indent_size);
+std::string GetProtoDebugString(const EncryptedData& value);
+std::string GetProtoDebugStringWithIndent(const SignedData& value,
+ int indent_size);
+std::string GetProtoDebugString(const SignedData& value);
+std::string GetProtoDebugStringWithIndent(
+ const EncryptedIdentityCredential& value,
+ int indent_size);
+std::string GetProtoDebugString(const EncryptedIdentityCredential& value);
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_PRINT_COMMON_PROTO_H_
diff --git a/attestation/common/print_interface_proto.cc b/attestation/common/print_interface_proto.cc
new file mode 100644
index 0000000..6776e96
--- /dev/null
+++ b/attestation/common/print_interface_proto.cc
@@ -0,0 +1,683 @@
+//
+// 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.
+//
+
+// THIS CODE IS GENERATED.
+
+#include "attestation/common/print_interface_proto.h"
+
+#include <string>
+
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
+
+#include "attestation/common/print_common_proto.h"
+
+namespace attestation {
+
+std::string GetProtoDebugString(AttestationStatus value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(AttestationStatus value,
+ int indent_size) {
+ if (value == STATUS_SUCCESS) {
+ return "STATUS_SUCCESS";
+ }
+ if (value == STATUS_UNEXPECTED_DEVICE_ERROR) {
+ return "STATUS_UNEXPECTED_DEVICE_ERROR";
+ }
+ if (value == STATUS_NOT_AVAILABLE) {
+ return "STATUS_NOT_AVAILABLE";
+ }
+ if (value == STATUS_NOT_READY) {
+ return "STATUS_NOT_READY";
+ }
+ if (value == STATUS_NOT_ALLOWED) {
+ return "STATUS_NOT_ALLOWED";
+ }
+ if (value == STATUS_INVALID_PARAMETER) {
+ return "STATUS_INVALID_PARAMETER";
+ }
+ if (value == STATUS_REQUEST_DENIED_BY_CA) {
+ return "STATUS_REQUEST_DENIED_BY_CA";
+ }
+ if (value == STATUS_CA_NOT_AVAILABLE) {
+ return "STATUS_CA_NOT_AVAILABLE";
+ }
+ return "<unknown>";
+}
+
+std::string GetProtoDebugString(const CreateGoogleAttestedKeyRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const CreateGoogleAttestedKeyRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_label()) {
+ output += indent + " key_label: ";
+ base::StringAppendF(&output, "%s", value.key_label().c_str());
+ output += "\n";
+ }
+ if (value.has_key_type()) {
+ output += indent + " key_type: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_type(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_key_usage()) {
+ output += indent + " key_usage: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_usage(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_certificate_profile()) {
+ output += indent + " certificate_profile: ";
+ base::StringAppendF(&output, "%s", GetProtoDebugStringWithIndent(
+ value.certificate_profile(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_username()) {
+ output += indent + " username: ";
+ base::StringAppendF(&output, "%s", value.username().c_str());
+ output += "\n";
+ }
+ if (value.has_origin()) {
+ output += indent + " origin: ";
+ base::StringAppendF(&output, "%s", value.origin().c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const CreateGoogleAttestedKeyReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const CreateGoogleAttestedKeyReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_server_error()) {
+ output += indent + " server_error: ";
+ base::StringAppendF(&output, "%s", value.server_error().c_str());
+ output += "\n";
+ }
+ if (value.has_certificate_chain()) {
+ output += indent + " certificate_chain: ";
+ base::StringAppendF(&output, "%s", value.certificate_chain().c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const GetKeyInfoRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const GetKeyInfoRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_label()) {
+ output += indent + " key_label: ";
+ base::StringAppendF(&output, "%s", value.key_label().c_str());
+ output += "\n";
+ }
+ if (value.has_username()) {
+ output += indent + " username: ";
+ base::StringAppendF(&output, "%s", value.username().c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const GetKeyInfoReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const GetKeyInfoReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_key_type()) {
+ output += indent + " key_type: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_type(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_key_usage()) {
+ output += indent + " key_usage: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_usage(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_public_key()) {
+ output += indent + " public_key: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.public_key().data(),
+ value.public_key().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_certify_info()) {
+ output += indent + " certify_info: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.certify_info().data(),
+ value.certify_info().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_certify_info_signature()) {
+ output += indent + " certify_info_signature: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.certify_info_signature().data(),
+ value.certify_info_signature().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_certificate()) {
+ output += indent + " certificate: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.certificate().data(),
+ value.certificate().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const GetEndorsementInfoRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const GetEndorsementInfoRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_type()) {
+ output += indent + " key_type: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_type(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const GetEndorsementInfoReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const GetEndorsementInfoReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_ek_public_key()) {
+ output += indent + " ek_public_key: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.ek_public_key().data(),
+ value.ek_public_key().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_ek_certificate()) {
+ output += indent + " ek_certificate: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.ek_certificate().data(),
+ value.ek_certificate().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const GetAttestationKeyInfoRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const GetAttestationKeyInfoRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_type()) {
+ output += indent + " key_type: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_type(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const GetAttestationKeyInfoReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const GetAttestationKeyInfoReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_public_key()) {
+ output += indent + " public_key: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.public_key().data(),
+ value.public_key().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_public_key_tpm_format()) {
+ output += indent + " public_key_tpm_format: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.public_key_tpm_format().data(),
+ value.public_key_tpm_format().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_certificate()) {
+ output += indent + " certificate: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.certificate().data(),
+ value.certificate().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_pcr0_quote()) {
+ output += indent + " pcr0_quote: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.pcr0_quote(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_pcr1_quote()) {
+ output += indent + " pcr1_quote: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.pcr1_quote(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const ActivateAttestationKeyRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const ActivateAttestationKeyRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_type()) {
+ output += indent + " key_type: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_type(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_encrypted_certificate()) {
+ output += indent + " encrypted_certificate: ";
+ base::StringAppendF(&output, "%s", GetProtoDebugStringWithIndent(
+ value.encrypted_certificate(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_save_certificate()) {
+ output += indent + " save_certificate: ";
+ base::StringAppendF(&output, "%s",
+ value.save_certificate() ? "true" : "false");
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const ActivateAttestationKeyReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const ActivateAttestationKeyReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_certificate()) {
+ output += indent + " certificate: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.certificate().data(),
+ value.certificate().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const CreateCertifiableKeyRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const CreateCertifiableKeyRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_label()) {
+ output += indent + " key_label: ";
+ base::StringAppendF(&output, "%s", value.key_label().c_str());
+ output += "\n";
+ }
+ if (value.has_username()) {
+ output += indent + " username: ";
+ base::StringAppendF(&output, "%s", value.username().c_str());
+ output += "\n";
+ }
+ if (value.has_key_type()) {
+ output += indent + " key_type: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_type(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_key_usage()) {
+ output += indent + " key_usage: ";
+ base::StringAppendF(&output, "%s",
+ GetProtoDebugStringWithIndent(value.key_usage(),
+ indent_size + 2).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const CreateCertifiableKeyReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const CreateCertifiableKeyReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_public_key()) {
+ output += indent + " public_key: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.public_key().data(),
+ value.public_key().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_certify_info()) {
+ output += indent + " certify_info: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.certify_info().data(),
+ value.certify_info().size()).c_str());
+ output += "\n";
+ }
+ if (value.has_certify_info_signature()) {
+ output += indent + " certify_info_signature: ";
+ base::StringAppendF(
+ &output, "%s",
+ base::HexEncode(value.certify_info_signature().data(),
+ value.certify_info_signature().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const DecryptRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const DecryptRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_label()) {
+ output += indent + " key_label: ";
+ base::StringAppendF(&output, "%s", value.key_label().c_str());
+ output += "\n";
+ }
+ if (value.has_username()) {
+ output += indent + " username: ";
+ base::StringAppendF(&output, "%s", value.username().c_str());
+ output += "\n";
+ }
+ if (value.has_encrypted_data()) {
+ output += indent + " encrypted_data: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.encrypted_data().data(),
+ value.encrypted_data().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const DecryptReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const DecryptReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_decrypted_data()) {
+ output += indent + " decrypted_data: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.decrypted_data().data(),
+ value.decrypted_data().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const SignRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const SignRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_label()) {
+ output += indent + " key_label: ";
+ base::StringAppendF(&output, "%s", value.key_label().c_str());
+ output += "\n";
+ }
+ if (value.has_username()) {
+ output += indent + " username: ";
+ base::StringAppendF(&output, "%s", value.username().c_str());
+ output += "\n";
+ }
+ if (value.has_data_to_sign()) {
+ output += indent + " data_to_sign: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.data_to_sign().data(),
+ value.data_to_sign().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const SignReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const SignReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ if (value.has_signature()) {
+ output += indent + " signature: ";
+ base::StringAppendF(&output, "%s",
+ base::HexEncode(value.signature().data(),
+ value.signature().size()).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const RegisterKeyWithChapsTokenRequest& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const RegisterKeyWithChapsTokenRequest& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_key_label()) {
+ output += indent + " key_label: ";
+ base::StringAppendF(&output, "%s", value.key_label().c_str());
+ output += "\n";
+ }
+ if (value.has_username()) {
+ output += indent + " username: ";
+ base::StringAppendF(&output, "%s", value.username().c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+std::string GetProtoDebugString(const RegisterKeyWithChapsTokenReply& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(
+ const RegisterKeyWithChapsTokenReply& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output =
+ base::StringPrintf("[%s] {\n", value.GetTypeName().c_str());
+
+ if (value.has_status()) {
+ output += indent + " status: ";
+ base::StringAppendF(
+ &output, "%s",
+ GetProtoDebugStringWithIndent(value.status(), indent_size + 2).c_str());
+ output += "\n";
+ }
+ output += indent + "}\n";
+ return output;
+}
+
+} // namespace attestation
diff --git a/attestation/common/print_interface_proto.h b/attestation/common/print_interface_proto.h
new file mode 100644
index 0000000..49b7b42
--- /dev/null
+++ b/attestation/common/print_interface_proto.h
@@ -0,0 +1,99 @@
+//
+// 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.
+//
+
+// THIS CODE IS GENERATED.
+
+#ifndef ATTESTATION_COMMON_PRINT_INTERFACE_PROTO_H_
+#define ATTESTATION_COMMON_PRINT_INTERFACE_PROTO_H_
+
+#include <string>
+
+#include "attestation/common/interface.pb.h"
+
+namespace attestation {
+
+std::string GetProtoDebugStringWithIndent(AttestationStatus value,
+ int indent_size);
+std::string GetProtoDebugString(AttestationStatus value);
+std::string GetProtoDebugStringWithIndent(
+ const CreateGoogleAttestedKeyRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const CreateGoogleAttestedKeyRequest& value);
+std::string GetProtoDebugStringWithIndent(
+ const CreateGoogleAttestedKeyReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const CreateGoogleAttestedKeyReply& value);
+std::string GetProtoDebugStringWithIndent(const GetKeyInfoRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const GetKeyInfoRequest& value);
+std::string GetProtoDebugStringWithIndent(const GetKeyInfoReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const GetKeyInfoReply& value);
+std::string GetProtoDebugStringWithIndent(
+ const GetEndorsementInfoRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const GetEndorsementInfoRequest& value);
+std::string GetProtoDebugStringWithIndent(const GetEndorsementInfoReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const GetEndorsementInfoReply& value);
+std::string GetProtoDebugStringWithIndent(
+ const GetAttestationKeyInfoRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const GetAttestationKeyInfoRequest& value);
+std::string GetProtoDebugStringWithIndent(
+ const GetAttestationKeyInfoReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const GetAttestationKeyInfoReply& value);
+std::string GetProtoDebugStringWithIndent(
+ const ActivateAttestationKeyRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const ActivateAttestationKeyRequest& value);
+std::string GetProtoDebugStringWithIndent(
+ const ActivateAttestationKeyReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const ActivateAttestationKeyReply& value);
+std::string GetProtoDebugStringWithIndent(
+ const CreateCertifiableKeyRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const CreateCertifiableKeyRequest& value);
+std::string GetProtoDebugStringWithIndent(
+ const CreateCertifiableKeyReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const CreateCertifiableKeyReply& value);
+std::string GetProtoDebugStringWithIndent(const DecryptRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const DecryptRequest& value);
+std::string GetProtoDebugStringWithIndent(const DecryptReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const DecryptReply& value);
+std::string GetProtoDebugStringWithIndent(const SignRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const SignRequest& value);
+std::string GetProtoDebugStringWithIndent(const SignReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const SignReply& value);
+std::string GetProtoDebugStringWithIndent(
+ const RegisterKeyWithChapsTokenRequest& value,
+ int indent_size);
+std::string GetProtoDebugString(const RegisterKeyWithChapsTokenRequest& value);
+std::string GetProtoDebugStringWithIndent(
+ const RegisterKeyWithChapsTokenReply& value,
+ int indent_size);
+std::string GetProtoDebugString(const RegisterKeyWithChapsTokenReply& value);
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_PRINT_INTERFACE_PROTO_H_
diff --git a/attestation/common/proto_print.py b/attestation/common/proto_print.py
new file mode 100755
index 0000000..63cffe6
--- /dev/null
+++ b/attestation/common/proto_print.py
@@ -0,0 +1,417 @@
+#!/usr/bin/python2
+
+#
+# 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.
+#
+
+"""A C++ code generator for printing protobufs which use the LITE_RUNTIME.
+
+Normally printing a protobuf would be done with Message::DebugString(). However,
+this is not available when using only MessageLite. This script generates code to
+emulate Message::DebugString() without using reflection. The input must be a
+valid .proto file.
+
+Usage: proto_print.py [--subdir=foo] <bar.proto>
+
+Files named print_bar_proto.h and print_bar_proto.cc will be created in the
+current working directory.
+"""
+
+from __future__ import print_function
+
+import argparse
+import collections
+from datetime import date
+import os
+import re
+import subprocess
+
+
+# Holds information about a protobuf message field.
+#
+# Attributes:
+# repeated: Whether the field is a repeated field.
+# type_: The type of the field. E.g. int32.
+# name: The name of the field.
+Field = collections.namedtuple('Field', 'repeated type_ name')
+
+
+class Message(object):
+ """Holds information about a protobuf message.
+
+ Attributes:
+ name: The name of the message.
+ fields: A list of Field tuples.
+ """
+
+ def __init__(self, name):
+ """Initializes a Message instance.
+
+ Args:
+ name: The protobuf message name.
+ """
+ self.name = name
+ self.fields = []
+
+ def AddField(self, attribute, field_type, field_name):
+ """Adds a new field to the message.
+
+ Args:
+ attribute: This should be 'optional', 'required', or 'repeated'.
+ field_type: The type of the field. E.g. int32.
+ field_name: The name of the field.
+ """
+ self.fields.append(Field(repeated=attribute == 'repeated',
+ type_=field_type, name=field_name))
+
+
+class Enum(object):
+ """Holds information about a protobuf enum.
+
+ Attributes:
+ name: The name of the enum.
+ values: A list of enum value names.
+ """
+
+ def __init__(self, name):
+ """Initializes a Enum instance.
+
+ Args:
+ name: The protobuf enum name.
+ """
+ self.name = name
+ self.values = []
+
+ def AddValue(self, value_name):
+ """Adds a new value to the enum.
+
+ Args:
+ value_name: The name of the value.
+ """
+ self.values.append(value_name)
+
+
+def ParseProto(input_file):
+ """Parses a proto file and returns a tuple of parsed information.
+
+ Args:
+ input_file: The proto file to parse.
+
+ Returns:
+ A tuple in the form (package, imports, messages, enums) where
+ package: A string holding the proto package.
+ imports: A list of strings holding proto imports.
+ messages: A list of Message objects; one for each message in the proto.
+ enums: A list of Enum objects; one for each enum in the proto.
+ """
+ package = ''
+ imports = []
+ messages = []
+ enums = []
+ current_message_stack = []
+ current_enum = None
+ package_re = re.compile(r'package\s+(\w+);')
+ import_re = re.compile(r'import\s+"(\w+).proto";')
+ message_re = re.compile(r'message\s+(\w+)\s*{')
+ field_re = re.compile(r'(optional|required|repeated)\s+(\w+)\s+(\w+)\s*=')
+ enum_re = re.compile(r'enum\s+(\w+)\s*{')
+ enum_value_re = re.compile(r'(\w+)\s*=')
+ for line in input_file:
+ line = line.strip()
+ if not line or line.startswith('//'):
+ continue
+ # Close off the current scope. Enums first because they can't be nested.
+ if line == '}':
+ if current_enum:
+ enums.append(current_enum)
+ current_enum = None
+ if current_message_stack:
+ messages.append(current_message_stack.pop())
+ continue
+ # Look for a message definition.
+ match = message_re.search(line)
+ if match:
+ prefix = ''
+ if current_message_stack:
+ prefix = '::'.join([m.name for m in current_message_stack]) + '::'
+ current_message_stack.append(Message(prefix + match.group(1)))
+ continue
+ # Look for a message field definition.
+ if current_message_stack:
+ match = field_re.search(line)
+ if match:
+ current_message_stack[-1].AddField(match.group(1),
+ match.group(2),
+ match.group(3))
+ continue
+ # Look for an enum definition.
+ match = enum_re.search(line)
+ if match:
+ current_enum = Enum(match.group(1))
+ continue
+ # Look for an enum value.
+ if current_enum:
+ match = enum_value_re.search(line)
+ if match:
+ current_enum.AddValue(match.group(1))
+ continue
+ # Look for a package statement.
+ match = package_re.search(line)
+ if match:
+ package = match.group(1)
+ # Look for an import statement.
+ match = import_re.search(line)
+ if match:
+ imports.append(match.group(1))
+ return package, imports, messages, enums
+
+
+def GenerateFileHeaders(proto_name, package, imports, subdir, header_file_name,
+ header_file, impl_file):
+ """Generates and prints file headers.
+
+ Args:
+ proto_name: The name of the proto file.
+ package: The protobuf package.
+ imports: A list of imported protos.
+ subdir: The --subdir arg.
+ header_file_name: The header file name.
+ header_file: The header file handle, open for writing.
+ impl_file: The implementation file handle, open for writing.
+ """
+ if subdir:
+ guard_name = '%s_%s_PRINT_%s_PROTO_H_' % (package.upper(),
+ subdir.upper(),
+ proto_name.upper())
+ package_with_subdir = '%s/%s' % (package, subdir)
+ else:
+ guard_name = '%s_PRINT_%s_PROTO_H_' % (package.upper(), proto_name.upper())
+ package_with_subdir = package
+ includes = '\n'.join(
+ ['#include "%(package_with_subdir)s/print_%(import)s_proto.h"' % {
+ 'package_with_subdir': package_with_subdir,
+ 'import': current_import} for current_import in imports])
+ header = """\
+// Copyright %(year)s 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.
+
+// THIS CODE IS GENERATED.
+
+#ifndef %(guard_name)s
+#define %(guard_name)s
+
+#include <string>
+
+#include "%(package_with_subdir)s/%(proto)s.pb.h"
+
+namespace %(package)s {
+""" % {'year': date.today().year,
+ 'guard_name': guard_name,
+ 'package': package,
+ 'proto': proto_name,
+ 'package_with_subdir': package_with_subdir}
+ impl = """\
+// Copyright %(year)s 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.
+
+// THIS CODE IS GENERATED.
+
+#include "%(package_with_subdir)s/%(header_file_name)s"
+
+#include <string>
+
+#include <base/strings/string_number_conversions.h>
+#include <base/strings/stringprintf.h>
+
+%(includes)s
+
+namespace %(package)s {
+""" % {'year': date.today().year,
+ 'package': package,
+ 'package_with_subdir': package_with_subdir,
+ 'header_file_name': header_file_name,
+ 'includes': includes}
+
+ header_file.write(header)
+ impl_file.write(impl)
+
+
+def GenerateFileFooters(proto_name, package, subdir, header_file, impl_file):
+ """Generates and prints file footers.
+
+ Args:
+ proto_name: The name of the proto file.
+ package: The protobuf package.
+ subdir: The --subdir arg.
+ header_file: The header file handle, open for writing.
+ impl_file: The implementation file handle, open for writing.
+ """
+ if subdir:
+ guard_name = '%s_%s_PRINT_%s_PROTO_H_' % (package.upper(),
+ subdir.upper(),
+ proto_name.upper())
+ else:
+ guard_name = '%s_PRINT_%s_PROTO_H_' % (package.upper(), proto_name.upper())
+ header = """
+
+} // namespace %(package)s
+
+#endif // %(guard_name)s
+""" % {'guard_name': guard_name, 'package': package}
+ impl = """
+} // namespace %(package)s
+""" % {'package': package}
+
+ header_file.write(header)
+ impl_file.write(impl)
+
+
+def GenerateEnumPrinter(enum, header_file, impl_file):
+ """Generates and prints a function to print an enum value.
+
+ Args:
+ enum: An Enum instance.
+ header_file: The header file handle, open for writing.
+ impl_file: The implementation file handle, open for writing.
+ """
+ declare = """
+std::string GetProtoDebugStringWithIndent(%(name)s value, int indent_size);
+std::string GetProtoDebugString(%(name)s value);""" % {'name': enum.name}
+ define_begin = """
+std::string GetProtoDebugString(%(name)s value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(%(name)s value, int indent_size) {
+""" % {'name': enum.name}
+ define_end = """
+ return "<unknown>";
+}
+"""
+ condition = """
+ if (value == %(value_name)s) {
+ return "%(value_name)s";
+ }"""
+
+ header_file.write(declare)
+ impl_file.write(define_begin)
+ for value_name in enum.values:
+ impl_file.write(condition % {'value_name': value_name})
+ impl_file.write(define_end)
+
+
+def GenerateMessagePrinter(message, header_file, impl_file):
+ """Generates and prints a function to print a message.
+
+ Args:
+ message: A Message instance.
+ header_file: The header file handle, open for writing.
+ impl_file: The implementation file handle, open for writing.
+ """
+ declare = """
+std::string GetProtoDebugStringWithIndent(const %(name)s& value,
+ int indent_size);
+std::string GetProtoDebugString(const %(name)s& value);""" % {'name':
+ message.name}
+ define_begin = """
+std::string GetProtoDebugString(const %(name)s& value) {
+ return GetProtoDebugStringWithIndent(value, 0);
+}
+
+std::string GetProtoDebugStringWithIndent(const %(name)s& value,
+ int indent_size) {
+ std::string indent(indent_size, ' ');
+ std::string output = base::StringPrintf("[%%s] {\\n",
+ value.GetTypeName().c_str());
+""" % {'name': message.name}
+ define_end = """
+ output += indent + "}\\n";
+ return output;
+}
+"""
+ singular_field = """
+ if (value.has_%(name)s()) {
+ output += indent + " %(name)s: ";
+ base::StringAppendF(&output, %(format)s);
+ output += "\\n";
+ }"""
+ repeated_field = """
+ output += indent + " %(name)s: {";
+ for (int i = 0; i < value.%(name)s_size(); ++i) {
+ base::StringAppendF(&output, %(format)s);
+ }
+ output += "}\\n";"""
+ singular_field_get = 'value.%(name)s()'
+ repeated_field_get = 'value.%(name)s(i)'
+ formats = {'bool': '"%%s", %(value)s ? "true" : "false"',
+ 'int32': '"%%d", %(value)s',
+ 'int64': '"%%ld", %(value)s',
+ 'uint32': '"%%u", %(value)s',
+ 'uint64': '"%%lu", %(value)s',
+ 'string': '"%%s", %(value)s.c_str()',
+ 'bytes': """"%%s", base::HexEncode(%(value)s.data(),
+ %(value)s.size()).c_str()"""}
+ subtype_format = ('"%%s", GetProtoDebugStringWithIndent(%(value)s, '
+ 'indent_size + 2).c_str()')
+
+ header_file.write(declare)
+ impl_file.write(define_begin)
+ for field in message.fields:
+ if field.repeated:
+ value_get = repeated_field_get % {'name': field.name}
+ field_code = repeated_field
+ else:
+ value_get = singular_field_get % {'name': field.name}
+ field_code = singular_field
+ if field.type_ in formats:
+ value_format = formats[field.type_] % {'value': value_get}
+ else:
+ value_format = subtype_format % {'value': value_get}
+ impl_file.write(field_code % {'name': field.name,
+ 'format': value_format})
+ impl_file.write(define_end)
+
+
+def FormatFile(filename):
+ subprocess.call(['clang-format', '-i', '-style=Chromium', filename])
+
+
+def main():
+ parser = argparse.ArgumentParser(description='print proto code generator')
+ parser.add_argument('input_file')
+ parser.add_argument('--subdir', default='')
+ args = parser.parse_args()
+ with open(args.input_file) as input_file:
+ package, imports, messages, enums = ParseProto(input_file)
+ proto_name = os.path.basename(args.input_file).rsplit('.', 1)[0]
+ header_file_name = 'print_%s_proto.h' % proto_name
+ impl_file_name = 'print_%s_proto.cc' % proto_name
+ with open(header_file_name, 'w') as header_file:
+ with open(impl_file_name, 'w') as impl_file:
+ GenerateFileHeaders(proto_name, package, imports, args.subdir,
+ header_file_name, header_file, impl_file)
+ for enum in enums:
+ GenerateEnumPrinter(enum, header_file, impl_file)
+ for message in messages:
+ GenerateMessagePrinter(message, header_file, impl_file)
+ GenerateFileFooters(proto_name, package, args.subdir, header_file,
+ impl_file)
+ FormatFile(header_file_name)
+ FormatFile(impl_file_name)
+
+if __name__ == '__main__':
+ main()
diff --git a/attestation/common/tpm_utility.h b/attestation/common/tpm_utility.h
new file mode 100644
index 0000000..5432d12
--- /dev/null
+++ b/attestation/common/tpm_utility.h
@@ -0,0 +1,97 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_COMMON_TPM_UTILITY_H_
+#define ATTESTATION_COMMON_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 and certifies a non-migratable key in the TPM. The new key will
+ // correspond to |key_type| and |key_usage|. The parent key will be the
+ // storage root key. The new key will be certified 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 CreateCertifiedKey(KeyType key_type,
+ KeyUsage key_usage,
+ const std::string& identity_key_blob,
+ const std::string& external_data,
+ std::string* key_blob,
+ std::string* public_key,
+ std::string* public_key_tpm_format,
+ std::string* key_info,
+ std::string* proof) = 0;
+
+ // Seals |data| to the current value of PCR0 with the SRK and produces the
+ // |sealed_data|. Returns true on success.
+ virtual bool SealToPCR0(const std::string& data,
+ std::string* sealed_data) = 0;
+
+ // Unseals |sealed_data| previously sealed with the SRK and produces the
+ // unsealed |data|. Returns true on success.
+ virtual bool Unseal(const std::string& sealed_data, std::string* data) = 0;
+
+ // Reads the endorsement public key from the TPM.
+ virtual bool GetEndorsementPublicKey(std::string* public_key) = 0;
+
+ // Unbinds |bound_data| with the key loaded from |key_blob| by decrypting
+ // using the TPM_ES_RSAESOAEP_SHA1_MGF1 scheme. The input must be in the
+ // format of a TPM_BOUND_DATA structure. On success returns true and provides
+ // the decrypted |data|.
+ virtual bool Unbind(const std::string& key_blob,
+ const std::string& bound_data,
+ std::string* data) = 0;
+
+ // Signs |data_to_sign| with the key loaded from |key_blob| using the
+ // TPM_SS_RSASSAPKCS1v15_DER scheme with SHA-256. On success returns true and
+ // provides the |signature|.
+ virtual bool Sign(const std::string& key_blob,
+ const std::string& data_to_sign,
+ std::string* signature) = 0;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_TPM_UTILITY_H_
diff --git a/attestation/common/tpm_utility_v1.cc b/attestation/common/tpm_utility_v1.cc
new file mode 100644
index 0000000..6b7ab17
--- /dev/null
+++ b/attestation/common/tpm_utility_v1.cc
@@ -0,0 +1,720 @@
+//
+// 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/common/tpm_utility_v1.h"
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/logging.h>
+#include <base/memory/scoped_ptr.h>
+#include <base/stl_util.h>
+#include <crypto/scoped_openssl_types.h>
+#include <crypto/sha2.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <trousers/scoped_tss_type.h>
+#include <trousers/trousers.h>
+#include <trousers/tss.h>
+
+#define TPM_LOG(severity, result) \
+ LOG(severity) << "TPM error 0x" << std::hex << result \
+ << " (" << Trspi_Error_String(result) << "): "
+
+using trousers::ScopedTssContext;
+using trousers::ScopedTssKey;
+using trousers::ScopedTssMemory;
+using trousers::ScopedTssPcrs;
+
+namespace {
+
+using ScopedByteArray = scoped_ptr<BYTE, base::FreeDeleter>;
+using ScopedTssEncryptedData = trousers::ScopedTssObject<TSS_HENCDATA>;
+using ScopedTssHash = trousers::ScopedTssObject<TSS_HHASH>;
+
+const char* kTpmEnabledFile = "/sys/class/misc/tpm0/device/enabled";
+const char* kTpmOwnedFile = "/sys/class/misc/tpm0/device/owned";
+const unsigned int kWellKnownExponent = 65537;
+const unsigned char kSha256DigestInfo[] = {
+ 0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04,
+ 0x02, 0x01, 0x05, 0x00, 0x04, 0x20
+};
+
+std::string GetFirstByte(const char* file_name) {
+ std::string content;
+ base::ReadFileToString(base::FilePath(file_name), &content);
+ if (content.size() > 1) {
+ content.resize(1);
+ }
+ return content;
+}
+
+BYTE* StringAsTSSBuffer(std::string* s) {
+ return reinterpret_cast<BYTE*>(string_as_array(s));
+}
+
+std::string TSSBufferAsString(const BYTE* buffer, size_t length) {
+ return std::string(reinterpret_cast<const char*>(buffer), length);
+}
+
+} // namespace
+
+namespace attestation {
+
+TpmUtilityV1::~TpmUtilityV1() {}
+
+bool TpmUtilityV1::Initialize() {
+ if (!ConnectContext(&context_handle_, &tpm_handle_)) {
+ LOG(ERROR) << __func__ << ": Failed to connect to the TPM.";
+ return false;
+ }
+ if (!IsTpmReady()) {
+ LOG(WARNING) << __func__ << ": TPM is not owned; attestation services will "
+ << "not be available until ownership is taken.";
+ }
+ return true;
+}
+
+bool TpmUtilityV1::IsTpmReady() {
+ if (!is_ready_) {
+ is_ready_ = (GetFirstByte(kTpmEnabledFile) == "1" &&
+ GetFirstByte(kTpmOwnedFile) == "1");
+ }
+ return is_ready_;
+}
+
+bool TpmUtilityV1::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) {
+ CHECK(credential);
+ if (!SetupSrk()) {
+ LOG(ERROR) << "SRK is not ready.";
+ return false;
+ }
+
+ // Connect to the TPM as the owner delegate.
+ ScopedTssContext context_handle;
+ TSS_HTPM tpm_handle;
+ if (!ConnectContextAsDelegate(delegate_blob, delegate_secret,
+ &context_handle, &tpm_handle)) {
+ LOG(ERROR) << __func__ << ": Could not connect to the TPM.";
+ return false;
+ }
+ // Load the Storage Root Key.
+ TSS_RESULT result;
+ ScopedTssKey srk_handle(context_handle);
+ if (!LoadSrk(context_handle, &srk_handle)) {
+ LOG(ERROR) << __func__ << ": Failed to load SRK.";
+ return false;
+ }
+ // Load the AIK (which is wrapped by the SRK).
+ std::string mutable_identity_key_blob(identity_key_blob);
+ BYTE* identity_key_blob_buffer = StringAsTSSBuffer(
+ &mutable_identity_key_blob);
+ ScopedTssKey identity_key(context_handle);
+ result = Tspi_Context_LoadKeyByBlob(
+ context_handle,
+ srk_handle,
+ identity_key_blob.size(),
+ identity_key_blob_buffer,
+ identity_key.ptr());
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to load AIK.";
+ return false;
+ }
+ std::string mutable_asym_ca_contents(asym_ca_contents);
+ BYTE* asym_ca_contents_buffer = StringAsTSSBuffer(&mutable_asym_ca_contents);
+ std::string mutable_sym_ca_attestation(sym_ca_attestation);
+ BYTE* sym_ca_attestation_buffer = StringAsTSSBuffer(
+ &mutable_sym_ca_attestation);
+ UINT32 credential_length = 0;
+ ScopedTssMemory credential_buffer(context_handle);
+ result = Tspi_TPM_ActivateIdentity(tpm_handle, identity_key,
+ asym_ca_contents.size(),
+ asym_ca_contents_buffer,
+ sym_ca_attestation.size(),
+ sym_ca_attestation_buffer,
+ &credential_length,
+ credential_buffer.ptr());
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to activate identity.";
+ return false;
+ }
+ credential->assign(TSSBufferAsString(credential_buffer.value(),
+ credential_length));
+ return true;
+}
+
+bool TpmUtilityV1::CreateCertifiedKey(KeyType key_type,
+ KeyUsage key_usage,
+ const std::string& identity_key_blob,
+ const std::string& external_data,
+ std::string* key_blob,
+ std::string* public_key,
+ std::string* public_key_tpm_format,
+ std::string* key_info,
+ std::string* proof) {
+ CHECK(key_blob && public_key && public_key_tpm_format && key_info && proof);
+ if (!SetupSrk()) {
+ LOG(ERROR) << "SRK is not ready.";
+ return false;
+ }
+ if (key_type != KEY_TYPE_RSA) {
+ LOG(ERROR) << "Only RSA supported on TPM v1.2.";
+ return false;
+ }
+
+ // Load the AIK (which is wrapped by the SRK).
+ ScopedTssKey identity_key(context_handle_);
+ if (!LoadKeyFromBlob(identity_key_blob, context_handle_, srk_handle_,
+ &identity_key)) {
+ LOG(ERROR) << __func__ << "Failed to load AIK.";
+ return false;
+ }
+
+ // Create a non-migratable RSA key.
+ ScopedTssKey key(context_handle_);
+ UINT32 tss_key_type = (key_usage == KEY_USAGE_SIGN) ? TSS_KEY_TYPE_SIGNING :
+ TSS_KEY_TYPE_BIND;
+ UINT32 init_flags = tss_key_type |
+ TSS_KEY_NOT_MIGRATABLE |
+ TSS_KEY_VOLATILE |
+ TSS_KEY_NO_AUTHORIZATION |
+ TSS_KEY_SIZE_2048;
+ TSS_RESULT result = Tspi_Context_CreateObject(context_handle_,
+ TSS_OBJECT_TYPE_RSAKEY,
+ init_flags, key.ptr());
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to create object.";
+ return false;
+ }
+ if (key_usage == KEY_USAGE_SIGN) {
+ result = Tspi_SetAttribUint32(key,
+ TSS_TSPATTRIB_KEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_SIGSCHEME,
+ TSS_SS_RSASSAPKCS1V15_DER);
+ } else {
+ result = Tspi_SetAttribUint32(key,
+ TSS_TSPATTRIB_KEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_ENCSCHEME,
+ TSS_ES_RSAESOAEP_SHA1_MGF1);
+ }
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to set scheme.";
+ return false;
+ }
+ result = Tspi_Key_CreateKey(key, srk_handle_, 0);
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to create key.";
+ return false;
+ }
+ result = Tspi_Key_LoadKey(key, srk_handle_);
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to load key.";
+ return false;
+ }
+
+ // Certify the key.
+ TSS_VALIDATION validation;
+ memset(&validation, 0, sizeof(validation));
+ validation.ulExternalDataLength = external_data.size();
+ std::string mutable_external_data(external_data);
+ validation.rgbExternalData = StringAsTSSBuffer(&mutable_external_data);
+ result = Tspi_Key_CertifyKey(key, identity_key, &validation);
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to certify key.";
+ return false;
+ }
+ ScopedTssMemory scoped_certified_data(0, validation.rgbData);
+ ScopedTssMemory scoped_proof(0, validation.rgbValidationData);
+
+ // Get the certified public key.
+ if (!GetDataAttribute(context_handle_,
+ key,
+ TSS_TSPATTRIB_KEY_BLOB,
+ TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
+ public_key_tpm_format)) {
+ LOG(ERROR) << __func__ << ": Failed to read public key.";
+ return false;
+ }
+ if (!ConvertPublicKeyToDER(*public_key_tpm_format, public_key)) {
+ return false;
+ }
+
+ // Get the certified key blob so we can load it later.
+ if (!GetDataAttribute(context_handle_,
+ key,
+ TSS_TSPATTRIB_KEY_BLOB,
+ TSS_TSPATTRIB_KEYBLOB_BLOB,
+ key_blob)) {
+ LOG(ERROR) << __func__ << ": Failed to read key blob.";
+ return false;
+ }
+
+ // Get the data that was certified.
+ key_info->assign(TSSBufferAsString(validation.rgbData,
+ validation.ulDataLength));
+
+ // Get the certification proof.
+ proof->assign(TSSBufferAsString(validation.rgbValidationData,
+ validation.ulValidationDataLength));
+ return true;
+}
+
+bool TpmUtilityV1::SealToPCR0(const std::string& data,
+ std::string* sealed_data) {
+ CHECK(sealed_data);
+ if (!SetupSrk()) {
+ LOG(ERROR) << "SRK is not ready.";
+ return false;
+ }
+
+ // Create a PCRS object which holds the value of PCR0.
+ ScopedTssPcrs pcrs_handle(context_handle_);
+ TSS_RESULT result;
+ if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle_,
+ TSS_OBJECT_TYPE_PCRS,
+ TSS_PCRS_STRUCT_INFO,
+ pcrs_handle.ptr()))) {
+ TPM_LOG(ERROR, result)
+ << __func__ << ": Error calling Tspi_Context_CreateObject";
+ return false;
+ }
+ UINT32 pcr_length = 0;
+ ScopedTssMemory pcr_value(context_handle_);
+ Tspi_TPM_PcrRead(tpm_handle_, 0, &pcr_length, pcr_value.ptr());
+ Tspi_PcrComposite_SetPcrValue(pcrs_handle, 0, pcr_length, pcr_value.value());
+
+ // Create a ENCDATA object to receive the sealed data.
+ ScopedTssKey encrypted_data_handle(context_handle_);
+ if (TPM_ERROR(result = Tspi_Context_CreateObject(
+ context_handle_,
+ TSS_OBJECT_TYPE_ENCDATA,
+ TSS_ENCDATA_SEAL,
+ encrypted_data_handle.ptr()))) {
+ TPM_LOG(ERROR, result)
+ << __func__ << ": Error calling Tspi_Context_CreateObject";
+ return false;
+ }
+
+ // Seal the given value with the SRK.
+ std::string mutable_data(data);
+ BYTE* data_buffer = StringAsTSSBuffer(&mutable_data);
+ if (TPM_ERROR(result = Tspi_Data_Seal(
+ encrypted_data_handle,
+ srk_handle_,
+ data.size(),
+ data_buffer,
+ pcrs_handle))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_Data_Seal";
+ return false;
+ }
+
+ // Extract the sealed value.
+ ScopedTssMemory encrypted_data(context_handle_);
+ UINT32 encrypted_data_length = 0;
+ if (TPM_ERROR(result = Tspi_GetAttribData(encrypted_data_handle,
+ TSS_TSPATTRIB_ENCDATA_BLOB,
+ TSS_TSPATTRIB_ENCDATABLOB_BLOB,
+ &encrypted_data_length,
+ encrypted_data.ptr()))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_GetAttribData";
+ return false;
+ }
+ sealed_data->assign(TSSBufferAsString(encrypted_data.value(),
+ encrypted_data_length));
+ return true;
+}
+
+bool TpmUtilityV1::Unseal(const std::string& sealed_data, std::string* data) {
+ CHECK(data);
+ if (!SetupSrk()) {
+ LOG(ERROR) << "SRK is not ready.";
+ return false;
+ }
+
+ // Create an ENCDATA object with the sealed value.
+ ScopedTssKey encrypted_data_handle(context_handle_);
+ TSS_RESULT result;
+ if (TPM_ERROR(result = Tspi_Context_CreateObject(
+ context_handle_,
+ TSS_OBJECT_TYPE_ENCDATA,
+ TSS_ENCDATA_SEAL,
+ encrypted_data_handle.ptr()))) {
+ TPM_LOG(ERROR, result)
+ << __func__ << ": Error calling Tspi_Context_CreateObject";
+ return false;
+ }
+
+ std::string mutable_sealed_data(sealed_data);
+ BYTE* sealed_data_buffer = StringAsTSSBuffer(&mutable_sealed_data);
+ if (TPM_ERROR(result = Tspi_SetAttribData(encrypted_data_handle,
+ TSS_TSPATTRIB_ENCDATA_BLOB,
+ TSS_TSPATTRIB_ENCDATABLOB_BLOB,
+ sealed_data.size(),
+ sealed_data_buffer))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_SetAttribData";
+ return false;
+ }
+
+ // Unseal using the SRK.
+ ScopedTssMemory decrypted_data(context_handle_);
+ UINT32 decrypted_data_length = 0;
+ if (TPM_ERROR(result = Tspi_Data_Unseal(encrypted_data_handle,
+ srk_handle_,
+ &decrypted_data_length,
+ decrypted_data.ptr()))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_Data_Unseal";
+ return false;
+ }
+ data->assign(TSSBufferAsString(decrypted_data.value(),
+ decrypted_data_length));
+ return true;
+}
+
+bool TpmUtilityV1::GetEndorsementPublicKey(std::string* public_key) {
+ // Get a handle to the EK public key.
+ ScopedTssKey ek_public_key_object(context_handle_);
+ TSS_RESULT result = Tspi_TPM_GetPubEndorsementKey(tpm_handle_, false, nullptr,
+ ek_public_key_object.ptr());
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to get key.";
+ return false;
+ }
+ // Get the public key in TPM_PUBKEY form.
+ std::string ek_public_key_blob;
+ if (!GetDataAttribute(context_handle_,
+ ek_public_key_object,
+ TSS_TSPATTRIB_KEY_BLOB,
+ TSS_TSPATTRIB_KEYBLOB_PUBLIC_KEY,
+ &ek_public_key_blob)) {
+ LOG(ERROR) << __func__ << ": Failed to read public key.";
+ return false;
+ }
+ // Get the public key in DER encoded form.
+ if (!ConvertPublicKeyToDER(ek_public_key_blob, public_key)) {
+ return false;
+ }
+ return true;
+}
+
+bool TpmUtilityV1::Unbind(const std::string& key_blob,
+ const std::string& bound_data,
+ std::string* data) {
+ CHECK(data);
+ if (!SetupSrk()) {
+ LOG(ERROR) << "SRK is not ready.";
+ return false;
+ }
+ ScopedTssKey key_handle(context_handle_);
+ if (!LoadKeyFromBlob(key_blob, context_handle_, srk_handle_, &key_handle)) {
+ return false;
+ }
+ TSS_RESULT result;
+ ScopedTssEncryptedData data_handle(context_handle_);
+ if (TPM_ERROR(result = Tspi_Context_CreateObject(context_handle_,
+ TSS_OBJECT_TYPE_ENCDATA,
+ TSS_ENCDATA_BIND,
+ data_handle.ptr()))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Tspi_Context_CreateObject failed.";
+ return false;
+ }
+ std::string mutable_bound_data(bound_data);
+ if (TPM_ERROR(result = Tspi_SetAttribData(
+ data_handle,
+ TSS_TSPATTRIB_ENCDATA_BLOB,
+ TSS_TSPATTRIB_ENCDATABLOB_BLOB,
+ bound_data.size(),
+ StringAsTSSBuffer(&mutable_bound_data)))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Tspi_SetAttribData failed.";
+ return false;
+ }
+
+ ScopedTssMemory decrypted_data(context_handle_);
+ UINT32 length = 0;
+ if (TPM_ERROR(result = Tspi_Data_Unbind(data_handle, key_handle,
+ &length, decrypted_data.ptr()))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Tspi_Data_Unbind failed.";
+ return false;
+ }
+ data->assign(TSSBufferAsString(decrypted_data.value(), length));
+ return true;
+}
+
+bool TpmUtilityV1::Sign(const std::string& key_blob,
+ const std::string& data_to_sign,
+ std::string* signature) {
+ CHECK(signature);
+ if (!SetupSrk()) {
+ LOG(ERROR) << "SRK is not ready.";
+ return false;
+ }
+ ScopedTssKey key_handle(context_handle_);
+ if (!LoadKeyFromBlob(key_blob, context_handle_, srk_handle_, &key_handle)) {
+ return false;
+ }
+ // Construct an ASN.1 DER DigestInfo.
+ std::string digest_to_sign(std::begin(kSha256DigestInfo),
+ std::end(kSha256DigestInfo));
+ digest_to_sign += crypto::SHA256HashString(data_to_sign);
+ // Create a hash object to hold the digest.
+ ScopedTssHash hash_handle(context_handle_);
+ TSS_RESULT result = Tspi_Context_CreateObject(context_handle_,
+ TSS_OBJECT_TYPE_HASH,
+ TSS_HASH_OTHER,
+ hash_handle.ptr());
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to create hash object.";
+ return false;
+ }
+ result = Tspi_Hash_SetHashValue(hash_handle,
+ digest_to_sign.size(),
+ StringAsTSSBuffer(&digest_to_sign));
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to set hash data.";
+ return false;
+ }
+ UINT32 length = 0;
+ ScopedTssMemory buffer(context_handle_);
+ result = Tspi_Hash_Sign(hash_handle, key_handle, &length, buffer.ptr());
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to generate signature.";
+ return false;
+ }
+ signature->assign(TSSBufferAsString(buffer.value(), length));
+ return true;
+}
+
+bool TpmUtilityV1::ConnectContext(ScopedTssContext* context, TSS_HTPM* tpm) {
+ *tpm = 0;
+ TSS_RESULT result;
+ if (TPM_ERROR(result = Tspi_Context_Create(context->ptr()))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_Context_Create";
+ return false;
+ }
+ if (TPM_ERROR(result = Tspi_Context_Connect(*context, nullptr))) {
+ TPM_LOG(ERROR, result) << __func__
+ << ": Error calling Tspi_Context_Connect";
+ return false;
+ }
+ if (TPM_ERROR(result = Tspi_Context_GetTpmObject(*context, tpm))) {
+ TPM_LOG(ERROR, result) << __func__
+ << ": Error calling Tspi_Context_GetTpmObject";
+ return false;
+ }
+ return true;
+}
+
+bool TpmUtilityV1::ConnectContextAsDelegate(const std::string& delegate_blob,
+ const std::string& delegate_secret,
+ ScopedTssContext* context,
+ TSS_HTPM* tpm) {
+ *tpm = 0;
+ if (!ConnectContext(context, tpm)) {
+ return false;
+ }
+ TSS_RESULT result;
+ TSS_HPOLICY tpm_usage_policy;
+ if (TPM_ERROR(result = Tspi_GetPolicyObject(*tpm,
+ TSS_POLICY_USAGE,
+ &tpm_usage_policy))) {
+ TPM_LOG(ERROR, result) << __func__
+ << ": Error calling Tspi_GetPolicyObject";
+ return false;
+ }
+ std::string mutable_delegate_secret(delegate_secret);
+ BYTE* secret_buffer = StringAsTSSBuffer(&mutable_delegate_secret);
+ if (TPM_ERROR(result = Tspi_Policy_SetSecret(tpm_usage_policy,
+ TSS_SECRET_MODE_PLAIN,
+ delegate_secret.size(),
+ secret_buffer))) {
+ TPM_LOG(ERROR, result) << __func__
+ << ": Error calling Tspi_Policy_SetSecret";
+ return false;
+ }
+ std::string mutable_delegate_blob(delegate_blob);
+ BYTE* blob_buffer = StringAsTSSBuffer(&mutable_delegate_blob);
+ if (TPM_ERROR(result = Tspi_SetAttribData(
+ tpm_usage_policy,
+ TSS_TSPATTRIB_POLICY_DELEGATION_INFO,
+ TSS_TSPATTRIB_POLDEL_OWNERBLOB,
+ delegate_blob.size(),
+ blob_buffer))) {
+ TPM_LOG(ERROR, result) << __func__ << ": Error calling Tspi_SetAttribData";
+ return false;
+ }
+ return true;
+}
+
+bool TpmUtilityV1::SetupSrk() {
+ if (!IsTpmReady()) {
+ return false;
+ }
+ if (srk_handle_) {
+ return true;
+ }
+ srk_handle_.reset(context_handle_, 0);
+ if (!LoadSrk(context_handle_, &srk_handle_)) {
+ LOG(ERROR) << __func__ << ": Failed to load SRK.";
+ return false;
+ }
+ // In order to wrap a key with the SRK we need access to the SRK public key
+ // and we need to get it manually. Once it's in the key object, we don't need
+ // to do this again.
+ UINT32 length = 0;
+ ScopedTssMemory buffer(context_handle_);
+ TSS_RESULT result;
+ result = Tspi_Key_GetPubKey(srk_handle_, &length, buffer.ptr());
+ if (result != TSS_SUCCESS) {
+ TPM_LOG(INFO, result) << __func__ << ": Failed to read SRK public key.";
+ return false;
+ }
+ return true;
+}
+
+bool TpmUtilityV1::LoadSrk(TSS_HCONTEXT context_handle,
+ ScopedTssKey* srk_handle) {
+ TSS_RESULT result;
+ TSS_UUID uuid = TSS_UUID_SRK;
+ if (TPM_ERROR(result = Tspi_Context_LoadKeyByUUID(context_handle,
+ TSS_PS_TYPE_SYSTEM,
+ uuid,
+ srk_handle->ptr()))) {
+ TPM_LOG(ERROR, result) << __func__
+ << ": Error calling Tspi_Context_LoadKeyByUUID";
+ return false;
+ }
+ // Check if the SRK wants a password.
+ UINT32 auth_usage;
+ if (TPM_ERROR(result = Tspi_GetAttribUint32(*srk_handle,
+ TSS_TSPATTRIB_KEY_INFO,
+ TSS_TSPATTRIB_KEYINFO_AUTHUSAGE,
+ &auth_usage))) {
+ TPM_LOG(ERROR, result) << __func__
+ << ": Error calling Tspi_GetAttribUint32";
+ return false;
+ }
+ if (auth_usage) {
+ // Give it an empty password if needed.
+ TSS_HPOLICY usage_policy;
+ if (TPM_ERROR(result = Tspi_GetPolicyObject(*srk_handle,
+ TSS_POLICY_USAGE,
+ &usage_policy))) {
+ TPM_LOG(ERROR, result) << __func__
+ << ": Error calling Tspi_GetPolicyObject";
+ return false;
+ }
+
+ BYTE empty_password[] = {};
+ if (TPM_ERROR(result = Tspi_Policy_SetSecret(usage_policy,
+ TSS_SECRET_MODE_PLAIN,
+ 0, empty_password))) {
+ TPM_LOG(ERROR, result) << __func__
+ << ": Error calling Tspi_Policy_SetSecret";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool TpmUtilityV1::LoadKeyFromBlob(const std::string& key_blob,
+ TSS_HCONTEXT context_handle,
+ TSS_HKEY parent_key_handle,
+ ScopedTssKey* key_handle) {
+ std::string mutable_key_blob(key_blob);
+ BYTE* key_blob_buffer = StringAsTSSBuffer(&mutable_key_blob);
+ TSS_RESULT result = Tspi_Context_LoadKeyByBlob(
+ context_handle,
+ parent_key_handle,
+ key_blob.size(),
+ key_blob_buffer,
+ key_handle->ptr());
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << ": Failed to load key by blob.";
+ return false;
+ }
+ return true;
+}
+
+bool TpmUtilityV1::GetDataAttribute(TSS_HCONTEXT context,
+ TSS_HOBJECT object,
+ TSS_FLAG flag,
+ TSS_FLAG sub_flag,
+ std::string* data) {
+ UINT32 length = 0;
+ ScopedTssMemory buffer(context);
+ TSS_RESULT result = Tspi_GetAttribData(object, flag, sub_flag, &length,
+ buffer.ptr());
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << __func__ << "Failed to read object attribute.";
+ return false;
+ }
+ data->assign(TSSBufferAsString(buffer.value(), length));
+ return true;
+}
+
+bool TpmUtilityV1::ConvertPublicKeyToDER(const std::string& public_key,
+ std::string* public_key_der) {
+ // Parse the serialized TPM_PUBKEY.
+ UINT64 offset = 0;
+ std::string mutable_public_key(public_key);
+ BYTE* buffer = StringAsTSSBuffer(&mutable_public_key);
+ TPM_PUBKEY parsed;
+ TSS_RESULT result = Trspi_UnloadBlob_PUBKEY(&offset, buffer, &parsed);
+ if (TPM_ERROR(result)) {
+ TPM_LOG(ERROR, result) << "Failed to parse TPM_PUBKEY.";
+ return false;
+ }
+ ScopedByteArray scoped_key(parsed.pubKey.key);
+ ScopedByteArray scoped_parms(parsed.algorithmParms.parms);
+ TPM_RSA_KEY_PARMS* parms =
+ reinterpret_cast<TPM_RSA_KEY_PARMS*>(parsed.algorithmParms.parms);
+ crypto::ScopedRSA rsa(RSA_new());
+ CHECK(rsa.get());
+ // Get the public exponent.
+ if (parms->exponentSize == 0) {
+ rsa.get()->e = BN_new();
+ CHECK(rsa.get()->e);
+ BN_set_word(rsa.get()->e, kWellKnownExponent);
+ } else {
+ rsa.get()->e = BN_bin2bn(parms->exponent, parms->exponentSize, nullptr);
+ CHECK(rsa.get()->e);
+ }
+ // Get the modulus.
+ rsa.get()->n = BN_bin2bn(parsed.pubKey.key, parsed.pubKey.keyLength, nullptr);
+ CHECK(rsa.get()->n);
+
+ // DER encode.
+ int der_length = i2d_RSAPublicKey(rsa.get(), nullptr);
+ if (der_length < 0) {
+ LOG(ERROR) << "Failed to DER-encode public key.";
+ return false;
+ }
+ public_key_der->resize(der_length);
+ unsigned char* der_buffer = reinterpret_cast<unsigned char*>(
+ string_as_array(public_key_der));
+ der_length = i2d_RSAPublicKey(rsa.get(), &der_buffer);
+ if (der_length < 0) {
+ LOG(ERROR) << "Failed to DER-encode public key.";
+ return false;
+ }
+ public_key_der->resize(der_length);
+ return true;
+}
+
+} // namespace attestation
diff --git a/attestation/common/tpm_utility_v1.h b/attestation/common/tpm_utility_v1.h
new file mode 100644
index 0000000..6cabbcc
--- /dev/null
+++ b/attestation/common/tpm_utility_v1.h
@@ -0,0 +1,120 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_COMMON_TPM_UTILITY_V1_H_
+#define ATTESTATION_COMMON_TPM_UTILITY_V1_H_
+
+#include "attestation/common/tpm_utility.h"
+
+#include <string>
+
+#include <base/macros.h>
+#include <trousers/scoped_tss_type.h>
+#include <trousers/tss.h>
+
+namespace attestation {
+
+// A TpmUtility implementation for TPM v1.2 modules.
+class TpmUtilityV1 : public TpmUtility {
+ public:
+ TpmUtilityV1() = default;
+ ~TpmUtilityV1() override;
+
+ // Initializes a TpmUtilityV1 instance. This method must be called
+ // successfully before calling any other methods.
+ bool Initialize();
+
+ // TpmUtility methods.
+ bool IsTpmReady() override;
+ 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) override;
+ bool CreateCertifiedKey(KeyType key_type,
+ KeyUsage key_usage,
+ const std::string& identity_key_blob,
+ const std::string& external_data,
+ std::string* key_blob,
+ std::string* public_key,
+ std::string* public_key_tpm_format,
+ std::string* key_info,
+ std::string* proof) override;
+ bool SealToPCR0(const std::string& data, std::string* sealed_data) override;
+ bool Unseal(const std::string& sealed_data, std::string* data) override;
+ bool GetEndorsementPublicKey(std::string* public_key) override;
+ bool Unbind(const std::string& key_blob,
+ const std::string& bound_data,
+ std::string* data) override;
+ bool Sign(const std::string& key_blob,
+ const std::string& data_to_sign,
+ std::string* signature) override;
+
+ private:
+ // Populates |context_handle| with a valid TSS_HCONTEXT and |tpm_handle| with
+ // its matching TPM object iff the context can be created and a TPM object
+ // exists in the TSS. Returns true on success.
+ bool ConnectContext(trousers::ScopedTssContext* context_handle,
+ TSS_HTPM* tpm_handle);
+
+ // Populates |context_handle| with a valid TSS_HCONTEXT and |tpm_handle| with
+ // its matching TPM object authorized by the given |delegate_blob| and
+ // |delegate_secret|. Returns true on success.
+ bool ConnectContextAsDelegate(const std::string& delegate_blob,
+ const std::string& delegate_secret,
+ trousers::ScopedTssContext* context,
+ TSS_HTPM* tpm);
+
+ // Sets up srk_handle_ if necessary. Returns true iff the SRK is ready.
+ bool SetupSrk();
+
+ // Loads the storage root key (SRK) and populates |srk_handle|. The
+ // |context_handle| must be connected and valid. Returns true on success.
+ bool LoadSrk(TSS_HCONTEXT context_handle, trousers::ScopedTssKey* srk_handle);
+
+ // Loads a key in the TPM given a |key_blob| and a |parent_key_handle|. The
+ // |context_handle| must be connected and valid. Returns true and populates
+ // |key_handle| on success.
+ bool LoadKeyFromBlob(const std::string& key_blob,
+ TSS_HCONTEXT context_handle,
+ TSS_HKEY parent_key_handle,
+ trousers::ScopedTssKey* key_handle);
+
+ // Retrieves a |data| attribute defined by |flag| and |sub_flag| from a TSS
+ // |object_handle|. The |context_handle| is only used for TSS memory
+ // management.
+ bool GetDataAttribute(TSS_HCONTEXT context_handle,
+ TSS_HOBJECT object_handle,
+ TSS_FLAG flag,
+ TSS_FLAG sub_flag,
+ std::string* data);
+
+ // Converts a public in TPM_PUBKEY format to a DER-encoded RSAPublicKey.
+ bool ConvertPublicKeyToDER(const std::string& public_key,
+ std::string* public_key_der);
+
+ bool is_ready_{false};
+ trousers::ScopedTssContext context_handle_;
+ TSS_HTPM tpm_handle_{0};
+ trousers::ScopedTssKey srk_handle_{0};
+
+ DISALLOW_COPY_AND_ASSIGN(TpmUtilityV1);
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_COMMON_TPM_UTILITY_V1_H_
diff --git a/attestation/server/attestation_service.cc b/attestation/server/attestation_service.cc
new file mode 100644
index 0000000..d7f7974
--- /dev/null
+++ b/attestation/server/attestation_service.cc
@@ -0,0 +1,937 @@
+//
+// 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
diff --git a/attestation/server/attestation_service.h b/attestation/server/attestation_service.h
new file mode 100644
index 0000000..8426272
--- /dev/null
+++ b/attestation/server/attestation_service.h
@@ -0,0 +1,313 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_SERVER_ATTESTATION_SERVICE_H_
+#define ATTESTATION_SERVER_ATTESTATION_SERVICE_H_
+
+#include "attestation/common/attestation_interface.h"
+
+#include <memory>
+#include <string>
+
+#include <base/callback.h>
+#include <base/macros.h>
+#include <base/memory/weak_ptr.h>
+#include <base/threading/thread.h>
+#include <brillo/bind_lambda.h>
+#include <brillo/http/http_transport.h>
+
+#include "attestation/common/crypto_utility.h"
+#include "attestation/common/crypto_utility_impl.h"
+#include "attestation/common/tpm_utility.h"
+#include "attestation/common/tpm_utility_v1.h"
+#include "attestation/server/database.h"
+#include "attestation/server/database_impl.h"
+#include "attestation/server/key_store.h"
+#include "attestation/server/pkcs11_key_store.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();
+ ~AttestationService() override = default;
+
+ // AttestationInterface methods.
+ bool Initialize() override;
+ void CreateGoogleAttestedKey(
+ const CreateGoogleAttestedKeyRequest& request,
+ const CreateGoogleAttestedKeyCallback& callback) override;
+ void GetKeyInfo(const GetKeyInfoRequest& request,
+ const GetKeyInfoCallback& callback) override;
+ void GetEndorsementInfo(const GetEndorsementInfoRequest& request,
+ const GetEndorsementInfoCallback& callback) override;
+ void GetAttestationKeyInfo(
+ const GetAttestationKeyInfoRequest& request,
+ const GetAttestationKeyInfoCallback& callback) override;
+ void ActivateAttestationKey(
+ const ActivateAttestationKeyRequest& request,
+ const ActivateAttestationKeyCallback& callback) override;
+ void CreateCertifiableKey(
+ const CreateCertifiableKeyRequest& request,
+ const CreateCertifiableKeyCallback& callback) override;
+ void Decrypt(const DecryptRequest& request,
+ const DecryptCallback& callback) override;
+ void Sign(const SignRequest& request, const SignCallback& callback) override;
+ void RegisterKeyWithChapsToken(
+ const RegisterKeyWithChapsTokenRequest& request,
+ const RegisterKeyWithChapsTokenCallback& callback) override;
+
+ // 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<brillo::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:
+ enum ACARequestType {
+ kEnroll, // Enrolls a device, certifying an identity key.
+ kGetCertificate, // Issues a certificate for a TPM-backed key.
+ };
+
+ // A relay callback which allows the use of weak pointer semantics for a reply
+ // to TaskRunner::PostTaskAndReply.
+ template<typename ReplyProtobufType>
+ void TaskRelayCallback(
+ const base::Callback<void(const ReplyProtobufType&)> callback,
+ const std::shared_ptr<ReplyProtobufType>& reply) {
+ callback.Run(*reply);
+ }
+
+ // A blocking implementation of CreateGoogleAttestedKey appropriate to run on
+ // the worker thread.
+ void CreateGoogleAttestedKeyTask(
+ const CreateGoogleAttestedKeyRequest& request,
+ const std::shared_ptr<CreateGoogleAttestedKeyReply>& result);
+
+ // A blocking implementation of GetKeyInfo.
+ void GetKeyInfoTask(
+ const GetKeyInfoRequest& request,
+ const std::shared_ptr<GetKeyInfoReply>& result);
+
+ // A blocking implementation of GetEndorsementInfo.
+ void GetEndorsementInfoTask(
+ const GetEndorsementInfoRequest& request,
+ const std::shared_ptr<GetEndorsementInfoReply>& result);
+
+ // A blocking implementation of GetAttestationKeyInfo.
+ void GetAttestationKeyInfoTask(
+ const GetAttestationKeyInfoRequest& request,
+ const std::shared_ptr<GetAttestationKeyInfoReply>& result);
+
+ // A blocking implementation of ActivateAttestationKey.
+ void ActivateAttestationKeyTask(
+ const ActivateAttestationKeyRequest& request,
+ const std::shared_ptr<ActivateAttestationKeyReply>& result);
+
+ // A blocking implementation of CreateCertifiableKey.
+ void CreateCertifiableKeyTask(
+ const CreateCertifiableKeyRequest& request,
+ const std::shared_ptr<CreateCertifiableKeyReply>& result);
+
+ // A blocking implementation of Decrypt.
+ void DecryptTask(const DecryptRequest& request,
+ const std::shared_ptr<DecryptReply>& result);
+
+ // A blocking implementation of Sign.
+ void SignTask(const SignRequest& request,
+ const std::shared_ptr<SignReply>& result);
+
+ // A synchronous implementation of RegisterKeyWithChapsToken.
+ void RegisterKeyWithChapsTokenTask(
+ const RegisterKeyWithChapsTokenRequest& request,
+ const std::shared_ptr<RegisterKeyWithChapsTokenReply>& 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 given |key|, according to the given |profile|, |username| and
+ // |origin|.
+ bool CreateCertificateRequest(const std::string& username,
+ const CertifiedKey& key,
+ 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,
+ CertifiedKey* key,
+ std::string* certificate_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, certifies, and saves a new |key| for |username| with the given
+ // |key_label|, |key_type|, and |key_usage|. Returns true on success.
+ bool CreateKey(const std::string& username,
+ const std::string& key_label,
+ KeyType key_type,
+ KeyUsage key_usage,
+ CertifiedKey* key);
+
+ // 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);
+
+ // 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;
+
+ // Creates a X.509/DER SubjectPublicKeyInfo for the given |key_type| and
+ // |public_key|. On success returns true and provides |public_key_info|.
+ bool GetSubjectPublicKeyInfo(KeyType key_type,
+ const std::string& public_key,
+ std::string* public_key_info) 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_{nullptr};
+ Database* database_{nullptr};
+ std::shared_ptr<brillo::http::Transport> http_transport_;
+ KeyStore* key_store_{nullptr};
+ TpmUtility* tpm_utility_{nullptr};
+
+ // Default implementations for the above interfaces. These will be setup
+ // during Initialize() if the corresponding interface has not been set with a
+ // mutator.
+ std::unique_ptr<CryptoUtilityImpl> default_crypto_utility_;
+ std::unique_ptr<DatabaseImpl> default_database_;
+ std::unique_ptr<Pkcs11KeyStore> default_key_store_;
+ std::unique_ptr<chaps::TokenManagerClient> pkcs11_token_manager_;
+ std::unique_ptr<TpmUtilityV1> default_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);
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_ATTESTATION_SERVICE_H_
diff --git a/attestation/server/attestation_service_test.cc b/attestation/server/attestation_service_test.cc
new file mode 100644
index 0000000..494904b
--- /dev/null
+++ b/attestation/server/attestation_service_test.cc
@@ -0,0 +1,1190 @@
+//
+// 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 <string>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/message_loop/message_loop.h>
+#include <base/run_loop.h>
+#include <brillo/bind_lambda.h>
+#include <brillo/data_encoding.h>
+#include <brillo/http/http_transport_fake.h>
+#include <brillo/mime_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "attestation/common/attestation_ca.pb.h"
+#include "attestation/common/mock_crypto_utility.h"
+#include "attestation/common/mock_tpm_utility.h"
+#include "attestation/server/attestation_service.h"
+#include "attestation/server/mock_database.h"
+#include "attestation/server/mock_key_store.h"
+
+using brillo::http::fake::ServerRequest;
+using brillo::http::fake::ServerResponse;
+using testing::_;
+using testing::DoAll;
+using testing::NiceMock;
+using testing::Return;
+using testing::ReturnRef;
+using testing::SetArgumentPointee;
+
+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<brillo::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 wrapped EK certificate by default.
+ mock_database_.GetMutableProtobuf()->mutable_credentials()->
+ mutable_default_encrypted_endorsement_credential()->
+ set_wrapping_key_id("default");
+ // Setup a fake Attestation CA for success by default.
+ SetupFakeCAEnroll(kSuccess);
+ SetupFakeCASign(kSuccess);
+ CHECK(service_->Initialize());
+ }
+
+ protected:
+ void SetupFakeCAEnroll(FakeCAState state) {
+ fake_http_transport_->AddHandler(
+ service_->attestation_ca_origin() + "/enroll",
+ brillo::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",
+ brillo::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 += brillo::data_encoding::Base64EncodeWrapLines("fake_cert");
+ pem += kEndCertificate + "\n" + kBeginCertificate;
+ pem += brillo::data_encoding::Base64EncodeWrapLines("fake_ca_cert");
+ pem += kEndCertificate + "\n" + kBeginCertificate;
+ pem += brillo::data_encoding::Base64EncodeWrapLines("fake_ca_cert2");
+ pem += kEndCertificate;
+ return pem;
+ }
+
+ CreateGoogleAttestedKeyRequest GetCreateRequest() {
+ CreateGoogleAttestedKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ request.set_certificate_profile(ENTERPRISE_MACHINE_CERTIFICATE);
+ request.set_username("user");
+ request.set_origin("origin");
+ return request;
+ }
+
+ void Run() {
+ run_loop_.Run();
+ }
+
+ void RunUntilIdle() {
+ run_loop_.RunUntilIdle();
+ }
+
+ void Quit() {
+ run_loop_.Quit();
+ }
+
+ std::shared_ptr<brillo::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(brillo::http::status_code::NotFound, std::string(),
+ brillo::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(brillo::http::status_code::Ok, tmp,
+ brillo::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(brillo::http::status_code::NotFound, std::string(),
+ brillo::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");
+ *response_pb.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+ }
+ std::string tmp;
+ response_pb.SerializeToString(&tmp);
+ response->ReplyText(brillo::http::status_code::Ok, tmp,
+ brillo::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 CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(GetFakeCertificateChain(), reply.certificate_chain());
+ EXPECT_FALSE(reply.has_server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeySuccessNoUser) {
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(GetFakeCertificateChain(), reply.certificate_chain());
+ EXPECT_FALSE(reply.has_server_error());
+ Quit();
+ };
+ CreateGoogleAttestedKeyRequest request = GetCreateRequest();
+ request.clear_username();
+ service_->CreateGoogleAttestedKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithEnrollHttpError) {
+ SetupFakeCAEnroll(kHttpFailure);
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_EQ(STATUS_CA_NOT_AVAILABLE, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithSignHttpError) {
+ SetupFakeCASign(kHttpFailure);
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_EQ(STATUS_CA_NOT_AVAILABLE, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithCAEnrollFailure) {
+ SetupFakeCAEnroll(kCommandFailure);
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_EQ(STATUS_REQUEST_DENIED_BY_CA, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("fake_enroll_error", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithCASignFailure) {
+ SetupFakeCASign(kCommandFailure);
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_EQ(STATUS_REQUEST_DENIED_BY_CA, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("fake_sign_error", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithBadCAMessageID) {
+ SetupFakeCASign(kBadMessageID);
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithNoEKCertificate) {
+ // Remove the default credential setup.
+ mock_database_.GetMutableProtobuf()->clear_credentials();
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), 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 CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), 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 CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), 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 CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), 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 CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ CreateGoogleAttestedKeyRequest request = GetCreateRequest();
+ request.clear_username();
+ service_->CreateGoogleAttestedKey(request, 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 CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), 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 CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), 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 CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyWithTpmCreateFailure) {
+ EXPECT_CALL(mock_tpm_utility_, CreateCertifiedKey(_, _, _, _, _, _, _, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateGoogleAttestedKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_certificate_chain());
+ EXPECT_EQ("", reply.server_error());
+ Quit();
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateGoogleAttestedKeyAndCancel) {
+ // Set expectations on the outputs.
+ int callback_count = 0;
+ auto callback = [&callback_count](const CreateGoogleAttestedKeyReply& reply) {
+ callback_count++;
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), 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 CreateGoogleAttestedKeyReply& reply) {
+ callback_count++;
+ };
+ service_->CreateGoogleAttestedKey(GetCreateRequest(), 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);
+}
+
+TEST_F(AttestationServiceTest, GetKeyInfoSuccess) {
+ // Setup a certified key in the key store.
+ CertifiedKey key;
+ key.set_public_key("public_key");
+ key.set_certified_key_credential("fake_cert");
+ key.set_intermediate_ca_cert("fake_ca_cert");
+ *key.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+ key.set_key_name("label");
+ key.set_certified_key_info("certify_info");
+ key.set_certified_key_proof("signature");
+ key.set_key_type(KEY_TYPE_RSA);
+ key.set_key_usage(KEY_USAGE_SIGN);
+ std::string key_bytes;
+ key.SerializeToString(&key_bytes);
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+
+ // Set expectations on the outputs.
+ auto callback = [this](const GetKeyInfoReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(KEY_TYPE_RSA, reply.key_type());
+ EXPECT_EQ(KEY_USAGE_SIGN, reply.key_usage());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("certify_info", reply.certify_info());
+ EXPECT_EQ("signature", reply.certify_info_signature());
+ EXPECT_EQ(GetFakeCertificateChain(), reply.certificate());
+ Quit();
+ };
+ GetKeyInfoRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ service_->GetKeyInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetKeyInfoSuccessNoUser) {
+ // Setup a certified key in the device key store.
+ CertifiedKey& key = *mock_database_.GetMutableProtobuf()->add_device_keys();
+ key.set_public_key("public_key");
+ key.set_certified_key_credential("fake_cert");
+ key.set_intermediate_ca_cert("fake_ca_cert");
+ *key.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+ key.set_key_name("label");
+ key.set_certified_key_info("certify_info");
+ key.set_certified_key_proof("signature");
+ key.set_key_type(KEY_TYPE_RSA);
+ key.set_key_usage(KEY_USAGE_SIGN);
+
+ // Set expectations on the outputs.
+ auto callback = [this](const GetKeyInfoReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(KEY_TYPE_RSA, reply.key_type());
+ EXPECT_EQ(KEY_USAGE_SIGN, reply.key_usage());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("certify_info", reply.certify_info());
+ EXPECT_EQ("signature", reply.certify_info_signature());
+ EXPECT_EQ(GetFakeCertificateChain(), reply.certificate());
+ Quit();
+ };
+ GetKeyInfoRequest request;
+ request.set_key_label("label");
+ service_->GetKeyInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetKeyInfoNoKey) {
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillRepeatedly(Return(false));
+
+ // Set expectations on the outputs.
+ auto callback = [this](const GetKeyInfoReply& reply) {
+ EXPECT_EQ(STATUS_INVALID_PARAMETER, reply.status());
+ Quit();
+ };
+ GetKeyInfoRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ service_->GetKeyInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetKeyInfoBadPublicKey) {
+ EXPECT_CALL(mock_crypto_utility_, GetRSASubjectPublicKeyInfo(_, _))
+ .WillRepeatedly(Return(false));
+
+ // Set expectations on the outputs.
+ auto callback = [this](const GetKeyInfoReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ GetKeyInfoRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ service_->GetKeyInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetEndorsementInfoSuccess) {
+ AttestationDatabase* database = mock_database_.GetMutableProtobuf();
+ database->mutable_credentials()->set_endorsement_public_key("public_key");
+ database->mutable_credentials()->set_endorsement_credential("certificate");
+ // Set expectations on the outputs.
+ auto callback = [this](const GetEndorsementInfoReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.ek_public_key());
+ EXPECT_EQ("certificate", reply.ek_certificate());
+ Quit();
+ };
+ GetEndorsementInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ service_->GetEndorsementInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetEndorsementInfoNoInfo) {
+ // Set expectations on the outputs.
+ auto callback = [this](const GetEndorsementInfoReply& reply) {
+ EXPECT_EQ(STATUS_NOT_AVAILABLE, reply.status());
+ EXPECT_FALSE(reply.has_ek_public_key());
+ EXPECT_FALSE(reply.has_ek_certificate());
+ Quit();
+ };
+ GetEndorsementInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ service_->GetEndorsementInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetEndorsementInfoNoCert) {
+ AttestationDatabase* database = mock_database_.GetMutableProtobuf();
+ database->mutable_credentials()->set_endorsement_public_key("public_key");
+ // Set expectations on the outputs.
+ auto callback = [this](const GetEndorsementInfoReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.ek_public_key());
+ EXPECT_FALSE(reply.has_ek_certificate());
+ Quit();
+ };
+ GetEndorsementInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ service_->GetEndorsementInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetAttestationKeyInfoSuccess) {
+ AttestationDatabase* database = mock_database_.GetMutableProtobuf();
+ database->mutable_identity_key()->set_identity_public_key("public_key");
+ database->mutable_identity_key()->set_identity_credential("certificate");
+ database->mutable_pcr0_quote()->set_quote("pcr0");
+ database->mutable_pcr1_quote()->set_quote("pcr1");
+ database->mutable_identity_binding()->set_identity_public_key("public_key2");
+ // Set expectations on the outputs.
+ auto callback = [this](const GetAttestationKeyInfoReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("public_key2", reply.public_key_tpm_format());
+ EXPECT_EQ("certificate", reply.certificate());
+ EXPECT_EQ("pcr0", reply.pcr0_quote().quote());
+ EXPECT_EQ("pcr1", reply.pcr1_quote().quote());
+ Quit();
+ };
+ GetAttestationKeyInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ service_->GetAttestationKeyInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetAttestationKeyInfoNoInfo) {
+ // Set expectations on the outputs.
+ auto callback = [this](const GetAttestationKeyInfoReply& reply) {
+ EXPECT_EQ(STATUS_NOT_AVAILABLE, reply.status());
+ EXPECT_FALSE(reply.has_public_key());
+ EXPECT_FALSE(reply.has_public_key_tpm_format());
+ EXPECT_FALSE(reply.has_certificate());
+ EXPECT_FALSE(reply.has_pcr0_quote());
+ EXPECT_FALSE(reply.has_pcr1_quote());
+ Quit();
+ };
+ GetAttestationKeyInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ service_->GetAttestationKeyInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, GetAttestationKeyInfoSomeInfo) {
+ AttestationDatabase* database = mock_database_.GetMutableProtobuf();
+ database->mutable_identity_key()->set_identity_credential("certificate");
+ database->mutable_pcr1_quote()->set_quote("pcr1");
+ // Set expectations on the outputs.
+ auto callback = [this](const GetAttestationKeyInfoReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_public_key());
+ EXPECT_FALSE(reply.has_public_key_tpm_format());
+ EXPECT_EQ("certificate", reply.certificate());
+ EXPECT_FALSE(reply.has_pcr0_quote());
+ EXPECT_EQ("pcr1", reply.pcr1_quote().quote());
+ Quit();
+ };
+ GetAttestationKeyInfoRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ service_->GetAttestationKeyInfo(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, ActivateAttestationKeySuccess) {
+ EXPECT_CALL(mock_database_, SaveChanges()).Times(1);
+ EXPECT_CALL(mock_tpm_utility_, ActivateIdentity(_, _, _, "encrypted1",
+ "encrypted2", _))
+ .WillOnce(DoAll(SetArgumentPointee<5>(std::string("certificate")),
+ Return(true)));
+ // Set expectations on the outputs.
+ auto callback = [this](const ActivateAttestationKeyReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("certificate", reply.certificate());
+ Quit();
+ };
+ ActivateAttestationKeyRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ request.mutable_encrypted_certificate()->set_asym_ca_contents("encrypted1");
+ request.mutable_encrypted_certificate()->set_sym_ca_attestation("encrypted2");
+ request.set_save_certificate(true);
+ service_->ActivateAttestationKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, ActivateAttestationKeySuccessNoSave) {
+ EXPECT_CALL(mock_database_, GetMutableProtobuf()).Times(0);
+ EXPECT_CALL(mock_database_, SaveChanges()).Times(0);
+ EXPECT_CALL(mock_tpm_utility_, ActivateIdentity(_, _, _, "encrypted1",
+ "encrypted2", _))
+ .WillOnce(DoAll(SetArgumentPointee<5>(std::string("certificate")),
+ Return(true)));
+ // Set expectations on the outputs.
+ auto callback = [this](const ActivateAttestationKeyReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("certificate", reply.certificate());
+ Quit();
+ };
+ ActivateAttestationKeyRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ request.mutable_encrypted_certificate()->set_asym_ca_contents("encrypted1");
+ request.mutable_encrypted_certificate()->set_sym_ca_attestation("encrypted2");
+ request.set_save_certificate(false);
+ service_->ActivateAttestationKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, ActivateAttestationKeySaveFailure) {
+ EXPECT_CALL(mock_database_, SaveChanges()).WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const ActivateAttestationKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ ActivateAttestationKeyRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ request.mutable_encrypted_certificate()->set_asym_ca_contents("encrypted1");
+ request.mutable_encrypted_certificate()->set_sym_ca_attestation("encrypted2");
+ request.set_save_certificate(true);
+ service_->ActivateAttestationKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, ActivateAttestationKeyActivateFailure) {
+ EXPECT_CALL(mock_tpm_utility_, ActivateIdentity(_, _, _, "encrypted1",
+ "encrypted2", _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const ActivateAttestationKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ ActivateAttestationKeyRequest request;
+ request.set_key_type(KEY_TYPE_RSA);
+ request.mutable_encrypted_certificate()->set_asym_ca_contents("encrypted1");
+ request.mutable_encrypted_certificate()->set_sym_ca_attestation("encrypted2");
+ request.set_save_certificate(true);
+ service_->ActivateAttestationKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateCertifiableKeySuccess) {
+ // Configure a fake TPM response.
+ EXPECT_CALL(mock_tpm_utility_, CreateCertifiedKey(KEY_TYPE_ECC,
+ KEY_USAGE_SIGN,
+ _, _, _, _, _, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<5>(std::string("public_key")),
+ SetArgumentPointee<7>(std::string("certify_info")),
+ SetArgumentPointee<8>(
+ std::string("certify_info_signature")),
+ Return(true)));
+ // Expect the key to be written exactly once.
+ EXPECT_CALL(mock_key_store_, Write("user", "label", _)).Times(1);
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateCertifiableKeyReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("certify_info", reply.certify_info());
+ EXPECT_EQ("certify_info_signature", reply.certify_info_signature());
+ Quit();
+ };
+ CreateCertifiableKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ request.set_username("user");
+ service_->CreateCertifiableKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateCertifiableKeySuccessNoUser) {
+ // Configure a fake TPM response.
+ EXPECT_CALL(mock_tpm_utility_, CreateCertifiedKey(KEY_TYPE_ECC,
+ KEY_USAGE_SIGN,
+ _, _, _, _, _, _, _))
+ .WillOnce(DoAll(SetArgumentPointee<5>(std::string("public_key")),
+ SetArgumentPointee<7>(std::string("certify_info")),
+ SetArgumentPointee<8>(
+ std::string("certify_info_signature")),
+ Return(true)));
+ // Expect the key to be written exactly once.
+ EXPECT_CALL(mock_database_, SaveChanges()).Times(1);
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateCertifiableKeyReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("certify_info", reply.certify_info());
+ EXPECT_EQ("certify_info_signature", reply.certify_info_signature());
+ Quit();
+ };
+ CreateCertifiableKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ service_->CreateCertifiableKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateCertifiableKeyRNGFailure) {
+ EXPECT_CALL(mock_crypto_utility_, GetRandom(_, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateCertifiableKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_public_key());
+ EXPECT_FALSE(reply.has_certify_info());
+ EXPECT_FALSE(reply.has_certify_info_signature());
+ Quit();
+ };
+ CreateCertifiableKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ service_->CreateCertifiableKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateCertifiableKeyTpmCreateFailure) {
+ EXPECT_CALL(mock_tpm_utility_, CreateCertifiedKey(_, _, _, _, _, _, _, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateCertifiableKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_public_key());
+ EXPECT_FALSE(reply.has_certify_info());
+ EXPECT_FALSE(reply.has_certify_info_signature());
+ Quit();
+ };
+ CreateCertifiableKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ service_->CreateCertifiableKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateCertifiableKeyDBFailure) {
+ EXPECT_CALL(mock_key_store_, Write(_, _, _)).WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateCertifiableKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_public_key());
+ EXPECT_FALSE(reply.has_certify_info());
+ EXPECT_FALSE(reply.has_certify_info_signature());
+ Quit();
+ };
+ CreateCertifiableKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ request.set_username("username");
+ service_->CreateCertifiableKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, CreateCertifiableKeyDBFailureNoUser) {
+ EXPECT_CALL(mock_database_, SaveChanges()).WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const CreateCertifiableKeyReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_public_key());
+ EXPECT_FALSE(reply.has_certify_info());
+ EXPECT_FALSE(reply.has_certify_info_signature());
+ Quit();
+ };
+ CreateCertifiableKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ service_->CreateCertifiableKey(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptSuccess) {
+ // Set expectations on the outputs.
+ auto callback = [this](const DecryptReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(MockTpmUtility::Transform("Unbind", "data"),
+ reply.decrypted_data());
+ Quit();
+ };
+ DecryptRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_encrypted_data("data");
+ service_->Decrypt(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptSuccessNoUser) {
+ mock_database_.GetMutableProtobuf()->add_device_keys()->set_key_name("label");
+ // Set expectations on the outputs.
+ auto callback = [this](const DecryptReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(MockTpmUtility::Transform("Unbind", "data"),
+ reply.decrypted_data());
+ Quit();
+ };
+ DecryptRequest request;
+ request.set_key_label("label");
+ request.set_encrypted_data("data");
+ service_->Decrypt(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptKeyNotFound) {
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const DecryptReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_decrypted_data());
+ Quit();
+ };
+ DecryptRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_encrypted_data("data");
+ service_->Decrypt(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptKeyNotFoundNoUser) {
+ // Set expectations on the outputs.
+ auto callback = [this](const DecryptReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_decrypted_data());
+ Quit();
+ };
+ DecryptRequest request;
+ request.set_key_label("label");
+ request.set_encrypted_data("data");
+ service_->Decrypt(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, DecryptUnbindFailure) {
+ EXPECT_CALL(mock_tpm_utility_, Unbind(_, _, _)).WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const DecryptReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_decrypted_data());
+ Quit();
+ };
+ DecryptRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_encrypted_data("data");
+ service_->Decrypt(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, SignSuccess) {
+ // Set expectations on the outputs.
+ auto callback = [this](const SignReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(MockTpmUtility::Transform("Sign", "data"), reply.signature());
+ Quit();
+ };
+ SignRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_data_to_sign("data");
+ service_->Sign(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, SignSuccessNoUser) {
+ mock_database_.GetMutableProtobuf()->add_device_keys()->set_key_name("label");
+ // Set expectations on the outputs.
+ auto callback = [this](const SignReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(MockTpmUtility::Transform("Sign", "data"), reply.signature());
+ Quit();
+ };
+ SignRequest request;
+ request.set_key_label("label");
+ request.set_data_to_sign("data");
+ service_->Sign(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, SignKeyNotFound) {
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const SignReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_signature());
+ Quit();
+ };
+ SignRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_data_to_sign("data");
+ service_->Sign(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, SignKeyNotFoundNoUser) {
+ // Set expectations on the outputs.
+ auto callback = [this](const SignReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_signature());
+ Quit();
+ };
+ SignRequest request;
+ request.set_key_label("label");
+ request.set_data_to_sign("data");
+ service_->Sign(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, SignUnbindFailure) {
+ EXPECT_CALL(mock_tpm_utility_, Sign(_, _, _)).WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const SignReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ EXPECT_FALSE(reply.has_signature());
+ Quit();
+ };
+ SignRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_data_to_sign("data");
+ service_->Sign(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterSuccess) {
+ // Setup a key in the user key store.
+ CertifiedKey key;
+ key.set_key_blob("key_blob");
+ key.set_public_key("public_key");
+ key.set_certified_key_credential("fake_cert");
+ key.set_intermediate_ca_cert("fake_ca_cert");
+ *key.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+ key.set_key_name("label");
+ key.set_key_type(KEY_TYPE_RSA);
+ key.set_key_usage(KEY_USAGE_SIGN);
+ std::string key_bytes;
+ key.SerializeToString(&key_bytes);
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+ // Cardinality is verified here to verify various steps are performed and to
+ // catch performance regressions.
+ EXPECT_CALL(mock_key_store_, Register("user",
+ "label",
+ KEY_TYPE_RSA,
+ KEY_USAGE_SIGN,
+ "key_blob",
+ "public_key",
+ "fake_cert")).Times(1);
+ EXPECT_CALL(mock_key_store_, RegisterCertificate("user", "fake_ca_cert"))
+ .Times(1);
+ EXPECT_CALL(mock_key_store_, RegisterCertificate("user", "fake_ca_cert2"))
+ .Times(1);
+ EXPECT_CALL(mock_key_store_, Delete("user", "label")).Times(1);
+ // Set expectations on the outputs.
+ auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterSuccessNoUser) {
+ // Setup a key in the device_keys field.
+ CertifiedKey& key = *mock_database_.GetMutableProtobuf()->add_device_keys();
+ key.set_key_blob("key_blob");
+ key.set_public_key("public_key");
+ key.set_certified_key_credential("fake_cert");
+ key.set_intermediate_ca_cert("fake_ca_cert");
+ *key.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+ key.set_key_name("label");
+ key.set_key_type(KEY_TYPE_RSA);
+ key.set_key_usage(KEY_USAGE_SIGN);
+ // Cardinality is verified here to verify various steps are performed and to
+ // catch performance regressions.
+ EXPECT_CALL(mock_key_store_, Register("",
+ "label",
+ KEY_TYPE_RSA,
+ KEY_USAGE_SIGN,
+ "key_blob",
+ "public_key",
+ "fake_cert")).Times(1);
+ EXPECT_CALL(mock_key_store_, RegisterCertificate("", "fake_ca_cert"))
+ .Times(1);
+ EXPECT_CALL(mock_key_store_, RegisterCertificate("", "fake_ca_cert2"))
+ .Times(1);
+ // Set expectations on the outputs.
+ auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(0, mock_database_.GetMutableProtobuf()->device_keys_size());
+ Quit();
+ };
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterNoKey) {
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterNoKeyNoUser) {
+ // Set expectations on the outputs.
+ auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterFailure) {
+ // Setup a key in the user key store.
+ CertifiedKey key;
+ key.set_key_name("label");
+ std::string key_bytes;
+ key.SerializeToString(&key_bytes);
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+ EXPECT_CALL(mock_key_store_, Register(_, _, _, _, _, _, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterIntermediateFailure) {
+ // Setup a key in the user key store.
+ CertifiedKey key;
+ key.set_key_name("label");
+ key.set_intermediate_ca_cert("fake_ca_cert");
+ std::string key_bytes;
+ key.SerializeToString(&key_bytes);
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+ EXPECT_CALL(mock_key_store_, RegisterCertificate(_, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+ Run();
+}
+
+TEST_F(AttestationServiceTest, RegisterAdditionalFailure) {
+ // Setup a key in the user key store.
+ CertifiedKey key;
+ key.set_key_name("label");
+ *key.add_additional_intermediate_ca_cert() = "fake_ca_cert2";
+ std::string key_bytes;
+ key.SerializeToString(&key_bytes);
+ EXPECT_CALL(mock_key_store_, Read("user", "label", _))
+ .WillOnce(DoAll(SetArgumentPointee<2>(key_bytes), Return(true)));
+ EXPECT_CALL(mock_key_store_, RegisterCertificate(_, _))
+ .WillRepeatedly(Return(false));
+ // Set expectations on the outputs.
+ auto callback = [this](const RegisterKeyWithChapsTokenReply& reply) {
+ EXPECT_NE(STATUS_SUCCESS, reply.status());
+ Quit();
+ };
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ service_->RegisterKeyWithChapsToken(request, base::Bind(callback));
+ Run();
+}
+
+} // namespace attestation
diff --git a/attestation/server/attestationd-seccomp-amd64.policy b/attestation/server/attestationd-seccomp-amd64.policy
new file mode 100644
index 0000000..9cdff43
--- /dev/null
+++ b/attestation/server/attestationd-seccomp-amd64.policy
@@ -0,0 +1,80 @@
+#
+# 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.
+#
+
+# Tested on link
+gettid: 1
+getuid: 1
+geteuid: 1
+getgid: 1
+getegid: 1
+getresuid: 1
+getresgid: 1
+
+clock_getres: 1
+clock_gettime: 1
+gettimeofday: 1
+time: 1
+
+socket: 1
+socketpair: 1
+connect: 1
+getsockname: 1
+pipe: 1
+sendmsg: 1
+sendto: 1
+recvmsg: 1
+recvfrom: 1
+
+epoll_create: 1
+epoll_wait: 1
+epoll_ctl: 1
+poll: 1
+
+open: 1
+read: 1
+write: 1
+close: 1
+
+inotify_init: 1
+inotify_add_watch: 1
+inotify_rm_watch: 1
+select: 1
+
+fstat: 1
+stat: 1
+lseek: 1
+lstat: 1
+fcntl: 1
+
+futex: 1
+set_robust_list: 1
+restart_syscall: 1
+exit: 1
+exit_group: 1
+rt_sigreturn: 1
+rt_sigprocmask: 1
+signalfd4: 1
+
+brk: 1
+mmap: 1
+madvise: 1
+mprotect: 1
+munmap: 1
+
+clone: 1
+# These calls are attempted but apparently not necessary; return EPERM
+prctl: return 1
+ioctl: return 1
diff --git a/attestation/server/attestationd-seccomp-arm.policy b/attestation/server/attestationd-seccomp-arm.policy
new file mode 100644
index 0000000..16d2a9f
--- /dev/null
+++ b/attestation/server/attestationd-seccomp-arm.policy
@@ -0,0 +1,50 @@
+# Tested on daisy_spring board
+gettid: 1
+getuid32: 1
+geteuid32: 1
+getgid32: 1
+getegid32: 1
+getresuid32: 1
+getresgid32: 1
+
+clock_getres: 1
+clock_gettime: 1
+gettimeofday: 1
+
+# Allow socket(domain==PF_LOCAL) or socket(domain==PF_NETLINK)
+socket: arg0 == 0x1 || arg0 == 0x10
+socketpair: 1
+connect: 1
+getsockname: 1
+pipe: 1
+send: 1
+sendmsg: 1
+recvmsg: 1
+
+epoll_create: 1
+epoll_wait: 1
+epoll_ctl: 1
+poll: 1
+
+open: 1
+read: 1
+write: 1
+close: 1
+
+fstat64: 1
+stat64: 1
+_llseek: 1
+fcntl64: 1
+
+futex: 1
+
+restart_syscall: 1
+exit: 1
+exit_group: 1
+rt_sigreturn: 1
+rt_sigprocmask: 1
+signalfd4: 1
+
+brk: 1
+mmap2: 1
+munmap: 1
diff --git a/attestation/server/attestationd-seccomp-x86.policy b/attestation/server/attestationd-seccomp-x86.policy
new file mode 100644
index 0000000..015e01e
--- /dev/null
+++ b/attestation/server/attestationd-seccomp-x86.policy
@@ -0,0 +1,45 @@
+# Tested on alex board
+gettid: 1
+geteuid32: 1
+getegid32: 1
+getuid32: 1
+getgid32: 1
+getresuid32: 1
+getresgid32: 1
+
+clock_getres: 1
+clock_gettime: 1
+gettimeofday: 1
+time: 1
+
+# TODO(namnguyen): filter socket system calls.
+socketcall: 1
+pipe: 1
+
+open: 1
+read: 1
+write: 1
+close: 1
+
+brk: 1
+mmap2: 1
+munmap: 1
+
+fstat64: 1
+stat64: 1
+_llseek: 1
+fcntl64: 1
+
+futex: 1
+
+restart_syscall: 1
+exit: 1
+exit_group: 1
+rt_sigreturn: 1
+rt_sigprocmask: 1
+signalfd4: 1
+
+epoll_create: 1
+epoll_wait: 1
+epoll_ctl: 1
+poll: 1
diff --git a/attestation/server/attestationd.conf b/attestation/server/attestationd.conf
new file mode 100644
index 0000000..0d5a74d
--- /dev/null
+++ b/attestation/server/attestationd.conf
@@ -0,0 +1,24 @@
+#
+# Copyright (C) 2014 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.
+#
+
+description "Chromium OS device attestation service."
+author "chromium-os-dev@chromium.org"
+
+start on started tcsd and started boot-services
+stop on stopping boot-services
+respawn
+
+exec /usr/sbin/attestationd
diff --git a/attestation/server/database.h b/attestation/server/database.h
new file mode 100644
index 0000000..7416e57
--- /dev/null
+++ b/attestation/server/database.h
@@ -0,0 +1,46 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_SERVER_DATABASE_H_
+#define ATTESTATION_SERVER_DATABASE_H_
+
+#include "attestation/common/database.pb.h"
+
+namespace attestation {
+
+// Manages a persistent database of attestation-related data.
+class Database {
+ public:
+ virtual ~Database() = default;
+
+ // Const access to the database protobuf.
+ virtual const AttestationDatabase& GetProtobuf() const = 0;
+
+ // Mutable access to the database protobuf. Changes made to the protobuf will
+ // be reflected immediately by GetProtobuf() but will not be persisted to disk
+ // until SaveChanges is called successfully.
+ virtual AttestationDatabase* GetMutableProtobuf() = 0;
+
+ // Writes the current database protobuf to disk.
+ virtual bool SaveChanges() = 0;
+
+ // Reloads the database protobuf from disk.
+ virtual bool Reload() = 0;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_DATABASE_H_
diff --git a/attestation/server/database_impl.cc b/attestation/server/database_impl.cc
new file mode 100644
index 0000000..48cdfe5
--- /dev/null
+++ b/attestation/server/database_impl.cc
@@ -0,0 +1,199 @@
+//
+// 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/database_impl.h"
+
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <string>
+
+#include <base/files/file_path.h>
+#include <base/files/file_util.h>
+#include <base/files/important_file_writer.h>
+#include <base/logging.h>
+#include <base/stl_util.h>
+#include <brillo/secure_blob.h>
+
+using base::FilePath;
+
+namespace {
+
+const char kDatabasePath[] =
+ "/mnt/stateful_partition/unencrypted/preserve/attestation.epb";
+const mode_t kDatabasePermissions = 0600;
+
+// A base::FilePathWatcher::Callback that just relays to |callback|.
+void FileWatcherCallback(const base::Closure& callback, const FilePath&, bool) {
+ callback.Run();
+}
+
+} // namespace
+
+namespace attestation {
+
+DatabaseImpl::DatabaseImpl(CryptoUtility* crypto) : io_(this), crypto_(crypto) {
+}
+
+DatabaseImpl::~DatabaseImpl() {
+ brillo::SecureMemset(string_as_array(&database_key_), 0,
+ database_key_.size());
+}
+
+void DatabaseImpl::Initialize() {
+ // Start thread-checking now.
+ thread_checker_.DetachFromThread();
+ DCHECK(thread_checker_.CalledOnValidThread());
+ io_->Watch(base::Bind(base::IgnoreResult(&DatabaseImpl::Reload),
+ base::Unretained(this)));
+ if (!Reload()) {
+ LOG(WARNING) << "Creating new attestation database.";
+ }
+}
+
+const AttestationDatabase& DatabaseImpl::GetProtobuf() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return protobuf_;
+}
+
+AttestationDatabase* DatabaseImpl::GetMutableProtobuf() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return &protobuf_;
+}
+
+bool DatabaseImpl::SaveChanges() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ std::string buffer;
+ if (!EncryptProtobuf(&buffer)) {
+ return false;
+ }
+ return io_->Write(buffer);
+}
+
+bool DatabaseImpl::Reload() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ LOG(INFO) << "Loading attestation database.";
+ std::string buffer;
+ if (!io_->Read(&buffer)) {
+ return false;
+ }
+ return DecryptProtobuf(buffer);
+}
+
+bool DatabaseImpl::Read(std::string* data) {
+ const int kMask = base::FILE_PERMISSION_OTHERS_MASK;
+ FilePath path(kDatabasePath);
+ int permissions = 0;
+ if (base::GetPosixFilePermissions(path, &permissions) &&
+ (permissions & kMask) != 0) {
+ base::SetPosixFilePermissions(path, permissions & ~kMask);
+ }
+ return base::ReadFileToString(path, data);
+}
+
+bool DatabaseImpl::Write(const std::string& data) {
+ FilePath file_path(kDatabasePath);
+ if (!base::CreateDirectory(file_path.DirName())) {
+ LOG(ERROR) << "Cannot create directory: " << file_path.DirName().value();
+ return false;
+ }
+ if (!base::ImportantFileWriter::WriteFileAtomically(file_path, data)) {
+ LOG(ERROR) << "Failed to write file: " << file_path.value();
+ return false;
+ }
+ if (!base::SetPosixFilePermissions(file_path, kDatabasePermissions)) {
+ LOG(ERROR) << "Failed to set permissions for file: " << file_path.value();
+ return false;
+ }
+ // Sync the parent directory.
+ std::string dir_name = file_path.DirName().value();
+ int dir_fd = HANDLE_EINTR(open(dir_name.c_str(), O_RDONLY|O_DIRECTORY));
+ if (dir_fd < 0) {
+ PLOG(WARNING) << "Could not open " << dir_name << " for syncing";
+ return false;
+ }
+ // POSIX specifies EINTR as a possible return value of fsync().
+ int result = HANDLE_EINTR(fsync(dir_fd));
+ if (result < 0) {
+ PLOG(WARNING) << "Failed to sync " << dir_name;
+ close(dir_fd);
+ return false;
+ }
+ // close() may not be retried on error.
+ result = IGNORE_EINTR(close(dir_fd));
+ if (result < 0) {
+ PLOG(WARNING) << "Failed to close after sync " << dir_name;
+ return false;
+ }
+ return true;
+}
+
+void DatabaseImpl::Watch(const base::Closure& callback) {
+ if (!file_watcher_) {
+ file_watcher_.reset(new base::FilePathWatcher());
+ file_watcher_->Watch(FilePath(kDatabasePath), false,
+ base::Bind(&FileWatcherCallback, callback));
+ }
+}
+
+bool DatabaseImpl::EncryptProtobuf(std::string* encrypted_output) {
+ std::string serial_proto;
+ if (!protobuf_.SerializeToString(&serial_proto)) {
+ LOG(ERROR) << "Failed to serialize db.";
+ return false;
+ }
+ if (database_key_.empty() || sealed_database_key_.empty()) {
+ if (!crypto_->CreateSealedKey(&database_key_, &sealed_database_key_)) {
+ LOG(ERROR) << "Failed to generate database key.";
+ return false;
+ }
+ }
+ if (!crypto_->EncryptData(serial_proto, database_key_, sealed_database_key_,
+ encrypted_output)) {
+ LOG(ERROR) << "Attestation: Failed to encrypt database.";
+ return false;
+ }
+ return true;
+}
+
+bool DatabaseImpl::DecryptProtobuf(const std::string& encrypted_input) {
+ if (!crypto_->UnsealKey(encrypted_input, &database_key_,
+ &sealed_database_key_)) {
+ LOG(ERROR) << "Attestation: Could not unseal decryption key.";
+ return false;
+ }
+ std::string serial_proto;
+ if (!crypto_->DecryptData(encrypted_input, database_key_, &serial_proto)) {
+ LOG(ERROR) << "Attestation: Failed to decrypt database.";
+ return false;
+ }
+ if (!protobuf_.ParseFromString(serial_proto)) {
+ // Previously the DB was encrypted with CryptoLib::AesEncrypt which appends
+ // a SHA-1. This can be safely ignored.
+ const size_t kLegacyJunkSize = 20;
+ if (serial_proto.size() < kLegacyJunkSize ||
+ !protobuf_.ParseFromArray(serial_proto.data(),
+ serial_proto.length() - kLegacyJunkSize)) {
+ LOG(ERROR) << "Failed to parse database.";
+ return false;
+ }
+ }
+ return true;
+}
+
+} // namespace attestation
diff --git a/attestation/server/database_impl.h b/attestation/server/database_impl.h
new file mode 100644
index 0000000..9007d92
--- /dev/null
+++ b/attestation/server/database_impl.h
@@ -0,0 +1,91 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_SERVER_DATABASE_IMPL_H_
+#define ATTESTATION_SERVER_DATABASE_IMPL_H_
+
+#include "attestation/server/database.h"
+
+#include <string>
+
+#include <base/callback_forward.h>
+#include <base/files/file_path_watcher.h>
+#include <base/threading/thread_checker.h>
+
+#include "attestation/common/crypto_utility.h"
+
+namespace attestation {
+
+// An I/O abstraction to help with testing.
+class DatabaseIO {
+ public:
+ // Reads the persistent database blob.
+ virtual bool Read(std::string* data) = 0;
+ // Writes the persistent database blob.
+ virtual bool Write(const std::string& data) = 0;
+ // Watch for external changes to the database.
+ virtual void Watch(const base::Closure& callback) = 0;
+};
+
+// An implementation of Database backed by an ordinary file. Not thread safe.
+// All methods must be called on the same thread as the Initialize() call.
+class DatabaseImpl : public Database,
+ public DatabaseIO {
+ public:
+ // Does not take ownership of pointers.
+ explicit DatabaseImpl(CryptoUtility* crypto);
+ ~DatabaseImpl() override;
+
+ // Reads and decrypts any existing database on disk synchronously. Must be
+ // called before calling other methods.
+ void Initialize();
+
+ // Database methods.
+ const AttestationDatabase& GetProtobuf() const override;
+ AttestationDatabase* GetMutableProtobuf() override;
+ bool SaveChanges() override;
+ bool Reload() override;
+
+ // DatabaseIO methods.
+ bool Read(std::string* data) override;
+ bool Write(const std::string& data) override;
+ void Watch(const base::Closure& callback) override;
+
+ // Useful for testing.
+ void set_io(DatabaseIO* io) {
+ io_ = io;
+ }
+
+ private:
+ // Encrypts |protobuf_| into |encrypted_output|. Returns true on success.
+ bool EncryptProtobuf(std::string* encrypted_output);
+
+ // Decrypts |encrypted_input| as output by EncryptProtobuf into |protobuf_|.
+ // Returns true on success.
+ bool DecryptProtobuf(const std::string& encrypted_input);
+
+ AttestationDatabase protobuf_;
+ DatabaseIO* io_;
+ CryptoUtility* crypto_;
+ std::string database_key_;
+ std::string sealed_database_key_;
+ std::unique_ptr<base::FilePathWatcher> file_watcher_;
+ base::ThreadChecker thread_checker_;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_DATABASE_IMPL_H_
diff --git a/attestation/server/database_impl_test.cc b/attestation/server/database_impl_test.cc
new file mode 100644
index 0000000..9b3d750
--- /dev/null
+++ b/attestation/server/database_impl_test.cc
@@ -0,0 +1,163 @@
+//
+// 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 <memory>
+#include <string>
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "attestation/common/mock_crypto_utility.h"
+#include "attestation/server/database_impl.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+using testing::WithArgs;
+
+namespace {
+
+const char kFakeCredential[] = "1234";
+
+} // namespace
+
+namespace attestation {
+
+class DatabaseImplTest : public testing::Test,
+ public DatabaseIO {
+ public:
+ ~DatabaseImplTest() override = default;
+ void SetUp() override {
+ database_.reset(new DatabaseImpl(&mock_crypto_utility_));
+ database_->set_io(this);
+ InitializeFakeData();
+ database_->Initialize();
+ }
+
+ // Fake DatabaseIO::Read.
+ bool Read(std::string* data) override {
+ if (fake_persistent_data_readable_) {
+ *data = fake_persistent_data_;
+ }
+ return fake_persistent_data_readable_;
+ }
+
+ // Fake DatabaseIO::Write.
+ bool Write(const std::string& data) override {
+ if (fake_persistent_data_writable_) {
+ fake_persistent_data_ = data;
+ }
+ return fake_persistent_data_writable_;
+ }
+
+ // Fake DatabaseIO::Watch.
+ void Watch(const base::Closure& callback) override {
+ fake_watch_callback_ = callback;
+ }
+
+ // Initializes fake_persistent_data_ with a default value.
+ void InitializeFakeData() {
+ AttestationDatabase proto;
+ proto.mutable_credentials()->set_conformance_credential(kFakeCredential);
+ proto.SerializeToString(&fake_persistent_data_);
+ }
+
+ protected:
+ std::string fake_persistent_data_;
+ bool fake_persistent_data_readable_{true};
+ bool fake_persistent_data_writable_{true};
+ base::Closure fake_watch_callback_;
+ NiceMock<MockCryptoUtility> mock_crypto_utility_;
+ std::unique_ptr<DatabaseImpl> database_;
+};
+
+TEST_F(DatabaseImplTest, ReadSuccess) {
+ database_->GetMutableProtobuf()->Clear();
+ EXPECT_TRUE(database_->Reload());
+ EXPECT_EQ(std::string(kFakeCredential),
+ database_->GetProtobuf().credentials().conformance_credential());
+}
+
+TEST_F(DatabaseImplTest, ReadFailure) {
+ fake_persistent_data_readable_ = false;
+ database_->GetMutableProtobuf()->Clear();
+ EXPECT_FALSE(database_->Reload());
+ EXPECT_FALSE(database_->GetProtobuf().has_credentials());
+}
+
+TEST_F(DatabaseImplTest, DecryptFailure) {
+ EXPECT_CALL(mock_crypto_utility_, DecryptData(_, _, _))
+ .WillRepeatedly(Return(false));
+ database_->GetMutableProtobuf()->Clear();
+ EXPECT_FALSE(database_->Reload());
+ EXPECT_FALSE(database_->GetProtobuf().has_credentials());
+}
+
+TEST_F(DatabaseImplTest, WriteSuccess) {
+ database_->GetMutableProtobuf()->mutable_credentials()->
+ set_platform_credential("test");
+ std::string expected_data;
+ database_->GetProtobuf().SerializeToString(&expected_data);
+ EXPECT_TRUE(database_->SaveChanges());
+ EXPECT_EQ(expected_data, fake_persistent_data_);
+}
+
+TEST_F(DatabaseImplTest, WriteFailure) {
+ fake_persistent_data_writable_ = false;
+ database_->GetMutableProtobuf()->mutable_credentials()->
+ set_platform_credential("test");
+ EXPECT_FALSE(database_->SaveChanges());
+}
+
+TEST_F(DatabaseImplTest, EncryptFailure) {
+ EXPECT_CALL(mock_crypto_utility_, EncryptData(_, _, _, _))
+ .WillRepeatedly(Return(false));
+ database_->GetMutableProtobuf()->mutable_credentials()->
+ set_platform_credential("test");
+ EXPECT_FALSE(database_->SaveChanges());
+}
+
+TEST_F(DatabaseImplTest, IgnoreLegacyEncryptJunk) {
+ // Legacy encryption scheme appended a SHA-1 hash before encrypting.
+ fake_persistent_data_ += std::string(20, 'A');
+ EXPECT_EQ(std::string(kFakeCredential),
+ database_->GetProtobuf().credentials().conformance_credential());
+}
+
+TEST_F(DatabaseImplTest, Reload) {
+ AttestationDatabase proto;
+ proto.mutable_credentials()->set_platform_credential(kFakeCredential);
+ proto.SerializeToString(&fake_persistent_data_);
+ EXPECT_EQ(std::string(),
+ database_->GetProtobuf().credentials().platform_credential());
+ EXPECT_TRUE(database_->Reload());
+ EXPECT_EQ(std::string(kFakeCredential),
+ database_->GetProtobuf().credentials().platform_credential());
+}
+
+TEST_F(DatabaseImplTest, AutoReload) {
+ AttestationDatabase proto;
+ proto.mutable_credentials()->set_platform_credential(kFakeCredential);
+ proto.SerializeToString(&fake_persistent_data_);
+ EXPECT_EQ(std::string(),
+ database_->GetProtobuf().credentials().platform_credential());
+ fake_watch_callback_.Run();
+ EXPECT_EQ(std::string(kFakeCredential),
+ database_->GetProtobuf().credentials().platform_credential());
+}
+
+} // namespace attestation
diff --git a/attestation/server/dbus_service.cc b/attestation/server/dbus_service.cc
new file mode 100644
index 0000000..9c25dab
--- /dev/null
+++ b/attestation/server/dbus_service.cc
@@ -0,0 +1,242 @@
+//
+// Copyright (C) 2014 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/dbus_service.h"
+
+#include <memory>
+#include <string>
+
+#include <brillo/bind_lambda.h>
+#include <dbus/bus.h>
+#include <dbus/object_path.h>
+
+#include "attestation/common/dbus_interface.h"
+
+using brillo::dbus_utils::DBusMethodResponse;
+
+namespace attestation {
+
+DBusService::DBusService(const scoped_refptr<dbus::Bus>& bus,
+ AttestationInterface* service)
+ : dbus_object_(nullptr, bus, dbus::ObjectPath(kAttestationServicePath)),
+ service_(service) {
+}
+
+void DBusService::Register(const CompletionAction& callback) {
+ brillo::dbus_utils::DBusInterface* dbus_interface =
+ dbus_object_.AddOrGetInterface(kAttestationInterface);
+
+ dbus_interface->AddMethodHandler(kCreateGoogleAttestedKey,
+ base::Unretained(this),
+ &DBusService::HandleCreateGoogleAttestedKey);
+ dbus_interface->AddMethodHandler(kGetKeyInfo,
+ base::Unretained(this),
+ &DBusService::HandleGetKeyInfo);
+ dbus_interface->AddMethodHandler(kGetEndorsementInfo,
+ base::Unretained(this),
+ &DBusService::HandleGetEndorsementInfo);
+ dbus_interface->AddMethodHandler(kGetAttestationKeyInfo,
+ base::Unretained(this),
+ &DBusService::HandleGetAttestationKeyInfo);
+ dbus_interface->AddMethodHandler(kActivateAttestationKey,
+ base::Unretained(this),
+ &DBusService::HandleActivateAttestationKey);
+ dbus_interface->AddMethodHandler(kCreateCertifiableKey,
+ base::Unretained(this),
+ &DBusService::HandleCreateCertifiableKey);
+ dbus_interface->AddMethodHandler(kDecrypt,
+ base::Unretained(this),
+ &DBusService::HandleDecrypt);
+ dbus_interface->AddMethodHandler(kSign,
+ base::Unretained(this),
+ &DBusService::HandleSign);
+ dbus_interface->AddMethodHandler(
+ kRegisterKeyWithChapsToken,
+ base::Unretained(this),
+ &DBusService::HandleRegisterKeyWithChapsToken);
+
+ dbus_object_.RegisterAsync(callback);
+}
+
+void DBusService::HandleCreateGoogleAttestedKey(
+ std::unique_ptr<DBusMethodResponse<const CreateGoogleAttestedKeyReply&>>
+ response,
+ const CreateGoogleAttestedKeyRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const CreateGoogleAttestedKeyReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const CreateGoogleAttestedKeyReply& reply) {
+ response->Return(reply);
+ };
+ service_->CreateGoogleAttestedKey(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+void DBusService::HandleGetKeyInfo(
+ std::unique_ptr<DBusMethodResponse<const GetKeyInfoReply&>> response,
+ const GetKeyInfoRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const GetKeyInfoReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const GetKeyInfoReply& reply) {
+ response->Return(reply);
+ };
+ service_->GetKeyInfo(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+void DBusService::HandleGetEndorsementInfo(
+ std::unique_ptr<DBusMethodResponse<const GetEndorsementInfoReply&>>
+ response,
+ const GetEndorsementInfoRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const GetEndorsementInfoReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const GetEndorsementInfoReply& reply) {
+ response->Return(reply);
+ };
+ service_->GetEndorsementInfo(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+void DBusService::HandleGetAttestationKeyInfo(
+ std::unique_ptr<DBusMethodResponse<const GetAttestationKeyInfoReply&>>
+ response,
+ const GetAttestationKeyInfoRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const GetAttestationKeyInfoReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const GetAttestationKeyInfoReply& reply) {
+ response->Return(reply);
+ };
+ service_->GetAttestationKeyInfo(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+void DBusService::HandleActivateAttestationKey(
+ std::unique_ptr<DBusMethodResponse<const ActivateAttestationKeyReply&>>
+ response,
+ const ActivateAttestationKeyRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const ActivateAttestationKeyReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const ActivateAttestationKeyReply& reply) {
+ response->Return(reply);
+ };
+ service_->ActivateAttestationKey(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+void DBusService::HandleCreateCertifiableKey(
+ std::unique_ptr<DBusMethodResponse<const CreateCertifiableKeyReply&>>
+ response,
+ const CreateCertifiableKeyRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const CreateCertifiableKeyReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const CreateCertifiableKeyReply& reply) {
+ response->Return(reply);
+ };
+ service_->CreateCertifiableKey(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+void DBusService::HandleDecrypt(
+ std::unique_ptr<DBusMethodResponse<const DecryptReply&>> response,
+ const DecryptRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const DecryptReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const DecryptReply& reply) {
+ response->Return(reply);
+ };
+ service_->Decrypt(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+void DBusService::HandleSign(
+ std::unique_ptr<DBusMethodResponse<const SignReply&>> response,
+ const SignRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const SignReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const SignReply& reply) {
+ response->Return(reply);
+ };
+ service_->Sign(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+void DBusService::HandleRegisterKeyWithChapsToken(
+ std::unique_ptr<DBusMethodResponse<const RegisterKeyWithChapsTokenReply&>>
+ response,
+ const RegisterKeyWithChapsTokenRequest& request) {
+ VLOG(1) << __func__;
+ // Convert |response| to a shared_ptr so |service_| can safely copy the
+ // callback.
+ using SharedResponsePointer = std::shared_ptr<
+ DBusMethodResponse<const RegisterKeyWithChapsTokenReply&>>;
+ // A callback that fills the reply protobuf and sends it.
+ auto callback = [](const SharedResponsePointer& response,
+ const RegisterKeyWithChapsTokenReply& reply) {
+ response->Return(reply);
+ };
+ service_->RegisterKeyWithChapsToken(
+ request,
+ base::Bind(callback, SharedResponsePointer(std::move(response))));
+}
+
+} // namespace attestation
diff --git a/attestation/server/dbus_service.h b/attestation/server/dbus_service.h
new file mode 100644
index 0000000..dd4281c
--- /dev/null
+++ b/attestation/server/dbus_service.h
@@ -0,0 +1,115 @@
+//
+// Copyright (C) 2014 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.
+//
+
+#ifndef ATTESTATION_SERVER_DBUS_SERVICE_H_
+#define ATTESTATION_SERVER_DBUS_SERVICE_H_
+
+#include <memory>
+
+#include <brillo/dbus/dbus_method_response.h>
+#include <brillo/dbus/dbus_object.h>
+#include <dbus/bus.h>
+
+#include "attestation/common/attestation_interface.h"
+
+namespace attestation {
+
+using CompletionAction =
+ brillo::dbus_utils::AsyncEventSequencer::CompletionAction;
+
+// Handles D-Bus calls to the attestation daemon.
+class DBusService {
+ public:
+ // DBusService does not take ownership of |service|; it must remain valid for
+ // the lifetime of the DBusService instance.
+ DBusService(const scoped_refptr<dbus::Bus>& bus,
+ AttestationInterface* service);
+ virtual ~DBusService() = default;
+
+ // Connects to D-Bus system bus and exports methods.
+ void Register(const CompletionAction& callback);
+
+ // Useful for testing.
+ void set_service(AttestationInterface* service) {
+ service_ = service;
+ }
+
+ private:
+ friend class DBusServiceTest;
+
+ // Handles a CreateGoogleAttestedKey D-Bus call.
+ void HandleCreateGoogleAttestedKey(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const CreateGoogleAttestedKeyReply&>> response,
+ const CreateGoogleAttestedKeyRequest& request);
+
+ // Handles a GetKeyInfo D-Bus call.
+ void HandleGetKeyInfo(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const GetKeyInfoReply&>> response,
+ const GetKeyInfoRequest& request);
+
+ // Handles a GetEndorsementInfo D-Bus call.
+ void HandleGetEndorsementInfo(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const GetEndorsementInfoReply&>> response,
+ const GetEndorsementInfoRequest& request);
+
+ // Handles a GetAttestationKeyInfo D-Bus call.
+ void HandleGetAttestationKeyInfo(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const GetAttestationKeyInfoReply&>> response,
+ const GetAttestationKeyInfoRequest& request);
+
+ // Handles a ActivateAttestationKey D-Bus call.
+ void HandleActivateAttestationKey(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const ActivateAttestationKeyReply&>> response,
+ const ActivateAttestationKeyRequest& request);
+
+ // Handles a CreateCertifiableKey D-Bus call.
+ void HandleCreateCertifiableKey(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const CreateCertifiableKeyReply&>> response,
+ const CreateCertifiableKeyRequest& request);
+
+ // Handles a Decrypt D-Bus call.
+ void HandleDecrypt(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const DecryptReply&>> response,
+ const DecryptRequest& request);
+
+ // Handles a Sign D-Bus call.
+ void HandleSign(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const SignReply&>> response,
+ const SignRequest& request);
+
+ // Handles a RegisterKeyWithChapsToken D-Bus call.
+ void HandleRegisterKeyWithChapsToken(
+ std::unique_ptr<brillo::dbus_utils::DBusMethodResponse<
+ const RegisterKeyWithChapsTokenReply&>> response,
+ const RegisterKeyWithChapsTokenRequest& request);
+
+ brillo::dbus_utils::DBusObject dbus_object_;
+ AttestationInterface* service_;
+
+ DISALLOW_COPY_AND_ASSIGN(DBusService);
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_DBUS_SERVICE_H_
diff --git a/attestation/server/dbus_service_test.cc b/attestation/server/dbus_service_test.cc
new file mode 100644
index 0000000..ebe6acc
--- /dev/null
+++ b/attestation/server/dbus_service_test.cc
@@ -0,0 +1,382 @@
+//
+// Copyright (C) 2014 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 <string>
+
+#include <brillo/bind_lambda.h>
+#include <brillo/dbus/dbus_object_test_helpers.h>
+#include <dbus/mock_bus.h>
+#include <dbus/mock_exported_object.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include "attestation/common/dbus_interface.h"
+#include "attestation/common/mock_attestation_interface.h"
+#include "attestation/server/dbus_service.h"
+
+using testing::_;
+using testing::Invoke;
+using testing::NiceMock;
+using testing::Return;
+using testing::StrictMock;
+using testing::WithArgs;
+
+namespace attestation {
+
+class DBusServiceTest : public testing::Test {
+ public:
+ ~DBusServiceTest() override = default;
+ void SetUp() override {
+ dbus::Bus::Options options;
+ mock_bus_ = new NiceMock<dbus::MockBus>(options);
+ dbus::ObjectPath path(kAttestationServicePath);
+ mock_exported_object_ = new NiceMock<dbus::MockExportedObject>(
+ mock_bus_.get(), path);
+ ON_CALL(*mock_bus_, GetExportedObject(path))
+ .WillByDefault(Return(mock_exported_object_.get()));
+ dbus_service_.reset(new DBusService(mock_bus_, &mock_service_));
+ dbus_service_->Register(brillo::dbus_utils::AsyncEventSequencer::
+ GetDefaultCompletionAction());
+ }
+
+ std::unique_ptr<dbus::Response> CallMethod(dbus::MethodCall* method_call) {
+ return brillo::dbus_utils::testing::CallMethod(
+ dbus_service_->dbus_object_, method_call);
+ }
+
+ std::unique_ptr<dbus::MethodCall> CreateMethodCall(
+ const std::string& method_name) {
+ std::unique_ptr<dbus::MethodCall> call(new dbus::MethodCall(
+ kAttestationInterface, method_name));
+ call->SetSerial(1);
+ return call;
+ }
+
+ protected:
+ scoped_refptr<dbus::MockBus> mock_bus_;
+ scoped_refptr<dbus::MockExportedObject> mock_exported_object_;
+ StrictMock<MockAttestationInterface> mock_service_;
+ std::unique_ptr<DBusService> dbus_service_;
+};
+
+TEST_F(DBusServiceTest, CreateGoogleAttestedKey) {
+ CreateGoogleAttestedKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ request.set_certificate_profile(ENTERPRISE_MACHINE_CERTIFICATE);
+ request.set_username("username");
+ request.set_origin("origin");
+ EXPECT_CALL(mock_service_, CreateGoogleAttestedKey(_, _))
+ .WillOnce(Invoke([](
+ const CreateGoogleAttestedKeyRequest& request,
+ const AttestationInterface::
+ CreateGoogleAttestedKeyCallback& callback) {
+ EXPECT_EQ("label", request.key_label());
+ EXPECT_EQ(KEY_TYPE_ECC, request.key_type());
+ EXPECT_EQ(KEY_USAGE_SIGN, request.key_usage());
+ EXPECT_EQ(ENTERPRISE_MACHINE_CERTIFICATE,
+ request.certificate_profile());
+ EXPECT_EQ("username", request.username());
+ EXPECT_EQ("origin", request.origin());
+ CreateGoogleAttestedKeyReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ reply.set_certificate_chain("certificate");
+ reply.set_server_error("server_error");
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(
+ kCreateGoogleAttestedKey);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ CreateGoogleAttestedKeyReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("certificate", reply.certificate_chain());
+ EXPECT_EQ("server_error", reply.server_error());
+}
+
+TEST_F(DBusServiceTest, CopyableCallback) {
+ EXPECT_CALL(mock_service_, CreateGoogleAttestedKey(_, _))
+ .WillOnce(WithArgs<1>(Invoke([](const AttestationInterface::
+ CreateGoogleAttestedKeyCallback& callback) {
+ // Copy the callback, then call the original.
+ CreateGoogleAttestedKeyReply reply;
+ base::Closure copy = base::Bind(callback, reply);
+ callback.Run(reply);
+ })));
+ std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(
+ kCreateGoogleAttestedKey);
+ CreateGoogleAttestedKeyRequest request;
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ CreateGoogleAttestedKeyReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+}
+
+TEST_F(DBusServiceTest, GetKeyInfo) {
+ GetKeyInfoRequest request;
+ request.set_key_label("label");
+ request.set_username("username");
+ EXPECT_CALL(mock_service_, GetKeyInfo(_, _))
+ .WillOnce(Invoke([](
+ const GetKeyInfoRequest& request,
+ const AttestationInterface::GetKeyInfoCallback& callback) {
+ EXPECT_EQ("label", request.key_label());
+ EXPECT_EQ("username", request.username());
+ GetKeyInfoReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ reply.set_key_type(KEY_TYPE_ECC);
+ reply.set_key_usage(KEY_USAGE_SIGN);
+ reply.set_public_key("public_key");
+ reply.set_certify_info("certify");
+ reply.set_certify_info_signature("signature");
+ reply.set_certificate("certificate");
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(kGetKeyInfo);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ GetKeyInfoReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ(KEY_TYPE_ECC, reply.key_type());
+ EXPECT_EQ(KEY_USAGE_SIGN, reply.key_usage());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("certify", reply.certify_info());
+ EXPECT_EQ("signature", reply.certify_info_signature());
+ EXPECT_EQ("certificate", reply.certificate());
+}
+
+TEST_F(DBusServiceTest, GetEndorsementInfo) {
+ GetEndorsementInfoRequest request;
+ request.set_key_type(KEY_TYPE_ECC);
+ EXPECT_CALL(mock_service_, GetEndorsementInfo(_, _))
+ .WillOnce(Invoke([](
+ const GetEndorsementInfoRequest& request,
+ const AttestationInterface::GetEndorsementInfoCallback& callback) {
+ EXPECT_EQ(KEY_TYPE_ECC, request.key_type());
+ GetEndorsementInfoReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ reply.set_ek_public_key("public_key");
+ reply.set_ek_certificate("certificate");
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call =
+ CreateMethodCall(kGetEndorsementInfo);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ GetEndorsementInfoReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.ek_public_key());
+ EXPECT_EQ("certificate", reply.ek_certificate());
+}
+
+TEST_F(DBusServiceTest, GetAttestationKeyInfo) {
+ GetAttestationKeyInfoRequest request;
+ request.set_key_type(KEY_TYPE_ECC);
+ EXPECT_CALL(mock_service_, GetAttestationKeyInfo(_, _))
+ .WillOnce(Invoke([](
+ const GetAttestationKeyInfoRequest& request,
+ const AttestationInterface::GetAttestationKeyInfoCallback& callback) {
+ EXPECT_EQ(KEY_TYPE_ECC, request.key_type());
+ GetAttestationKeyInfoReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ reply.set_public_key("public_key");
+ reply.set_public_key_tpm_format("public_key_tpm_format");
+ reply.set_certificate("certificate");
+ reply.mutable_pcr0_quote()->set_quote("pcr0");
+ reply.mutable_pcr1_quote()->set_quote("pcr1");
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call =
+ CreateMethodCall(kGetAttestationKeyInfo);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ GetAttestationKeyInfoReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("public_key_tpm_format", reply.public_key_tpm_format());
+ EXPECT_EQ("certificate", reply.certificate());
+ EXPECT_EQ("pcr0", reply.pcr0_quote().quote());
+ EXPECT_EQ("pcr1", reply.pcr1_quote().quote());
+}
+
+TEST_F(DBusServiceTest, ActivateAttestationKey) {
+ ActivateAttestationKeyRequest request;
+ request.set_key_type(KEY_TYPE_ECC);
+ request.mutable_encrypted_certificate()->set_asym_ca_contents("encrypted1");
+ request.mutable_encrypted_certificate()->set_sym_ca_attestation("encrypted2");
+ request.set_save_certificate(true);
+ EXPECT_CALL(mock_service_, ActivateAttestationKey(_, _))
+ .WillOnce(Invoke([](
+ const ActivateAttestationKeyRequest& request,
+ const AttestationInterface::ActivateAttestationKeyCallback&
+ callback) {
+ EXPECT_EQ(KEY_TYPE_ECC, request.key_type());
+ EXPECT_EQ("encrypted1",
+ request.encrypted_certificate().asym_ca_contents());
+ EXPECT_EQ("encrypted2",
+ request.encrypted_certificate().sym_ca_attestation());
+ EXPECT_TRUE(request.save_certificate());
+ ActivateAttestationKeyReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ reply.set_certificate("certificate");
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call =
+ CreateMethodCall(kActivateAttestationKey);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ ActivateAttestationKeyReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("certificate", reply.certificate());
+}
+
+TEST_F(DBusServiceTest, CreateCertifiableKey) {
+ CreateCertifiableKeyRequest request;
+ request.set_key_label("label");
+ request.set_key_type(KEY_TYPE_ECC);
+ request.set_key_usage(KEY_USAGE_SIGN);
+ request.set_username("user");
+ EXPECT_CALL(mock_service_, CreateCertifiableKey(_, _))
+ .WillOnce(Invoke([](
+ const CreateCertifiableKeyRequest& request,
+ const AttestationInterface::CreateCertifiableKeyCallback& callback) {
+ EXPECT_EQ("label", request.key_label());
+ EXPECT_EQ(KEY_TYPE_ECC, request.key_type());
+ EXPECT_EQ(KEY_USAGE_SIGN, request.key_usage());
+ EXPECT_EQ("user", request.username());
+ CreateCertifiableKeyReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ reply.set_public_key("public_key");
+ reply.set_certify_info("certify_info");
+ reply.set_certify_info_signature("signature");
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call =
+ CreateMethodCall(kCreateCertifiableKey);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ CreateCertifiableKeyReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("public_key", reply.public_key());
+ EXPECT_EQ("certify_info", reply.certify_info());
+ EXPECT_EQ("signature", reply.certify_info_signature());
+}
+
+TEST_F(DBusServiceTest, Decrypt) {
+ DecryptRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_encrypted_data("data");
+ EXPECT_CALL(mock_service_, Decrypt(_, _))
+ .WillOnce(Invoke([](
+ const DecryptRequest& request,
+ const AttestationInterface::DecryptCallback& callback) {
+ EXPECT_EQ("label", request.key_label());
+ EXPECT_EQ("user", request.username());
+ EXPECT_EQ("data", request.encrypted_data());
+ DecryptReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ reply.set_decrypted_data("data");
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(kDecrypt);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ DecryptReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("data", reply.decrypted_data());
+}
+
+TEST_F(DBusServiceTest, Sign) {
+ SignRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ request.set_data_to_sign("data");
+ EXPECT_CALL(mock_service_, Sign(_, _))
+ .WillOnce(Invoke([](
+ const SignRequest& request,
+ const AttestationInterface::SignCallback& callback) {
+ EXPECT_EQ("label", request.key_label());
+ EXPECT_EQ("user", request.username());
+ EXPECT_EQ("data", request.data_to_sign());
+ SignReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ reply.set_signature("signature");
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call = CreateMethodCall(kSign);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ SignReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+ EXPECT_EQ("signature", reply.signature());
+}
+
+TEST_F(DBusServiceTest, RegisterKeyWithChapsToken) {
+ RegisterKeyWithChapsTokenRequest request;
+ request.set_key_label("label");
+ request.set_username("user");
+ EXPECT_CALL(mock_service_, RegisterKeyWithChapsToken(_, _))
+ .WillOnce(Invoke([](
+ const RegisterKeyWithChapsTokenRequest& request,
+ const AttestationInterface::RegisterKeyWithChapsTokenCallback&
+ callback) {
+ EXPECT_EQ("label", request.key_label());
+ EXPECT_EQ("user", request.username());
+ RegisterKeyWithChapsTokenReply reply;
+ reply.set_status(STATUS_SUCCESS);
+ callback.Run(reply);
+ }));
+ std::unique_ptr<dbus::MethodCall> call =
+ CreateMethodCall(kRegisterKeyWithChapsToken);
+ dbus::MessageWriter writer(call.get());
+ writer.AppendProtoAsArrayOfBytes(request);
+ auto response = CallMethod(call.get());
+ dbus::MessageReader reader(response.get());
+ RegisterKeyWithChapsTokenReply reply;
+ EXPECT_TRUE(reader.PopArrayOfBytesAsProto(&reply));
+ EXPECT_EQ(STATUS_SUCCESS, reply.status());
+}
+
+
+} // namespace attestation
diff --git a/attestation/server/key_store.h b/attestation/server/key_store.h
new file mode 100644
index 0000000..7137223
--- /dev/null
+++ b/attestation/server/key_store.h
@@ -0,0 +1,82 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_SERVER_KEY_STORE_H_
+#define ATTESTATION_SERVER_KEY_STORE_H_
+
+#include <string>
+
+#include <base/macros.h>
+
+#include "attestation/common/common.pb.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,
+ KeyType key_type,
+ KeyUsage key_usage,
+ 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/attestation/server/main.cc b/attestation/server/main.cc
new file mode 100644
index 0000000..b22ba18
--- /dev/null
+++ b/attestation/server/main.cc
@@ -0,0 +1,113 @@
+//
+// Copyright (C) 2014 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 <sysexits.h>
+
+#include <memory>
+#include <string>
+
+#include <base/command_line.h>
+#include <brillo/daemons/dbus_daemon.h>
+#include <brillo/dbus/async_event_sequencer.h>
+#include <brillo/minijail/minijail.h>
+#include <brillo/syslog_logging.h>
+#include <brillo/userdb_utils.h>
+
+#include "attestation/common/dbus_interface.h"
+#include "attestation/server/attestation_service.h"
+#include "attestation/server/dbus_service.h"
+
+#include <chromeos/libminijail.h>
+
+namespace {
+
+const uid_t kRootUID = 0;
+const char kAttestationUser[] = "attestation";
+const char kAttestationGroup[] = "attestation";
+const char kAttestationSeccompPath[] =
+ "/usr/share/policy/attestationd-seccomp.policy";
+
+void InitMinijailSandbox() {
+ uid_t attestation_uid;
+ gid_t attestation_gid;
+ CHECK(brillo::userdb::GetUserInfo(kAttestationUser,
+ &attestation_uid,
+ &attestation_gid))
+ << "Error getting attestation uid and gid.";
+ CHECK_EQ(getuid(), kRootUID) << "AttestationDaemon not initialized as root.";
+ brillo::Minijail* minijail = brillo::Minijail::GetInstance();
+ struct minijail* jail = minijail->New();
+
+ minijail->DropRoot(jail, kAttestationUser, kAttestationGroup);
+ minijail->UseSeccompFilter(jail, kAttestationSeccompPath);
+ minijail->Enter(jail);
+ minijail->Destroy(jail);
+ CHECK_EQ(getuid(), attestation_uid)
+ << "AttestationDaemon was not able to drop to attestation user.";
+ CHECK_EQ(getgid(), attestation_gid)
+ << "AttestationDaemon was not able to drop to attestation group.";
+}
+
+} // namespace
+
+using brillo::dbus_utils::AsyncEventSequencer;
+
+class AttestationDaemon : public brillo::DBusServiceDaemon {
+ public:
+ AttestationDaemon()
+ : brillo::DBusServiceDaemon(attestation::kAttestationServiceName) {
+ attestation_service_.reset(new attestation::AttestationService);
+ // Move initialize call down to OnInit
+ CHECK(attestation_service_->Initialize());
+ }
+
+ protected:
+ int OnInit() override {
+ int result = brillo::DBusServiceDaemon::OnInit();
+ if (result != EX_OK) {
+ LOG(ERROR) << "Error starting attestation dbus daemon.";
+ return result;
+ }
+ return EX_OK;
+ }
+
+ void RegisterDBusObjectsAsync(AsyncEventSequencer* sequencer) override {
+ dbus_service_.reset(new attestation::DBusService(
+ bus_,
+ attestation_service_.get()));
+ dbus_service_->Register(sequencer->GetHandler("Register() failed.", true));
+ }
+
+ private:
+ std::unique_ptr<attestation::AttestationInterface> attestation_service_;
+ std::unique_ptr<attestation::DBusService> dbus_service_;
+
+ DISALLOW_COPY_AND_ASSIGN(AttestationDaemon);
+};
+
+int main(int argc, char* argv[]) {
+ base::CommandLine::Init(argc, argv);
+ base::CommandLine *cl = base::CommandLine::ForCurrentProcess();
+ int flags = brillo::kLogToSyslog;
+ if (cl->HasSwitch("log_to_stderr")) {
+ flags |= brillo::kLogToStderr;
+ }
+ brillo::InitLog(flags);
+ AttestationDaemon daemon;
+ LOG(INFO) << "Attestation Daemon Started.";
+ InitMinijailSandbox();
+ return daemon.Run();
+}
diff --git a/attestation/server/mock_database.cc b/attestation/server/mock_database.cc
new file mode 100644
index 0000000..fd0bdc6
--- /dev/null
+++ b/attestation/server/mock_database.cc
@@ -0,0 +1,33 @@
+//
+// 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/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));
+ ON_CALL(*this, Reload()).WillByDefault(Return(true));
+}
+
+MockDatabase::~MockDatabase() {}
+
+} // namespace attestation
diff --git a/attestation/server/mock_database.h b/attestation/server/mock_database.h
new file mode 100644
index 0000000..08cef22
--- /dev/null
+++ b/attestation/server/mock_database.h
@@ -0,0 +1,42 @@
+//
+// 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.
+//
+
+#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());
+ MOCK_METHOD0(Reload, bool());
+
+ private:
+ AttestationDatabase fake_;
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_MOCK_DATABASE_H_
diff --git a/attestation/server/mock_key_store.cc b/attestation/server/mock_key_store.cc
new file mode 100644
index 0000000..56aa632
--- /dev/null
+++ b/attestation/server/mock_key_store.cc
@@ -0,0 +1,35 @@
+//
+// 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/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/attestation/server/mock_key_store.h b/attestation/server/mock_key_store.h
new file mode 100644
index 0000000..f6566ca
--- /dev/null
+++ b/attestation/server/mock_key_store.h
@@ -0,0 +1,60 @@
+//
+// 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.
+//
+
+#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_METHOD7(Register, bool(const std::string& username,
+ const std::string& label,
+ KeyType key_type,
+ KeyUsage key_usage,
+ 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/attestation/server/org.chromium.Attestation.conf b/attestation/server/org.chromium.Attestation.conf
new file mode 100644
index 0000000..2adae8e
--- /dev/null
+++ b/attestation/server/org.chromium.Attestation.conf
@@ -0,0 +1,15 @@
+<!DOCTYPE busconfig PUBLIC
+ "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="attestation">
+ <allow own="org.chromium.Attestation" />
+ <allow send_destination="org.chromium.Attestation" />
+ </policy>
+ <policy context="default">
+ <allow send_destination="org.chromium.Attestation" />
+ <!-- introspection denied -->
+ <deny send_destination="org.chromium.Attestation"
+ send_interface="org.freedesktop.DBus.Introspectable" />
+ </policy>
+</busconfig>
diff --git a/attestation/server/pkcs11_key_store.cc b/attestation/server/pkcs11_key_store.cc
new file mode 100644
index 0000000..45be47e
--- /dev/null
+++ b/attestation/server/pkcs11_key_store.cc
@@ -0,0 +1,685 @@
+//
+// 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/pkcs11_key_store.h"
+
+#include <memory>
+#include <string>
+
+#include <base/bind.h>
+#include <base/callback.h>
+#include <base/files/file_path.h>
+#include <base/logging.h>
+#include <base/stl_util.h>
+#include <base/strings/string_util.h>
+#include <chaps/isolate.h>
+#include <chaps/pkcs11/cryptoki.h>
+#include <chaps/token_manager_client.h>
+#include <brillo/cryptohome.h>
+#include <crypto/scoped_openssl_types.h>
+#include <openssl/rsa.h>
+#include <openssl/sha.h>
+#include <openssl/x509.h>
+
+namespace {
+
+std::string Sha1(const std::string& input) {
+ unsigned char output[SHA_DIGEST_LENGTH];
+ SHA1(reinterpret_cast<const unsigned char*>(input.data()), input.size(),
+ output);
+ return std::string(reinterpret_cast<char*>(output), SHA_DIGEST_LENGTH);
+}
+
+} // namespace
+
+namespace attestation {
+
+typedef crypto::ScopedOpenSSL<X509, X509_free> ScopedX509;
+
+// An arbitrary application ID to identify PKCS #11 objects.
+const char kApplicationID[] = "CrOS_d5bbc079d2497110feadfc97c40d718ae46f4658";
+
+// A helper class to scope a PKCS #11 session.
+class ScopedSession {
+ public:
+ explicit ScopedSession(CK_SLOT_ID slot) : handle_(CK_INVALID_HANDLE) {
+ CK_RV rv = C_Initialize(nullptr);
+ if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
+ // This may be normal in a test environment.
+ LOG(INFO) << "PKCS #11 is not available.";
+ return;
+ }
+ CK_FLAGS flags = CKF_RW_SESSION | CKF_SERIAL_SESSION;
+ if (C_OpenSession(slot, flags, nullptr, nullptr, &handle_) != CKR_OK) {
+ LOG(ERROR) << "Failed to open PKCS #11 session.";
+ return;
+ }
+ }
+
+ ~ScopedSession() {
+ if (IsValid() && (C_CloseSession(handle_) != CKR_OK)) {
+ LOG(WARNING) << "Failed to close PKCS #11 session.";
+ handle_ = CK_INVALID_HANDLE;
+ }
+ }
+
+ CK_SESSION_HANDLE handle() const {
+ return handle_;
+ }
+
+ bool IsValid() const {
+ return (handle_ != CK_INVALID_HANDLE);
+ }
+
+ private:
+ CK_SESSION_HANDLE handle_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedSession);
+};
+
+Pkcs11KeyStore::Pkcs11KeyStore(chaps::TokenManagerClient* token_manager)
+ : token_manager_(token_manager) {}
+
+Pkcs11KeyStore::~Pkcs11KeyStore() {}
+
+bool Pkcs11KeyStore::Read(const std::string& username,
+ const std::string& key_name,
+ std::string* key_data) {
+ CK_SLOT_ID slot;
+ if (!GetUserSlot(username, &slot)) {
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
+ return false;
+ }
+ ScopedSession session(slot);
+ if (!session.IsValid()) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
+ return false;
+ }
+ CK_OBJECT_HANDLE key_handle = FindObject(session.handle(), key_name);
+ if (key_handle == CK_INVALID_HANDLE) {
+ LOG(WARNING) << "Pkcs11KeyStore: Key does not exist: " << key_name;
+ return false;
+ }
+ // First get the attribute with a NULL buffer which will give us the length.
+ CK_ATTRIBUTE attribute = {CKA_VALUE, nullptr, 0};
+ if (C_GetAttributeValue(session.handle(),
+ key_handle,
+ &attribute, 1) != CKR_OK) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to read key data: " << key_name;
+ return false;
+ }
+ key_data->resize(attribute.ulValueLen);
+ attribute.pValue = string_as_array(key_data);
+ if (C_GetAttributeValue(session.handle(),
+ key_handle,
+ &attribute, 1) != CKR_OK) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to read key data: " << key_name;
+ return false;
+ }
+ key_data->resize(attribute.ulValueLen);
+ return true;
+}
+
+bool Pkcs11KeyStore::Write(const std::string& username,
+ const std::string& key_name,
+ const std::string& key_data) {
+ // Delete any existing key with the same name.
+ if (!Delete(username, key_name)) {
+ return false;
+ }
+ CK_SLOT_ID slot;
+ if (!GetUserSlot(username, &slot)) {
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
+ return false;
+ }
+ ScopedSession session(slot);
+ if (!session.IsValid()) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
+ return false;
+ }
+ std::string mutable_key_name(key_name);
+ std::string mutable_key_data(key_data);
+ std::string mutable_application_id(kApplicationID);
+ // Create a new data object for the key.
+ CK_OBJECT_CLASS object_class = CKO_DATA;
+ CK_BBOOL true_value = CK_TRUE;
+ CK_BBOOL false_value = CK_FALSE;
+ CK_ATTRIBUTE attributes[] = {
+ {CKA_CLASS, &object_class, sizeof(object_class)},
+ {
+ CKA_LABEL,
+ string_as_array(&mutable_key_name),
+ mutable_key_name.size()
+ },
+ {
+ CKA_VALUE,
+ string_as_array(&mutable_key_data),
+ mutable_key_data.size()
+ },
+ {
+ CKA_APPLICATION,
+ string_as_array(&mutable_application_id),
+ mutable_application_id.size()
+ },
+ {CKA_TOKEN, &true_value, sizeof(true_value)},
+ {CKA_PRIVATE, &true_value, sizeof(true_value)},
+ {CKA_MODIFIABLE, &false_value, sizeof(false_value)}
+ };
+ CK_OBJECT_HANDLE key_handle = CK_INVALID_HANDLE;
+ if (C_CreateObject(session.handle(),
+ attributes,
+ arraysize(attributes),
+ &key_handle) != CKR_OK) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to write key data: " << key_name;
+ return false;
+ }
+ return true;
+}
+
+bool Pkcs11KeyStore::Delete(const std::string& username,
+ const std::string& key_name) {
+ CK_SLOT_ID slot;
+ if (!GetUserSlot(username, &slot)) {
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
+ return false;
+ }
+ ScopedSession session(slot);
+ if (!session.IsValid()) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
+ return false;
+ }
+ CK_OBJECT_HANDLE key_handle = FindObject(session.handle(), key_name);
+ if (key_handle != CK_INVALID_HANDLE) {
+ if (C_DestroyObject(session.handle(), key_handle) != CKR_OK) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to delete key data.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Pkcs11KeyStore::DeleteByPrefix(const std::string& username,
+ const std::string& key_prefix) {
+ CK_SLOT_ID slot;
+ if (!GetUserSlot(username, &slot)) {
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
+ return false;
+ }
+ ScopedSession session(slot);
+ if (!session.IsValid()) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
+ return false;
+ }
+ EnumObjectsCallback callback = base::Bind(
+ &Pkcs11KeyStore::DeleteIfMatchesPrefix,
+ base::Unretained(this),
+ session.handle(),
+ key_prefix);
+ if (!EnumObjects(session.handle(), callback)) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to delete key data.";
+ return false;
+ }
+ return true;
+}
+
+bool Pkcs11KeyStore::Register(const std::string& username,
+ const std::string& label,
+ KeyType key_type,
+ KeyUsage key_usage,
+ const std::string& private_key_blob,
+ const std::string& public_key_der,
+ const std::string& certificate) {
+ const CK_ATTRIBUTE_TYPE kKeyBlobAttribute = CKA_VENDOR_DEFINED + 1;
+
+ if (key_type != KEY_TYPE_RSA) {
+ LOG(ERROR) << "Pkcs11KeyStore: Only RSA supported.";
+ return false;
+ }
+ CK_SLOT_ID slot;
+ if (!GetUserSlot(username, &slot)) {
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
+ return false;
+ }
+ ScopedSession session(slot);
+ if (!session.IsValid()) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
+ return false;
+ }
+
+ // Extract the modulus from the public key.
+ const unsigned char* asn1_ptr = reinterpret_cast<const unsigned char*>(
+ public_key_der.data());
+ crypto::ScopedRSA public_key(d2i_RSAPublicKey(nullptr,
+ &asn1_ptr,
+ public_key_der.size()));
+ if (!public_key.get()) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to decode public key.";
+ return false;
+ }
+ std::string modulus(BN_num_bytes(public_key.get()->n), 0);
+ int length = BN_bn2bin(public_key.get()->n, reinterpret_cast<unsigned char*>(
+ string_as_array(&modulus)));
+ if (length <= 0) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to extract public key modulus.";
+ return false;
+ }
+ modulus.resize(length);
+
+ // Construct a PKCS #11 template for the public key object.
+ CK_BBOOL true_value = CK_TRUE;
+ CK_BBOOL false_value = CK_FALSE;
+ CK_KEY_TYPE p11_key_type = CKK_RSA;
+ CK_OBJECT_CLASS public_key_class = CKO_PUBLIC_KEY;
+ std::string id = Sha1(modulus);
+ std::string mutable_label(label);
+ CK_ULONG modulus_bits = modulus.size() * 8;
+ CK_BBOOL sign_usage = (key_usage == KEY_USAGE_SIGN);
+ CK_BBOOL decrypt_usage = (key_usage == KEY_USAGE_DECRYPT);
+ unsigned char public_exponent[] = {1, 0, 1};
+ CK_ATTRIBUTE public_key_attributes[] = {
+ {CKA_CLASS, &public_key_class, sizeof(public_key_class)},
+ {CKA_TOKEN, &true_value, sizeof(true_value)},
+ {CKA_DERIVE, &false_value, sizeof(false_value)},
+ {CKA_WRAP, &false_value, sizeof(false_value)},
+ {CKA_VERIFY, &sign_usage, sizeof(sign_usage)},
+ {CKA_VERIFY_RECOVER, &false_value, sizeof(false_value)},
+ {CKA_ENCRYPT, &decrypt_usage, sizeof(decrypt_usage)},
+ {CKA_KEY_TYPE, &p11_key_type, sizeof(p11_key_type)},
+ {CKA_ID, string_as_array(&id), id.size()},
+ {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
+ {CKA_MODULUS_BITS, &modulus_bits, sizeof(modulus_bits)},
+ {CKA_PUBLIC_EXPONENT, public_exponent, arraysize(public_exponent)},
+ {CKA_MODULUS, string_as_array(&modulus), modulus.size()}
+ };
+
+ CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
+ if (C_CreateObject(session.handle(),
+ public_key_attributes,
+ arraysize(public_key_attributes),
+ &object_handle) != CKR_OK) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to create public key object.";
+ return false;
+ }
+
+ // Construct a PKCS #11 template for the private key object.
+ std::string mutable_private_key_blob(private_key_blob);
+ CK_OBJECT_CLASS private_key_class = CKO_PRIVATE_KEY;
+ CK_ATTRIBUTE private_key_attributes[] = {
+ {CKA_CLASS, &private_key_class, sizeof(private_key_class)},
+ {CKA_TOKEN, &true_value, sizeof(true_value)},
+ {CKA_PRIVATE, &true_value, sizeof(true_value)},
+ {CKA_SENSITIVE, &true_value, sizeof(true_value)},
+ {CKA_EXTRACTABLE, &false_value, sizeof(false_value)},
+ {CKA_DERIVE, &false_value, sizeof(false_value)},
+ {CKA_UNWRAP, &false_value, sizeof(false_value)},
+ {CKA_SIGN, &sign_usage, sizeof(sign_usage)},
+ {CKA_SIGN_RECOVER, &false_value, sizeof(false_value)},
+ {CKA_DECRYPT, &decrypt_usage, sizeof(decrypt_usage)},
+ {CKA_KEY_TYPE, &p11_key_type, sizeof(p11_key_type)},
+ {CKA_ID, string_as_array(&id), id.size()},
+ {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
+ {CKA_PUBLIC_EXPONENT, public_exponent, arraysize(public_exponent)},
+ {CKA_MODULUS, string_as_array(&modulus), modulus.size()},
+ {
+ kKeyBlobAttribute,
+ string_as_array(&mutable_private_key_blob),
+ mutable_private_key_blob.size()
+ }
+ };
+
+ if (C_CreateObject(session.handle(),
+ private_key_attributes,
+ arraysize(private_key_attributes),
+ &object_handle) != CKR_OK) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to create private key object.";
+ return false;
+ }
+
+ if (!certificate.empty()) {
+ std::string subject;
+ std::string issuer;
+ std::string serial_number;
+ if (!GetCertificateFields(certificate, &subject, &issuer, &serial_number)) {
+ LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate fields.";
+ }
+ // Construct a PKCS #11 template for a certificate object.
+ std::string mutable_certificate = certificate;
+ CK_OBJECT_CLASS certificate_class = CKO_CERTIFICATE;
+ CK_CERTIFICATE_TYPE certificate_type = CKC_X_509;
+ CK_ATTRIBUTE certificate_attributes[] = {
+ {CKA_CLASS, &certificate_class, sizeof(certificate_class)},
+ {CKA_TOKEN, &true_value, sizeof(true_value)},
+ {CKA_PRIVATE, &false_value, sizeof(false_value)},
+ {CKA_ID, string_as_array(&id), id.size()},
+ {CKA_LABEL, string_as_array(&mutable_label), mutable_label.size()},
+ {CKA_CERTIFICATE_TYPE, &certificate_type, sizeof(certificate_type)},
+ {CKA_SUBJECT, string_as_array(&subject), subject.size()},
+ {CKA_ISSUER, string_as_array(&issuer), issuer.size()},
+ {
+ CKA_SERIAL_NUMBER,
+ string_as_array(&serial_number),
+ serial_number.size()
+ },
+ {
+ CKA_VALUE,
+ string_as_array(&mutable_certificate),
+ mutable_certificate.size()
+ }
+ };
+
+ if (C_CreateObject(session.handle(),
+ certificate_attributes,
+ arraysize(certificate_attributes),
+ &object_handle) != CKR_OK) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to create certificate object.";
+ return false;
+ }
+ }
+
+ // Close all sessions in an attempt to trigger other modules to find the new
+ // objects.
+ C_CloseAllSessions(slot);
+
+ return true;
+}
+
+bool Pkcs11KeyStore::RegisterCertificate(const std::string& username,
+ const std::string& certificate) {
+ CK_SLOT_ID slot;
+ if (!GetUserSlot(username, &slot)) {
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
+ return false;
+ }
+ ScopedSession session(slot);
+ if (!session.IsValid()) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to open token session.";
+ return false;
+ }
+
+ if (DoesCertificateExist(session.handle(), certificate)) {
+ LOG(INFO) << "Pkcs11KeyStore: Certificate already exists.";
+ return true;
+ }
+ std::string subject;
+ std::string issuer;
+ std::string serial_number;
+ if (!GetCertificateFields(certificate, &subject, &issuer, &serial_number)) {
+ LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate fields.";
+ }
+ // Construct a PKCS #11 template for a certificate object.
+ std::string mutable_certificate = certificate;
+ CK_OBJECT_CLASS certificate_class = CKO_CERTIFICATE;
+ CK_CERTIFICATE_TYPE certificate_type = CKC_X_509;
+ CK_BBOOL true_value = CK_TRUE;
+ CK_BBOOL false_value = CK_FALSE;
+ CK_ATTRIBUTE certificate_attributes[] = {
+ {CKA_CLASS, &certificate_class, sizeof(certificate_class)},
+ {CKA_TOKEN, &true_value, sizeof(true_value)},
+ {CKA_PRIVATE, &false_value, sizeof(false_value)},
+ {CKA_CERTIFICATE_TYPE, &certificate_type, sizeof(certificate_type)},
+ {CKA_SUBJECT, string_as_array(&subject), subject.size()},
+ {CKA_ISSUER, string_as_array(&issuer), issuer.size()},
+ {CKA_SERIAL_NUMBER, string_as_array(&serial_number), serial_number.size()},
+ {
+ CKA_VALUE,
+ string_as_array(&mutable_certificate),
+ mutable_certificate.size()
+ }
+ };
+ CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
+ if (C_CreateObject(session.handle(),
+ certificate_attributes,
+ arraysize(certificate_attributes),
+ &object_handle) != CKR_OK) {
+ LOG(ERROR) << "Pkcs11KeyStore: Failed to create certificate object.";
+ return false;
+ }
+ return true;
+}
+
+CK_OBJECT_HANDLE Pkcs11KeyStore::FindObject(CK_SESSION_HANDLE session_handle,
+ const std::string& key_name) {
+ // Assemble a search template.
+ std::string mutable_key_name(key_name);
+ std::string mutable_application_id(kApplicationID);
+ CK_OBJECT_CLASS object_class = CKO_DATA;
+ CK_BBOOL true_value = CK_TRUE;
+ CK_BBOOL false_value = CK_FALSE;
+ CK_ATTRIBUTE attributes[] = {
+ {CKA_CLASS, &object_class, sizeof(object_class)},
+ {
+ CKA_LABEL,
+ string_as_array(&mutable_key_name),
+ mutable_key_name.size()
+ },
+ {
+ CKA_APPLICATION,
+ string_as_array(&mutable_application_id),
+ mutable_application_id.size()
+ },
+ {CKA_TOKEN, &true_value, sizeof(true_value)},
+ {CKA_PRIVATE, &true_value, sizeof(true_value)},
+ {CKA_MODIFIABLE, &false_value, sizeof(false_value)}
+ };
+ CK_OBJECT_HANDLE key_handle = CK_INVALID_HANDLE;
+ CK_ULONG count = 0;
+ if ((C_FindObjectsInit(session_handle,
+ attributes,
+ arraysize(attributes)) != CKR_OK) ||
+ (C_FindObjects(session_handle, &key_handle, 1, &count) != CKR_OK) ||
+ (C_FindObjectsFinal(session_handle) != CKR_OK)) {
+ LOG(ERROR) << "Key search failed: " << key_name;
+ return CK_INVALID_HANDLE;
+ }
+ if (count == 1)
+ return key_handle;
+ return CK_INVALID_HANDLE;
+}
+
+bool Pkcs11KeyStore::GetUserSlot(const std::string& username,
+ CK_SLOT_ID_PTR slot) {
+ const char kChapsDaemonName[] = "chaps";
+ const char kChapsSystemToken[] = "/var/lib/chaps";
+ base::FilePath token_path = username.empty() ?
+ base::FilePath(kChapsSystemToken) :
+ brillo::cryptohome::home::GetDaemonPath(username, kChapsDaemonName);
+ CK_RV rv;
+ rv = C_Initialize(nullptr);
+ if (rv != CKR_OK && rv != CKR_CRYPTOKI_ALREADY_INITIALIZED) {
+ LOG(WARNING) << __func__ << ": C_Initialize failed.";
+ return false;
+ }
+ CK_ULONG num_slots = 0;
+ rv = C_GetSlotList(CK_TRUE, nullptr, &num_slots);
+ if (rv != CKR_OK) {
+ LOG(WARNING) << __func__ << ": C_GetSlotList(nullptr) failed.";
+ return false;
+ }
+ std::unique_ptr<CK_SLOT_ID[]> slot_list(new CK_SLOT_ID[num_slots]);
+ rv = C_GetSlotList(CK_TRUE, slot_list.get(), &num_slots);
+ if (rv != CKR_OK) {
+ LOG(WARNING) << __func__ << ": C_GetSlotList failed.";
+ return false;
+ }
+ // Look through all slots for |token_path|.
+ for (CK_ULONG i = 0; i < num_slots; ++i) {
+ base::FilePath slot_path;
+ if (token_manager_->GetTokenPath(
+ chaps::IsolateCredentialManager::GetDefaultIsolateCredential(),
+ slot_list[i],
+ &slot_path) && (token_path == slot_path)) {
+ *slot = slot_list[i];
+ return true;
+ }
+ }
+ LOG(WARNING) << __func__ << ": Path not found.";
+ return false;
+}
+
+bool Pkcs11KeyStore::EnumObjects(
+ CK_SESSION_HANDLE session_handle,
+ const Pkcs11KeyStore::EnumObjectsCallback& callback) {
+ std::string mutable_application_id(kApplicationID);
+ // Assemble a search template.
+ CK_OBJECT_CLASS object_class = CKO_DATA;
+ CK_BBOOL true_value = CK_TRUE;
+ CK_BBOOL false_value = CK_FALSE;
+ CK_ATTRIBUTE attributes[] = {
+ {CKA_CLASS, &object_class, sizeof(object_class)},
+ {
+ CKA_APPLICATION,
+ string_as_array(&mutable_application_id),
+ mutable_application_id.size()
+ },
+ {CKA_TOKEN, &true_value, sizeof(true_value)},
+ {CKA_PRIVATE, &true_value, sizeof(true_value)},
+ {CKA_MODIFIABLE, &false_value, sizeof(false_value)}
+ };
+ const CK_ULONG kMaxHandles = 100; // Arbitrary.
+ CK_OBJECT_HANDLE handles[kMaxHandles];
+ CK_ULONG count = 0;
+ if ((C_FindObjectsInit(session_handle,
+ attributes,
+ arraysize(attributes)) != CKR_OK) ||
+ (C_FindObjects(session_handle, handles, kMaxHandles, &count) != CKR_OK)) {
+ LOG(ERROR) << "Key search failed.";
+ return false;
+ }
+ while (count > 0) {
+ for (CK_ULONG i = 0; i < count; ++i) {
+ std::string key_name;
+ if (!GetKeyName(session_handle, handles[i], &key_name)) {
+ LOG(WARNING) << "Found key object but failed to get name.";
+ continue;
+ }
+ if (!callback.Run(key_name, handles[i]))
+ return false;
+ }
+ if (C_FindObjects(session_handle, handles, kMaxHandles, &count) != CKR_OK) {
+ LOG(ERROR) << "Key search continuation failed.";
+ return false;
+ }
+ }
+ if (C_FindObjectsFinal(session_handle) != CKR_OK) {
+ LOG(WARNING) << "Failed to finalize key search.";
+ }
+ return true;
+}
+
+bool Pkcs11KeyStore::GetKeyName(CK_SESSION_HANDLE session_handle,
+ CK_OBJECT_HANDLE object_handle,
+ std::string* key_name) {
+ CK_ATTRIBUTE attribute = {CKA_LABEL, nullptr, 0};
+ if (C_GetAttributeValue(session_handle, object_handle, &attribute, 1) !=
+ CKR_OK) {
+ LOG(ERROR) << "C_GetAttributeValue(CKA_LABEL) [length] failed.";
+ return false;
+ }
+ key_name->resize(attribute.ulValueLen);
+ attribute.pValue = string_as_array(key_name);
+ if (C_GetAttributeValue(session_handle, object_handle, &attribute, 1) !=
+ CKR_OK) {
+ LOG(ERROR) << "C_GetAttributeValue(CKA_LABEL) failed.";
+ return false;
+ }
+ return true;
+}
+
+bool Pkcs11KeyStore::DeleteIfMatchesPrefix(CK_SESSION_HANDLE session_handle,
+ const std::string& key_prefix,
+ const std::string& key_name,
+ CK_OBJECT_HANDLE object_handle) {
+ if (base::StartsWithASCII(key_name, key_prefix, true /*case_sensitive*/)) {
+ if (C_DestroyObject(session_handle, object_handle) != CKR_OK) {
+ LOG(ERROR) << "C_DestroyObject failed.";
+ return false;
+ }
+ }
+ return true;
+}
+
+bool Pkcs11KeyStore::GetCertificateFields(const std::string& certificate,
+ std::string* subject,
+ std::string* issuer,
+ std::string* serial_number) {
+ const unsigned char* asn1_ptr = reinterpret_cast<const unsigned char*>(
+ certificate.data());
+ ScopedX509 x509(d2i_X509(nullptr, &asn1_ptr, certificate.size()));
+ if (!x509.get() || !x509->cert_info || !x509->cert_info->subject) {
+ LOG(WARNING) << "Pkcs11KeyStore: Failed to decode certificate.";
+ return false;
+ }
+ unsigned char* subject_buffer = nullptr;
+ int length = i2d_X509_NAME(x509->cert_info->subject, &subject_buffer);
+ crypto::ScopedOpenSSLBytes scoped_subject_buffer(subject_buffer);
+ if (length <= 0) {
+ LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate subject.";
+ return false;
+ }
+ subject->assign(reinterpret_cast<char*>(subject_buffer), length);
+
+ unsigned char* issuer_buffer = nullptr;
+ length = i2d_X509_NAME(x509->cert_info->issuer, &issuer_buffer);
+ crypto::ScopedOpenSSLBytes scoped_issuer_buffer(issuer_buffer);
+ if (length <= 0) {
+ LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate issuer.";
+ return false;
+ }
+ issuer->assign(reinterpret_cast<char*>(issuer_buffer), length);
+
+ unsigned char* serial_number_buffer = nullptr;
+ length = i2d_ASN1_INTEGER(x509->cert_info->serialNumber,
+ &serial_number_buffer);
+ crypto::ScopedOpenSSLBytes scoped_serial_number_buffer(serial_number_buffer);
+ if (length <= 0) {
+ LOG(WARNING) << "Pkcs11KeyStore: Failed to encode certificate serial "
+ "number.";
+ return false;
+ }
+ serial_number->assign(reinterpret_cast<char*>(serial_number_buffer), length);
+ return true;
+}
+
+bool Pkcs11KeyStore::DoesCertificateExist(
+ CK_SESSION_HANDLE session_handle,
+ const std::string& certificate) {
+ CK_OBJECT_CLASS object_class = CKO_CERTIFICATE;
+ CK_BBOOL true_value = CK_TRUE;
+ CK_BBOOL false_value = CK_FALSE;
+ std::string mutable_certificate = certificate;
+ CK_ATTRIBUTE attributes[] = {
+ {CKA_CLASS, &object_class, sizeof(object_class)},
+ {CKA_TOKEN, &true_value, sizeof(true_value)},
+ {CKA_PRIVATE, &false_value, sizeof(false_value)},
+ {
+ CKA_VALUE,
+ string_as_array(&mutable_certificate),
+ mutable_certificate.size()
+ }
+ };
+ CK_OBJECT_HANDLE object_handle = CK_INVALID_HANDLE;
+ CK_ULONG count = 0;
+ if ((C_FindObjectsInit(session_handle,
+ attributes,
+ arraysize(attributes)) != CKR_OK) ||
+ (C_FindObjects(session_handle, &object_handle, 1, &count) != CKR_OK) ||
+ (C_FindObjectsFinal(session_handle) != CKR_OK)) {
+ return false;
+ }
+ return (count > 0);
+}
+
+} // namespace attestation
diff --git a/attestation/server/pkcs11_key_store.h b/attestation/server/pkcs11_key_store.h
new file mode 100644
index 0000000..a1ccd7e
--- /dev/null
+++ b/attestation/server/pkcs11_key_store.h
@@ -0,0 +1,123 @@
+//
+// 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.
+//
+
+#ifndef ATTESTATION_SERVER_PKCS11_KEY_STORE_H_
+#define ATTESTATION_SERVER_PKCS11_KEY_STORE_H_
+
+#include "attestation/server/key_store.h"
+
+#include <string>
+
+#include <base/callback_forward.h>
+#include <base/macros.h>
+#include <chaps/pkcs11/cryptoki.h>
+#include <chaps/token_manager_client.h>
+
+namespace attestation {
+
+// This class uses a PKCS #11 token as storage for key data. The key data is
+// stored in data objects with the following attributes:
+// CKA_CLASS - CKO_DATA
+// CKA_LABEL - A key name.
+// CKA_VALUE - Binary key data (opaque to this class and the PKCS #11 token).
+// CKA_APPLICATION - A constant value associated with this class.
+// CKA_TOKEN - True
+// CKA_PRIVATE - True
+// CKA_MODIFIABLE - False
+// There is no barrier between the objects created by this class and any other
+// objects residing in the same token. In practice, this means that any
+// component with access to the PKCS #11 token also has access to read or delete
+// key data.
+class Pkcs11KeyStore : public KeyStore {
+ public:
+ // Does not take ownership of pointers.
+ explicit Pkcs11KeyStore(chaps::TokenManagerClient* token_manager);
+ ~Pkcs11KeyStore() override;
+
+ // KeyStore interface.
+ bool Read(const std::string& username,
+ const std::string& key_name,
+ std::string* key_data) override;
+ bool Write(const std::string& username,
+ const std::string& key_name,
+ const std::string& key_data) override;
+ bool Delete(const std::string& username,
+ const std::string& key_name) override;
+ bool DeleteByPrefix(const std::string& username,
+ const std::string& key_prefix) override;
+ bool Register(const std::string& username,
+ const std::string& label,
+ KeyType key_type,
+ KeyUsage key_usage,
+ const std::string& private_key_blob,
+ const std::string& public_key_der,
+ const std::string& certificate) override;
+ bool RegisterCertificate(const std::string& username,
+ const std::string& certificate) override;
+
+ private:
+ using EnumObjectsCallback =
+ base::Callback<bool(const std::string& key_name,
+ CK_OBJECT_HANDLE object_handle)>;
+
+ // Searches for a PKCS #11 object for a given key name. If one exists, the
+ // object handle is returned, otherwise CK_INVALID_HANDLE is returned.
+ CK_OBJECT_HANDLE FindObject(CK_SESSION_HANDLE session_handle,
+ const std::string& key_name);
+
+ // Gets a slot for the given |username| if |is_user_specific| or the system
+ // slot otherwise. Returns false if no appropriate slot is found.
+ bool GetUserSlot(const std::string& username,
+ CK_SLOT_ID_PTR slot);
+
+ // Enumerates all PKCS #11 objects associated with keys. The |callback| is
+ // called once for each object.
+ bool EnumObjects(CK_SESSION_HANDLE session_handle,
+ const EnumObjectsCallback& callback);
+
+ // Looks up the key name for the given |object_handle| which is associated
+ // with a key. Returns true on success.
+ bool GetKeyName(CK_SESSION_HANDLE session_handle,
+ CK_OBJECT_HANDLE object_handle,
+ std::string* key_name);
+
+ // An EnumObjectsCallback for use with DeleteByPrefix. Destroys the key
+ // object identified by |object_handle| if |key_name| matches |key_prefix|.
+ // Returns true on success.
+ bool DeleteIfMatchesPrefix(CK_SESSION_HANDLE session_handle,
+ const std::string& key_prefix,
+ const std::string& key_name,
+ CK_OBJECT_HANDLE object_handle);
+
+ // Extracts the |subject|, |issuer|, and |serial_number| information from an
+ // X.509 |certificate|. Returns false if the value cannot be determined.
+ bool GetCertificateFields(const std::string& certificate,
+ std::string* subject,
+ std::string* issuer,
+ std::string* serial_number);
+
+ // Returns true iff the given certificate already exists in the token.
+ bool DoesCertificateExist(CK_SESSION_HANDLE session_handle,
+ const std::string& certificate);
+
+ chaps::TokenManagerClient* token_manager_;
+
+ DISALLOW_COPY_AND_ASSIGN(Pkcs11KeyStore);
+};
+
+} // namespace attestation
+
+#endif // ATTESTATION_SERVER_PKCS11_KEY_STORE_H_
diff --git a/attestation/server/pkcs11_key_store_test.cc b/attestation/server/pkcs11_key_store_test.cc
new file mode 100644
index 0000000..5cd4e82
--- /dev/null
+++ b/attestation/server/pkcs11_key_store_test.cc
@@ -0,0 +1,598 @@
+//
+// 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/pkcs11_key_store.h"
+
+#include <map>
+#include <string>
+#include <vector>
+
+#include <base/logging.h>
+#include <base/strings/string_number_conversions.h>
+#include <chaps/attributes.h>
+#include <chaps/chaps_proxy_mock.h>
+#include <chaps/token_manager_client_mock.h>
+#include <brillo/cryptohome.h>
+#include <brillo/map_utils.h>
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+using ::testing::_;
+using ::testing::DoAll;
+using ::testing::Invoke;
+using ::testing::NiceMock;
+using ::testing::Return;
+using ::testing::SetArgumentPointee;
+
+namespace {
+
+const uint64_t kSession = 7; // Arbitrary non-zero value.
+const char kDefaultUser[] = "test_user";
+
+const char kValidPublicKeyHex[] =
+ "3082010A0282010100"
+ "961037BC12D2A298BEBF06B2D5F8C9B64B832A2237F8CF27D5F96407A6041A4D"
+ "AD383CB5F88E625F412E8ACD5E9D69DF0F4FA81FCE7955829A38366CBBA5A2B1"
+ "CE3B48C14B59E9F094B51F0A39155874C8DE18A0C299EBF7A88114F806BE4F25"
+ "3C29A509B10E4B19E31675AFE3B2DA77077D94F43D8CE61C205781ED04D183B4"
+ "C349F61B1956C64B5398A3A98FAFF17D1B3D9120C832763EDFC8F4137F6EFBEF"
+ "46D8F6DE03BD00E49DEF987C10BDD5B6F8758B6A855C23C982DDA14D8F0F2B74"
+ "E6DEFA7EEE5A6FC717EB0FF103CB8049F693A2C8A5039EF1F5C025DC44BD8435"
+ "E8D8375DADE00E0C0F5C196E04B8483CC98B1D5B03DCD7E0048B2AB343FFC11F"
+ "0203"
+ "010001";
+
+const char kValidCertificateHex[] =
+ "3082040f308202f7a003020102020900bd0f8fd6bf496b67300d06092a864886"
+ "f70d01010b050030819d310b3009060355040613025553311330110603550408"
+ "0c0a43616c69666f726e69613116301406035504070c0d4d6f756e7461696e20"
+ "5669657731133011060355040a0c0a4368726f6d69756d4f533111300f060355"
+ "040b0c08556e6974546573743117301506035504030c0e506b637331314b6579"
+ "53746f72653120301e06092a864886f70d010901161174657374406368726f6d"
+ "69756d2e6f7267301e170d3135303231383137303132345a170d313731313133"
+ "3137303132345a30819d310b3009060355040613025553311330110603550408"
+ "0c0a43616c69666f726e69613116301406035504070c0d4d6f756e7461696e20"
+ "5669657731133011060355040a0c0a4368726f6d69756d4f533111300f060355"
+ "040b0c08556e6974546573743117301506035504030c0e506b637331314b6579"
+ "53746f72653120301e06092a864886f70d010901161174657374406368726f6d"
+ "69756d2e6f726730820122300d06092a864886f70d01010105000382010f0030"
+ "82010a0282010100a8fb9e12b1e5298b9a24fabc3901d00c32057392c763836e"
+ "0b55cff8e67d39b9b9853920fd615688b3e13c03a10cb5668187819172d1d269"
+ "70f0ff8d4371ac581f6970a0e43a1d0d61a94741a771fe86aee45ab0ca059b1f"
+ "c067f7416f08544cc4d08ec884b6d4327bb3ec0dc0789639375bd159df0efd87"
+ "1cf4d605778c7a68c96b94cf0a6c29f9a23bc027e8250084eb2dfca817b20f57"
+ "a6fe09513f884389db7b90788aea70c6e1638f24e39553ac0f859e585965c425"
+ "9ed7b9680fde3e059f254d8c9494f6ab425ede80d63366dfcb7cc311f5bc6fb0"
+ "1c27d81f4c5112d04b7614c37ba19c014916816372c773e4e44564fac34565ad"
+ "ebf38fe56c1413170203010001a350304e301d0603551d0e04160414fe13c7db"
+ "459bd2881e9113198e1f072e16cea144301f0603551d23041830168014fe13c7"
+ "db459bd2881e9113198e1f072e16cea144300c0603551d13040530030101ff30"
+ "0d06092a864886f70d01010b05000382010100a163d636ac64bd6f67eca53708"
+ "5f92abc993a40fd0c0222a56b262c29f88057a3edf9abac024756ad85d7453d8"
+ "4782e0be65d176aecfb0fbfc88ca567d17124fa190cb5ce832264360dd6daee1"
+ "e121428de28dda0b8ba117a1be3cf438efd060a3b5fc812e7eba70cec12cb609"
+ "738fc7d0912546c42b5aaadb142adce2167c7f30cd9e0049687d384334335aff"
+ "72aebd1745a0aac4be816365969347f064f36f7fdec69f970f28b87061650470"
+ "c63be8475bb23d0485985fb77c7cdd9d9fe008211a9ddd0fe68efb0b47cf629c"
+ "941d31e3c2f88e670e7e4ef1129febad000e6a16222779fbfe34641e5243ca38"
+ "74e2ad06f9585a00bec014744d3175ecc4808d";
+
+std::string HexDecode(const std::string hex) {
+ std::vector<uint8> output;
+ CHECK(base::HexStringToBytes(hex, &output));
+ return std::string(reinterpret_cast<char*>(output.data()), output.size());
+}
+
+class ScopedFakeSalt {
+ public:
+ ScopedFakeSalt() : salt_(128, 0) {
+ brillo::cryptohome::home::SetSystemSalt(&salt_);
+ }
+ ~ScopedFakeSalt() {
+ brillo::cryptohome::home::SetSystemSalt(nullptr);
+ }
+
+ private:
+ std::string salt_;
+};
+
+class ScopedDisableVerboseLogging {
+ public:
+ ScopedDisableVerboseLogging()
+ : original_severity_(logging::GetMinLogLevel()) {
+ logging::SetMinLogLevel(logging::LOG_INFO);
+ }
+ ~ScopedDisableVerboseLogging() {
+ logging::SetMinLogLevel(original_severity_);
+ }
+
+ private:
+ logging::LogSeverity original_severity_;
+};
+
+} // namespace
+
+namespace attestation {
+
+typedef chaps::ChapsProxyMock Pkcs11Mock;
+
+// Implements a fake PKCS #11 object store. Labeled data blobs can be stored
+// and later retrieved. The mocked interface is ChapsInterface so these
+// tests must be linked with the Chaps PKCS #11 library. The mock class itself
+// is part of the Chaps package; it is reused here to avoid duplication (see
+// chaps_proxy_mock.h).
+class KeyStoreTest : public testing::Test {
+ public:
+ KeyStoreTest()
+ : pkcs11_(false), // Do not pre-initialize the mock PKCS #11 library.
+ // This just controls whether the first call to
+ // C_Initialize returns 'already initialized'.
+ next_handle_(1) {}
+ ~KeyStoreTest() override = default;
+
+ void SetUp() override {
+ std::vector<uint64_t> slot_list = {0, 1};
+ ON_CALL(pkcs11_, GetSlotList(_, _, _))
+ .WillByDefault(DoAll(SetArgumentPointee<2>(slot_list), Return(0)));
+ ON_CALL(pkcs11_, OpenSession(_, _, _, _))
+ .WillByDefault(DoAll(SetArgumentPointee<3>(kSession), Return(0)));
+ ON_CALL(pkcs11_, CloseSession(_, _))
+ .WillByDefault(Return(0));
+ ON_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .WillByDefault(Invoke(this, &KeyStoreTest::CreateObject));
+ ON_CALL(pkcs11_, DestroyObject(_, _, _))
+ .WillByDefault(Invoke(this, &KeyStoreTest::DestroyObject));
+ ON_CALL(pkcs11_, GetAttributeValue(_, _, _, _, _))
+ .WillByDefault(Invoke(this, &KeyStoreTest::GetAttributeValue));
+ ON_CALL(pkcs11_, SetAttributeValue(_, _, _, _))
+ .WillByDefault(Invoke(this, &KeyStoreTest::SetAttributeValue));
+ ON_CALL(pkcs11_, FindObjectsInit(_, _, _))
+ .WillByDefault(Invoke(this, &KeyStoreTest::FindObjectsInit));
+ ON_CALL(pkcs11_, FindObjects(_, _, _, _))
+ .WillByDefault(Invoke(this, &KeyStoreTest::FindObjects));
+ ON_CALL(pkcs11_, FindObjectsFinal(_, _))
+ .WillByDefault(Return(0));
+ base::FilePath system_path("/var/lib/chaps");
+ ON_CALL(token_manager_, GetTokenPath(_, 0, _))
+ .WillByDefault(DoAll(SetArgumentPointee<2>(system_path), Return(true)));
+ base::FilePath user_path(brillo::cryptohome::home::GetDaemonPath(
+ kDefaultUser, "chaps"));
+ ON_CALL(token_manager_, GetTokenPath(_, 1, _))
+ .WillByDefault(DoAll(SetArgumentPointee<2>(user_path), Return(true)));
+ }
+
+ // Stores a new labeled object, only CKA_LABEL and CKA_VALUE are relevant.
+ virtual uint32_t CreateObject(const brillo::SecureBlob& isolate,
+ uint64_t session_id,
+ const std::vector<uint8_t>& attributes,
+ uint64_t* new_object_handle) {
+ *new_object_handle = next_handle_++;
+ std::string label = GetValue(attributes, CKA_LABEL);
+ handles_[*new_object_handle] = label;
+ values_[label] = GetValue(attributes, CKA_VALUE);
+ labels_[label] = *new_object_handle;
+ return CKR_OK;
+ }
+
+ // Deletes a labeled object.
+ virtual uint32_t DestroyObject(const brillo::SecureBlob& isolate,
+ uint64_t session_id,
+ uint64_t object_handle) {
+ std::string label = handles_[object_handle];
+ handles_.erase(object_handle);
+ values_.erase(label);
+ labels_.erase(label);
+ return CKR_OK;
+ }
+
+ // Supports reading CKA_VALUE.
+ virtual uint32_t GetAttributeValue(const brillo::SecureBlob& isolate,
+ uint64_t session_id,
+ uint64_t object_handle,
+ const std::vector<uint8_t>& attributes_in,
+ std::vector<uint8_t>* attributes_out) {
+ std::string label = handles_[object_handle];
+ std::string value = values_[label];
+ chaps::Attributes parsed;
+ parsed.Parse(attributes_in);
+ if (parsed.num_attributes() == 1 &&
+ parsed.attributes()[0].type == CKA_LABEL)
+ value = label;
+ if (parsed.num_attributes() != 1 ||
+ (parsed.attributes()[0].type != CKA_VALUE &&
+ parsed.attributes()[0].type != CKA_LABEL) ||
+ (parsed.attributes()[0].pValue &&
+ parsed.attributes()[0].ulValueLen != value.size()))
+ return CKR_GENERAL_ERROR;
+ parsed.attributes()[0].ulValueLen = value.size();
+ if (parsed.attributes()[0].pValue)
+ memcpy(parsed.attributes()[0].pValue, value.data(), value.size());
+ parsed.Serialize(attributes_out);
+ return CKR_OK;
+ }
+
+ // Supports writing CKA_VALUE.
+ virtual uint32_t SetAttributeValue(
+ const brillo::SecureBlob& isolate,
+ uint64_t session_id,
+ uint64_t object_handle,
+ const std::vector<uint8_t>& attributes) {
+ values_[handles_[object_handle]] = GetValue(attributes, CKA_VALUE);
+ return CKR_OK;
+ }
+
+ // Finds stored objects by CKA_LABEL or CKA_VALUE. If no CKA_LABEL or
+ // CKA_VALUE, find all objects.
+ virtual uint32_t FindObjectsInit(const brillo::SecureBlob& isolate,
+ uint64_t session_id,
+ const std::vector<uint8_t>& attributes) {
+ std::string label = GetValue(attributes, CKA_LABEL);
+ std::string value = GetValue(attributes, CKA_VALUE);
+ found_objects_.clear();
+ if (label.empty() && value.empty()) {
+ // Find all objects.
+ found_objects_ = brillo::GetMapKeysAsVector(handles_);
+ } else if (!label.empty() && labels_.count(label) > 0) {
+ // Find only the object with |label|.
+ found_objects_.push_back(labels_[label]);
+ } else {
+ // Find all objects with |value|.
+ for (const auto& item : values_) {
+ if (item.second == value && labels_.count(item.first) > 0) {
+ found_objects_.push_back(labels_[item.first]);
+ }
+ }
+ }
+ return CKR_OK;
+ }
+
+ // Reports a 'found' object based on find_status_.
+ virtual uint32_t FindObjects(const brillo::SecureBlob& isolate,
+ uint64_t session_id,
+ uint64_t max_object_count,
+ std::vector<uint64_t>* object_list) {
+ while (!found_objects_.empty() && object_list->size() < max_object_count) {
+ object_list->push_back(found_objects_.back());
+ found_objects_.pop_back();
+ }
+ return CKR_OK;
+ }
+
+ protected:
+ NiceMock<Pkcs11Mock> pkcs11_;
+ NiceMock<chaps::TokenManagerClientMock> token_manager_;
+
+ private:
+ // A helper to pull the value for a given attribute out of a serialized
+ // template.
+ std::string GetValue(const std::vector<uint8_t>& attributes,
+ CK_ATTRIBUTE_TYPE type) {
+ chaps::Attributes parsed;
+ parsed.Parse(attributes);
+ CK_ATTRIBUTE_PTR array = parsed.attributes();
+ for (CK_ULONG i = 0; i < parsed.num_attributes(); ++i) {
+ if (array[i].type == type) {
+ if (!array[i].pValue)
+ return "";
+ return std::string(reinterpret_cast<char*>(array[i].pValue),
+ array[i].ulValueLen);
+ }
+ }
+ return "";
+ }
+
+ std::map<std::string, std::string> values_; // The fake store: label->value
+ std::map<uint64_t, std::string> handles_; // The fake store: handle->label
+ std::map<std::string, uint64_t> labels_; // The fake store: label->handle
+ std::vector<uint64_t> found_objects_; // The most recent search results
+ uint64_t next_handle_; // Tracks handle assignment
+ ScopedFakeSalt fake_system_salt_;
+ // We want to avoid all the Chaps verbose logging.
+ ScopedDisableVerboseLogging no_verbose_logging;
+
+ DISALLOW_COPY_AND_ASSIGN(KeyStoreTest);
+};
+
+// This test assumes that chaps in not available on the system running the test.
+// The purpose of this test is to exercise the C_Initialize failure code path.
+// Without a mock, the Chaps library will attempt to connect to the Chaps daemon
+// unsuccessfully, resulting in a C_Initialize failure.
+TEST(KeyStoreTest_NoMock, Pkcs11NotAvailable) {
+ chaps::TokenManagerClient token_manager;
+ Pkcs11KeyStore key_store(&token_manager);
+ std::string blob;
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+ EXPECT_FALSE(key_store.Write(kDefaultUser, "test", blob));
+ EXPECT_FALSE(key_store.Read("", "test", &blob));
+ EXPECT_FALSE(key_store.Write("", "test", blob));
+}
+
+// Exercises the key store when PKCS #11 returns success. This exercises all
+// non-error-handling code paths.
+TEST_F(KeyStoreTest, Pkcs11Success) {
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string blob;
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_TRUE(key_store.Read(kDefaultUser, "test", &blob));
+ EXPECT_EQ("test_data", blob);
+ // Try with a different key name.
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test2", &blob));
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test2", "test_data2"));
+ EXPECT_TRUE(key_store.Read(kDefaultUser, "test2", &blob));
+ EXPECT_EQ("test_data2", blob);
+ // Read the original key again.
+ EXPECT_TRUE(key_store.Read(kDefaultUser, "test", &blob));
+ EXPECT_EQ("test_data", blob);
+ // Replace key data.
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test", "test_data3"));
+ EXPECT_TRUE(key_store.Read(kDefaultUser, "test", &blob));
+ EXPECT_EQ("test_data3", blob);
+ // Delete key data.
+ EXPECT_TRUE(key_store.Delete(kDefaultUser, "test2"));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test2", &blob));
+ EXPECT_TRUE(key_store.Read(kDefaultUser, "test", &blob));
+}
+
+TEST_F(KeyStoreTest, Pkcs11Success_NoUser) {
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string blob;
+ EXPECT_FALSE(key_store.Read("", "test", &blob));
+ EXPECT_TRUE(key_store.Write("", "test", "test_data"));
+ EXPECT_TRUE(key_store.Read("", "test", &blob));
+ EXPECT_EQ("test_data", blob);
+ // Try with a different key name.
+ EXPECT_FALSE(key_store.Read("", "test2", &blob));
+ EXPECT_TRUE(key_store.Write("", "test2", "test_data2"));
+ EXPECT_TRUE(key_store.Read("", "test2", &blob));
+ EXPECT_EQ("test_data2", blob);
+ // Read the original key again.
+ EXPECT_TRUE(key_store.Read("", "test", &blob));
+ EXPECT_EQ("test_data", blob);
+ // Replace key data.
+ EXPECT_TRUE(key_store.Write("", "test", "test_data3"));
+ EXPECT_TRUE(key_store.Read("", "test", &blob));
+ EXPECT_EQ("test_data3", blob);
+ // Delete key data.
+ EXPECT_TRUE(key_store.Delete("", "test2"));
+ EXPECT_FALSE(key_store.Read("", "test2", &blob));
+ EXPECT_TRUE(key_store.Read("", "test", &blob));
+}
+
+// Tests the key store when PKCS #11 has no token for the given user.
+TEST_F(KeyStoreTest, TokenNotAvailable) {
+ EXPECT_CALL(token_manager_, GetTokenPath(_, _, _))
+ .WillRepeatedly(Return(false));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string blob;
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+ EXPECT_FALSE(key_store.Write(kDefaultUser, "test", blob));
+ EXPECT_FALSE(key_store.Read("", "test", &blob));
+ EXPECT_FALSE(key_store.Write("", "test", blob));
+}
+
+// Tests the key store when PKCS #11 fails to open a session.
+TEST_F(KeyStoreTest, NoSession) {
+ EXPECT_CALL(pkcs11_, OpenSession(_, _, _, _))
+ .WillRepeatedly(Return(CKR_GENERAL_ERROR));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string blob;
+ EXPECT_FALSE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+}
+
+// Tests the key store when PKCS #11 fails to create an object.
+TEST_F(KeyStoreTest, CreateObjectFail) {
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .WillRepeatedly(Return(CKR_GENERAL_ERROR));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string blob;
+ EXPECT_FALSE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+}
+
+// Tests the key store when PKCS #11 fails to read attribute values.
+TEST_F(KeyStoreTest, ReadValueFail) {
+ EXPECT_CALL(pkcs11_, GetAttributeValue(_, _, _, _, _))
+ .WillRepeatedly(Return(CKR_GENERAL_ERROR));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string blob;
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+}
+
+// Tests the key store when PKCS #11 fails to delete key data.
+TEST_F(KeyStoreTest, DeleteValueFail) {
+ EXPECT_CALL(pkcs11_, DestroyObject(_, _, _))
+ .WillRepeatedly(Return(CKR_GENERAL_ERROR));
+ Pkcs11KeyStore key_store(&token_manager_);
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_FALSE(key_store.Write(kDefaultUser, "test", "test_data2"));
+ EXPECT_FALSE(key_store.Delete(kDefaultUser, "test"));
+}
+
+// Tests the key store when PKCS #11 fails to find objects. Tests each part of
+// the multi-part find operation individually.
+TEST_F(KeyStoreTest, FindFail) {
+ EXPECT_CALL(pkcs11_, FindObjectsInit(_, _, _))
+ .WillRepeatedly(Return(CKR_GENERAL_ERROR));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string blob;
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+
+ EXPECT_CALL(pkcs11_, FindObjectsInit(_, _, _))
+ .WillRepeatedly(Return(CKR_OK));
+ EXPECT_CALL(pkcs11_, FindObjects(_, _, _, _))
+ .WillRepeatedly(Return(CKR_GENERAL_ERROR));
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+
+ EXPECT_CALL(pkcs11_, FindObjects(_, _, _, _))
+ .WillRepeatedly(Return(CKR_OK));
+ EXPECT_CALL(pkcs11_, FindObjectsFinal(_, _))
+ .WillRepeatedly(Return(CKR_GENERAL_ERROR));
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+}
+
+// Tests the key store when PKCS #11 successfully finds zero objects.
+TEST_F(KeyStoreTest, FindNoObjects) {
+ std::vector<uint64_t> empty;
+ EXPECT_CALL(pkcs11_, FindObjects(_, _, _, _))
+ .WillRepeatedly(DoAll(SetArgumentPointee<3>(empty), Return(CKR_OK)));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string blob;
+ EXPECT_TRUE(key_store.Write(kDefaultUser, "test", "test_data"));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "test", &blob));
+}
+
+TEST_F(KeyStoreTest, RegisterKeyWithoutCertificate) {
+ Pkcs11KeyStore key_store(&token_manager_);
+ // Try with a malformed public key.
+ EXPECT_FALSE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+ KEY_USAGE_SIGN, "private_key_blob",
+ "bad_pubkey", ""));
+ // Try with a well-formed public key.
+ std::string public_key_der = HexDecode(kValidPublicKeyHex);
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .Times(2) // Public, private (no certificate).
+ .WillRepeatedly(Return(CKR_OK));
+ EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+ KEY_USAGE_SIGN, "private_key_blob",
+ public_key_der, ""));
+}
+
+TEST_F(KeyStoreTest, RegisterKeyWithCertificate) {
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .Times(3) // Public, private, and certificate.
+ .WillRepeatedly(Return(CKR_OK));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string public_key_der = HexDecode(kValidPublicKeyHex);
+ std::string certificate_der = HexDecode(kValidCertificateHex);
+ EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+ KEY_USAGE_SIGN, "private_key_blob",
+ public_key_der, certificate_der));
+ // Also try with the system token.
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .Times(3) // Public, private, and certificate.
+ .WillRepeatedly(Return(CKR_OK));
+ EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+ KEY_USAGE_SIGN, "private_key_blob",
+ public_key_der, certificate_der));
+}
+
+TEST_F(KeyStoreTest, RegisterKeyWithBadCertificate) {
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .Times(3) // Public, private, and certificate.
+ .WillRepeatedly(Return(CKR_OK));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string public_key_der = HexDecode(kValidPublicKeyHex);
+ EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+ KEY_USAGE_SIGN, "private_key_blob",
+ public_key_der, "bad_certificate"));
+}
+
+TEST_F(KeyStoreTest, RegisterWithUnsupportedKeyType) {
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string public_key_der = HexDecode(kValidPublicKeyHex);
+ EXPECT_FALSE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_ECC,
+ KEY_USAGE_SIGN, "private_key_blob",
+ public_key_der, ""));
+}
+
+TEST_F(KeyStoreTest, RegisterDecryptionKey) {
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .WillRepeatedly(Return(CKR_OK));
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string public_key_der = HexDecode(kValidPublicKeyHex);
+ EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+ KEY_USAGE_DECRYPT, "private_key_blob",
+ public_key_der, ""));
+}
+
+TEST_F(KeyStoreTest, RegisterCertificate) {
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string certificate_der = HexDecode(kValidCertificateHex);
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .Times(2); // Once for valid, once for invalid.
+ // Try with a valid certificate (hit multiple times to check dup logic).
+ EXPECT_TRUE(key_store.RegisterCertificate(kDefaultUser, certificate_der));
+ EXPECT_TRUE(key_store.RegisterCertificate(kDefaultUser, certificate_der));
+ EXPECT_TRUE(key_store.RegisterCertificate(kDefaultUser, certificate_der));
+ // Try with an invalid certificate.
+ EXPECT_TRUE(key_store.RegisterCertificate(kDefaultUser, "bad_certificate"));
+}
+
+TEST_F(KeyStoreTest, RegisterCertificateError) {
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string certificate_der = HexDecode(kValidCertificateHex);
+ // Handle an error from PKCS #11.
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .WillOnce(Return(CKR_GENERAL_ERROR));
+ EXPECT_FALSE(key_store.RegisterCertificate(kDefaultUser, certificate_der));
+}
+
+TEST_F(KeyStoreTest, RegisterCertificateSystemToken) {
+ Pkcs11KeyStore key_store(&token_manager_);
+ std::string certificate_der = HexDecode(kValidCertificateHex);
+ // Try with the system token.
+ EXPECT_CALL(pkcs11_, CreateObject(_, _, _, _))
+ .WillOnce(Return(CKR_OK));
+ EXPECT_TRUE(key_store.RegisterCertificate(kDefaultUser, certificate_der));
+}
+
+// Tests that the DeleteByPrefix() method removes the correct objects and only
+// the correct objects.
+TEST_F(KeyStoreTest, DeleteByPrefix) {
+ Pkcs11KeyStore key_store(&token_manager_);
+
+ // Test with no keys.
+ ASSERT_TRUE(key_store.DeleteByPrefix(kDefaultUser, "prefix"));
+
+ // Test with a single matching key.
+ ASSERT_TRUE(key_store.Write(kDefaultUser, "prefix_test", "test"));
+ ASSERT_TRUE(key_store.DeleteByPrefix(kDefaultUser, "prefix"));
+ std::string blob;
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "prefix_test", &blob));
+
+ // Test with a single non-matching key.
+ ASSERT_TRUE(key_store.Write(kDefaultUser, "_prefix_", "test"));
+ ASSERT_TRUE(key_store.DeleteByPrefix(kDefaultUser, "prefix"));
+ EXPECT_TRUE(key_store.Read(kDefaultUser, "_prefix_", &blob));
+
+ // Test with an empty prefix.
+ ASSERT_TRUE(key_store.DeleteByPrefix(kDefaultUser, ""));
+ EXPECT_FALSE(key_store.Read(kDefaultUser, "_prefix_", &blob));
+
+ // Test with multiple matching and non-matching keys.
+ const int kNumKeys = 110; // Pkcs11KeyStore max is 100 for FindObjects.
+ key_store.Write(kDefaultUser, "other1", "test");
+ for (int i = 0; i < kNumKeys; ++i) {
+ std::string key_name = std::string("prefix") + base::IntToString(i);
+ key_store.Write(kDefaultUser, key_name, std::string(key_name));
+ }
+ ASSERT_TRUE(key_store.Write(kDefaultUser, "other2", "test"));
+ ASSERT_TRUE(key_store.DeleteByPrefix(kDefaultUser, "prefix"));
+ EXPECT_TRUE(key_store.Read(kDefaultUser, "other1", &blob));
+ EXPECT_TRUE(key_store.Read(kDefaultUser, "other2", &blob));
+ for (int i = 0; i < kNumKeys; ++i) {
+ std::string key_name = std::string("prefix") + base::IntToString(i);
+ EXPECT_FALSE(key_store.Read(kDefaultUser, key_name, &blob));
+ }
+}
+
+} // namespace attestation