attestation: Implemented RegisterKeyWithChapsToken.
BUG=brillo:737
TEST=unit, manually using 'attestation_client register' and 'p11_replay
--list_objects' as well as manually checking the chrome certificate
manager after registration.
Change-Id: I147f52fea8b0cb1c3cccaad98cffca5c08d032e3
Reviewed-on: https://chromium-review.googlesource.com/270144
Reviewed-by: Utkarsh Sanghi <usanghi@chromium.org>
Reviewed-by: Alex Vakulenko <avakulenko@chromium.org>
Commit-Queue: Darren Krahn <dkrahn@chromium.org>
Tested-by: Darren Krahn <dkrahn@chromium.org>
diff --git a/client/dbus_proxy.cc b/client/dbus_proxy.cc
index 2089f05..77fea34 100644
--- a/client/dbus_proxy.cc
+++ b/client/dbus_proxy.cc
@@ -175,4 +175,22 @@
request);
}
+void DBusProxy::RegisterKeyWithChapsToken(
+ const RegisterKeyWithChapsTokenRequest& request,
+ const RegisterKeyWithChapsTokenCallback& callback) {
+ auto on_error = [callback](chromeos::Error* error) {
+ RegisterKeyWithChapsTokenReply reply;
+ reply.set_status(STATUS_NOT_AVAILABLE);
+ callback.Run(reply);
+ };
+ chromeos::dbus_utils::CallMethodWithTimeout(
+ kDBusTimeoutMS,
+ object_proxy_,
+ attestation::kAttestationInterface,
+ attestation::kRegisterKeyWithChapsToken,
+ callback,
+ base::Bind(on_error),
+ request);
+}
+
} // namespace attestation
diff --git a/client/dbus_proxy.h b/client/dbus_proxy.h
index 678070d..3e7ec75 100644
--- a/client/dbus_proxy.h
+++ b/client/dbus_proxy.h
@@ -45,6 +45,9 @@
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) {
diff --git a/client/dbus_proxy_test.cc b/client/dbus_proxy_test.cc
index f0855f0..19eb606 100644
--- a/client/dbus_proxy_test.cc
+++ b/client/dbus_proxy_test.cc
@@ -364,4 +364,39 @@
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/client/main.cc b/client/main.cc
index 1d00b18..76ec20e 100644
--- a/client/main.cc
+++ b/client/main.cc
@@ -33,6 +33,7 @@
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:
@@ -70,6 +71,9 @@
--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.
@@ -239,6 +243,11 @@
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;
}
@@ -454,6 +463,15 @@
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.
diff --git a/common/attestation_interface.h b/common/attestation_interface.h
index 2ed7213..ac72bd3 100644
--- a/common/attestation_interface.h
+++ b/common/attestation_interface.h
@@ -70,17 +70,23 @@
const CreateCertifiableKeyRequest& request,
const CreateCertifiableKeyCallback& callback) = 0;
- // Processes a DecryptRequest and responds with a
- // DecryptReply.
+ // 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.
+ // 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
diff --git a/common/dbus_interface.h b/common/dbus_interface.h
index 97cde33..ffe90ab 100644
--- a/common/dbus_interface.h
+++ b/common/dbus_interface.h
@@ -21,6 +21,7 @@
constexpr char kCreateCertifiableKey[] = "CreateCertifiableKey";
constexpr char kDecrypt[] = "Decrypt";
constexpr char kSign[] = "Sign";
+constexpr char kRegisterKeyWithChapsToken[] = "RegisterKeyWithChapsToken";
} // namespace attestation
diff --git a/common/interface.proto b/common/interface.proto
index 0caa67b..ffdb21d 100644
--- a/common/interface.proto
+++ b/common/interface.proto
@@ -148,3 +148,12 @@
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/common/mock_attestation_interface.h b/common/mock_attestation_interface.h
index d568a43..eebd05b 100644
--- a/common/mock_attestation_interface.h
+++ b/common/mock_attestation_interface.h
@@ -36,6 +36,9 @@
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
diff --git a/server/attestation_service.cc b/server/attestation_service.cc
index 2d50ecf..407cfcd 100644
--- a/server/attestation_service.cc
+++ b/server/attestation_service.cc
@@ -460,6 +460,54 @@
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;
diff --git a/server/attestation_service.h b/server/attestation_service.h
index 1876c2a..8218abe 100644
--- a/server/attestation_service.h
+++ b/server/attestation_service.h
@@ -75,6 +75,9 @@
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) {
@@ -157,6 +160,11 @@
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();
diff --git a/server/attestation_service_test.cc b/server/attestation_service_test.cc
index ab2e2f6..2004f51 100644
--- a/server/attestation_service_test.cc
+++ b/server/attestation_service_test.cc
@@ -1003,4 +1003,176 @@
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/server/dbus_service.cc b/server/dbus_service.cc
index d8bc29a..7973ad3 100644
--- a/server/dbus_service.cc
+++ b/server/dbus_service.cc
@@ -51,6 +51,10 @@
dbus_interface->AddMethodHandler(kSign,
base::Unretained(this),
&DBusService::HandleSign);
+ dbus_interface->AddMethodHandler(
+ kRegisterKeyWithChapsToken,
+ base::Unretained(this),
+ &DBusService::HandleRegisterKeyWithChapsToken);
dbus_object_.RegisterAsync(callback);
}
@@ -204,4 +208,23 @@
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/server/dbus_service.h b/server/dbus_service.h
index 01e18f7..7224efa 100644
--- a/server/dbus_service.h
+++ b/server/dbus_service.h
@@ -86,6 +86,12 @@
const SignReply&>> response,
const SignRequest& request);
+ // Handles a RegisterKeyWithChapsToken D-Bus call.
+ void HandleRegisterKeyWithChapsToken(
+ std::unique_ptr<chromeos::dbus_utils::DBusMethodResponse<
+ const RegisterKeyWithChapsTokenReply&>> response,
+ const RegisterKeyWithChapsTokenRequest& request);
+
chromeos::dbus_utils::DBusObject dbus_object_;
AttestationInterface* service_;
diff --git a/server/dbus_service_test.cc b/server/dbus_service_test.cc
index 8894e3b..b8c0605 100644
--- a/server/dbus_service_test.cc
+++ b/server/dbus_service_test.cc
@@ -340,4 +340,31 @@
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/server/key_store.h b/server/key_store.h
index 6fc4421..3ae6a87 100644
--- a/server/key_store.h
+++ b/server/key_store.h
@@ -9,6 +9,8 @@
#include <base/macros.h>
+#include "attestation/common/common.pb.h"
+
namespace attestation {
// A mock-able key storage interface.
@@ -48,6 +50,8 @@
// 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;
diff --git a/server/mock_key_store.cc b/server/mock_key_store.cc
index 2558f35..792ebdd 100644
--- a/server/mock_key_store.cc
+++ b/server/mock_key_store.cc
@@ -14,7 +14,7 @@
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, Register(_, _, _, _, _, _, _)).WillByDefault(Return(true));
ON_CALL(*this, RegisterCertificate(_, _)).WillByDefault(Return(true));
}
diff --git a/server/mock_key_store.h b/server/mock_key_store.h
index 40d04f1..505fb76 100644
--- a/server/mock_key_store.h
+++ b/server/mock_key_store.h
@@ -29,8 +29,10 @@
const std::string& name));
MOCK_METHOD2(DeleteByPrefix, bool(const std::string& username,
const std::string& key_prefix));
- MOCK_METHOD5(Register, bool(const std::string& username,
+ 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));
diff --git a/server/pkcs11_key_store.cc b/server/pkcs11_key_store.cc
index 4f59928..bb9ffa2 100644
--- a/server/pkcs11_key_store.cc
+++ b/server/pkcs11_key_store.cc
@@ -88,7 +88,7 @@
std::string* key_data) {
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
- LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
@@ -130,7 +130,7 @@
}
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
- LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
@@ -181,7 +181,7 @@
const std::string& key_name) {
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
- LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
@@ -203,7 +203,7 @@
const std::string& key_prefix) {
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
- LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
@@ -225,14 +225,20 @@
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: " << username;
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
@@ -263,21 +269,23 @@
// 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 key_type = CKK_RSA;
+ 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, &true_value, sizeof(true_value)},
+ {CKA_VERIFY, &sign_usage, sizeof(sign_usage)},
{CKA_VERIFY_RECOVER, &false_value, sizeof(false_value)},
- {CKA_ENCRYPT, &false_value, sizeof(false_value)},
- {CKA_KEY_TYPE, &key_type, sizeof(key_type)},
+ {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)},
@@ -305,10 +313,10 @@
{CKA_EXTRACTABLE, &false_value, sizeof(false_value)},
{CKA_DERIVE, &false_value, sizeof(false_value)},
{CKA_UNWRAP, &false_value, sizeof(false_value)},
- {CKA_SIGN, &true_value, sizeof(true_value)},
+ {CKA_SIGN, &sign_usage, sizeof(sign_usage)},
{CKA_SIGN_RECOVER, &false_value, sizeof(false_value)},
- {CKA_DECRYPT, &false_value, sizeof(false_value)},
- {CKA_KEY_TYPE, &key_type, sizeof(key_type)},
+ {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)},
@@ -330,8 +338,10 @@
if (!certificate.empty()) {
std::string subject;
- if (!GetCertificateSubject(certificate, &subject)) {
- LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate 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;
@@ -345,6 +355,12 @@
{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),
@@ -372,7 +388,7 @@
const std::string& certificate) {
CK_SLOT_ID slot;
if (!GetUserSlot(username, &slot)) {
- LOG(ERROR) << "Pkcs11KeyStore: No token for user: " << username;
+ LOG(ERROR) << "Pkcs11KeyStore: No token for user.";
return false;
}
ScopedSession session(slot);
@@ -386,8 +402,10 @@
return true;
}
std::string subject;
- if (!GetCertificateSubject(certificate, &subject)) {
- LOG(WARNING) << "Pkcs11KeyStore: Failed to find certificate 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;
@@ -401,6 +419,8 @@
{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),
@@ -579,8 +599,10 @@
return true;
}
-bool Pkcs11KeyStore::GetCertificateSubject(const std::string& certificate,
- std::string* subject) {
+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()));
@@ -588,14 +610,34 @@
LOG(WARNING) << "Pkcs11KeyStore: Failed to decode certificate.";
return false;
}
- unsigned char* buffer = nullptr;
- int length = i2d_X509_NAME(x509->cert_info->subject, &buffer);
- crypto::ScopedOpenSSLBytes scoped_buffer(buffer);
+ 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*>(buffer), length);
+ 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;
}
diff --git a/server/pkcs11_key_store.h b/server/pkcs11_key_store.h
index 5dbda4b..5fb02cd 100644
--- a/server/pkcs11_key_store.h
+++ b/server/pkcs11_key_store.h
@@ -48,6 +48,8 @@
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;
@@ -88,10 +90,12 @@
const std::string& key_name,
CK_OBJECT_HANDLE object_handle);
- // Extracts the subject information from an X.509 certificate. Returns false
- // if the subject cannot be determined.
- bool GetCertificateSubject(const std::string& certificate,
- std::string* subject);
+ // 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,
diff --git a/server/pkcs11_key_store_test.cc b/server/pkcs11_key_store_test.cc
index 7e9dbf7..f8c9058 100644
--- a/server/pkcs11_key_store_test.cc
+++ b/server/pkcs11_key_store_test.cc
@@ -451,15 +451,17 @@
TEST_F(KeyStoreTest, RegisterKeyWithoutCertificate) {
Pkcs11KeyStore key_store(&token_manager_);
// Try with a malformed public key.
- EXPECT_FALSE(key_store.Register(kDefaultUser, "test_label",
- "private_key_blob", "bad_pubkey", ""));
+ 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",
- "private_key_blob", public_key_der, ""));
+ EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+ KEY_USAGE_SIGN, "private_key_blob",
+ public_key_der, ""));
}
TEST_F(KeyStoreTest, RegisterKeyWithCertificate) {
@@ -469,13 +471,15 @@
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", "private_key_blob",
+ 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", "private_key_blob",
+ EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", KEY_TYPE_RSA,
+ KEY_USAGE_SIGN, "private_key_blob",
public_key_der, certificate_der));
}
@@ -485,10 +489,29 @@
.WillRepeatedly(Return(CKR_OK));
Pkcs11KeyStore key_store(&token_manager_);
std::string public_key_der = HexDecode(kValidPublicKeyHex);
- EXPECT_TRUE(key_store.Register(kDefaultUser, "test_label", "private_key_blob",
+ 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);