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_responder_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 DeviceToDeviceResponderOperations::ValidateHelloMessage(),
28 // after the [Hello] message is unwrapped.
29 void OnHelloMessageUnwrapped(
30 const DeviceToDeviceResponderOperations::ValidateHelloCallback
& callback
,
32 const std::string
& payload
,
33 const securemessage::Header
& header
) {
34 securemessage::InitiatorHello initiator_hello
;
35 if (!verified
|| !initiator_hello
.ParseFromString(header
.public_metadata())) {
36 callback
.Run(false, std::string());
40 callback
.Run(true, initiator_hello
.public_dh_key().SerializeAsString());
43 // Helper struct containing all the context needed to create the [Responder
45 struct CreateResponderAuthMessageContext
{
46 std::string hello_message
;
47 std::string session_public_key
;
48 std::string session_private_key
;
49 std::string persistent_private_key
;
50 std::string persistent_symmetric_key
;
51 SecureMessageDelegate
* secure_message_delegate
;
52 DeviceToDeviceResponderOperations::MessageCallback callback
;
53 std::string hello_public_key
;
54 std::string middle_message
;
57 // Forward declarations of functions used to create the [Responder Auth]
58 // message, declared in order in which they are called during the creation flow.
59 void OnHelloMessageValidatedForResponderAuth(
60 CreateResponderAuthMessageContext context
,
61 bool hello_message_validated
,
62 const std::string
& hello_public_key
);
63 void OnInnerMessageCreatedForResponderAuth(
64 CreateResponderAuthMessageContext context
,
65 const std::string
& inner_message
);
66 void OnMiddleMessageCreatedForResponderAuth(
67 CreateResponderAuthMessageContext context
,
68 const std::string
& middle_message
);
69 void OnSessionSymmetricKeyDerivedForResponderAuth(
70 CreateResponderAuthMessageContext context
,
71 const std::string
& session_symmetric_key
);
73 // Called after the initiator's [Hello] message is unwrapped.
74 void OnHelloMessageValidatedForResponderAuth(
75 CreateResponderAuthMessageContext context
,
76 bool hello_message_validated
,
77 const std::string
& hello_public_key
) {
78 if (!hello_message_validated
) {
79 PA_LOG(INFO
) << "Invalid [Hello] while creating [Responder Auth]";
80 context
.callback
.Run(std::string());
84 context
.hello_public_key
= hello_public_key
;
86 // Create the inner most wrapped message of [Responder Auth].
87 cryptauth::GcmMetadata gcm_metadata
;
88 gcm_metadata
.set_type(cryptauth::UNLOCK_KEY_SIGNED_CHALLENGE
);
89 gcm_metadata
.set_version(kGcmMetadataVersion
);
91 SecureMessageDelegate::CreateOptions create_options
;
92 create_options
.encryption_scheme
= securemessage::NONE
;
93 create_options
.signature_scheme
= securemessage::ECDSA_P256_SHA256
;
94 gcm_metadata
.SerializeToString(&create_options
.public_metadata
);
95 create_options
.associated_data
= context
.hello_message
;
97 context
.secure_message_delegate
->CreateSecureMessage(
98 kPayloadFiller
, context
.persistent_private_key
, create_options
,
99 base::Bind(&OnInnerMessageCreatedForResponderAuth
, context
));
102 // Called after the inner-most layer of [Responder Auth] is created.
103 void OnInnerMessageCreatedForResponderAuth(
104 CreateResponderAuthMessageContext context
,
105 const std::string
& inner_message
) {
106 if (inner_message
.empty()) {
107 PA_LOG(INFO
) << "Failed to create middle message for [Responder Auth]";
108 context
.callback
.Run(std::string());
112 // Create the middle message.
113 SecureMessageDelegate::CreateOptions create_options
;
114 create_options
.encryption_scheme
= securemessage::AES_256_CBC
;
115 create_options
.signature_scheme
= securemessage::HMAC_SHA256
;
116 create_options
.associated_data
= context
.hello_message
;
117 context
.secure_message_delegate
->CreateSecureMessage(
118 inner_message
, context
.persistent_symmetric_key
, create_options
,
119 base::Bind(&OnMiddleMessageCreatedForResponderAuth
, context
));
122 // Called after the middle layer of [Responder Auth] is created.
123 void OnMiddleMessageCreatedForResponderAuth(
124 CreateResponderAuthMessageContext context
,
125 const std::string
& middle_message
) {
126 if (middle_message
.empty()) {
127 PA_LOG(ERROR
) << "Error inner message while creating [Responder Auth]";
128 context
.callback
.Run(std::string());
132 // Before we can create the outer-most message layer, we need to perform a key
133 // agreement for the session symmetric key.
134 context
.middle_message
= middle_message
;
135 context
.secure_message_delegate
->DeriveKey(
136 context
.session_private_key
, context
.hello_public_key
,
137 base::Bind(&OnSessionSymmetricKeyDerivedForResponderAuth
, context
));
140 // Called after the session symmetric key is derived, so we can create the outer
141 // most layer of [Responder Auth].
142 void OnSessionSymmetricKeyDerivedForResponderAuth(
143 CreateResponderAuthMessageContext context
,
144 const std::string
& session_symmetric_key
) {
145 if (session_symmetric_key
.empty()) {
146 PA_LOG(ERROR
) << "Error inner message while creating [Responder Auth]";
147 context
.callback
.Run(std::string());
151 cryptauth::GcmMetadata gcm_metadata
;
152 gcm_metadata
.set_type(cryptauth::DEVICE_TO_DEVICE_RESPONDER_HELLO_PAYLOAD
);
153 gcm_metadata
.set_version(kGcmMetadataVersion
);
155 // Store the responder's session public key in plaintext in the header.
156 securemessage::ResponderHello responder_hello
;
157 if (!responder_hello
.mutable_public_dh_key()->ParseFromString(
158 context
.session_public_key
)) {
159 PA_LOG(ERROR
) << "Error parsing public key while creating [Responder Auth]";
160 PA_LOG(ERROR
) << context
.session_public_key
;
161 context
.callback
.Run(std::string());
165 // Create the outer most message, wrapping the other messages created
167 securemessage::DeviceToDeviceMessage device_to_device_message
;
168 device_to_device_message
.set_message(context
.middle_message
);
169 device_to_device_message
.set_sequence_number(1);
171 SecureMessageDelegate::CreateOptions create_options
;
172 create_options
.encryption_scheme
= securemessage::AES_256_CBC
;
173 create_options
.signature_scheme
= securemessage::HMAC_SHA256
;
174 create_options
.public_metadata
= gcm_metadata
.SerializeAsString();
175 responder_hello
.SerializeToString(&create_options
.decryption_key_id
);
177 context
.secure_message_delegate
->CreateSecureMessage(
178 device_to_device_message
.SerializeAsString(), session_symmetric_key
,
179 create_options
, context
.callback
);
182 // Helper struct containing all the context needed to validate the [Initiator
184 struct ValidateInitiatorAuthMessageContext
{
185 std::string persistent_symmetric_key
;
186 std::string responder_auth_message
;
187 SecureMessageDelegate
* secure_message_delegate
;
188 DeviceToDeviceResponderOperations::ValidationCallback callback
;
191 // Called after the inner-most layer of [Initiator Auth] is unwrapped.
192 void OnInnerMessageUnwrappedForInitiatorAuth(
193 const ValidateInitiatorAuthMessageContext
& context
,
195 const std::string
& payload
,
196 const securemessage::Header
& header
) {
198 PA_LOG(INFO
) << "Failed to inner [Initiator Auth] message.";
199 context
.callback
.Run(verified
);
202 // Called after the outer-most layer of [Initiator Auth] is unwrapped.
203 void OnOuterMessageUnwrappedForInitiatorAuth(
204 const ValidateInitiatorAuthMessageContext
& context
,
206 const std::string
& payload
,
207 const securemessage::Header
& header
) {
209 PA_LOG(INFO
) << "Failed to verify outer [Initiator Auth] message";
210 context
.callback
.Run(false);
214 // Parse the decrypted payload.
215 securemessage::DeviceToDeviceMessage device_to_device_message
;
216 if (!device_to_device_message
.ParseFromString(payload
) ||
217 device_to_device_message
.sequence_number() != 2) {
218 PA_LOG(INFO
) << "Failed to validate DeviceToDeviceMessage payload.";
219 context
.callback
.Run(false);
223 // Unwrap the inner message of [Initiator Auth].
224 SecureMessageDelegate::UnwrapOptions unwrap_options
;
225 unwrap_options
.encryption_scheme
= securemessage::AES_256_CBC
;
226 unwrap_options
.signature_scheme
= securemessage::HMAC_SHA256
;
227 unwrap_options
.associated_data
= context
.responder_auth_message
;
228 context
.secure_message_delegate
->UnwrapSecureMessage(
229 device_to_device_message
.message(), context
.persistent_symmetric_key
,
231 base::Bind(&OnInnerMessageUnwrappedForInitiatorAuth
, context
));
237 void DeviceToDeviceResponderOperations::ValidateHelloMessage(
238 const std::string
& hello_message
,
239 const std::string
& persistent_symmetric_key
,
240 SecureMessageDelegate
* secure_message_delegate
,
241 const ValidateHelloCallback
& callback
) {
242 // The [Hello] message has the structure:
244 // header: <session_public_key>,
245 // Sig(<session_public_key>, persistent_symmetric_key)
248 SecureMessageDelegate::UnwrapOptions unwrap_options
;
249 unwrap_options
.encryption_scheme
= securemessage::NONE
;
250 unwrap_options
.signature_scheme
= securemessage::HMAC_SHA256
;
251 secure_message_delegate
->UnwrapSecureMessage(
252 hello_message
, persistent_symmetric_key
, unwrap_options
,
253 base::Bind(&OnHelloMessageUnwrapped
, callback
));
257 void DeviceToDeviceResponderOperations::CreateResponderAuthMessage(
258 const std::string
& hello_message
,
259 const std::string
& session_public_key
,
260 const std::string
& session_private_key
,
261 const std::string
& persistent_private_key
,
262 const std::string
& persistent_symmetric_key
,
263 SecureMessageDelegate
* secure_message_delegate
,
264 const MessageCallback
& callback
) {
265 // The [Responder Auth] message has the structure:
267 // header: <responder_public_key>,
268 // Sig(<responder_public_key> + payload1,
269 // session_symmetric_key),
271 // header: Sig(payload2 + <hello_message>, persistent_symmetric_key),
273 // sequence_number: 1,
275 // header: Sig(payload3 + <hello_message>,
276 // persistent_responder_public_key),
278 // }, persistent_symmetric_key)
280 // }, session_symmetric_key),
282 CreateResponderAuthMessageContext context
= {hello_message
,
285 persistent_private_key
,
286 persistent_symmetric_key
,
287 secure_message_delegate
,
290 // To create the [Responder Auth] message, we need to first parse the
291 // initiator's [Hello] message and extract the initiator's session public key.
292 DeviceToDeviceResponderOperations::ValidateHelloMessage(
293 hello_message
, persistent_symmetric_key
, secure_message_delegate
,
294 base::Bind(&OnHelloMessageValidatedForResponderAuth
, context
));
298 void DeviceToDeviceResponderOperations::ValidateInitiatorAuthMessage(
299 const std::string
& initiator_auth_message
,
300 const std::string
& session_symmetric_key
,
301 const std::string
& persistent_symmetric_key
,
302 const std::string
& responder_auth_message
,
303 SecureMessageDelegate
* secure_message_delegate
,
304 const ValidationCallback
& callback
) {
305 // The [Initiator Auth] message has the structure:
307 // header: Sig(payload1, session_symmetric_key)
309 // sequence_number: 2,
311 // header: Sig(payload2 + responder_auth_message,
312 // persistent_symmetric_key)
315 // }, session_symmetric_key)
317 ValidateInitiatorAuthMessageContext context
= {
318 persistent_symmetric_key
, responder_auth_message
, secure_message_delegate
,
321 SecureMessageDelegate::UnwrapOptions unwrap_options
;
322 unwrap_options
.encryption_scheme
= securemessage::AES_256_CBC
;
323 unwrap_options
.signature_scheme
= securemessage::HMAC_SHA256
;
324 secure_message_delegate
->UnwrapSecureMessage(
325 initiator_auth_message
, session_symmetric_key
, unwrap_options
,
326 base::Bind(&OnOuterMessageUnwrappedForInitiatorAuth
, context
));