proxygen
EncryptedRecordLayer.cpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018-present, Facebook, Inc.
3  * All rights reserved.
4  *
5  * This source code is licensed under the BSD-style license found in the
6  * LICENSE file in the root directory of this source tree.
7  */
8 
10 
11 namespace fizz {
12 
14 using ProtocolVersionType =
16 
17 static constexpr uint16_t kMaxEncryptedRecordSize = 0x4000 + 256; // 16k + 256
18 static constexpr size_t kEncryptedHeaderSize =
19  sizeof(ContentType) + sizeof(ProtocolVersion) + sizeof(uint16_t);
20 
22  EncryptionLevel encryptionLevel)
23  : encryptionLevel_(encryptionLevel) {}
24 
26  folly::IOBufQueue& buf) {
27  while (true) {
28  folly::io::Cursor cursor(buf.front());
29 
30  if (buf.empty() || !cursor.canAdvance(kEncryptedHeaderSize)) {
31  return folly::none;
32  }
33 
34  std::array<uint8_t, kEncryptedHeaderSize> ad;
35  folly::io::Cursor adCursor(cursor);
36  adCursor.pull(ad.data(), ad.size());
38 
39  auto contentType =
40  static_cast<ContentType>(cursor.readBE<ContentTypeType>());
41  cursor.skip(sizeof(ProtocolVersion));
42 
43  auto length = cursor.readBE<uint16_t>();
44  if (length == 0) {
45  throw std::runtime_error("received 0 length encrypted record");
46  }
47  if (length > kMaxEncryptedRecordSize) {
48  throw std::runtime_error("received too long encrypted record");
49  }
50  if (buf.chainLength() < (cursor - buf.front()) + length) {
51  return folly::none;
52  }
53 
54  if (contentType == ContentType::alert && length == 2) {
55  auto alert = decode<Alert>(cursor);
56  throw std::runtime_error(folly::to<std::string>(
57  "received plaintext alert in encrypted record: ",
58  toString(alert.description)));
59  }
60 
61  std::unique_ptr<folly::IOBuf> encrypted;
62  cursor.clone(encrypted, length);
63  buf.trimStart(cursor - buf.front());
64 
65  if (contentType == ContentType::change_cipher_spec) {
66  encrypted->coalesce();
67  if (encrypted->length() == 1 && *encrypted->data() == 0x01) {
68  continue;
69  } else {
70  throw FizzException(
71  "received ccs", AlertDescription::illegal_parameter);
72  }
73  }
74 
75  TLSMessage msg;
77  throw std::runtime_error("max read seq num");
78  }
80  auto decryptAttempt = aead_->tryDecrypt(
81  std::move(encrypted), useAdditionalData_ ? &adBuf : nullptr, seqNum_);
82  if (decryptAttempt) {
83  seqNum_++;
84  skipFailedDecryption_ = false;
85  return decryptAttempt;
86  } else {
87  continue;
88  }
89  } else {
90  return aead_->decrypt(
91  std::move(encrypted),
92  useAdditionalData_ ? &adBuf : nullptr,
93  seqNum_++);
94  }
95  }
96 }
97 
99  folly::IOBufQueue& buf) {
100  auto decryptedBuf = getDecryptedBuf(buf);
101  if (!decryptedBuf) {
102  return folly::none;
103  }
104 
105  folly::IOBufQueue decrypted;
106  decrypted.append(std::move(*decryptedBuf));
107 
108  folly::io::Cursor paddingCursor(decrypted.front());
109  paddingCursor.advanceToEnd();
110  while (!*(paddingCursor -= 1).data()) {
111  }
112  TLSMessage msg;
113  msg.type = static_cast<ContentType>(paddingCursor.readBE<ContentTypeType>());
114  decrypted.trimEnd(paddingCursor.totalLength() + sizeof(ContentType));
115  msg.fragment = decrypted.move();
116 
117  switch (msg.type) {
119  case ContentType::alert:
121  break;
122  default:
123  throw std::runtime_error(folly::to<std::string>(
124  "received encrypted content type ",
125  static_cast<ContentTypeType>(msg.type)));
126  }
127 
128  if (!msg.fragment) {
129  if (msg.type == ContentType::application_data) {
131  } else {
132  throw std::runtime_error("received empty fragment");
133  }
134  }
135 
136  return std::move(msg);
137 }
138 
140  return encryptionLevel_;
141 }
142 
144  EncryptionLevel encryptionLevel)
145  : encryptionLevel_(encryptionLevel) {}
146 
148  folly::IOBufQueue queue;
149  queue.append(std::move(msg.fragment));
150  std::unique_ptr<folly::IOBuf> outBuf;
151  std::array<uint8_t, kEncryptedHeaderSize> headerBuf;
152  auto header = folly::IOBuf::wrapBufferAsValue(folly::range(headerBuf));
153  aead_->setEncryptedBufferHeadroom(kEncryptedHeaderSize);
154  while (!queue.empty()) {
155  auto dataBuf = getBufToEncrypt(queue);
156  // Currently we never send padding.
157 
158  // check if we have enough room to add the encrypted footer.
159  if (!dataBuf->isShared() &&
160  dataBuf->prev()->tailroom() >= sizeof(ContentType)) {
161  // extend it and add it
162  folly::io::Appender appender(dataBuf.get(), 0);
163  appender.writeBE(static_cast<ContentTypeType>(msg.type));
164  } else {
165  // not enough or shared - let's add enough for the tag as well
166  auto encryptedFooter = folly::IOBuf::create(
167  sizeof(ContentType) + aead_->getCipherOverhead());
168  folly::io::Appender appender(encryptedFooter.get(), 0);
169  appender.writeBE(static_cast<ContentTypeType>(msg.type));
170  dataBuf->prependChain(std::move(encryptedFooter));
171  }
172 
174  throw std::runtime_error("max write seq num");
175  }
176 
177  // we will either be able to memcpy directly into the ciphertext or
178  // need to create a new buf to insert before the ciphertext but we need
179  // it for additional data
180  header.clear();
181  folly::io::Appender appender(&header, 0);
182  appender.writeBE(
183  static_cast<ContentTypeType>(ContentType::application_data));
184  appender.writeBE(static_cast<ProtocolVersionType>(recordVersion_));
185  auto ciphertextLength =
186  dataBuf->computeChainDataLength() + aead_->getCipherOverhead();
187  appender.writeBE<uint16_t>(ciphertextLength);
188 
189  auto cipherText = aead_->encrypt(
190  std::move(dataBuf), useAdditionalData_ ? &header : nullptr, seqNum_++);
191 
192  std::unique_ptr<folly::IOBuf> record;
193  if (!cipherText->isShared() &&
194  cipherText->headroom() >= kEncryptedHeaderSize) {
195  // prepend and then write it in
196  cipherText->prepend(kEncryptedHeaderSize);
197  memcpy(cipherText->writableData(), header.data(), header.length());
198  record = std::move(cipherText);
199  } else {
200  record = folly::IOBuf::copyBuffer(header.data(), header.length());
201  record->prependChain(std::move(cipherText));
202  }
203 
204  if (!outBuf) {
205  outBuf = std::move(record);
206  } else {
207  outBuf->prependChain(std::move(record));
208  }
209  }
210 
211  if (!outBuf) {
212  outBuf = folly::IOBuf::create(0);
213  }
214 
215  TLSContent content;
216  content.data = std::move(outBuf);
217  content.contentType = msg.type;
219  return content;
220 }
221 
223  if (queue.front()->length() > maxRecord_) {
224  return queue.splitAtMost(maxRecord_);
225  } else if (queue.front()->length() >= desiredMinRecord_) {
226  return queue.pop_front();
227  } else {
228  return queue.splitAtMost(desiredMinRecord_);
229  }
230 }
231 
233  return encryptionLevel_;
234 }
235 } // namespace fizz
Buf fragment
Definition: Types.h:56
EncryptionLevel getEncryptionLevel() const override
const folly::IOBuf * front() const
Definition: IOBufQueue.h:476
folly::StringPiece toString(StateEnum state)
Definition: State.cpp:16
void append(std::unique_ptr< folly::IOBuf > &&buf, bool pack=false)
Definition: IOBufQueue.cpp:143
size_t chainLength() const
Definition: IOBufQueue.h:492
EncryptedReadRecordLayer(EncryptionLevel encryptionLevel)
std::unique_ptr< folly::IOBuf > splitAtMost(size_t n)
Definition: IOBufQueue.h:428
static std::unique_ptr< IOBuf > create(std::size_t capacity)
Definition: IOBuf.cpp:229
LogLevel max
Definition: LogLevel.cpp:31
ByteRange coalesce()
Definition: IOBuf.h:1095
folly::Optional< Buf > getDecryptedBuf(folly::IOBufQueue &buf)
PskType type
static constexpr uint16_t kMaxEncryptedRecordSize
bool empty() const
Definition: IOBufQueue.h:503
constexpr detail::Map< Move > move
Definition: Base-inl.h:2567
const uint8_t * data() const
Definition: IOBuf.h:499
std::unique_ptr< folly::IOBuf > move()
Definition: IOBufQueue.h:459
void pull(void *buf, size_t len)
Definition: Cursor.h:418
ProtocolVersion recordVersion_
Definition: RecordLayer.h:106
ProtocolVersion
Definition: Types.h:24
EncryptionLevel
Definition: Types.h:29
std::size_t length() const
Definition: IOBuf.h:533
constexpr Range< Iter > range(Iter first, Iter last)
Definition: Range.h:1114
EncryptionLevel encryptionLevel
Definition: RecordLayer.h:21
Definition: Actions.h:16
EncryptionLevel getEncryptionLevel() const override
EncryptedWriteRecordLayer(EncryptionLevel encryptionLevel)
TLSContent write(TLSMessage &&msg) const override
static constexpr size_t kEncryptedHeaderSize
Buf getBufToEncrypt(folly::IOBufQueue &queue) const
static IOBuf wrapBufferAsValue(const void *buf, std::size_t capacity) noexcept
Definition: IOBuf.cpp:357
void prependChain(std::unique_ptr< IOBuf > &&iobuf)
Definition: IOBuf.cpp:509
typename std::underlying_type< ProtocolVersion >::type ProtocolVersionType
ContentType contentType
Definition: RecordLayer.h:20
std::unique_ptr< folly::IOBuf > Buf
Definition: Types.h:22
void trimStart(size_t amount)
Definition: IOBufQueue.cpp:255
folly::Optional< TLSMessage > read(folly::IOBufQueue &buf) override
ContentType type
Definition: Types.h:55
typename std::underlying_type< ContentType >::type ContentTypeType
static std::unique_ptr< IOBuf > copyBuffer(const void *buf, std::size_t size, std::size_t headroom=0, std::size_t minTailroom=0)
Definition: IOBuf.h:1587
void trimEnd(size_t amount)
Definition: IOBufQueue.cpp:283
static constexpr uint64_t data[1]
Definition: Fingerprint.cpp:43
ContentType
Definition: Types.h:46
constexpr None none
Definition: Optional.h:87
std::unique_ptr< folly::IOBuf > pop_front()
Definition: IOBufQueue.cpp:316
void writeBE(T value)
Definition: Cursor.h:744