1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/proximity_auth/device_to_device_initiator_operations.h"
8 #include "base/callback.h"
9 #include "components/proximity_auth/cryptauth/proto/cryptauth_api.pb.h"
10 #include "components/proximity_auth/cryptauth/proto/securemessage.pb.h"
11 #include "components/proximity_auth/cryptauth/secure_message_delegate.h"
12 #include "components/proximity_auth/logging/logging.h"
14 namespace proximity_auth
{
18 // TODO(tengs): Due to a bug with the ChromeOS secure message daemon, we cannot
19 // create SecureMessages with empty payloads. To workaround this bug, this value
20 // is put into the payload if it would otherwise be empty.
21 // See crbug.com/512894.
22 const char kPayloadFiller
[] = "\xae";
24 // The version to put in the GcmMetadata field.
25 const int kGcmMetadataVersion
= 1;
27 // Callback for DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage(),
28 // after the inner message is created.
29 void OnInnerMessageCreatedForInitiatorAuth(
30 const std::string
& session_symmetric_key
,
31 SecureMessageDelegate
* secure_message_delegate
,
32 const DeviceToDeviceInitiatorOperations::MessageCallback
& callback
,
33 const std::string
& inner_message
) {
34 if (inner_message
.empty()) {
35 PA_LOG(INFO
) << "Failed to create inner message for [Initiator Auth].";
36 callback
.Run(std::string());
40 cryptauth::GcmMetadata gcm_metadata
;
41 gcm_metadata
.set_type(cryptauth::DEVICE_TO_DEVICE_MESSAGE
);
42 gcm_metadata
.set_version(kGcmMetadataVersion
);
44 // Store the inner message inside a DeviceToDeviceMessage proto.
45 securemessage::DeviceToDeviceMessage device_to_device_message
;
46 device_to_device_message
.set_message(inner_message
);
47 device_to_device_message
.set_sequence_number(2);
49 // Create and return the outer message, which wraps the inner message.
50 SecureMessageDelegate::CreateOptions create_options
;
51 create_options
.encryption_scheme
= securemessage::AES_256_CBC
;
52 create_options
.signature_scheme
= securemessage::HMAC_SHA256
;
53 gcm_metadata
.SerializeToString(&create_options
.public_metadata
);
54 secure_message_delegate
->CreateSecureMessage(
55 device_to_device_message
.SerializeAsString(), session_symmetric_key
,
56 create_options
, callback
);
59 // Helper struct containing all the context needed to validate the
60 // [Responder Auth] message.
61 struct ValidateResponderAuthMessageContext
{
62 std::string responder_auth_message
;
63 std::string persistent_responder_public_key
;
64 std::string persistent_symmetric_key
;
65 std::string session_private_key
;
66 std::string hello_message
;
67 SecureMessageDelegate
* secure_message_delegate
;
68 DeviceToDeviceInitiatorOperations::ValidateResponderAuthCallback callback
;
69 std::string responder_session_public_key
;
70 std::string session_symmetric_key
;
73 // Forward declarations of functions used in the [Responder Auth] validation
74 // flow. These functions are declared in order in which they are called during
76 void BeginResponderAuthValidation(ValidateResponderAuthMessageContext context
);
77 void OnSessionSymmetricKeyDerived(ValidateResponderAuthMessageContext context
,
78 const std::string
& session_symmetric_key
);
79 void OnOuterMessageUnwrappedForResponderAuth(
80 const ValidateResponderAuthMessageContext
& context
,
82 const std::string
& payload
,
83 const securemessage::Header
& header
);
84 void OnMiddleMessageUnwrappedForResponderAuth(
85 const ValidateResponderAuthMessageContext
& context
,
87 const std::string
& payload
,
88 const securemessage::Header
& header
);
89 void OnInnerMessageUnwrappedForResponderAuth(
90 const ValidateResponderAuthMessageContext
& context
,
92 const std::string
& payload
,
93 const securemessage::Header
& header
);
95 // Begins the [Responder Auth] validation flow by validating the header.
96 void BeginResponderAuthValidation(ValidateResponderAuthMessageContext context
) {
97 // Parse the encrypted SecureMessage so we can get plaintext data from the
98 // header. Note that the payload will be encrypted.
99 securemessage::SecureMessage encrypted_message
;
100 securemessage::HeaderAndBody header_and_body
;
101 if (!encrypted_message
.ParseFromString(context
.responder_auth_message
) ||
102 !header_and_body
.ParseFromString(encrypted_message
.header_and_body())) {
103 PA_LOG(WARNING
) << "Failed to parse [Responder Hello] message";
104 context
.callback
.Run(false, std::string());
108 // Check that header public_metadata contains the correct metadata fields.
109 securemessage::Header header
= header_and_body
.header();
110 cryptauth::GcmMetadata gcm_metadata
;
111 if (!gcm_metadata
.ParseFromString(header
.public_metadata()) ||
112 gcm_metadata
.type() !=
113 cryptauth::DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD
||
114 gcm_metadata
.version() != kGcmMetadataVersion
) {
115 PA_LOG(WARNING
) << "Failed to validate GcmMetadata in "
116 << "[Responder Auth] header.";
117 context
.callback
.Run(false, std::string());
121 // Extract responder session public key from |decryption_key_id| field.
122 securemessage::ResponderHello responder_hello
;
123 if (!responder_hello
.ParseFromString(header
.decryption_key_id()) ||
124 !responder_hello
.public_dh_key().SerializeToString(
125 &context
.responder_session_public_key
)) {
126 PA_LOG(INFO
) << "Failed to extract responder session public key in "
127 << "[Responder Auth] header.";
128 context
.callback
.Run(false, std::string());
132 // Perform a Diffie-Helmann key exchange to get the session symmetric key.
133 context
.secure_message_delegate
->DeriveKey(
134 context
.session_private_key
, context
.responder_session_public_key
,
135 base::Bind(&OnSessionSymmetricKeyDerived
, context
));
138 // Called after the session symmetric key is derived, so now we can unwrap the
139 // outer message of [Responder Auth].
140 void OnSessionSymmetricKeyDerived(ValidateResponderAuthMessageContext context
,
141 const std::string
& session_symmetric_key
) {
142 context
.session_symmetric_key
= session_symmetric_key
;
144 // Unwrap the outer message, using symmetric key encryption and signature.
145 SecureMessageDelegate::UnwrapOptions unwrap_options
;
146 unwrap_options
.encryption_scheme
= securemessage::AES_256_CBC
;
147 unwrap_options
.signature_scheme
= securemessage::HMAC_SHA256
;
148 context
.secure_message_delegate
->UnwrapSecureMessage(
149 context
.responder_auth_message
, session_symmetric_key
, unwrap_options
,
150 base::Bind(&OnOuterMessageUnwrappedForResponderAuth
, context
));
153 // Called after the outer-most layer of [Responder Auth] is unwrapped.
154 void OnOuterMessageUnwrappedForResponderAuth(
155 const ValidateResponderAuthMessageContext
& context
,
157 const std::string
& payload
,
158 const securemessage::Header
& header
) {
160 PA_LOG(INFO
) << "Failed to unwrap outer [Responder Auth] message.";
161 context
.callback
.Run(false, std::string());
165 // Parse the decrypted payload.
166 securemessage::DeviceToDeviceMessage device_to_device_message
;
167 if (!device_to_device_message
.ParseFromString(payload
) ||
168 device_to_device_message
.sequence_number() != 1) {
169 PA_LOG(INFO
) << "Failed to validate DeviceToDeviceMessage payload.";
170 context
.callback
.Run(false, std::string());
174 // Unwrap the middle level SecureMessage, using symmetric key encryption and
176 SecureMessageDelegate::UnwrapOptions unwrap_options
;
177 unwrap_options
.encryption_scheme
= securemessage::AES_256_CBC
;
178 unwrap_options
.signature_scheme
= securemessage::HMAC_SHA256
;
179 unwrap_options
.associated_data
= context
.hello_message
;
180 context
.secure_message_delegate
->UnwrapSecureMessage(
181 device_to_device_message
.message(), context
.persistent_symmetric_key
,
183 base::Bind(&OnMiddleMessageUnwrappedForResponderAuth
, context
));
186 // Called after the middle layer of [Responder Auth] is unwrapped.
187 void OnMiddleMessageUnwrappedForResponderAuth(
188 const ValidateResponderAuthMessageContext
& context
,
190 const std::string
& payload
,
191 const securemessage::Header
& header
) {
193 PA_LOG(INFO
) << "Failed to unwrap middle [Responder Auth] message.";
194 context
.callback
.Run(false, std::string());
198 // Unwrap the inner-most SecureMessage, using no encryption and an asymmetric
200 SecureMessageDelegate::UnwrapOptions unwrap_options
;
201 unwrap_options
.encryption_scheme
= securemessage::NONE
;
202 unwrap_options
.signature_scheme
= securemessage::ECDSA_P256_SHA256
;
203 unwrap_options
.associated_data
= context
.hello_message
;
204 context
.secure_message_delegate
->UnwrapSecureMessage(
205 payload
, context
.persistent_responder_public_key
, unwrap_options
,
206 base::Bind(&OnInnerMessageUnwrappedForResponderAuth
, context
));
209 // Called after the inner-most layer of [Responder Auth] is unwrapped.
210 void OnInnerMessageUnwrappedForResponderAuth(
211 const ValidateResponderAuthMessageContext
& context
,
213 const std::string
& payload
,
214 const securemessage::Header
& header
) {
216 PA_LOG(INFO
) << "Failed to unwrap inner [Responder Auth] message.";
218 // Note: The GMS Core implementation does not properly set the metadata
219 // version, so we only check that the type is UNLOCK_KEY_SIGNED_CHALLENGE.
220 cryptauth::GcmMetadata gcm_metadata
;
221 if (!gcm_metadata
.ParseFromString(header
.public_metadata()) ||
222 gcm_metadata
.type() != cryptauth::UNLOCK_KEY_SIGNED_CHALLENGE
) {
223 PA_LOG(WARNING
) << "Failed to validate GcmMetadata in inner-most "
224 << "[Responder Auth] message.";
225 context
.callback
.Run(false, std::string());
229 context
.callback
.Run(verified
, context
.session_symmetric_key
);
235 void DeviceToDeviceInitiatorOperations::CreateHelloMessage(
236 const std::string
& session_public_key
,
237 const std::string
& persistent_symmetric_key
,
238 SecureMessageDelegate
* secure_message_delegate
,
239 const MessageCallback
& callback
) {
240 // Decode public key into the |initator_hello| proto.
241 securemessage::InitiatorHello initator_hello
;
242 if (!initator_hello
.mutable_public_dh_key()->ParseFromString(
243 session_public_key
)) {
244 PA_LOG(ERROR
) << "Unable to parse user's public key";
245 callback
.Run(std::string());
249 // The [Hello] message has the structure:
251 // header: <session_public_key>,
252 // Sig(<session_public_key>, persistent_symmetric_key)
255 SecureMessageDelegate::CreateOptions create_options
;
256 create_options
.encryption_scheme
= securemessage::NONE
;
257 create_options
.signature_scheme
= securemessage::HMAC_SHA256
;
258 initator_hello
.SerializeToString(&create_options
.public_metadata
);
259 secure_message_delegate
->CreateSecureMessage(
260 kPayloadFiller
, persistent_symmetric_key
, create_options
, callback
);
264 void DeviceToDeviceInitiatorOperations::ValidateResponderAuthMessage(
265 const std::string
& responder_auth_message
,
266 const std::string
& persistent_responder_public_key
,
267 const std::string
& persistent_symmetric_key
,
268 const std::string
& session_private_key
,
269 const std::string
& hello_message
,
270 SecureMessageDelegate
* secure_message_delegate
,
271 const ValidateResponderAuthCallback
& callback
) {
272 // The [Responder Auth] message has the structure:
274 // header: <responder_public_key>,
275 // Sig(<responder_public_key> + payload1,
276 // session_symmetric_key),
278 // header: Sig(payload2 + <hello_message>, persistent_symmetric_key),
280 // sequence_number: 1,
282 // header: Sig(payload3 + <hello_message>,
283 // persistent_responder_public_key),
285 // }, persistent_symmetric_key)
287 // }, session_symmetric_key),
289 struct ValidateResponderAuthMessageContext context
= {
290 responder_auth_message
,
291 persistent_responder_public_key
,
292 persistent_symmetric_key
,
295 secure_message_delegate
,
297 BeginResponderAuthValidation(context
);
301 void DeviceToDeviceInitiatorOperations::CreateInitiatorAuthMessage(
302 const std::string
& session_symmetric_key
,
303 const std::string
& persistent_symmetric_key
,
304 const std::string
& responder_auth_message
,
305 SecureMessageDelegate
* secure_message_delegate
,
306 const MessageCallback
& callback
) {
307 // The [Initiator Auth] message has the structure:
309 // header: Sig(payload1, session_symmetric_key)
311 // sequence_number: 2,
313 // header: Sig(payload2 + responder_auth_message,
314 // persistent_symmetric_key)
317 // }, session_symmetric_key)
319 SecureMessageDelegate::CreateOptions create_options
;
320 create_options
.encryption_scheme
= securemessage::AES_256_CBC
;
321 create_options
.signature_scheme
= securemessage::HMAC_SHA256
;
322 create_options
.associated_data
= responder_auth_message
;
323 secure_message_delegate
->CreateSecureMessage(
324 kPayloadFiller
, persistent_symmetric_key
, create_options
,
325 base::Bind(&OnInnerMessageCreatedForInitiatorAuth
, session_symmetric_key
,
326 secure_message_delegate
, callback
));