diff options
Diffstat (limited to 'net/quic/crypto')
-rw-r--r-- | net/quic/crypto/crypto_framer.cc | 156 | ||||
-rw-r--r-- | net/quic/crypto/crypto_framer.h | 106 | ||||
-rw-r--r-- | net/quic/crypto/crypto_framer_test.cc | 316 | ||||
-rw-r--r-- | net/quic/crypto/crypto_protocol.cc | 12 | ||||
-rw-r--r-- | net/quic/crypto/crypto_protocol.h | 45 | ||||
-rw-r--r-- | net/quic/crypto/null_decrypter.cc | 35 | ||||
-rw-r--r-- | net/quic/crypto/null_decrypter.h | 28 | ||||
-rw-r--r-- | net/quic/crypto/null_decrypter_test.cc | 77 | ||||
-rw-r--r-- | net/quic/crypto/null_encrypter.cc | 37 | ||||
-rw-r--r-- | net/quic/crypto/null_encrypter.h | 30 | ||||
-rw-r--r-- | net/quic/crypto/null_encrypter_test.cc | 56 | ||||
-rw-r--r-- | net/quic/crypto/quic_decrypter.cc | 21 | ||||
-rw-r--r-- | net/quic/crypto/quic_decrypter.h | 28 | ||||
-rw-r--r-- | net/quic/crypto/quic_encrypter.cc | 21 | ||||
-rw-r--r-- | net/quic/crypto/quic_encrypter.h | 38 |
15 files changed, 1006 insertions, 0 deletions
diff --git a/net/quic/crypto/crypto_framer.cc b/net/quic/crypto/crypto_framer.cc new file mode 100644 index 0000000..806b336 --- /dev/null +++ b/net/quic/crypto/crypto_framer.cc @@ -0,0 +1,156 @@ +// Copyright (c) 2012 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 "net/quic/crypto/crypto_framer.h" + +#include "net/quic/quic_data_reader.h" +#include "net/quic/quic_data_writer.h" +#include "net/quic/quic_protocol.h" + +using base::StringPiece; + +namespace net { + +CryptoFramer::CryptoFramer() + : visitor_(NULL) { + Clear(); +} + +CryptoFramer::~CryptoFramer() {} + +bool CryptoFramer::ProcessInput(StringPiece input) { + DCHECK_EQ(QUIC_NO_ERROR, error_); + if (error_ != QUIC_NO_ERROR) { + return false; + } + // Add this data to the buffer. + buffer_.append(input.data(), input.length()); + QuicDataReader reader(buffer_.data(), buffer_.length()); + + switch (state_) { + case STATE_READING_TAG: + if (reader.BytesRemaining() < sizeof(uint32)) { + break; + } + reader.ReadUInt32(&message_tag_); + state_ = STATE_READING_NUM_ENTRIES; + case STATE_READING_NUM_ENTRIES: + if (reader.BytesRemaining() < sizeof(uint16)) { + break; + } + reader.ReadUInt16(&num_entries_); + if (num_entries_ > kMaxEntries) { + error_ = QUIC_CRYPTO_TOO_MANY_ENTRIES; + return false; + } + state_ = STATE_READING_KEY_TAGS; + case STATE_READING_KEY_TAGS: + if (reader.BytesRemaining() < num_entries_ * sizeof(uint32)) { + break; + } + for (int i = 0; i < num_entries_; ++i) { + CryptoTag tag; + reader.ReadUInt32(&tag); + if (i > 0 && tag <= tags_.back()) { + error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER; + return false; + } + tags_.push_back(tag); + } + state_ = STATE_READING_LENGTHS; + case STATE_READING_LENGTHS: + if (reader.BytesRemaining() < num_entries_ * sizeof(uint16)) { + break; + } + values_len_ = 0; + for (int i = 0; i < num_entries_; ++i) { + uint16 len; + reader.ReadUInt16(&len); + tag_length_map_[tags_[i]] = len; + values_len_ += len; + if (len == 0 && i != num_entries_ - 1) { + error_ = QUIC_CRYPTO_INVALID_VALUE_LENGTH; + return false; + } + } + state_ = STATE_READING_VALUES; + case STATE_READING_VALUES: + if (reader.BytesRemaining() < values_len_) { + break; + } + for (int i = 0; i < num_entries_; ++i) { + StringPiece value; + reader.ReadStringPiece(&value, tag_length_map_[tags_[i]]); + tag_value_map_[tags_[i]] = value; + } + CryptoHandshakeMessage message; + message.tag = message_tag_; + message.tag_value_map.swap(tag_value_map_); + visitor_->OnHandshakeMessage(message); + Clear(); + state_ = STATE_READING_TAG; + break; + } + // Save any left over data + buffer_ = reader.PeekRemainingPayload().as_string(); + return true; +} + +bool CryptoFramer::ConstructHandshakeMessage( + const CryptoHandshakeMessage& message, + QuicData** packet) { + if (message.tag_value_map.size() > kMaxEntries) { + return false; + } + size_t len = sizeof(uint32); // message tag + len += sizeof(uint16); // number of map entries + CryptoTagValueMap::const_iterator it = message.tag_value_map.begin(); + while (it != message.tag_value_map.end()) { + len += sizeof(uint32); // tag + len += sizeof(uint16); // value len + len += it->second.length(); // value + if (it->second.length() == 0) { + return false; + } + ++it; + } + if (message.tag_value_map.size() % 2 == 1) { + len += sizeof(uint16); // padding + } + + QuicDataWriter writer(len); + CHECK(writer.WriteUInt32(message.tag)); + CHECK(writer.WriteUInt16(message.tag_value_map.size())); + // Tags + for (it = message.tag_value_map.begin(); + it != message.tag_value_map.end(); ++it) { + CHECK(writer.WriteUInt32(it->first)); + } + // Lengths + for (it = message.tag_value_map.begin(); + it != message.tag_value_map.end(); ++it) { + CHECK(writer.WriteUInt16(it->second.length())); + } + // Possible padding + if (message.tag_value_map.size() % 2 == 1) { + CHECK(writer.WriteUInt16(0xABAB)); + } + // Values + for (it = message.tag_value_map.begin(); + it != message.tag_value_map.end(); ++it) { + CHECK(writer.WriteBytes(it->second.data(), it->second.length())); + } + *packet = new QuicData(writer.take(), len, true); + return true; +} + +void CryptoFramer::Clear() { + tag_value_map_.clear(); + tag_length_map_.clear(); + tags_.clear(); + error_ = QUIC_NO_ERROR; + state_ = STATE_READING_TAG; +} + +} // namespace net diff --git a/net/quic/crypto/crypto_framer.h b/net/quic/crypto/crypto_framer.h new file mode 100644 index 0000000..e52b264 --- /dev/null +++ b/net/quic/crypto/crypto_framer.h @@ -0,0 +1,106 @@ +// Copyright (c) 2012 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. + +#ifndef NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ +#define NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ + +#include <map> +#include <vector> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/string_piece.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class CryptoFramer; +class QuicDataReader; +class QuicData; + +class NET_EXPORT_PRIVATE CryptoFramerVisitorInterface { + public: + virtual ~CryptoFramerVisitorInterface() {} + + // Called if an error is detected. + virtual void OnError(CryptoFramer* framer) = 0; + + // Called when a complete handshake message has been parsed. + virtual void OnHandshakeMessage(const CryptoHandshakeMessage& message) = 0; +}; + +// A class for framing the crypto message that are exchanged in a QUIC session. +class NET_EXPORT_PRIVATE CryptoFramer { + public: + CryptoFramer(); + + virtual ~CryptoFramer(); + + // Set callbacks to be called from the framer. A visitor must be set, or + // else the framer will likely crash. It is acceptable for the visitor + // to do nothing. If this is called multiple times, only the last visitor + // will be used. |visitor| will be owned by the framer. + void set_visitor(CryptoFramerVisitorInterface* visitor) { + visitor_ = visitor; + } + + QuicErrorCode error() const { + return error_; + } + + // Processes input data, which must be delievered in order. Returns + // false if there was an error, and true otherwise. + bool ProcessInput(base::StringPiece input); + + // Serializes |message| into |packet|. Returns false if there was an + // error, and true otherwise. + bool ConstructHandshakeMessage(const CryptoHandshakeMessage& message, + QuicData** packet); + + private: + // Clears per-message state. Does not clear the visitor. + void Clear(); + + void set_error(QuicErrorCode error) { + error_ = error; + } + + enum CryptoFramerState { + STATE_READING_TAG, + STATE_READING_NUM_ENTRIES, + STATE_READING_KEY_TAGS, + STATE_READING_LENGTHS, + STATE_READING_VALUES + }; + + // Visitor to send invoke when messages are parsed. + CryptoFramerVisitorInterface* visitor_; + // Last error. + QuicErrorCode error_; + // Remaining unparsed data. + std::string buffer_; + // Current state of the parsing. + CryptoFramerState state_; + // Tag of the message currently being parsed. + CryptoTag message_tag_; + // Number of entires in the message currently being parsed. + uint16 num_entries_; + // Vector of tags in the message currently being parsed. + std::vector<CryptoTag> tags_; + // Length of the data associated with each tag in the message currently + // being parsed. + std::map<CryptoTag, size_t> tag_length_map_; + // Length of the data associated with each tag in the message currently + // being parsed. + CryptoTagValueMap tag_value_map_; + // Cumulative length of all values in the message currently being parsed. + size_t values_len_; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_ diff --git a/net/quic/crypto/crypto_framer_test.cc b/net/quic/crypto/crypto_framer_test.cc new file mode 100644 index 0000000..8a32f26 --- /dev/null +++ b/net/quic/crypto/crypto_framer_test.cc @@ -0,0 +1,316 @@ +// Copyright (c) 2012 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 <map> +#include <vector> + +#include "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "net/quic/crypto/crypto_framer.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/test_tools/quic_test_utils.h" + +using base::StringPiece; +using std::map; +using std::string; +using std::vector; + +namespace net { + +namespace { + +char* AsChars(unsigned char* data) { + return reinterpret_cast<char*>(data); +} + +} // namespace + +namespace test { + +class TestCryptoVisitor : public ::net::CryptoFramerVisitorInterface { + public: + TestCryptoVisitor() + : error_count_(0) { + } + + ~TestCryptoVisitor() {} + + virtual void OnError(CryptoFramer* f) { + LOG(INFO) << "CryptoFramer Error: " << f->error(); + error_count_++; + } + + virtual void OnHandshakeMessage(const CryptoHandshakeMessage& message) { + message_tags_.push_back(message.tag); + message_maps_.push_back(map<CryptoTag, string>()); + CryptoTagValueMap::const_iterator it = message.tag_value_map.begin(); + while (it != message.tag_value_map.end()) { + message_maps_.back()[it->first] = it->second.as_string(); + ++it; + } + } + + CryptoFramer framer_; + + // Counters from the visitor callbacks. + int error_count_; + + vector<CryptoTag> message_tags_; + vector<map<CryptoTag, string> > message_maps_; +}; + +} // namespace test + +TEST(CryptoFramerTest, ConstructHandshakeMessage) { + CryptoHandshakeMessage message; + message.tag = 0xFFAA7733; + message.tag_value_map[0x12345678] = "abcdef"; + message.tag_value_map[0x12345679] = "ghijk"; + message.tag_value_map[0x1234567A] = "lmnopqr"; + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x03, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // tag 3 + 0x7A, 0x56, 0x34, 0x12, + // len 1 + 0x06, 0x00, + // len 2 + 0x05, 0x00, + // len 3 + 0x07, 0x00, + // padding + 0xAB, 0xAB, + // value 1 + 'a', 'b', 'c', 'd', + 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', + 'k', + // value 3 + 'l', 'm', 'n', 'o', + 'p', 'q', 'r', + }; + + CryptoFramer framer; + QuicData* data; + EXPECT_TRUE(framer.ConstructHandshakeMessage(message, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageWithTwoKeys) { + CryptoHandshakeMessage message; + message.tag = 0xFFAA7733; + message.tag_value_map[0x12345678] = "abcdef"; + message.tag_value_map[0x12345679] = "ghijk"; + + unsigned char packet[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // len 1 + 0x06, 0x00, + // len 2 + 0x05, 0x00, + // value 1 + 'a', 'b', 'c', 'd', + 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', + 'k', + }; + + CryptoFramer framer; + QuicData* data; + EXPECT_TRUE(framer.ConstructHandshakeMessage(message, &data)); + + test::CompareCharArraysWithHexError("constructed packet", + data->data(), data->length(), + AsChars(packet), arraysize(packet)); + + delete data; +} + +TEST(CryptoFramerTest, ConstructHandshakeMessageTooManyEntries) { + CryptoHandshakeMessage message; + message.tag = 0xFFAA7733; + for (uint32 key = 1; key <= kMaxEntries + 1; key++) { + message.tag_value_map[key] = "abcdef"; + } + + CryptoFramer framer; + QuicData* data = NULL; + EXPECT_FALSE(framer.ConstructHandshakeMessage(message, &data)); + delete data; +} + + +TEST(CryptoFramerTest, ConstructHandshakeMessageInvalidLength) { + CryptoHandshakeMessage message; + message.tag = 0xFFAA7733; + message.tag_value_map[0x12345678] = ""; + + CryptoFramer framer; + QuicData* data = NULL; + EXPECT_FALSE(framer.ConstructHandshakeMessage(message, &data)); + delete data; +} + +TEST(CryptoFramerTest, EmptyPacket) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); +} + +TEST(CryptoFramerTest, ProcessInput) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // len 1 + 0x06, 0x00, + // len 2 + 0x05, 0x00, + // value 1 + 'a', 'b', 'c', 'd', + 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', + 'k', + }; + + EXPECT_TRUE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + ASSERT_EQ(1u, visitor.message_tags_.size()); + EXPECT_EQ(0xFFAA7733, visitor.message_tags_[0]); + EXPECT_EQ(2u, visitor.message_maps_[0].size()); + EXPECT_EQ("abcdef",visitor.message_maps_[0][0x12345678]); + EXPECT_EQ("ghijk", visitor.message_maps_[0][0x12345679]); +} + +TEST(CryptoFramerTest, ProcessInputIncrementally) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // len 1 + 0x06, 0x00, + // len 2 + 0x05, 0x00, + // value 1 + 'a', 'b', 'c', 'd', + 'e', 'f', + // value 2 + 'g', 'h', 'i', 'j', + 'k', + }; + + for (size_t i = 0; i < arraysize(input); i++) { + EXPECT_TRUE(framer.ProcessInput(StringPiece(AsChars(input)+ i, 1))); + } + ASSERT_EQ(1u, visitor.message_tags_.size()); + EXPECT_EQ(0xFFAA7733, visitor.message_tags_[0]); + EXPECT_EQ(2u, visitor.message_maps_[0].size()); + EXPECT_EQ("abcdef",visitor.message_maps_[0][0x12345678]); + EXPECT_EQ("ghijk", visitor.message_maps_[0][0x12345679]); +} + +TEST(CryptoFramerTest, ProcessInputTagsOutOfOrder) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x79, 0x56, 0x34, 0x12, + // tag 2 + 0x78, 0x56, 0x34, 0x12, + }; + + EXPECT_FALSE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + EXPECT_EQ(QUIC_CRYPTO_TAGS_OUT_OF_ORDER, framer.error()); +} + +TEST(CryptoFramerTest, ProcessInputTooManyEntries) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0xA0, 0x00, + }; + + EXPECT_FALSE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + EXPECT_EQ(QUIC_CRYPTO_TOO_MANY_ENTRIES, framer.error()); +} + +TEST(CryptoFramerTest, ProcessInputInvalidLength) { + test::TestCryptoVisitor visitor; + CryptoFramer framer; + framer.set_visitor(&visitor); + + unsigned char input[] = { + // tag + 0x33, 0x77, 0xAA, 0xFF, + // num entires + 0x02, 0x00, + // tag 1 + 0x78, 0x56, 0x34, 0x12, + // tag 2 + 0x79, 0x56, 0x34, 0x12, + // len 1 + 0x00, 0x00, + // len 2 + 0x05, 0x00, + }; + + EXPECT_FALSE(framer.ProcessInput(StringPiece(AsChars(input), + arraysize(input)))); + EXPECT_EQ(QUIC_CRYPTO_INVALID_VALUE_LENGTH, framer.error()); +} + +} // namespace net diff --git a/net/quic/crypto/crypto_protocol.cc b/net/quic/crypto/crypto_protocol.cc new file mode 100644 index 0000000..ba87906 --- /dev/null +++ b/net/quic/crypto/crypto_protocol.cc @@ -0,0 +1,12 @@ +// Copyright (c) 2012 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 "net/quic/crypto/crypto_protocol.h" + +namespace net { + +CryptoHandshakeMessage::CryptoHandshakeMessage() {} +CryptoHandshakeMessage::~CryptoHandshakeMessage() {} + +} // namespace net diff --git a/net/quic/crypto/crypto_protocol.h b/net/quic/crypto/crypto_protocol.h new file mode 100644 index 0000000..a9c7083 --- /dev/null +++ b/net/quic/crypto/crypto_protocol.h @@ -0,0 +1,45 @@ +// Copyright (c) 2012 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. + +#ifndef NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ +#define NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ + +#include <map> + +#include "base/basictypes.h" +#include "base/logging.h" +#include "base/string_piece.h" +#include "net/base/net_export.h" + +namespace net { + +typedef uint32 CryptoTag; +typedef std::map<CryptoTag, base::StringPiece> CryptoTagValueMap; +struct NET_EXPORT_PRIVATE CryptoHandshakeMessage { + CryptoHandshakeMessage(); + ~CryptoHandshakeMessage(); + CryptoTag tag; + CryptoTagValueMap tag_value_map; +}; + +// Crypto tags are written to the wire with a big-endian +// representation of the name of the tag. For example +// the client hello tag (CHLO) will be written as the +// following 4 bytes: 'C' 'H' 'L' 'O'. Since it is +// stored in memory as a little endian uint32, we need +// to reverse the order of the bytes. +#define MAKE_TAG(a,b,c,d) (d << 24) + (c << 16) + (b << 8) + a + +const CryptoTag kCHLO = MAKE_TAG('C', 'H', 'L', 'O'); // Client hello +const CryptoTag kSHLO = MAKE_TAG('S', 'H', 'L', 'O'); // Server hello + +// AEAD algorithms +const CryptoTag kNULL = MAKE_TAG('N', 'U', 'L', 'L'); // null algorithm +const CryptoTag kAESH = MAKE_TAG('A', 'E', 'S', 'H'); // AES128 + SHA256 + +const size_t kMaxEntries = 16; // Max number of entries in a message. + +} // namespace net + +#endif // NET_QUIC_CRYPTO_CRYPTO_PROTOCOL_H_ diff --git a/net/quic/crypto/null_decrypter.cc b/net/quic/crypto/null_decrypter.cc new file mode 100644 index 0000000..072c684 --- /dev/null +++ b/net/quic/crypto/null_decrypter.cc @@ -0,0 +1,35 @@ +// Copyright (c) 2012 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 "net/quic/crypto/null_decrypter.h" +#include "net/quic/quic_utils.h" +#include "net/quic/quic_data_reader.h" + +using base::StringPiece; +using std::string; + +namespace net { + +QuicData* NullDecrypter::Decrypt(StringPiece associated_data, + StringPiece ciphertext) { + QuicDataReader reader(ciphertext.data(), ciphertext.length()); + + uint128 hash; + if (!reader.ReadUInt128(&hash)) { + return NULL; + } + + StringPiece plaintext = reader.ReadRemainingPayload(); + + // TODO(rch): avoid buffer copy here + string buffer = associated_data.as_string(); + plaintext.AppendToString(&buffer); + + if (hash != QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length())) { + return NULL; + } + return new QuicData(plaintext.data(), plaintext.length()); +} + +} // namespace net diff --git a/net/quic/crypto/null_decrypter.h b/net/quic/crypto/null_decrypter.h new file mode 100644 index 0000000..5495092 --- /dev/null +++ b/net/quic/crypto/null_decrypter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012 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. + +#ifndef NET_QUIC_CRYPTO_NULL_DECRYPTER_H_ +#define NET_QUIC_CRYPTO_NULL_DECRYPTER_H_ + +#include "base/compiler_specific.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/quic_decrypter.h" + +namespace net { + +// A NullDecrypter is a QuicDecrypter used before a crypto negotiation +// has occurred. It does not actually decrypt the payload, but does +// verify a hash (fnv128) over both the payload and associated data. +class NET_EXPORT_PRIVATE NullDecrypter : public QuicDecrypter { + public: + virtual ~NullDecrypter() {} + + // QuicDecrypter implementation + virtual QuicData* Decrypt(base::StringPiece associated_data, + base::StringPiece ciphertext) OVERRIDE; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_NULL_DECRYPTER_H_ diff --git a/net/quic/crypto/null_decrypter_test.cc b/net/quic/crypto/null_decrypter_test.cc new file mode 100644 index 0000000..6c914d7 --- /dev/null +++ b/net/quic/crypto/null_decrypter_test.cc @@ -0,0 +1,77 @@ +// Copyright (c) 2012 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 "net/quic/crypto/null_decrypter.h" +#include "net/quic/test_tools/quic_test_utils.h" + +using base::StringPiece; + +namespace net { + +namespace test { + +TEST(NullDecrypterTest, Decrypt) { + unsigned char expected[] = { + // fnv hash + 0x07, 0x2d, 0x42, 0xf0, + 0xbe, 0x69, 0x12, 0x3d, + 0x20, 0x80, 0x5f, 0x9a, + 0x84, 0x9d, 0xd6, 0x0a, + /* TODO(rch): replace this when uint128 multiplication is implemented. + 0x47, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, 0x37, + */ + // payload + 'g', 'o', 'o', 'd', + 'b', 'y', 'e', '!', + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted( + decrypter.Decrypt("hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_TRUE(decrypted.get()); + EXPECT_EQ("goodbye!", decrypted->AsStringPiece()); +} + +TEST(NullDecrypterTest, BadHash) { + unsigned char expected[] = { + // fnv hash + 0x46, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, 0x37, + // payload + 'g', 'o', 'o', 'd', + 'b', 'y', 'e', '!', + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted( + decrypter.Decrypt("hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_FALSE(decrypted.get()); +} + +TEST(NullDecrypterTest, ShortInput) { + unsigned char expected[] = { + // fnv hash (truncated) + 0x46, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, + }; + NullDecrypter decrypter; + scoped_ptr<QuicData> decrypted( + decrypter.Decrypt("hello world!", + StringPiece(reinterpret_cast<const char*>(expected), + arraysize(expected)))); + ASSERT_FALSE(decrypted.get()); +} + +} // namespace test + +} // namespace net diff --git a/net/quic/crypto/null_encrypter.cc b/net/quic/crypto/null_encrypter.cc new file mode 100644 index 0000000..fda844b --- /dev/null +++ b/net/quic/crypto/null_encrypter.cc @@ -0,0 +1,37 @@ +// Copyright (c) 2012 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 "net/quic/crypto/null_encrypter.h" +#include "net/quic/quic_data_writer.h" +#include "net/quic/quic_utils.h" + +using base::StringPiece; +using std::string; + +namespace net { + +const size_t kHashSize = 16; // size of uint128 serialized + +QuicData* NullEncrypter::Encrypt(StringPiece associated_data, + StringPiece plaintext) { + // TODO(rch): avoid buffer copy here + string buffer = associated_data.as_string(); + plaintext.AppendToString(&buffer); + uint128 hash = QuicUtils::FNV1a_128_Hash(buffer.data(), buffer.length()); + QuicDataWriter writer(plaintext.length() + kHashSize); + writer.WriteUInt128(hash); + writer.WriteBytes(plaintext.data(), plaintext.length()); + size_t len = writer.length(); + return new QuicData(writer.take(), len, true); +} + +size_t NullEncrypter::GetMaxPlaintextSize(size_t ciphertext_size) { + return ciphertext_size - kHashSize; +} + +size_t NullEncrypter::GetCiphertextSize(size_t plaintext_size) { + return plaintext_size + kHashSize; +} + +} // namespace net diff --git a/net/quic/crypto/null_encrypter.h b/net/quic/crypto/null_encrypter.h new file mode 100644 index 0000000..e73423d --- /dev/null +++ b/net/quic/crypto/null_encrypter.h @@ -0,0 +1,30 @@ +// Copyright (c) 2012 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. + +#ifndef NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_ +#define NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_ + +#include "base/compiler_specific.h" +#include "net/base/net_export.h" +#include "net/quic/crypto/quic_encrypter.h" + +namespace net { + +// A NullEncrypter is a QuicEncrypter used before a crypto negotiation +// has occurred. It does not actually encrypt the payload, but does +// generate a MAC (fnv128) over both the payload and associated data. +class NET_EXPORT_PRIVATE NullEncrypter : public QuicEncrypter { + public: + virtual ~NullEncrypter() {} + + // QuicEncrypter implementation + virtual QuicData* Encrypt(base::StringPiece associated_data, + base::StringPiece plaintext) OVERRIDE; + virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) OVERRIDE; + virtual size_t GetCiphertextSize(size_t plaintext_size) OVERRIDE; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_NULL_ENCRYPTER_H_ diff --git a/net/quic/crypto/null_encrypter_test.cc b/net/quic/crypto/null_encrypter_test.cc new file mode 100644 index 0000000..a71ee34 --- /dev/null +++ b/net/quic/crypto/null_encrypter_test.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2012 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 "net/quic/crypto/null_encrypter.h" +#include "net/quic/test_tools/quic_test_utils.h" + +using base::StringPiece; + +namespace net { + +namespace test { + +TEST(NullEncrypterTest, Encrypt) { + unsigned char expected[] = { + // fnv hash + 0x07, 0x2d, 0x42, 0xf0, + 0xbe, 0x69, 0x12, 0x3d, + 0x20, 0x80, 0x5f, 0x9a, + 0x84, 0x9d, 0xd6, 0x0a, + /* TODO(rch): use this when uint128 multiplication is implemented. + 0x47, 0x11, 0xea, 0x5f, + 0xcf, 0x1d, 0x66, 0x5b, + 0xba, 0xf0, 0xbc, 0xfd, + 0x88, 0x79, 0xca, 0x37, + */ + // payload + 'g', 'o', 'o', 'd', + 'b', 'y', 'e', '!', + }; + NullEncrypter encrypter; + scoped_ptr<QuicData> encrypted(encrypter.Encrypt("hello world!", + "goodbye!")); + ASSERT_TRUE(encrypted.get()); + test::CompareCharArraysWithHexError( + "encrypted data", encrypted->data(), encrypted->length(), + reinterpret_cast<const char*>(expected), arraysize(expected)); +} + +TEST(NullEncrypterTest, GetMaxPlaintextSize) { + NullEncrypter encrypter; + EXPECT_EQ(1000u, encrypter.GetMaxPlaintextSize(1016)); + EXPECT_EQ(100u, encrypter.GetMaxPlaintextSize(116)); + EXPECT_EQ(10u, encrypter.GetMaxPlaintextSize(26)); +} + +TEST(NullEncrypterTest, GetCiphertextSize) { + NullEncrypter encrypter; + EXPECT_EQ(1016u, encrypter.GetCiphertextSize(1000)); + EXPECT_EQ(116u, encrypter.GetCiphertextSize(100)); + EXPECT_EQ(26u, encrypter.GetCiphertextSize(10)); +} + +} // namespace test + +} // namespace net diff --git a/net/quic/crypto/quic_decrypter.cc b/net/quic/crypto/quic_decrypter.cc new file mode 100644 index 0000000..997a311 --- /dev/null +++ b/net/quic/crypto/quic_decrypter.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2012 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 "net/quic/crypto/quic_decrypter.h" +#include "net/quic/crypto/null_decrypter.h" + +namespace net { + +// static +QuicDecrypter* QuicDecrypter::Create(CryptoTag algorithm) { + switch (algorithm) { + case kNULL: + return new NullDecrypter(); + default: + LOG(FATAL) << "Unsupported algorithm: " << algorithm; + return NULL; + } +} + +} // namespace net diff --git a/net/quic/crypto/quic_decrypter.h b/net/quic/crypto/quic_decrypter.h new file mode 100644 index 0000000..e5c5635 --- /dev/null +++ b/net/quic/crypto/quic_decrypter.h @@ -0,0 +1,28 @@ +// Copyright (c) 2012 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. + +#ifndef NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_ +#define NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_ + +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class NET_EXPORT_PRIVATE QuicDecrypter { + public: + virtual ~QuicDecrypter() {} + + static QuicDecrypter* Create(CryptoTag algorithm); + + // Returns a newly created QuicData object containing the decrypted + // |ciphertext| or NULL if there is an error. + virtual QuicData* Decrypt(base::StringPiece associated_data, + base::StringPiece ciphertext) = 0; +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_QUIC_DECRYPTER_H_ diff --git a/net/quic/crypto/quic_encrypter.cc b/net/quic/crypto/quic_encrypter.cc new file mode 100644 index 0000000..cc13013 --- /dev/null +++ b/net/quic/crypto/quic_encrypter.cc @@ -0,0 +1,21 @@ +// Copyright (c) 2012 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 "net/quic/crypto/quic_encrypter.h" +#include "net/quic/crypto/null_encrypter.h" + +namespace net { + +// static +QuicEncrypter* QuicEncrypter::Create(CryptoTag algorithm) { + switch (algorithm) { + case kNULL: + return new NullEncrypter(); + default: + LOG(FATAL) << "Unsupported algorithm: " << algorithm; + return NULL; + } +} + +} // namespace net diff --git a/net/quic/crypto/quic_encrypter.h b/net/quic/crypto/quic_encrypter.h new file mode 100644 index 0000000..f077c1f --- /dev/null +++ b/net/quic/crypto/quic_encrypter.h @@ -0,0 +1,38 @@ +// Copyright (c) 2012 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. + +#ifndef NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ +#define NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ + +#include "net/base/net_export.h" +#include "net/quic/crypto/crypto_protocol.h" +#include "net/quic/quic_protocol.h" + +namespace net { + +class NET_EXPORT_PRIVATE QuicEncrypter { + public: + virtual ~QuicEncrypter() {} + + static QuicEncrypter* Create(CryptoTag algorithm); + + // Returns a newly created QuicData object containing the encrypted + // |plaintext| as well as a MAC over both |plaintext| and |associated_data|, + // or NULL if there is an error. + virtual QuicData* Encrypt(base::StringPiece associated_data, + base::StringPiece plaintext) = 0; + + // Returns the maximum length of plaintext that can be encrypted + // to ciphertext no larger than |ciphertext_size|. + virtual size_t GetMaxPlaintextSize(size_t ciphertext_size) = 0; + + // Returns the length of the ciphertext that would be generated by encrypting + // to plaintext of size |plaintext_size|. + virtual size_t GetCiphertextSize(size_t plaintext_size) = 0; + +}; + +} // namespace net + +#endif // NET_QUIC_CRYPTO_QUIC_ENCRYPTER_H_ |