// 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: , // Sig(, 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: , // Sig( + payload1, // session_symmetric_key), // payload1: Enc({ // header: Sig(payload2 + , persistent_symmetric_key), // payload2: { // sequence_number: 1, // body: Enc({ // header: Sig(payload3 + , // 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