Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / components / proximity_auth / wire_message.cc
blob8ee5deb55fe53df6eb02d19f8277ae97ab15280f
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 <limits>
9 #include "base/json/json_reader.h"
10 #include "base/json/json_writer.h"
11 #include "base/macros.h"
12 #include "base/values.h"
13 #include "components/proximity_auth/cryptauth/base64url.h"
14 #include "components/proximity_auth/logging/logging.h"
16 // The wire messages have a simple format:
17 // [ message version ] [ body length ] [ JSON body ]
18 // 1 byte 2 bytes body length
19 // The JSON body contains two fields: an optional permit_id field and a required
20 // data field.
22 namespace proximity_auth {
23 namespace {
25 // The length of the message header, in bytes.
26 const size_t kHeaderLength = 3;
28 // The protocol version of the message format.
29 const int kMessageFormatVersionThree = 3;
31 const char kPayloadKey[] = "payload";
32 const char kPermitIdKey[] = "permit_id";
34 // Parses the |serialized_message|'s header. Returns |true| iff the message has
35 // a valid header, is complete, and is well-formed according to the header. Sets
36 // |is_incomplete_message| to true iff the message does not have enough data to
37 // parse the header, or if the message length encoded in the message header
38 // exceeds the size of the |serialized_message|.
39 bool ParseHeader(const std::string& serialized_message,
40 bool* is_incomplete_message) {
41 *is_incomplete_message = false;
42 if (serialized_message.size() < kHeaderLength) {
43 *is_incomplete_message = true;
44 return false;
47 static_assert(kHeaderLength > 2, "kHeaderLength too small");
48 size_t version = serialized_message[0];
49 if (version != kMessageFormatVersionThree) {
50 PA_LOG(WARNING) << "Error: Invalid message version. Got " << version
51 << ", expected " << kMessageFormatVersionThree;
52 return false;
55 uint16_t expected_body_length =
56 (static_cast<uint8_t>(serialized_message[1]) << 8) |
57 (static_cast<uint8_t>(serialized_message[2]) << 0);
58 size_t expected_message_length = kHeaderLength + expected_body_length;
59 if (serialized_message.size() < expected_message_length) {
60 *is_incomplete_message = true;
61 return false;
63 if (serialized_message.size() != expected_message_length) {
64 PA_LOG(WARNING) << "Error: Invalid message length. Got "
65 << serialized_message.size() << ", expected "
66 << expected_message_length;
67 return false;
70 return true;
73 } // namespace
75 WireMessage::~WireMessage() {
78 // static
79 scoped_ptr<WireMessage> WireMessage::Deserialize(
80 const std::string& serialized_message,
81 bool* is_incomplete_message) {
82 if (!ParseHeader(serialized_message, is_incomplete_message))
83 return scoped_ptr<WireMessage>();
85 scoped_ptr<base::Value> body_value =
86 base::JSONReader::Read(serialized_message.substr(kHeaderLength));
87 if (!body_value || !body_value->IsType(base::Value::TYPE_DICTIONARY)) {
88 PA_LOG(WARNING) << "Error: Unable to parse message as JSON.";
89 return scoped_ptr<WireMessage>();
92 base::DictionaryValue* body;
93 bool success = body_value->GetAsDictionary(&body);
94 DCHECK(success);
96 // The permit ID is optional. In the Easy Unlock protocol, only the first
97 // message includes this field.
98 std::string permit_id;
99 body->GetString(kPermitIdKey, &permit_id);
101 std::string payload_base64;
102 if (!body->GetString(kPayloadKey, &payload_base64) ||
103 payload_base64.empty()) {
104 PA_LOG(WARNING) << "Error: Missing payload.";
105 return scoped_ptr<WireMessage>();
108 std::string payload;
109 if (!Base64UrlDecode(payload_base64, &payload)) {
110 PA_LOG(WARNING) << "Error: Invalid base64 encoding for payload.";
111 return scoped_ptr<WireMessage>();
114 return make_scoped_ptr(new WireMessage(payload, permit_id));
117 std::string WireMessage::Serialize() const {
118 if (payload_.empty()) {
119 PA_LOG(ERROR) << "Failed to serialize empty wire message.";
120 return std::string();
123 // Create JSON body containing permit id and payload.
124 base::DictionaryValue body;
125 if (!permit_id_.empty())
126 body.SetString(kPermitIdKey, permit_id_);
128 std::string base64_payload;
129 Base64UrlEncode(payload_, &base64_payload);
130 body.SetString(kPayloadKey, base64_payload);
132 std::string json_body;
133 if (!base::JSONWriter::Write(body, &json_body)) {
134 PA_LOG(ERROR) << "Failed to convert WireMessage body to JSON: " << body;
135 return std::string();
138 // Create header containing version and payload size.
139 size_t body_size = json_body.size();
140 if (body_size > std::numeric_limits<uint16_t>::max()) {
141 PA_LOG(ERROR) << "Can not create WireMessage because body size exceeds "
142 << "16-bit unsigned integer: " << body_size;
143 return std::string();
146 uint8_t header[] = {
147 static_cast<uint8_t>(kMessageFormatVersionThree),
148 static_cast<uint8_t>((body_size >> 8) & 0xFF),
149 static_cast<uint8_t>(body_size & 0xFF),
151 static_assert(sizeof(header) == kHeaderLength, "Malformed header.");
153 std::string header_string(kHeaderLength, 0);
154 std::memcpy(&header_string[0], header, kHeaderLength);
155 return header_string + json_body;
158 WireMessage::WireMessage(const std::string& payload)
159 : WireMessage(payload, std::string()) {}
161 WireMessage::WireMessage(const std::string& payload,
162 const std::string& permit_id)
163 : payload_(payload), permit_id_(permit_id) {}
165 } // namespace proximity_auth