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