summaryrefslogtreecommitdiffstats
path: root/net/quic/crypto/crypto_framer.cc
blob: 806b336b1a2e3008583611cc2c72406e076e4f0a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
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