1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 #include <algorithm>
6 #include "shared.h"
7 #include "tls_parser.h"
8 
9 #include "ssl.h"
10 extern "C" {
11 #include "sslimpl.h"
12 }
13 
14 using namespace nss_test;
15 
16 // Number of additional bytes in the TLS header.
17 // Used to properly skip DTLS seqnums.
18 static size_t gExtraHeaderBytes = 0;
19 
20 // Helper class to simplify TLS record manipulation.
21 class Record {
22  public:
Create(const uint8_t * data,size_t size,size_t remaining)23   static std::unique_ptr<Record> Create(const uint8_t *data, size_t size,
24                                         size_t remaining) {
25     return std::unique_ptr<Record>(new Record(data, size, remaining));
26   }
27 
insert_before(const std::unique_ptr<Record> & other)28   void insert_before(const std::unique_ptr<Record> &other) {
29     assert(data_ && size_ > 0);
30 
31     // Copy data in case other == this.
32     uint8_t buf[size_];
33     memcpy(buf, data_, size_);
34 
35     uint8_t *dest = const_cast<uint8_t *>(other->data());
36     // Make room for the record we want to insert.
37     memmove(dest + size_, other->data(), other->size() + other->remaining());
38     // Insert the record.
39     memcpy(dest, buf, size_);
40   }
41 
truncate(size_t length)42   void truncate(size_t length) {
43     assert(length >= 5 + gExtraHeaderBytes);
44     uint8_t *dest = const_cast<uint8_t *>(data_);
45     size_t l = length - (5 + gExtraHeaderBytes);
46     dest[3] = (l >> 8) & 0xff;
47     dest[4] = l & 0xff;
48     memmove(dest + length, data_ + size_, remaining_);
49   }
50 
drop()51   void drop() {
52     uint8_t *dest = const_cast<uint8_t *>(data_);
53     memmove(dest, data_ + size_, remaining_);
54   }
55 
data()56   const uint8_t *data() { return data_; }
remaining()57   size_t remaining() { return remaining_; }
size()58   size_t size() { return size_; }
59 
60  private:
Record(const uint8_t * data,size_t size,size_t remaining)61   Record(const uint8_t *data, size_t size, size_t remaining)
62       : data_(data), remaining_(remaining), size_(size) {}
63 
64   const uint8_t *data_;
65   size_t remaining_;
66   size_t size_;
67 };
68 
69 // Parse records contained in a given TLS transcript.
ParseRecords(const uint8_t * data,size_t size)70 std::vector<std::unique_ptr<Record>> ParseRecords(const uint8_t *data,
71                                                   size_t size) {
72   std::vector<std::unique_ptr<Record>> records;
73   TlsParser parser(data, size);
74 
75   while (parser.remaining()) {
76     size_t offset = parser.consumed();
77 
78     // Skip type, version, and DTLS seqnums.
79     if (!parser.Skip(3 + gExtraHeaderBytes)) {
80       break;
81     }
82 
83     DataBuffer fragment;
84     if (!parser.ReadVariable(&fragment, 2)) {
85       break;
86     }
87 
88     records.push_back(Record::Create(data + offset,
89                                      fragment.len() + 5 + gExtraHeaderBytes,
90                                      parser.remaining()));
91   }
92 
93   return records;
94 }
95 
96 namespace TlsMutators {
97 
98 // Handle seqnums in DTLS transcripts.
SetIsDTLS()99 void SetIsDTLS() { gExtraHeaderBytes = 8; }
100 
101 // Mutator that drops whole TLS records.
DropRecord(uint8_t * data,size_t size,size_t max_size,unsigned int seed)102 size_t DropRecord(uint8_t *data, size_t size, size_t max_size,
103                   unsigned int seed) {
104   std::mt19937 rng(seed);
105 
106   // Find TLS records in the corpus.
107   auto records = ParseRecords(data, size);
108   if (records.empty()) {
109     return 0;
110   }
111 
112   // Pick a record to drop at random.
113   std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
114   auto &rec = records.at(dist(rng));
115 
116   // Drop the record.
117   rec->drop();
118 
119   // Return the new final size.
120   return size - rec->size();
121 }
122 
123 // Mutator that shuffles TLS records in a transcript.
ShuffleRecords(uint8_t * data,size_t size,size_t max_size,unsigned int seed)124 size_t ShuffleRecords(uint8_t *data, size_t size, size_t max_size,
125                       unsigned int seed) {
126   std::mt19937 rng(seed);
127 
128   // Store the original corpus.
129   uint8_t buf[size];
130   memcpy(buf, data, size);
131 
132   // Find TLS records in the corpus.
133   auto records = ParseRecords(buf, sizeof(buf));
134   if (records.empty()) {
135     return 0;
136   }
137 
138   // Find offset of first record in target buffer.
139   uint8_t *dest = const_cast<uint8_t *>(ParseRecords(data, size).at(0)->data());
140 
141   // Shuffle record order.
142   std::shuffle(records.begin(), records.end(), rng);
143 
144   // Write records to their new positions.
145   for (auto &rec : records) {
146     memcpy(dest, rec->data(), rec->size());
147     dest += rec->size();
148   }
149 
150   // Final size hasn't changed.
151   return size;
152 }
153 
154 // Mutator that duplicates a single TLS record and randomly inserts it.
DuplicateRecord(uint8_t * data,size_t size,size_t max_size,unsigned int seed)155 size_t DuplicateRecord(uint8_t *data, size_t size, size_t max_size,
156                        unsigned int seed) {
157   std::mt19937 rng(seed);
158 
159   // Find TLS records in the corpus.
160   const auto records = ParseRecords(data, size);
161   if (records.empty()) {
162     return 0;
163   }
164 
165   // Pick a record to duplicate at random.
166   std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
167   auto &rec = records.at(dist(rng));
168   if (size + rec->size() > max_size) {
169     return 0;
170   }
171 
172   // Insert before random record.
173   rec->insert_before(records.at(dist(rng)));
174 
175   // Return the new final size.
176   return size + rec->size();
177 }
178 
179 // Mutator that truncates a TLS record.
TruncateRecord(uint8_t * data,size_t size,size_t max_size,unsigned int seed)180 size_t TruncateRecord(uint8_t *data, size_t size, size_t max_size,
181                       unsigned int seed) {
182   std::mt19937 rng(seed);
183 
184   // Find TLS records in the corpus.
185   const auto records = ParseRecords(data, size);
186   if (records.empty()) {
187     return 0;
188   }
189 
190   // Pick a record to truncate at random.
191   std::uniform_int_distribution<size_t> dist(0, records.size() - 1);
192   auto &rec = records.at(dist(rng));
193 
194   // Need a record with data.
195   if (rec->size() <= 5 + gExtraHeaderBytes) {
196     return 0;
197   }
198 
199   // Truncate.
200   std::uniform_int_distribution<size_t> dist2(5 + gExtraHeaderBytes,
201                                               rec->size() - 1);
202   size_t new_length = dist2(rng);
203   rec->truncate(new_length);
204 
205   // Return the new final size.
206   return size + new_length - rec->size();
207 }
208 
209 // Mutator that splits a TLS record in two.
FragmentRecord(uint8_t * data,size_t size,size_t max_size,unsigned int seed)210 size_t FragmentRecord(uint8_t *data, size_t size, size_t max_size,
211                       unsigned int seed) {
212   std::mt19937 rng(seed);
213 
214   // We can't deal with DTLS yet.
215   if (gExtraHeaderBytes > 0) {
216     return 0;
217   }
218 
219   if (size + 5 > max_size) {
220     return 0;
221   }
222 
223   // Find TLS records in the corpus.
224   const auto records = ParseRecords(data, size);
225   if (records.empty()) {
226     return 0;
227   }
228 
229   // Pick a record to fragment at random.
230   std::uniform_int_distribution<size_t> rand_record(0, records.size() - 1);
231   auto &rec = records.at(rand_record(rng));
232   uint8_t *rdata = const_cast<uint8_t *>(rec->data());
233   size_t length = rec->size();
234   size_t content_length = length - 5;
235 
236   if (content_length < 2) {
237     return 0;
238   }
239 
240   // Assign a new length to the first fragment.
241   std::uniform_int_distribution<size_t> rand_size(1, content_length - 1);
242   size_t first_length = rand_size(rng);
243   size_t second_length = content_length - first_length;
244   rdata[3] = (first_length >> 8) & 0xff;
245   rdata[4] = first_length & 0xff;
246   uint8_t *second_record = rdata + 5 + first_length;
247 
248   // Make room for the header of the second record.
249   memmove(second_record + 5, second_record,
250           rec->remaining() + content_length - first_length);
251 
252   // Write second header.
253   memcpy(second_record, rdata, 3);
254   second_record[3] = (second_length >> 8) & 0xff;
255   second_record[4] = second_length & 0xff;
256 
257   return size + 5;
258 }
259 
260 // Cross-over function that merges and shuffles two transcripts.
CrossOver(const uint8_t * data1,size_t size1,const uint8_t * data2,size_t size2,uint8_t * out,size_t max_out_size,unsigned int seed)261 size_t CrossOver(const uint8_t *data1, size_t size1, const uint8_t *data2,
262                  size_t size2, uint8_t *out, size_t max_out_size,
263                  unsigned int seed) {
264   std::mt19937 rng(seed);
265 
266   // Find TLS records in the corpus.
267   auto records1 = ParseRecords(data1, size1);
268   if (records1.empty()) {
269     return 0;
270   }
271 
272   {  // Merge the two vectors.
273     auto records2 = ParseRecords(data2, size2);
274     if (records2.empty()) {
275       return 0;
276     }
277     std::move(records2.begin(), records2.end(), std::back_inserter(records1));
278   }
279 
280   // Shuffle record order.
281   std::shuffle(records1.begin(), records1.end(), rng);
282 
283   size_t total = 0;
284   for (auto &rec : records1) {
285     size_t length = rec->size();
286     if (total + length > max_out_size) {
287       break;
288     }
289 
290     // Write record to its new position.
291     memcpy(out + total, rec->data(), length);
292     total += length;
293   }
294 
295   return total;
296 }
297 
298 }  // namespace TlsMutators
299