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.
4
5#include "components/proximity_auth/wire_message.h"
6
7#include "base/base64.h"
8#include "base/json/json_reader.h"
9#include "base/logging.h"
10#include "base/macros.h"
11#include "base/values.h"
12
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
17// data field.
18
19namespace proximity_auth {
20namespace {
21
22// The length of the message header, in bytes.
23const size_t kHeaderLength = 3;
24
25// The protocol version of the message format.
26const int kExpectedMessageFormatVersion = 3;
27
28const char kPayloadKey[] = "payload";
29const char kPermitIdKey[] = "permit_id";
30
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|.
36bool 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;
41    return false;
42  }
43
44  COMPILE_ASSERT(kHeaderLength > 2, header_length_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;
49    return false;
50  }
51
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;
58    return false;
59  }
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;
64    return false;
65  }
66
67  return true;
68}
69
70}  // namespace
71
72WireMessage::~WireMessage() {
73}
74
75// static
76scoped_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>();
81
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>();
87  }
88
89  base::DictionaryValue* body;
90  bool success = body_value->GetAsDictionary(&body);
91  DCHECK(success);
92
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);
97
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>();
103  }
104
105  std::string payload;
106  if (!base::Base64Decode(payload_base64, &payload)) {
107    VLOG(1) << "Error: Invalid base64 encoding for payload.";
108    return scoped_ptr<WireMessage>();
109  }
110
111  return scoped_ptr<WireMessage>(new WireMessage(permit_id, payload));
112}
113
114std::string WireMessage::Serialize() const {
115  // TODO(isherman): Implement.
116  return "This method is not yet implemented.";
117}
118
119WireMessage::WireMessage(const std::string& permit_id,
120                         const std::string& payload)
121    : permit_id_(permit_id), payload_(payload) {
122}
123
124}  // namespace proximity_auth
125