diff options
author | tengs <tengs@chromium.org> | 2015-07-23 14:23:31 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2015-07-23 21:24:10 +0000 |
commit | 0b8ae150b8f61ee86114ab94f4b636d7bfdee54f (patch) | |
tree | 7a5c41abc71d879bb98fdb2f430c50fe55d0bafe /components/proximity_auth | |
parent | 5275af6d8d1355996dda02191a2824804efd8952 (diff) | |
download | chromium_src-0b8ae150b8f61ee86114ab94f4b636d7bfdee54f.zip chromium_src-0b8ae150b8f61ee86114ab94f4b636d7bfdee54f.tar.gz chromium_src-0b8ae150b8f61ee86114ab94f4b636d7bfdee54f.tar.bz2 |
Implement operations used in the DeviceToDevice protocol.
The DeviceToDevice is used by Smart Lock to provide mutual authentication between the
PC and mobile phone.
Although Chrome only runs the initiator side of this protocol, we also implement
the responder side to test that the ledgers match.
BUG=508761
TEST=unit test
Review URL: https://codereview.chromium.org/1238633003
Cr-Commit-Position: refs/heads/master@{#340170}
Diffstat (limited to 'components/proximity_auth')
7 files changed, 1190 insertions, 0 deletions
diff --git a/components/proximity_auth/BUILD.gn b/components/proximity_auth/BUILD.gn index d5defb3..f5b2c8b 100644 --- a/components/proximity_auth/BUILD.gn +++ b/components/proximity_auth/BUILD.gn @@ -24,6 +24,8 @@ source_set("proximity_auth") { "connection.h", "connection_finder.h", "connection_observer.h", + "device_to_device_initiator_operations.cc", + "device_to_device_initiator_operations.h", "metrics.cc", "metrics.h", "proximity_auth_client.h", @@ -57,6 +59,20 @@ source_set("proximity_auth") { ] } +source_set("test_support") { + testonly = true + + sources = [ + "device_to_device_responder_operations.cc", + "device_to_device_responder_operations.h", + ] + + deps = [ + "cryptauth:test_support", + "//base", + ] +} + source_set("unit_tests") { testonly = true sources = [ @@ -65,6 +81,7 @@ source_set("unit_tests") { "bluetooth_throttler_impl_unittest.cc", "client_impl_unittest.cc", "connection_unittest.cc", + "device_to_device_operations_unittest.cc", "proximity_auth_system_unittest.cc", "proximity_monitor_impl_unittest.cc", "remote_status_update_unittest.cc", @@ -76,7 +93,9 @@ source_set("unit_tests") { deps = [ ":proximity_auth", + ":test_support", "ble:unit_tests", + "cryptauth:test_support", "cryptauth:unit_tests", "logging:unit_tests", "//base/test:test_support", diff --git a/components/proximity_auth/cryptauth/proto/securemessage.proto b/components/proximity_auth/cryptauth/proto/securemessage.proto index 9e5d9cb..c086145 100644 --- a/components/proximity_auth/cryptauth/proto/securemessage.proto +++ b/components/proximity_auth/cryptauth/proto/securemessage.proto @@ -93,3 +93,24 @@ message GenericPublicKey { // Use only as a last resort. optional DhPublicKey dh2048_public_key = 4; } + +// Used by protocols for communicating between a pair of devices. +message DeviceToDeviceMessage { + // The payload of the message. + optional bytes message = 1; + + // The sequence number of the message - must be increasing. + optional int32 sequence_number = 2; +} + +// Sent as the first message from initiator to responder in an unauthenticated +// Diffie-Hellman Key Exchange. +message InitiatorHello { + optional GenericPublicKey public_dh_key = 1; +} + +// Sent inside the header of the first message from the responder to the +// initiator in an unauthenticated Diffie-Hellman Key Exchange. +message ResponderHello { + optional GenericPublicKey public_dh_key = 1; +} diff --git a/components/proximity_auth/device_to_device_initiator_operations.cc b/components/proximity_auth/device_to_device_initiator_operations.cc new file mode 100644 index 0000000..d0560ca --- /dev/null +++ b/components/proximity_auth/device_to_device_initiator_operations.cc @@ -0,0 +1,328 @@ +// 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. + +#include "components/proximity_auth/device_to_device_initiator_operations.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h" +#include "components/proximity_auth/cryptauth/proto/securemessage.pb.h" +#include "components/proximity_auth/cryptauth/secure_message_delegate.h" +#include "components/proximity_auth/logging/logging.h" + +namespace proximity_auth { + +namespace { + +// TODO(tengs): Due to a bug with the ChromeOS secure message daemon, we cannot +// create SecureMessages with empty payloads. To workaround this bug, this value +// is put into the payload if it would otherwise be empty. +// See crbug.com/512894. +const char kPayloadFiller[] = "\xae"; + +// The version to put in the GcmMetadata field. +const int kGcmMetadataVersion = 1; + +// Callback for DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage(), +// after the inner message is created. +void OnInnerMessageCreatedForInitiatorAuth( + const std::string& session_symmetric_key, + SecureMessageDelegate* secure_message_delegate, + const DeviceToDeviceInitiatorOperations::MessageCallback& callback, + const std::string& inner_message) { + if (inner_message.empty()) { + PA_LOG(INFO) << "Failed to create inner message for [Initiator Auth]."; + callback.Run(std::string()); + return; + } + + cryptauth::GcmMetadata gcm_metadata; + gcm_metadata.set_type(cryptauth::DEVICE_TO_DEVICE_MESSAGE); + gcm_metadata.set_version(kGcmMetadataVersion); + + // Store the inner message inside a DeviceToDeviceMessage proto. + securemessage::DeviceToDeviceMessage device_to_device_message; + device_to_device_message.set_message(inner_message); + device_to_device_message.set_sequence_number(2); + + // Create and return the outer message, which wraps the inner message. + SecureMessageDelegate::CreateOptions create_options; + create_options.encryption_scheme = securemessage::AES_256_CBC; + create_options.signature_scheme = securemessage::HMAC_SHA256; + gcm_metadata.SerializeToString(&create_options.public_metadata); + secure_message_delegate->CreateSecureMessage( + device_to_device_message.SerializeAsString(), session_symmetric_key, + create_options, callback); +} + +// Helper struct containing all the context needed to validate the +// [Responder Auth] message. +struct ValidateResponderAuthMessageContext { + std::string responder_auth_message; + std::string persistent_responder_public_key; + std::string persistent_symmetric_key; + std::string session_private_key; + std::string hello_message; + SecureMessageDelegate* secure_message_delegate; + DeviceToDeviceInitiatorOperations::ValidateResponderAuthCallback callback; + std::string responder_session_public_key; + std::string session_symmetric_key; +}; + +// Forward declarations of functions used in the [Responder Auth] validation +// flow. These functions are declared in order in which they are called during +// the flow. +void BeginResponderAuthValidation(ValidateResponderAuthMessageContext context); +void OnSessionSymmetricKeyDerived(ValidateResponderAuthMessageContext context, + const std::string& session_symmetric_key); +void OnOuterMessageUnwrappedForResponderAuth( + const ValidateResponderAuthMessageContext& context, + bool verified, + const std::string& payload, + const securemessage::Header& header); +void OnMiddleMessageUnwrappedForResponderAuth( + const ValidateResponderAuthMessageContext& context, + bool verified, + const std::string& payload, + const securemessage::Header& header); +void OnInnerMessageUnwrappedForResponderAuth( + const ValidateResponderAuthMessageContext& context, + bool verified, + const std::string& payload, + const securemessage::Header& header); + +// Begins the [Responder Auth] validation flow by validating the header. +void BeginResponderAuthValidation(ValidateResponderAuthMessageContext context) { + // Parse the encrypted SecureMessage so we can get plaintext data from the + // header. Note that the payload will be encrypted. + securemessage::SecureMessage encrypted_message; + securemessage::HeaderAndBody header_and_body; + if (!encrypted_message.ParseFromString(context.responder_auth_message) || + !header_and_body.ParseFromString(encrypted_message.header_and_body())) { + PA_LOG(WARNING) << "Failed to parse [Responder Hello] message"; + context.callback.Run(false, std::string()); + return; + } + + // Check that header public_metadata contains the correct metadata fields. + securemessage::Header header = header_and_body.header(); + cryptauth::GcmMetadata gcm_metadata; + if (!gcm_metadata.ParseFromString(header.public_metadata()) || + gcm_metadata.type() != + cryptauth::DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD || + gcm_metadata.version() != kGcmMetadataVersion) { + PA_LOG(WARNING) << "Failed to validate GcmMetadata in " + << "[Responder Auth] header."; + context.callback.Run(false, std::string()); + return; + } + + // Extract responder session public key from |decryption_key_id| field. + securemessage::ResponderHello responder_hello; + if (!responder_hello.ParseFromString(header.decryption_key_id()) || + !responder_hello.public_dh_key().SerializeToString( + &context.responder_session_public_key)) { + PA_LOG(INFO) << "Failed to extract responder session public key in " + << "[Responder Auth] header."; + context.callback.Run(false, std::string()); + return; + } + + // Perform a Diffie-Helmann key exchange to get the session symmetric key. + context.secure_message_delegate->DeriveKey( + context.session_private_key, context.responder_session_public_key, + base::Bind(&OnSessionSymmetricKeyDerived, context)); +} + +// Called after the session symmetric key is derived, so now we can unwrap the +// outer message of [Responder Auth]. +void OnSessionSymmetricKeyDerived(ValidateResponderAuthMessageContext context, + const std::string& session_symmetric_key) { + context.session_symmetric_key = session_symmetric_key; + + // Unwrap the outer message, using symmetric key encryption and signature. + SecureMessageDelegate::UnwrapOptions unwrap_options; + unwrap_options.encryption_scheme = securemessage::AES_256_CBC; + unwrap_options.signature_scheme = securemessage::HMAC_SHA256; + context.secure_message_delegate->UnwrapSecureMessage( + context.responder_auth_message, session_symmetric_key, unwrap_options, + base::Bind(&OnOuterMessageUnwrappedForResponderAuth, context)); +} + +// Called after the outer-most layer of [Responder Auth] is unwrapped. +void OnOuterMessageUnwrappedForResponderAuth( + const ValidateResponderAuthMessageContext& context, + bool verified, + const std::string& payload, + const securemessage::Header& header) { + if (!verified) { + PA_LOG(INFO) << "Failed to unwrap outer [Responder Auth] message."; + context.callback.Run(false, std::string()); + return; + } + + // Parse the decrypted payload. + securemessage::DeviceToDeviceMessage device_to_device_message; + if (!device_to_device_message.ParseFromString(payload) || + device_to_device_message.sequence_number() != 1) { + PA_LOG(INFO) << "Failed to validate DeviceToDeviceMessage payload."; + context.callback.Run(false, std::string()); + return; + } + + // Unwrap the middle level SecureMessage, using symmetric key encryption and + // signature. + SecureMessageDelegate::UnwrapOptions unwrap_options; + unwrap_options.encryption_scheme = securemessage::AES_256_CBC; + unwrap_options.signature_scheme = securemessage::HMAC_SHA256; + unwrap_options.associated_data = context.hello_message; + context.secure_message_delegate->UnwrapSecureMessage( + device_to_device_message.message(), context.persistent_symmetric_key, + unwrap_options, + base::Bind(&OnMiddleMessageUnwrappedForResponderAuth, context)); +} + +// Called after the middle layer of [Responder Auth] is unwrapped. +void OnMiddleMessageUnwrappedForResponderAuth( + const ValidateResponderAuthMessageContext& context, + bool verified, + const std::string& payload, + const securemessage::Header& header) { + if (!verified) { + PA_LOG(INFO) << "Failed to unwrap middle [Responder Auth] message."; + context.callback.Run(false, std::string()); + return; + } + + // Unwrap the inner-most SecureMessage, using no encryption and an asymmetric + // key signature. + SecureMessageDelegate::UnwrapOptions unwrap_options; + unwrap_options.encryption_scheme = securemessage::NONE; + unwrap_options.signature_scheme = securemessage::ECDSA_P256_SHA256; + unwrap_options.associated_data = context.hello_message; + context.secure_message_delegate->UnwrapSecureMessage( + payload, context.persistent_responder_public_key, unwrap_options, + base::Bind(&OnInnerMessageUnwrappedForResponderAuth, context)); +} + +// Called after the inner-most layer of [Responder Auth] is unwrapped. +void OnInnerMessageUnwrappedForResponderAuth( + const ValidateResponderAuthMessageContext& context, + bool verified, + const std::string& payload, + const securemessage::Header& header) { + if (!verified) + PA_LOG(INFO) << "Failed to unwrap inner [Responder Auth] message."; + + cryptauth::GcmMetadata gcm_metadata; + if (!gcm_metadata.ParseFromString(header.public_metadata()) || + gcm_metadata.type() != cryptauth::UNLOCK_KEY_SIGNED_CHALLENGE || + gcm_metadata.version() != kGcmMetadataVersion) { + PA_LOG(WARNING) << "Failed to validate GcmMetadata in inner-most " + << "[Responder Auth] message."; + context.callback.Run(false, std::string()); + return; + } + + context.callback.Run(verified, context.session_symmetric_key); +} + +} // namespace + +// static +void DeviceToDeviceInitiatorOperations::CreateHelloMessage( + const std::string& session_public_key, + const std::string& persistent_symmetric_key, + SecureMessageDelegate* secure_message_delegate, + const MessageCallback& callback) { + // Decode public key into the |initator_hello| proto. + securemessage::InitiatorHello initator_hello; + if (!initator_hello.mutable_public_dh_key()->ParseFromString( + session_public_key)) { + PA_LOG(ERROR) << "Unable to parse user's public key"; + callback.Run(std::string()); + return; + } + + // The [Hello] message has the structure: + // { + // header: <session_public_key>, + // Sig(<session_public_key>, persistent_symmetric_key) + // payload: "" + // } + SecureMessageDelegate::CreateOptions create_options; + create_options.encryption_scheme = securemessage::NONE; + create_options.signature_scheme = securemessage::HMAC_SHA256; + initator_hello.SerializeToString(&create_options.public_metadata); + secure_message_delegate->CreateSecureMessage( + kPayloadFiller, persistent_symmetric_key, create_options, callback); +} + +// static +void DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage( + const std::string& responder_auth_message, + const std::string& persistent_responder_public_key, + const std::string& persistent_symmetric_key, + const std::string& session_private_key, + const std::string& hello_message, + SecureMessageDelegate* secure_message_delegate, + const ValidateResponderAuthCallback& callback) { + // The [Responder Auth] message has the structure: + // { + // header: <responder_public_key>, + // Sig(<responder_public_key> + payload1, + // session_symmetric_key), + // payload1: Enc({ + // header: Sig(payload2 + <hello_message>, persistent_symmetric_key), + // payload2: { + // sequence_number: 1, + // body: Enc({ + // header: Sig(payload3 + <hello_message>, + // persistent_responder_public_key), + // payload3: "" + // }, persistent_symmetric_key) + // } + // }, session_symmetric_key), + // } + struct ValidateResponderAuthMessageContext context = { + responder_auth_message, + persistent_responder_public_key, + persistent_symmetric_key, + session_private_key, + hello_message, + secure_message_delegate, + callback}; + BeginResponderAuthValidation(context); +} + +// static +void DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage( + const std::string& session_symmetric_key, + const std::string& persistent_symmetric_key, + const std::string& responder_auth_message, + SecureMessageDelegate* secure_message_delegate, + const MessageCallback& callback) { + // The [Initiator Auth] message has the structure: + // { + // header: Sig(payload1, session_symmetric_key) + // payload1: Enc({ + // sequence_number: 2, + // body: { + // header: Sig(payload2 + responder_auth_message, + // persistent_symmetric_key) + // payload2: "" + // } + // }, session_symmetric_key) + // } + SecureMessageDelegate::CreateOptions create_options; + create_options.encryption_scheme = securemessage::AES_256_CBC; + create_options.signature_scheme = securemessage::HMAC_SHA256; + create_options.associated_data = responder_auth_message; + secure_message_delegate->CreateSecureMessage( + kPayloadFiller, persistent_symmetric_key, create_options, + base::Bind(&OnInnerMessageCreatedForInitiatorAuth, session_symmetric_key, + secure_message_delegate, callback)); +} + +} // proximity_auth diff --git a/components/proximity_auth/device_to_device_initiator_operations.h b/components/proximity_auth/device_to_device_initiator_operations.h new file mode 100644 index 0000000..bce148d --- /dev/null +++ b/components/proximity_auth/device_to_device_initiator_operations.h @@ -0,0 +1,112 @@ +// 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. + +#ifndef COMPONENTS_PROXIMITY_AUTH_DEVICE_TO_DEVICE_INITIATOR_OPERATIONS_H +#define COMPONENTS_PROXIMITY_AUTH_DEVICE_TO_DEVICE_INITIATOR_OPERATIONS_H + +#include <string> + +#include "base/callback_forward.h" +#include "base/macros.h" + +namespace proximity_auth { + +class SecureMessageDelegate; + +// Utility class containing operations in the DeviceToDevice protocol that the +// initiator needs to perform. For Smart Lock, in which a phone unlocks a +// laptop, the initiator is laptop. +// +// All operations are asynchronous because we use the SecureMessageDelegate for +// crypto operations, whose implementation may be asynchronous. +// +// In the DeviceToDevice protocol, the initiator needs to send two messages to +// the responder and parse one message from the responder: +// 1. Send [Hello] Message +// This message contains a public key that the initiator generates for the +// current session. This message is signed by the long term symmetric key. +// 2. Parse [Responder Auth] Message +// The responder parses [Hello] and sends this message, which contains the +// responder's session public key. This message also contains sufficient +// information for the initiator to authenticate the responder. +// 3. Send [Initiator Auth] Message +// After receiving the responder's session public key, the initiator crafts +// and sends this message so the responder can authenticate the initiator. +class DeviceToDeviceInitiatorOperations { + public: + // Callback for operations that create a message. Invoked with the serialized + // SecureMessage upon success or the empty string upon failure. + typedef base::Callback<void(const std::string&)> MessageCallback; + + // Callback for ValidateResponderAuthMessage. The first argument will be + // called with the validation outcome. If validation succeeded, then the + // second argument will contain the session symmetric key derived from the + // [Responder Auth] message. + typedef base::Callback<void(bool, const std::string&)> + ValidateResponderAuthCallback; + + // Creates the [Hello] message, which is the first message that is sent: + // |session_public_key|: This session public key will be stored in plaintext + // (but signed) so the responder can parse it. + // |persistent_symmetric_key|: The long-term symmetric key that is shared by + // the initiator and responder. + // |secure_message_delegate|: Delegate for SecureMessage operations. This + // instance is not owned, and must live until after |callback| is invoked. + // |callback|: Invoked upon operation completion with the serialized message + // or an empty string. + static void CreateHelloMessage(const std::string& session_public_key, + const std::string& persistent_symmetric_key, + SecureMessageDelegate* secure_message_delegate, + const MessageCallback& callback); + + // Validates that the [Responder Auth] message, received from the responder, + // is properly signed and encrypted. + // |responder_auth_message|: The bytes of the [Responder Auth] message to + // validate. + // |persistent_responder_public_key|: The long-term public key possessed by + // the responder device. + // |persistent_symmetric_key|: The long-term symmetric key that is shared by + // the initiator and responder. + // |session_private_key|: The session private key is used in an Diffie-Helmann + // key exchange once the responder public key is extracted. The derived + // session symmetric key is used in the validation process. + // |hello_message|: The initial [Hello] message that was sent, which is used + // in the signature calculation. + // |secure_message_delegate|: Delegate for SecureMessage operations. This + // instance is not owned, and must live until after |callback| is invoked. + // |callback|: Invoked upon operation completion with whether + // |responder_auth_message| is validated successfully. + static void ValidateResponderAuthMessage( + const std::string& responder_auth_message, + const std::string& persistent_responder_public_key, + const std::string& persistent_symmetric_key, + const std::string& session_private_key, + const std::string& hello_message, + SecureMessageDelegate* secure_message_delegate, + const ValidateResponderAuthCallback& callback); + + // Creates the [Initiator Auth] message, which allows the responder to + // authenticate the initiator: + // |persistent_symmetric_key|: The long-term symmetric key that is shared by + // the initiator and responder. + // |responder_auth_message|: The [Responder Auth] message sent previously to + // the responder. These bytes are used in the signature calculation. + // |secure_message_delegate|: Delegate for SecureMessage operations. This + // instance is not owned, and must live until after |callback| is invoked. + // |callback|: Invoked upon operation completion with the serialized message + // or an empty string. + static void CreateInitiatorAuthMessage( + const std::string& session_symmetric_key, + const std::string& persistent_symmetric_key, + const std::string& responder_auth_message, + SecureMessageDelegate* secure_message_delegate, + const MessageCallback& callback); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DeviceToDeviceInitiatorOperations); +}; + +} // proximity_auth + +#endif // COMPONENTS_PROXIMITY_AUTH_DEVICE_TO_DEVICE_INITIATOR_OPERATIONS_H diff --git a/components/proximity_auth/device_to_device_operations_unittest.cc b/components/proximity_auth/device_to_device_operations_unittest.cc new file mode 100644 index 0000000..0ba34ad --- /dev/null +++ b/components/proximity_auth/device_to_device_operations_unittest.cc @@ -0,0 +1,264 @@ +// 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. + +#include "base/bind.h" +#include "components/proximity_auth/cryptauth/base64url.h" +#include "components/proximity_auth/cryptauth/fake_secure_message_delegate.h" +#include "components/proximity_auth/device_to_device_initiator_operations.h" +#include "components/proximity_auth/device_to_device_responder_operations.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace proximity_auth { + +namespace { + +// The initiator's session public key in base64url form. Note that this is +// actually a serialized proto. +const char kInitiatorSessionPublicKeyBase64[] = + "CAESRQogOlH8DgPMQu7eAt-b6yoTXcazG8mAl6SPC5Ds-LTULIcSIQDZDMqsoYRO4tNMej1FB" + "El1sTiTiVDqrcGq-CkYCzDThw=="; + +// The responder's session public key in base64url form. Note that this is +// actually a serialized proto. +const char kResponderSessionPublicKeyBase64[] = + "CAESRgohAN9QYU5HySO14Gi9PDIClacBnC0C8wqPwXsNHUNG_vXlEiEAggzU80ZOd9DWuCBdp" + "6bzpGcC-oj1yrwdVCHGg_yeaAQ="; + +// The long-term public key possessed by the responder device. +const char kResponderPersistentPublicKey[] = "responder persistent public key"; + +// Used as a callback for message creation operations to save |message| into +// |out_message|. +void SaveMessageResult(std::string* out_message, const std::string& message) { + *out_message = message; +} + +// Used as a callback for validation operations to save |success| and into +// |out_success|. +void SaveValidationResult(bool* out_success, bool success) { + *out_success = success; +} + +// Used as a callback for the ValidateResponderAuthMessage and +// ValidateHelloMessage operations, saving both the outcome and the returned +// key. +void SaveValidationResultWithKey(bool* out_success, + std::string* out_key, + bool success, + const std::string& key) { + *out_success = success; + *out_key = key; +} + +} // namespace + +class ProximityAuthDeviceToDeviceOperationsTest : public testing::Test { + protected: + ProximityAuthDeviceToDeviceOperationsTest() {} + ~ProximityAuthDeviceToDeviceOperationsTest() override {} + + void SetUp() override { + ASSERT_TRUE(Base64UrlDecode(kInitiatorSessionPublicKeyBase64, + &local_session_public_key_)); + local_session_private_key_ = + secure_message_delegate_.GetPrivateKeyForPublicKey( + local_session_public_key_); + + ASSERT_TRUE(Base64UrlDecode(kResponderSessionPublicKeyBase64, + &remote_session_public_key_)); + remote_session_private_key_ = + secure_message_delegate_.GetPrivateKeyForPublicKey( + remote_session_public_key_); + + // Note: FakeSecureMessageDelegate functions are synchronous. + secure_message_delegate_.DeriveKey( + local_session_private_key_, remote_session_public_key_, + base::Bind(&SaveMessageResult, &session_symmetric_key_)); + + persistent_symmetric_key_ = "persistent symmetric key"; + } + + // Creates the initator's [Hello] message. + std::string CreateHelloMessage() { + std::string hello_message; + DeviceToDeviceInitiatorOperations::CreateHelloMessage( + local_session_public_key_, persistent_symmetric_key_, + &secure_message_delegate_, + base::Bind(&SaveMessageResult, &hello_message)); + EXPECT_FALSE(hello_message.empty()); + return hello_message; + } + + // Creates the responder's [Remote Auth] message. + std::string CreateResponderAuthMessage(const std::string& hello_message) { + std::string persistent_responder_private_key = + secure_message_delegate_.GetPrivateKeyForPublicKey( + kResponderPersistentPublicKey); + + std::string remote_auth_message; + DeviceToDeviceResponderOperations::CreateResponderAuthMessage( + hello_message, remote_session_public_key_, remote_session_private_key_, + persistent_responder_private_key, persistent_symmetric_key_, + &secure_message_delegate_, + base::Bind(&SaveMessageResult, &remote_auth_message)); + EXPECT_FALSE(remote_auth_message.empty()); + return remote_auth_message; + } + + // Creates the initiator's [Initiator Auth] message. + std::string CreateInitiatorAuthMessage( + const std::string& remote_auth_message) { + std::string local_auth_message; + DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage( + session_symmetric_key_, persistent_symmetric_key_, remote_auth_message, + &secure_message_delegate_, + base::Bind(&SaveMessageResult, &local_auth_message)); + EXPECT_FALSE(local_auth_message.empty()); + return local_auth_message; + } + + FakeSecureMessageDelegate secure_message_delegate_; + + std::string persistent_symmetric_key_; + std::string local_session_public_key_; + std::string local_session_private_key_; + std::string remote_session_public_key_; + std::string remote_session_private_key_; + std::string session_symmetric_key_; + + DISALLOW_COPY_AND_ASSIGN(ProximityAuthDeviceToDeviceOperationsTest); +}; + +TEST_F(ProximityAuthDeviceToDeviceOperationsTest, + ValidateHelloMessage_Success) { + bool validation_success = false; + std::string hello_public_key; + DeviceToDeviceResponderOperations::ValidateHelloMessage( + CreateHelloMessage(), persistent_symmetric_key_, + &secure_message_delegate_, + base::Bind(&SaveValidationResultWithKey, &validation_success, + &hello_public_key)); + + EXPECT_TRUE(validation_success); + EXPECT_EQ(local_session_public_key_, hello_public_key); +} + +TEST_F(ProximityAuthDeviceToDeviceOperationsTest, + ValidateHelloMessage_Failure) { + bool validation_success = true; + std::string hello_public_key = "non-empty string"; + DeviceToDeviceResponderOperations::ValidateHelloMessage( + "some random string", persistent_symmetric_key_, + &secure_message_delegate_, + base::Bind(&SaveValidationResultWithKey, &validation_success, + &hello_public_key)); + + EXPECT_FALSE(validation_success); + EXPECT_TRUE(hello_public_key.empty()); +} + +TEST_F(ProximityAuthDeviceToDeviceOperationsTest, + ValidateResponderAuthMessage_Success) { + std::string hello_message = CreateHelloMessage(); + std::string remote_auth_message = CreateResponderAuthMessage(hello_message); + + bool validation_success = false; + std::string session_symmetric_key; + DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage( + remote_auth_message, kResponderPersistentPublicKey, + persistent_symmetric_key_, local_session_private_key_, hello_message, + &secure_message_delegate_, + base::Bind(&SaveValidationResultWithKey, &validation_success, + &session_symmetric_key)); + + EXPECT_TRUE(validation_success); + EXPECT_EQ(session_symmetric_key_, session_symmetric_key); +} + +TEST_F(ProximityAuthDeviceToDeviceOperationsTest, + ValidateResponderAuthMessage_InvalidHelloMessage) { + std::string hello_message = CreateHelloMessage(); + std::string remote_auth_message = CreateResponderAuthMessage(hello_message); + + bool validation_success = true; + std::string session_symmetric_key = "non empty"; + DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage( + remote_auth_message, kResponderPersistentPublicKey, + persistent_symmetric_key_, local_session_private_key_, + "invalid hello message", &secure_message_delegate_, + base::Bind(&SaveValidationResultWithKey, &validation_success, + &session_symmetric_key)); + + EXPECT_FALSE(validation_success); + EXPECT_TRUE(session_symmetric_key.empty()); +} + +TEST_F(ProximityAuthDeviceToDeviceOperationsTest, + ValidateResponderAuthMessage_InvalidPSK) { + std::string hello_message = CreateHelloMessage(); + std::string remote_auth_message = CreateResponderAuthMessage(hello_message); + + bool validation_success = true; + std::string session_symmetric_key = "non empty"; + DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage( + remote_auth_message, kResponderPersistentPublicKey, + "invalid persistent symmetric key", local_session_private_key_, + hello_message, &secure_message_delegate_, + base::Bind(&SaveValidationResultWithKey, &validation_success, + &session_symmetric_key)); + + EXPECT_FALSE(validation_success); + EXPECT_TRUE(session_symmetric_key.empty()); +} + +TEST_F(ProximityAuthDeviceToDeviceOperationsTest, + ValidateInitiatorAuthMessage_Success) { + std::string hello_message = CreateHelloMessage(); + std::string remote_auth_message = CreateResponderAuthMessage(hello_message); + std::string local_auth_message = + CreateInitiatorAuthMessage(remote_auth_message); + + bool validation_success = false; + DeviceToDeviceResponderOperations::ValidateInitiatorAuthMessage( + local_auth_message, session_symmetric_key_, persistent_symmetric_key_, + remote_auth_message, &secure_message_delegate_, + base::Bind(&SaveValidationResult, &validation_success)); + + EXPECT_TRUE(validation_success); +} + +TEST_F(ProximityAuthDeviceToDeviceOperationsTest, + ValidateInitiatorAuthMessage_InvalidRemoteAuth) { + std::string hello_message = CreateHelloMessage(); + std::string remote_auth_message = CreateResponderAuthMessage(hello_message); + std::string local_auth_message = + CreateInitiatorAuthMessage(remote_auth_message); + + bool validation_success = true; + DeviceToDeviceResponderOperations::ValidateInitiatorAuthMessage( + local_auth_message, session_symmetric_key_, persistent_symmetric_key_, + "invalid remote auth", &secure_message_delegate_, + base::Bind(&SaveValidationResult, &validation_success)); + + EXPECT_FALSE(validation_success); +} + +TEST_F(ProximityAuthDeviceToDeviceOperationsTest, + ValidateInitiatorAuthMessage_InvalidPSK) { + std::string hello_message = CreateHelloMessage(); + std::string remote_auth_message = CreateResponderAuthMessage(hello_message); + std::string local_auth_message = + CreateInitiatorAuthMessage(remote_auth_message); + + bool validation_success = true; + DeviceToDeviceResponderOperations::ValidateInitiatorAuthMessage( + local_auth_message, session_symmetric_key_, + "invalid persistent symmetric key", remote_auth_message, + &secure_message_delegate_, + base::Bind(&SaveValidationResult, &validation_success)); + + EXPECT_FALSE(validation_success); +} + +} // namespace proximity_auth diff --git a/components/proximity_auth/device_to_device_responder_operations.cc b/components/proximity_auth/device_to_device_responder_operations.cc new file mode 100644 index 0000000..c1b3ec1 --- /dev/null +++ b/components/proximity_auth/device_to_device_responder_operations.cc @@ -0,0 +1,329 @@ +// 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. + +#include "components/proximity_auth/device_to_device_responder_operations.h" + +#include "base/bind.h" +#include "base/callback.h" +#include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h" +#include "components/proximity_auth/cryptauth/proto/securemessage.pb.h" +#include "components/proximity_auth/cryptauth/secure_message_delegate.h" +#include "components/proximity_auth/logging/logging.h" + +namespace proximity_auth { + +namespace { + +// TODO(tengs): Due to a bug with the ChromeOS secure message daemon, we cannot +// create SecureMessages with empty payloads. To workaround this bug, this value +// is put into the payload if it would otherwise be empty. +// See crbug.com/512894. +const char kPayloadFiller[] = "\xae"; + +// The version to put in the GcmMetadata field. +const int kGcmMetadataVersion = 1; + +// Callback for DeviceToDeviceResponderOperations::ValidateHelloMessage(), +// after the [Hello] message is unwrapped. +void OnHelloMessageUnwrapped( + const DeviceToDeviceResponderOperations::ValidateHelloCallback& callback, + bool verified, + const std::string& payload, + const securemessage::Header& header) { + securemessage::InitiatorHello initiator_hello; + if (!verified || !initiator_hello.ParseFromString(header.public_metadata())) { + callback.Run(false, std::string()); + return; + } + + callback.Run(true, initiator_hello.public_dh_key().SerializeAsString()); +} + +// Helper struct containing all the context needed to create the [Responder +// Auth] message. +struct CreateResponderAuthMessageContext { + std::string hello_message; + std::string session_public_key; + std::string session_private_key; + std::string persistent_private_key; + std::string persistent_symmetric_key; + SecureMessageDelegate* secure_message_delegate; + DeviceToDeviceResponderOperations::MessageCallback callback; + std::string hello_public_key; + std::string middle_message; +}; + +// Forward declarations of functions used to create the [Responder Auth] +// message, declared in order in which they are called during the creation flow. +void OnHelloMessageValidatedForResponderAuth( + CreateResponderAuthMessageContext context, + bool hello_message_validated, + const std::string& hello_public_key); +void OnInnerMessageCreatedForResponderAuth( + CreateResponderAuthMessageContext context, + const std::string& inner_message); +void OnMiddleMessageCreatedForResponderAuth( + CreateResponderAuthMessageContext context, + const std::string& middle_message); +void OnSessionSymmetricKeyDerivedForResponderAuth( + CreateResponderAuthMessageContext context, + const std::string& session_symmetric_key); + +// Called after the initiator's [Hello] message is unwrapped. +void OnHelloMessageValidatedForResponderAuth( + CreateResponderAuthMessageContext context, + bool hello_message_validated, + const std::string& hello_public_key) { + if (!hello_message_validated) { + PA_LOG(INFO) << "Invalid [Hello] while creating [Responder Auth]"; + context.callback.Run(std::string()); + return; + } + + context.hello_public_key = hello_public_key; + + // Create the inner most wrapped message of [Responder Auth]. + cryptauth::GcmMetadata gcm_metadata; + gcm_metadata.set_type(cryptauth::UNLOCK_KEY_SIGNED_CHALLENGE); + gcm_metadata.set_version(kGcmMetadataVersion); + + SecureMessageDelegate::CreateOptions create_options; + create_options.encryption_scheme = securemessage::NONE; + create_options.signature_scheme = securemessage::ECDSA_P256_SHA256; + gcm_metadata.SerializeToString(&create_options.public_metadata); + create_options.associated_data = context.hello_message; + + context.secure_message_delegate->CreateSecureMessage( + kPayloadFiller, context.persistent_private_key, create_options, + base::Bind(&OnInnerMessageCreatedForResponderAuth, context)); +} + +// Called after the inner-most layer of [Responder Auth] is created. +void OnInnerMessageCreatedForResponderAuth( + CreateResponderAuthMessageContext context, + const std::string& inner_message) { + if (inner_message.empty()) { + PA_LOG(INFO) << "Failed to create middle message for [Responder Auth]"; + context.callback.Run(std::string()); + return; + } + + // Create the middle message. + SecureMessageDelegate::CreateOptions create_options; + create_options.encryption_scheme = securemessage::AES_256_CBC; + create_options.signature_scheme = securemessage::HMAC_SHA256; + create_options.associated_data = context.hello_message; + context.secure_message_delegate->CreateSecureMessage( + inner_message, context.persistent_symmetric_key, create_options, + base::Bind(&OnMiddleMessageCreatedForResponderAuth, context)); +} + +// Called after the middle layer of [Responder Auth] is created. +void OnMiddleMessageCreatedForResponderAuth( + CreateResponderAuthMessageContext context, + const std::string& middle_message) { + if (middle_message.empty()) { + PA_LOG(ERROR) << "Error inner message while creating [Responder Auth]"; + context.callback.Run(std::string()); + return; + } + + // Before we can create the outer-most message layer, we need to perform a key + // agreement for the session symmetric key. + context.middle_message = middle_message; + context.secure_message_delegate->DeriveKey( + context.session_private_key, context.hello_public_key, + base::Bind(&OnSessionSymmetricKeyDerivedForResponderAuth, context)); +} + +// Called after the session symmetric key is derived, so we can create the outer +// most layer of [Responder Auth]. +void OnSessionSymmetricKeyDerivedForResponderAuth( + CreateResponderAuthMessageContext context, + const std::string& session_symmetric_key) { + if (session_symmetric_key.empty()) { + PA_LOG(ERROR) << "Error inner message while creating [Responder Auth]"; + context.callback.Run(std::string()); + return; + } + + cryptauth::GcmMetadata gcm_metadata; + gcm_metadata.set_type(cryptauth::DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD); + gcm_metadata.set_version(kGcmMetadataVersion); + + // Store the responder's session public key in plaintext in the header. + securemessage::ResponderHello responder_hello; + if (!responder_hello.mutable_public_dh_key()->ParseFromString( + context.session_public_key)) { + PA_LOG(ERROR) << "Error parsing public key while creating [Responder Auth]"; + PA_LOG(ERROR) << context.session_public_key; + context.callback.Run(std::string()); + return; + } + + // Create the outer most message, wrapping the other messages created + // previously. + securemessage::DeviceToDeviceMessage device_to_device_message; + device_to_device_message.set_message(context.middle_message); + device_to_device_message.set_sequence_number(1); + + SecureMessageDelegate::CreateOptions create_options; + create_options.encryption_scheme = securemessage::AES_256_CBC; + create_options.signature_scheme = securemessage::HMAC_SHA256; + create_options.public_metadata = gcm_metadata.SerializeAsString(); + responder_hello.SerializeToString(&create_options.decryption_key_id); + + context.secure_message_delegate->CreateSecureMessage( + device_to_device_message.SerializeAsString(), session_symmetric_key, + create_options, context.callback); +} + +// Helper struct containing all the context needed to validate the [Initiator +// Auth] message. +struct ValidateInitiatorAuthMessageContext { + std::string persistent_symmetric_key; + std::string responder_auth_message; + SecureMessageDelegate* secure_message_delegate; + DeviceToDeviceResponderOperations::ValidationCallback callback; +}; + +// Called after the inner-most layer of [Initiator Auth] is unwrapped. +void OnInnerMessageUnwrappedForInitiatorAuth( + const ValidateInitiatorAuthMessageContext& context, + bool verified, + const std::string& payload, + const securemessage::Header& header) { + if (!verified) + PA_LOG(INFO) << "Failed to inner [Initiator Auth] message."; + context.callback.Run(verified); +} + +// Called after the outer-most layer of [Initiator Auth] is unwrapped. +void OnOuterMessageUnwrappedForInitiatorAuth( + const ValidateInitiatorAuthMessageContext& context, + bool verified, + const std::string& payload, + const securemessage::Header& header) { + if (!verified) { + PA_LOG(INFO) << "Failed to verify outer [Initiator Auth] message"; + context.callback.Run(false); + return; + } + + // Parse the decrypted payload. + securemessage::DeviceToDeviceMessage device_to_device_message; + if (!device_to_device_message.ParseFromString(payload) || + device_to_device_message.sequence_number() != 2) { + PA_LOG(INFO) << "Failed to validate DeviceToDeviceMessage payload."; + context.callback.Run(false); + return; + } + + // Unwrap the inner message of [Initiator Auth]. + SecureMessageDelegate::UnwrapOptions unwrap_options; + unwrap_options.encryption_scheme = securemessage::AES_256_CBC; + unwrap_options.signature_scheme = securemessage::HMAC_SHA256; + unwrap_options.associated_data = context.responder_auth_message; + context.secure_message_delegate->UnwrapSecureMessage( + device_to_device_message.message(), context.persistent_symmetric_key, + unwrap_options, + base::Bind(&OnInnerMessageUnwrappedForInitiatorAuth, context)); +} + +} // namespace + +// static +void DeviceToDeviceResponderOperations::ValidateHelloMessage( + const std::string& hello_message, + const std::string& persistent_symmetric_key, + SecureMessageDelegate* secure_message_delegate, + const ValidateHelloCallback& callback) { + // The [Hello] message has the structure: + // { + // header: <session_public_key>, + // Sig(<session_public_key>, persistent_symmetric_key) + // payload: "" + // } + SecureMessageDelegate::UnwrapOptions unwrap_options; + unwrap_options.encryption_scheme = securemessage::NONE; + unwrap_options.signature_scheme = securemessage::HMAC_SHA256; + secure_message_delegate->UnwrapSecureMessage( + hello_message, persistent_symmetric_key, unwrap_options, + base::Bind(&OnHelloMessageUnwrapped, callback)); +} + +// static +void DeviceToDeviceResponderOperations::CreateResponderAuthMessage( + const std::string& hello_message, + const std::string& session_public_key, + const std::string& session_private_key, + const std::string& persistent_private_key, + const std::string& persistent_symmetric_key, + SecureMessageDelegate* secure_message_delegate, + const MessageCallback& callback) { + // The [Responder Auth] message has the structure: + // { + // header: <responder_public_key>, + // Sig(<responder_public_key> + payload1, + // session_symmetric_key), + // payload1: Enc({ + // header: Sig(payload2 + <hello_message>, persistent_symmetric_key), + // payload2: { + // sequence_number: 1, + // body: Enc({ + // header: Sig(payload3 + <hello_message>, + // persistent_responder_public_key), + // payload3: "" + // }, persistent_symmetric_key) + // } + // }, session_symmetric_key), + // } + CreateResponderAuthMessageContext context = {hello_message, + session_public_key, + session_private_key, + persistent_private_key, + persistent_symmetric_key, + secure_message_delegate, + callback}; + + // To create the [Responder Auth] message, we need to first parse the + // initiator's [Hello] message and extract the initiator's session public key. + DeviceToDeviceResponderOperations::ValidateHelloMessage( + hello_message, persistent_symmetric_key, secure_message_delegate, + base::Bind(&OnHelloMessageValidatedForResponderAuth, context)); +} + +// static +void DeviceToDeviceResponderOperations::ValidateInitiatorAuthMessage( + const std::string& initiator_auth_message, + const std::string& session_symmetric_key, + const std::string& persistent_symmetric_key, + const std::string& responder_auth_message, + SecureMessageDelegate* secure_message_delegate, + const ValidationCallback& callback) { + // The [Initiator Auth] message has the structure: + // { + // header: Sig(payload1, session_symmetric_key) + // payload1: Enc({ + // sequence_number: 2, + // body: { + // header: Sig(payload2 + responder_auth_message, + // persistent_symmetric_key) + // payload2: "" + // } + // }, session_symmetric_key) + // } + ValidateInitiatorAuthMessageContext context = { + persistent_symmetric_key, responder_auth_message, secure_message_delegate, + callback}; + + SecureMessageDelegate::UnwrapOptions unwrap_options; + unwrap_options.encryption_scheme = securemessage::AES_256_CBC; + unwrap_options.signature_scheme = securemessage::HMAC_SHA256; + secure_message_delegate->UnwrapSecureMessage( + initiator_auth_message, session_symmetric_key, unwrap_options, + base::Bind(&OnOuterMessageUnwrappedForInitiatorAuth, context)); +} + +} // proximity_auth diff --git a/components/proximity_auth/device_to_device_responder_operations.h b/components/proximity_auth/device_to_device_responder_operations.h new file mode 100644 index 0000000..6a3f1c9 --- /dev/null +++ b/components/proximity_auth/device_to_device_responder_operations.h @@ -0,0 +1,117 @@ +// 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. + +#ifndef COMPONENTS_PROXIMITY_AUTH_DEVICE_TO_DEVICE_RESPONDER_OPERATIONS_H +#define COMPONENTS_PROXIMITY_AUTH_DEVICE_TO_DEVICE_RESPONDER_OPERATIONS_H + +#include <string> + +#include "base/callback_forward.h" +#include "base/macros.h" + +namespace proximity_auth { + +class SecureMessageDelegate; + +// Utility class containing operations in the DeviceToDevice protocol that the +// initiator needs to perform. For Smart Lock, in which a phone unlocks a +// laptop, the responder is the phone. Because the responder side of this +// protocol does not run in Chrome, this class is implemented solely for +// testing purposes. +// +// All operations are asynchronous because we use the SecureMessageDelegate for +// crypto operations, whose implementation may be asynchronous. +// +// In the DeviceToDevice protocol, the responder parses two messages received +// from the initiator and sends one message: +// 1. Parse [Hello] Message +// This message contains the initiator's session public key and is signed +// by the long term symmetric key. +// 2. Send [Responder Auth] Message +// This message contains the responder's session public key, and allows the +// initiator to authenticate the responder. After both sides have each +// other's public keys, they can derive a symmetric key for the session. +// 3. Parse [Initiator Auth] Message +// This message allows the responder to authenticate the initiator. +class DeviceToDeviceResponderOperations { + public: + // Callback for operations that create a message. Invoked with the serialized + // SecureMessage upon success or the empty string upon failure. + typedef base::Callback<void(const std::string&)> MessageCallback; + + // Callback for operations that validates a message. + typedef base::Callback<void(bool)> ValidationCallback; + + // Callback for ValidateHelloMessage. The first argument will be called with + // the validation outcome. If validation succeeded, then the second argument + // will contain the initiator's public key. + typedef base::Callback<void(bool, const std::string&)> ValidateHelloCallback; + + // Validates that the [Hello] message, received from the initiator, + // is properly signed and encrypted. + // |hello_message|: The bytes of the [Hello] message to validate. + // |persistent_symmetric_key|: The long-term symmetric key that is shared by + // the initiator and responder. + // |secure_message_delegate|: Delegate for SecureMessage operations. This + // instance is not owned, and must live until after |callback| is invoked. + // |callback|: Invoked upon operation completion with whether + // |responder_auth_message| is validated successfully and the initiator's + // public key. + static void ValidateHelloMessage( + const std::string& hello_message, + const std::string& persistent_symmetric_key, + SecureMessageDelegate* secure_message_delegate, + const ValidateHelloCallback& callback); + + // Creates the [Responder Auth] message: + // |hello_message|: The initial [Hello] message that was sent, which is used + // in the signature calculation. + // |session_public_key|: This session public key will be stored in plaintext + // to be read by the initiator. + // |session_private_key|: The session private key is used in conjunction with + // the initiator's public key to derive the session symmetric key. + // |persistent_private_key|: The long-term private key possessed by the + // responder device. + // |persistent_symmetric_key|: The long-term symmetric key that is shared by + // the initiator and responder. + // |secure_message_delegate|: Delegate for SecureMessage operations. This + // instance is not owned, and must live until after |callback| is invoked. + // |callback|: Invoked upon operation completion with the serialized message + // or an empty string. + static void CreateResponderAuthMessage( + const std::string& hello_message, + const std::string& session_public_key, + const std::string& session_private_key, + const std::string& persistent_private_key, + const std::string& persistent_symmetric_key, + SecureMessageDelegate* secure_message_delegate, + const MessageCallback& callback); + + // Validates that the [Initiator Auth] message, received from the initiator, + // is properly signed and encrypted. + // |initiator_auth_message|: The bytes of the [Local Auth] message to + // validate. + // |session_symmetric_key|: The derived symmetric key used just for the + // session. + // |persistent_symmetric_key|: The long-term symmetric key that is shared by + // the initiator and responder. + // |secure_message_delegate|: Delegate for SecureMessage operations. This + // instance is not owned, and must live until after |callback| is invoked. + // |callback|: Invoked upon operation completion with whether + // |responder_auth_message| is validated successfully. + static void ValidateInitiatorAuthMessage( + const std::string& initiator_auth_message, + const std::string& session_symmetric_key, + const std::string& persistent_symmetric_key, + const std::string& responder_auth_message, + SecureMessageDelegate* secure_message_delegate, + const ValidationCallback& callback); + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(DeviceToDeviceResponderOperations); +}; + +} // proximity_auth + +#endif // COMPONENTS_PROXIMITY_AUTH_DEVICE_TO_DEVICE_RESPONDER_OPERATIONS_H |