1 // Copyright 2014 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/wire_message.h"
7 #include "base/json/json_reader.h"
8 #include "base/logging.h"
9 #include "base/macros.h"
10 #include "base/values.h"
11 #include "components/proximity_auth/base64url.h"
13 // The wire messages have a simple format:
14 // [ message version ] [ body length ] [ JSON body ]
15 // 1 byte 2 bytes body length
16 // The JSON body contains two fields: an optional permit_id field and a required
19 namespace proximity_auth
{
22 // The length of the message header, in bytes.
23 const size_t kHeaderLength
= 3;
25 // The protocol version of the message format.
26 const int kExpectedMessageFormatVersion
= 3;
28 const char kPayloadKey
[] = "payload";
29 const char kPermitIdKey
[] = "permit_id";
31 // Parses the |serialized_message|'s header. Returns |true| iff the message has
32 // a valid header, is complete, and is well-formed according to the header. Sets
33 // |is_incomplete_message| to true iff the message does not have enough data to
34 // parse the header, or if the message length encoded in the message header
35 // exceeds the size of the |serialized_message|.
36 bool ParseHeader(const std::string
& serialized_message
,
37 bool* is_incomplete_message
) {
38 *is_incomplete_message
= false;
39 if (serialized_message
.size() < kHeaderLength
) {
40 *is_incomplete_message
= true;
44 static_assert(kHeaderLength
> 2, "kHeaderLength too small");
45 size_t version
= serialized_message
[0];
46 if (version
!= kExpectedMessageFormatVersion
) {
47 VLOG(1) << "Error: Invalid message version. Got " << version
48 << ", expected " << kExpectedMessageFormatVersion
;
52 size_t expected_body_length
=
53 (static_cast<size_t>(serialized_message
[1]) << 8) |
54 (static_cast<size_t>(serialized_message
[2]) << 0);
55 size_t expected_message_length
= kHeaderLength
+ expected_body_length
;
56 if (serialized_message
.size() < expected_message_length
) {
57 *is_incomplete_message
= true;
60 if (serialized_message
.size() != expected_message_length
) {
61 VLOG(1) << "Error: Invalid message length. Got "
62 << serialized_message
.size() << ", expected "
63 << expected_message_length
;
72 WireMessage::~WireMessage() {
76 scoped_ptr
<WireMessage
> WireMessage::Deserialize(
77 const std::string
& serialized_message
,
78 bool* is_incomplete_message
) {
79 if (!ParseHeader(serialized_message
, is_incomplete_message
))
80 return scoped_ptr
<WireMessage
>();
82 scoped_ptr
<base::Value
> body_value(
83 base::JSONReader::Read(serialized_message
.substr(kHeaderLength
)));
84 if (!body_value
|| !body_value
->IsType(base::Value::TYPE_DICTIONARY
)) {
85 VLOG(1) << "Error: Unable to parse message as JSON.";
86 return scoped_ptr
<WireMessage
>();
89 base::DictionaryValue
* body
;
90 bool success
= body_value
->GetAsDictionary(&body
);
93 // The permit ID is optional. In the Easy Unlock protocol, only the first
94 // message includes this field.
95 std::string permit_id
;
96 body
->GetString(kPermitIdKey
, &permit_id
);
98 std::string payload_base64
;
99 if (!body
->GetString(kPayloadKey
, &payload_base64
) ||
100 payload_base64
.empty()) {
101 VLOG(1) << "Error: Missing payload.";
102 return scoped_ptr
<WireMessage
>();
106 if (!Base64UrlDecode(payload_base64
, &payload
)) {
107 VLOG(1) << "Error: Invalid base64 encoding for payload.";
108 return scoped_ptr
<WireMessage
>();
111 return scoped_ptr
<WireMessage
>(new WireMessage(permit_id
, payload
));
114 std::string
WireMessage::Serialize() const {
115 // TODO(isherman): Implement.
116 return "This method is not yet implemented.";
119 WireMessage::WireMessage(const std::string
& permit_id
,
120 const std::string
& payload
)
121 : permit_id_(permit_id
), payload_(payload
) {
124 } // namespace proximity_auth