Skip to content

Commit

Permalink
Land Recent QUIC Changes
Browse files Browse the repository at this point in the history
Alter the serialisation format of the crypto messages.

This changes the format of the crypto messages so that:
  * We can cope with > 65K values in order to be robust to
    post-quantum algorithms in the future.
  * Rather than encoding lengths, we encode the offset one byte past the end of
    the value. This allows an implementation to binary search the header
    without having to do all the allocation and copying the we currently do.

Merge internal change: 44699015

Automated rollback of changelist 44685914.

Rollback: Bugfix infinite wait

Merge internal change: 44693957

QUIC: retransmit packets with the correct encryption.

This change does four things:
  * Splits the concept of a completed handshake in two: when encryption is
    established and when the server has confirmed the handshake. In order to do
    0-RTT, we have to start sending after the first of those events.
  * Retransmits packets using the same encryption level as they were sent with.
    Without this, the loss of a client hello message is fatal to the connection
    because it will be retransmitted under encryption and the server will never
    be able to process it.
  * Makes decryption failures an ignored error. This is needed because, if a
    client hello message is lost, the subsequent packets will be encrypted and
    the server won't have the decrypter to process them.
  * Changes how decrypters are handled by the framer. A server now replaces its
    decrypter completely - thus removing the NullDecrypter. The client now has
    latching alternative decrypters which replace the primary decrypter when
    used. This doesn't completely close the hole: the connection still needs to
    worry about plaintext packets injected into the client.

This change does not implement the correct fallback for the server rejecting a
full client hello. It also doesn't implement a limit for the number of packets
that we'll send without the server confirming the handshake. I'm hoping that
rch can do that much more easily than I can!

Merge internal change: 44690884

R=rch@chromium.org

Review URL: https://chromiumcodereview.appspot.com/14718011

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@198099 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
rtenneti@chromium.org committed May 3, 2013
1 parent a5f9060 commit 8ba8121
Show file tree
Hide file tree
Showing 47 changed files with 919 additions and 452 deletions.
1 change: 0 additions & 1 deletion net/quic/congestion_control/tcp_cubic_sender.cc
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,6 @@ void TcpCubicSender::AckAccounting(QuicTime::Delta rtt) {
}
hybrid_slow_start_.Update(rtt, delay_min_);
if (hybrid_slow_start_.Exit()) {
DLOG(INFO) << "Set slowstart threshold:" << congestion_window_;
slowstart_threshold_ = congestion_window_;
}
}
Expand Down
111 changes: 54 additions & 57 deletions net/quic/crypto/crypto_framer.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,16 @@
#include "net/quic/quic_data_writer.h"

using base::StringPiece;
using std::make_pair;
using std::pair;
using std::vector;

