summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorisherman <isherman@chromium.org>2014-09-15 17:21:03 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-16 00:22:57 +0000
commit3ca088e1bf653a570216e7c092657acfc36cdb4a (patch)
tree51c65d1de4771c9d241a77bae62905e2075d9ae9
parent4c02231133fdd769143debb286620505a3456ba0 (diff)
downloadchromium_src-3ca088e1bf653a570216e7c092657acfc36cdb4a.zip
chromium_src-3ca088e1bf653a570216e7c092657acfc36cdb4a.tar.gz
chromium_src-3ca088e1bf653a570216e7c092657acfc36cdb4a.tar.bz2
[EasyUnlock] Port PermitMessage class to native code.
BUG=412882 TEST=components_unittests R=tengs@chromium.org Review URL: https://codereview.chromium.org/562763004 Cr-Commit-Position: refs/heads/master@{#294939}
-rw-r--r--components/components_tests.gyp1
-rw-r--r--components/proximity_auth/BUILD.gn1
-rw-r--r--components/proximity_auth/connection.cc31
-rw-r--r--components/proximity_auth/connection.h12
-rw-r--r--components/proximity_auth/connection_unittest.cc30
-rw-r--r--components/proximity_auth/wire_message.cc111
-rw-r--r--components/proximity_auth/wire_message.h24
-rw-r--r--components/proximity_auth/wire_message_unittest.cc169
8 files changed, 327 insertions, 52 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp
index 837921e..e4204d8 100644
--- a/components/components_tests.gyp
+++ b/components/components_tests.gyp
@@ -180,6 +180,7 @@
'precache/core/precache_url_table_unittest.cc',
'proximity_auth/connection_unittest.cc',
'proximity_auth/proximity_auth_system_unittest.cc',
+ 'proximity_auth/wire_message_unittest.cc',
'query_parser/query_parser_unittest.cc',
'query_parser/snippet_unittest.cc',
'rappor/bloom_filter_unittest.cc',
diff --git a/components/proximity_auth/BUILD.gn b/components/proximity_auth/BUILD.gn
index 1f25402..01d4efc2 100644
--- a/components/proximity_auth/BUILD.gn
+++ b/components/proximity_auth/BUILD.gn
@@ -24,6 +24,7 @@ source_set("unit_tests") {
sources = [
"connection_unittest.cc",
"proximity_auth_system_unittest.cc",
+ "wire_message_unittest.cc",
]
deps = [
diff --git a/components/proximity_auth/connection.cc b/components/proximity_auth/connection.cc
index 5a35e4d..65f6dd6 100644
--- a/components/proximity_auth/connection.cc
+++ b/components/proximity_auth/connection.cc
@@ -77,25 +77,26 @@ void Connection::OnBytesReceived(const std::string& bytes) {
}
received_bytes_ += bytes;
- if (HasReceivedCompleteMessage()) {
- scoped_ptr<WireMessage> message = DeserializeWireMessage();
- if (message) {
- FOR_EACH_OBSERVER(
- ConnectionObserver, observers_, OnMessageReceived(*this, *message));
- }
-
- // Whether the message was parsed successfully or not, clear the
- // |received_bytes_| buffer.
- received_bytes_.clear();
+
+ bool is_incomplete_message;
+ scoped_ptr<WireMessage> message =
+ DeserializeWireMessage(&is_incomplete_message);
+ if (is_incomplete_message)
+ return;
+
+ if (message) {
+ FOR_EACH_OBSERVER(
+ ConnectionObserver, observers_, OnMessageReceived(*this, *message));
}
-}
-bool Connection::HasReceivedCompleteMessage() {
- return WireMessage::IsCompleteMessage(received_bytes_);
+ // Whether the message was parsed successfully or not, clear the
+ // |received_bytes_| buffer.
+ received_bytes_.clear();
}
-scoped_ptr<WireMessage> Connection::DeserializeWireMessage() {
- return WireMessage::Deserialize(received_bytes_);
+scoped_ptr<WireMessage> Connection::DeserializeWireMessage(
+ bool* is_incomplete_message) {
+ return WireMessage::Deserialize(received_bytes_, is_incomplete_message);
}
} // namespace proximity_auth
diff --git a/components/proximity_auth/connection.h b/components/proximity_auth/connection.h
index b904f84..b0275da 100644
--- a/components/proximity_auth/connection.h
+++ b/components/proximity_auth/connection.h
@@ -75,13 +75,13 @@ class Connection {
// in progress.
virtual void SendMessageImpl(scoped_ptr<WireMessage> message) = 0;
- // Returns |true| iff the |received_bytes_| are long enough to contain a
- // complete wire message. Exposed for testing.
- virtual bool HasReceivedCompleteMessage();
-
// Deserializes the |recieved_bytes_| and returns the resulting WireMessage,
- // or NULL if the message is malformed. Exposed for testing.
- virtual scoped_ptr<WireMessage> DeserializeWireMessage();
+ // or NULL if the message is malformed. Sets |is_incomplete_message| to true
+ // if the |serialized_message| does not have enough data to parse the header,
+ // or if the message length encoded in the message header exceeds the size of
+ // the |serialized_message|. Exposed for testing.
+ virtual scoped_ptr<WireMessage> DeserializeWireMessage(
+ bool* is_incomplete_message);
private:
// The remote device corresponding to this connection.
diff --git a/components/proximity_auth/connection_unittest.cc b/components/proximity_auth/connection_unittest.cc
index 2f9fbdb..2e3678d 100644
--- a/components/proximity_auth/connection_unittest.cc
+++ b/components/proximity_auth/connection_unittest.cc
@@ -10,8 +10,10 @@
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
+using testing::DoAll;
using testing::NiceMock;
using testing::Return;
+using testing::SetArgPointee;
using testing::StrictMock;
namespace proximity_auth {
@@ -27,8 +29,8 @@ class MockConnection : public Connection {
MOCK_METHOD0(Disconnect, void());
MOCK_METHOD0(CancelConnectionAttempt, void());
MOCK_METHOD1(SendMessageImplProxy, void(WireMessage* message));
- MOCK_METHOD0(HasReceivedCompleteMessage, bool());
- MOCK_METHOD0(DeserializeWireMessageProxy, WireMessage*());
+ MOCK_METHOD1(DeserializeWireMessageProxy,
+ WireMessage*(bool* is_incomplete_message));
// Gmock only supports copyable types, so create simple wrapper methods for
// ease of mocking.
@@ -36,8 +38,9 @@ class MockConnection : public Connection {
SendMessageImplProxy(message.get());
}
- virtual scoped_ptr<WireMessage> DeserializeWireMessage() OVERRIDE {
- return make_scoped_ptr(DeserializeWireMessageProxy());
+ virtual scoped_ptr<WireMessage> DeserializeWireMessage(
+ bool* is_incomplete_message) OVERRIDE {
+ return make_scoped_ptr(DeserializeWireMessageProxy(is_incomplete_message));
}
using Connection::status;
@@ -72,7 +75,7 @@ class MockConnectionObserver : public ConnectionObserver {
// Unlike WireMessage, offers a public constructor.
class TestWireMessage : public WireMessage {
public:
- TestWireMessage() {}
+ TestWireMessage() : WireMessage(std::string(), std::string()) {}
virtual ~TestWireMessage() {}
private:
@@ -191,9 +194,9 @@ TEST(ProximityAuthConnectionTest,
StrictMock<MockConnectionObserver> observer;
connection.AddObserver(&observer);
- ON_CALL(connection, HasReceivedCompleteMessage()).WillByDefault(Return(true));
- ON_CALL(connection, DeserializeWireMessageProxy())
- .WillByDefault(Return(new TestWireMessage));
+ ON_CALL(connection, DeserializeWireMessageProxy(_))
+ .WillByDefault(DoAll(SetArgPointee<0>(false),
+ Return(new TestWireMessage)));
EXPECT_CALL(observer, OnMessageReceived(Ref(connection), _));
connection.OnBytesReceived(std::string());
}
@@ -218,8 +221,9 @@ TEST(ProximityAuthConnectionTest,
StrictMock<MockConnectionObserver> observer;
connection.AddObserver(&observer);
- ON_CALL(connection, HasReceivedCompleteMessage())
- .WillByDefault(Return(false));
+ ON_CALL(connection, DeserializeWireMessageProxy(_))
+ .WillByDefault(DoAll(SetArgPointee<0>(true),
+ Return(static_cast<WireMessage*>(NULL))));
EXPECT_CALL(observer, OnMessageReceived(_, _)).Times(0);
connection.OnBytesReceived(std::string());
}
@@ -232,9 +236,9 @@ TEST(ProximityAuthConnectionTest,
StrictMock<MockConnectionObserver> observer;
connection.AddObserver(&observer);
- ON_CALL(connection, HasReceivedCompleteMessage()).WillByDefault(Return(true));
- ON_CALL(connection, DeserializeWireMessageProxy())
- .WillByDefault(Return(static_cast<WireMessage*>(NULL)));
+ ON_CALL(connection, DeserializeWireMessageProxy(_))
+ .WillByDefault(DoAll(SetArgPointee<0>(false),
+ Return(static_cast<WireMessage*>(NULL))));
EXPECT_CALL(observer, OnMessageReceived(_, _)).Times(0);
connection.OnBytesReceived(std::string());
}
diff --git a/components/proximity_auth/wire_message.cc b/components/proximity_auth/wire_message.cc
index b6b5770..678014d 100644
--- a/components/proximity_auth/wire_message.cc
+++ b/components/proximity_auth/wire_message.cc
@@ -4,26 +4,117 @@
#include "components/proximity_auth/wire_message.h"
+#include "base/base64.h"
+#include "base/json/json_reader.h"
+#include "base/logging.h"
+#include "base/macros.h"
+#include "base/values.h"
+
+// The wire messages have a simple format:
+// [ message version ] [ body length ] [ JSON body ]
+// 1 byte 2 bytes body length
+// The JSON body contains two fields: an optional permit_id field and a required
+// data field.
+
namespace proximity_auth {
+namespace {
-WireMessage::~WireMessage() {
+// The length of the message header, in bytes.
+const size_t kHeaderLength = 3;
+
+// The protocol version of the message format.
+const int kExpectedMessageFormatVersion = 3;
+
+const char kPayloadKey[] = "payload";
+const char kPermitIdKey[] = "permit_id";
+
+// Parses the |serialized_message|'s header. Returns |true| iff the message has
+// a valid header, is complete, and is well-formed according to the header. Sets
+// |is_incomplete_message| to true iff the message does not have enough data to
+// parse the header, or if the message length encoded in the message header
+// exceeds the size of the |serialized_message|.
+bool ParseHeader(const std::string& serialized_message,
+ bool* is_incomplete_message) {
+ *is_incomplete_message = false;
+ if (serialized_message.size() < kHeaderLength) {
+ *is_incomplete_message = true;
+ return false;
+ }
+
+ COMPILE_ASSERT(kHeaderLength > 2, header_length_too_small);
+ size_t version = serialized_message[0];
+ if (version != kExpectedMessageFormatVersion) {
+ VLOG(1) << "Error: Invalid message version. Got " << version
+ << ", expected " << kExpectedMessageFormatVersion;
+ return false;
+ }
+
+ size_t expected_body_length =
+ (static_cast<size_t>(serialized_message[1]) << 8) |
+ (static_cast<size_t>(serialized_message[2]) << 0);
+ size_t expected_message_length = kHeaderLength + expected_body_length;
+ if (serialized_message.size() < expected_message_length) {
+ *is_incomplete_message = true;
+ return false;
+ }
+ if (serialized_message.size() != expected_message_length) {
+ VLOG(1) << "Error: Invalid message length. Got "
+ << serialized_message.size() << ", expected "
+ << expected_message_length;
+ return false;
+ }
+
+ return true;
}
-// static
-bool WireMessage::IsCompleteMessage(const std::string& serialized_message) {
- // TODO(isherman): Implement.
- return false;
+} // namespace
+
+WireMessage::~WireMessage() {
}
// static
scoped_ptr<WireMessage> WireMessage::Deserialize(
- const std::string& serialized_message) {
- // TODO(isherman): Implement.
- return scoped_ptr<WireMessage>();
+ const std::string& serialized_message,
+ bool* is_incomplete_message) {
+ if (!ParseHeader(serialized_message, is_incomplete_message))
+ return scoped_ptr<WireMessage>();
+
+ scoped_ptr<base::Value> body_value(
+ base::JSONReader::Read(serialized_message.substr(kHeaderLength)));
+ if (!body_value || !body_value->IsType(base::Value::TYPE_DICTIONARY)) {
+ VLOG(1) << "Error: Unable to parse message as JSON.";
+ return scoped_ptr<WireMessage>();
+ }
+
+ base::DictionaryValue* body;
+ bool success = body_value->GetAsDictionary(&body);
+ DCHECK(success);
+
+ // The permit ID is optional. In the Easy Unlock protocol, only the first
+ // message includes this field.
+ std::string permit_id;
+ body->GetString(kPermitIdKey, &permit_id);
+
+ std::string payload_base64;
+ if (!body->GetString(kPayloadKey, &payload_base64) ||
+ payload_base64.empty()) {
+ VLOG(1) << "Error: Missing payload.";
+ return scoped_ptr<WireMessage>();
+ }
+
+ std::string payload;
+ if (!base::Base64Decode(payload_base64, &payload)) {
+ VLOG(1) << "Error: Invalid base64 encoding for payload.";
+ return scoped_ptr<WireMessage>();
+ }
+
+ return scoped_ptr<WireMessage>(new WireMessage(permit_id, payload));
}
-WireMessage::WireMessage() {
- // TODO(isherman): Implement.
+WireMessage::WireMessage(const std::string& permit_id,
+ const std::string& payload)
+ : permit_id_(permit_id),
+ payload_(payload) {
}
} // namespace proximity_auth
diff --git a/components/proximity_auth/wire_message.h b/components/proximity_auth/wire_message.h
index db5af37..3610762 100644
--- a/components/proximity_auth/wire_message.h
+++ b/components/proximity_auth/wire_message.h
@@ -16,21 +16,29 @@ class WireMessage {
public:
virtual ~WireMessage();
- // Returns |true| iff the size of |message_bytes| is at least equal to the
- // message length encoded in the message header. Returns false if the message
- // header is not available.
- static bool IsCompleteMessage(const std::string& message_bytes);
-
// Returns the deserialized message from |serialized_message|, or NULL if the
- // message is malformed.
+ // message is malformed. Sets |is_incomplete_message| to true if the message
+ // does not have enough data to parse the header, or if the message length
+ // encoded in the message header exceeds the size of the |serialized_message|.
static scoped_ptr<WireMessage> Deserialize(
- const std::string& serialized_message);
+ const std::string& serialized_message,
+ bool* is_incomplete_message);
+
+ const std::string& permit_id() const { return permit_id_; }
+ const std::string& payload() const { return payload_; }
protected:
// Visible for tests.
- WireMessage();
+ WireMessage(const std::string& permit_id, const std::string& payload);
private:
+ // Identifier of the permit being used.
+ // TODO(isherman): Describe in a bit more detail.
+ const std::string permit_id_;
+
+ // The message payload.
+ const std::string payload_;
+
DISALLOW_COPY_AND_ASSIGN(WireMessage);
};
diff --git a/components/proximity_auth/wire_message_unittest.cc b/components/proximity_auth/wire_message_unittest.cc
new file mode 100644
index 0000000..afa05ef
--- /dev/null
+++ b/components/proximity_auth/wire_message_unittest.cc
@@ -0,0 +1,169 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/proximity_auth/wire_message.h"
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace proximity_auth {
+
+TEST(ProximityAuthWireMessage, Deserialize_EmptyMessage) {
+ bool is_incomplete;
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(std::string(), &is_incomplete);
+ EXPECT_TRUE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_IncompleteHeader) {
+ bool is_incomplete;
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize("\3", &is_incomplete);
+ EXPECT_TRUE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_UnexpectedMessageFormatVersion) {
+ bool is_incomplete;
+ // Version 2 is below the minimum supported version.
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize("\2\1\1", &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_BodyOfSizeZero) {
+ bool is_incomplete;
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(std::string("\3\0\0", 3), &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_IncompleteBody) {
+ bool is_incomplete;
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(std::string("\3\0\5", 3), &is_incomplete);
+ EXPECT_TRUE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_BodyLongerThanSpecifiedInHeader) {
+ bool is_incomplete;
+ scoped_ptr<WireMessage> message = WireMessage::Deserialize(
+ std::string("\3\0\5", 3) + "123456", &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_BodyIsNotValidJSON) {
+ bool is_incomplete;
+ scoped_ptr<WireMessage> message = WireMessage::Deserialize(
+ std::string("\3\0\5", 3) + "12345", &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_BodyIsNotADictionary) {
+ bool is_incomplete;
+ std::string header("\3\0\x29", 3);
+ std::string bytes =
+ header + "[{\"permit_id\": \"Hi!\", \"payload\": \"YQ==\"}]";
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+// The permit ID is optional.
+TEST(ProximityAuthWireMessage, Deserialize_BodyLacksPermitId) {
+ bool is_incomplete;
+ std::string header("\3\0\x13", 3);
+ std::string bytes = header + "{\"payload\": \"YQ==\"}";
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_TRUE(message);
+ EXPECT_EQ(std::string(), message->permit_id());
+ EXPECT_EQ("a", message->payload());
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_BodyLacksPayload) {
+ bool is_incomplete;
+ std::string header("\3\0\x14", 3);
+ std::string bytes = header + "{\"permit_id\": \"Hi!\"}";
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+// The permit ID is optional.
+TEST(ProximityAuthWireMessage, Deserialize_BodyHasEmptyPermitId) {
+ bool is_incomplete;
+ std::string header("\3\0\x24", 3);
+ std::string bytes =
+ header + "{\"permit_id\": \"\", \"payload\": \"YQ==\"}";
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_TRUE(message);
+ EXPECT_EQ(std::string(), message->permit_id());
+ EXPECT_EQ("a", message->payload());
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_BodyHasEmptyPayload) {
+ bool is_incomplete;
+ std::string header("\3\0\x23", 3);
+ std::string bytes =
+ header + "{\"permit_id\": \"Hi!\", \"payload\": \"\"}";
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_PayloadIsNotBase64Encoded) {
+ bool is_incomplete;
+ std::string header("\3\0\x2A", 3);
+ std::string bytes =
+ header + "{\"permit_id\": \"Hi!\", \"payload\": \"garbage\"}";
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_FALSE(message);
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_ValidMessage) {
+ bool is_incomplete;
+ std::string header("\3\0\x27", 3);
+ std::string bytes =
+ header + "{\"permit_id\": \"Hi!\", \"payload\": \"YQ==\"}";
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_TRUE(message);
+ EXPECT_EQ("Hi!", message->permit_id());
+ EXPECT_EQ("a", message->payload());
+}
+
+TEST(ProximityAuthWireMessage, Deserialize_ValidMessageWithExtraUnknownFields) {
+ bool is_incomplete;
+ std::string header("\3\0\x46", 3);
+ std::string bytes =
+ header +
+ "{"
+ " \"permit_id\": \"Hi!\","
+ " \"payload\": \"YQ==\","
+ " \"unexpected\": \"surprise!\""
+ "}";
+ scoped_ptr<WireMessage> message =
+ WireMessage::Deserialize(bytes, &is_incomplete);
+ EXPECT_FALSE(is_incomplete);
+ EXPECT_TRUE(message);
+ EXPECT_EQ("Hi!", message->permit_id());
+ EXPECT_EQ("a", message->payload());
+}
+
+} // namespace proximity_auth