| // 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/cryptauth/device_to_device_responder_operations.h" | 
 |  | 
 | #include "base/bind.h" | 
 | #include "base/callback.h" | 
 | #include "components/cryptauth/proto/cryptauth_api.pb.h" | 
 | #include "components/cryptauth/proto/securemessage.pb.h" | 
 | #include "components/cryptauth/secure_message_delegate.h" | 
 | #include "components/proximity_auth/logging/logging.h" | 
 |  | 
 | namespace cryptauth { | 
 |  | 
 | 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]. | 
 |   GcmMetadata gcm_metadata; | 
 |   gcm_metadata.set_type(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; | 
 |   } | 
 |  | 
 |   GcmMetadata gcm_metadata; | 
 |   gcm_metadata.set_type(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)); | 
 | } | 
 |  | 
 | }  // cryptauth |