namespace net {

namespace {

const size_t kCryptoTagSize = sizeof(uint32);
const size_t kCryptoEndOffsetSize = sizeof(uint32);
const size_t kNumEntriesSize = sizeof(uint16);
const size_t kValueLenSize = sizeof(uint16);

Expand Down Expand Up @@ -90,64 +94,63 @@ bool CryptoFramer::ProcessInput(StringPiece input) {
message_.set_tag(message_tag);
state_ = STATE_READING_NUM_ENTRIES;
case STATE_READING_NUM_ENTRIES:
if (reader.BytesRemaining() < kNumEntriesSize) {
if (reader.BytesRemaining() < kNumEntriesSize + 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_ * kCryptoTagSize) {
uint16 padding;
reader.ReadUInt16(&padding);

tags_and_lengths_.reserve(num_entries_);
state_ = STATE_READING_TAGS_AND_LENGTHS;
values_len_ = 0;
case STATE_READING_TAGS_AND_LENGTHS: {
if (reader.BytesRemaining() < num_entries_ * (kCryptoTagSize +
kCryptoEndOffsetSize)) {
break;
}
for (int i = 0; i < num_entries_; ++i) {

uint32 last_end_offset = 0;
for (unsigned i = 0; i < num_entries_; ++i) {
CryptoTag tag;
reader.ReadUInt32(&tag);
if (i > 0 && tag <= tags_.back()) {
error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
if (i > 0 && tag <= tags_and_lengths_[i-1].first) {
if (tag == tags_and_lengths_[i-1].first) {
error_ = QUIC_CRYPTO_DUPLICATE_TAG;
} else {
error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
}
return false;
}
tags_.push_back(tag);
}
state_ = STATE_READING_LENGTHS;
case STATE_READING_LENGTHS: {
size_t expected_bytes = num_entries_ * kValueLenSize;
bool has_padding = (num_entries_ % 2 == 1);
if (has_padding) {
expected_bytes += kValueLenSize;
}
if (reader.BytesRemaining() < expected_bytes) {
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;
}
// Possible padding
if (has_padding) {
uint16 len;
reader.ReadUInt16(&len);
if (len != 0) {
error_ = QUIC_CRYPTO_INVALID_VALUE_LENGTH;

uint32 end_offset;
reader.ReadUInt32(&end_offset);

if (end_offset < last_end_offset) {
error_ = QUIC_CRYPTO_TAGS_OUT_OF_ORDER;
return false;
}
tags_and_lengths_.push_back(
make_pair(tag, static_cast<size_t>(end_offset - last_end_offset)));
last_end_offset = end_offset;
}
values_len_ = last_end_offset;
state_ = STATE_READING_VALUES;
}
case STATE_READING_VALUES:
if (reader.BytesRemaining() < values_len_) {
break;
}
for (int i = 0; i < num_entries_; ++i) {
for (vector<pair<CryptoTag, size_t> >::const_iterator
it = tags_and_lengths_.begin(); it != tags_and_lengths_.end();
it++) {
StringPiece value;
reader.ReadStringPiece(&value, tag_length_map_[tags_[i]]);
message_.SetStringPiece(tags_[i], value);
reader.ReadStringPiece(&value, it->second);
message_.SetStringPiece(it->first, value);
}
visitor_->OnHandshakeMessage(message_);
Clear();
Expand All @@ -165,18 +168,16 @@ QuicData* CryptoFramer::ConstructHandshakeMessage(
if (message.tag_value_map().size() > kMaxEntries) {
return NULL;
}
size_t len = sizeof(uint32); // message tag
size_t len = kCryptoTagSize; // message tag
len += sizeof(uint16); // number of map entries
len += sizeof(uint16); // padding.
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 += kCryptoTagSize; // tag
len += kCryptoEndOffsetSize; // end offset
len += it->second.length(); // value
++it;
}
if (message.tag_value_map().size() % 2 == 1) {
len += sizeof(uint16); // padding
}

QuicDataWriter writer(len);
if (!writer.WriteUInt32(message.tag())) {
Expand All @@ -187,29 +188,26 @@ QuicData* CryptoFramer::ConstructHandshakeMessage(
DCHECK(false) << "Failed to write size.";
return NULL;
}
// Tags
if (!writer.WriteUInt16(0)) {
DCHECK(false) << "Failed to write padding.";
return NULL;
}

uint32 end_offset = 0;
// Tags and offsets
for (it = message.tag_value_map().begin();
it != message.tag_value_map().end(); ++it) {
if (!writer.WriteUInt32(it->first)) {
DCHECK(false) << "Failed to write tag.";
return NULL;
}
}
// Lengths
for (it = message.tag_value_map().begin();
it != message.tag_value_map().end(); ++it) {
if (!writer.WriteUInt16(it->second.length())) {
DCHECK(false) << "Failed to write length.";
return NULL;
}
}
// Possible padding
if (message.tag_value_map().size() % 2 == 1) {
if (!writer.WriteUInt16(0)) {
DCHECK(false) << "Failed to write padding.";
end_offset += it->second.length();
if (!writer.WriteUInt32(end_offset)) {
DCHECK(false) << "Failed to write end offset.";
return NULL;
}
}

// Values
for (it = message.tag_value_map().begin();
it != message.tag_value_map().end(); ++it) {
Expand All @@ -223,8 +221,7 @@ QuicData* CryptoFramer::ConstructHandshakeMessage(

void CryptoFramer::Clear() {
message_.Clear();
tag_length_map_.clear();
tags_.clear();
tags_and_lengths_.clear();
error_ = QUIC_NO_ERROR;
state_ = STATE_READING_TAG;
}
Expand Down
14 changes: 6 additions & 8 deletions net/quic/crypto/crypto_framer.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
#ifndef NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_
#define NET_QUIC_CRYPTO_CRYPTO_FRAMER_H_

#include <map>
#include <utility>
#include <vector>

#include "base/basictypes.h"
#include "base/logging.h"
Expand Down Expand Up @@ -86,8 +87,7 @@ class NET_EXPORT_PRIVATE CryptoFramer {
enum CryptoFramerState {
STATE_READING_TAG,
STATE_READING_NUM_ENTRIES,
STATE_READING_KEY_TAGS,
STATE_READING_LENGTHS,
STATE_READING_TAGS_AND_LENGTHS,
STATE_READING_VALUES
};

Expand All @@ -103,11 +103,9 @@ class NET_EXPORT_PRIVATE CryptoFramer {
CryptoHandshakeMessage message_;
// Number of entires in the message currently being parsed.
uint16 num_entries_;
// Vector of tags in the message currently being parsed.
CryptoTagVector tags_;
// Length of the data associated with each tag in the message currently
// being parsed.
std::map<CryptoTag, size_t> tag_length_map_;
// tags_and_lengths_ contains the tags that are currently being parsed and
// their lengths.
std::vector<std::pair<CryptoTag, size_t> > tags_and_lengths_;
// Cumulative length of all values in the message currently being parsed.
size_t values_len_;
};
Expand Down
Loading

0 comments on commit 8ba8121

Please sign in to comment.