]>
Commit | Line | Data |
---|---|---|
ac3d0e13 | 1 | # Copyright 2016 The OpenSSL Project Authors. All Rights Reserved. |
631c1206 | 2 | # |
ac3d0e13 RS |
3 | # Licensed under the OpenSSL license (the "License"). You may not use |
4 | # this file except in compliance with the License. You can obtain a copy | |
5 | # in the file LICENSE in the source distribution or at | |
6 | # https://www.openssl.org/source/license.html | |
631c1206 MC |
7 | |
8 | use strict; | |
9 | ||
10 | package TLSProxy::Message; | |
11 | ||
12 | use constant TLS_MESSAGE_HEADER_LENGTH => 4; | |
13 | ||
14 | #Message types | |
15 | use constant { | |
16 | MT_HELLO_REQUEST => 0, | |
17 | MT_CLIENT_HELLO => 1, | |
18 | MT_SERVER_HELLO => 2, | |
19 | MT_NEW_SESSION_TICKET => 4, | |
e46f2334 | 20 | MT_ENCRYPTED_EXTENSIONS => 8, |
631c1206 MC |
21 | MT_CERTIFICATE => 11, |
22 | MT_SERVER_KEY_EXCHANGE => 12, | |
23 | MT_CERTIFICATE_REQUEST => 13, | |
24 | MT_SERVER_HELLO_DONE => 14, | |
25 | MT_CERTIFICATE_VERIFY => 15, | |
26 | MT_CLIENT_KEY_EXCHANGE => 16, | |
27 | MT_FINISHED => 20, | |
28 | MT_CERTIFICATE_STATUS => 22, | |
29 | MT_NEXT_PROTO => 67 | |
30 | }; | |
8af538e5 MC |
31 | |
32 | #Alert levels | |
33 | use constant { | |
34 | AL_LEVEL_WARN => 1, | |
35 | AL_LEVEL_FATAL => 2 | |
36 | }; | |
37 | ||
38 | #Alert descriptions | |
39 | use constant { | |
c3fd55d4 | 40 | AL_DESC_CLOSE_NOTIFY => 0, |
a2a0c86b MC |
41 | AL_DESC_UNEXPECTED_MESSAGE => 10, |
42 | AL_DESC_NO_RENEGOTIATION => 100 | |
8af538e5 MC |
43 | }; |
44 | ||
631c1206 MC |
45 | my %message_type = ( |
46 | MT_HELLO_REQUEST, "HelloRequest", | |
47 | MT_CLIENT_HELLO, "ClientHello", | |
48 | MT_SERVER_HELLO, "ServerHello", | |
49 | MT_NEW_SESSION_TICKET, "NewSessionTicket", | |
e46f2334 | 50 | MT_ENCRYPTED_EXTENSIONS, "EncryptedExtensions", |
631c1206 MC |
51 | MT_CERTIFICATE, "Certificate", |
52 | MT_SERVER_KEY_EXCHANGE, "ServerKeyExchange", | |
53 | MT_CERTIFICATE_REQUEST, "CertificateRequest", | |
54 | MT_SERVER_HELLO_DONE, "ServerHelloDone", | |
55 | MT_CERTIFICATE_VERIFY, "CertificateVerify", | |
56 | MT_CLIENT_KEY_EXCHANGE, "ClientKeyExchange", | |
57 | MT_FINISHED, "Finished", | |
58 | MT_CERTIFICATE_STATUS, "CertificateStatus", | |
59 | MT_NEXT_PROTO, "NextProto" | |
60 | ); | |
61 | ||
aa474d1f | 62 | use constant { |
9ce3ed2a | 63 | EXT_SERVER_NAME => 0, |
aa474d1f | 64 | EXT_STATUS_REQUEST => 5, |
5a8e54d9 | 65 | EXT_SUPPORTED_GROUPS => 10, |
9ce3ed2a MC |
66 | EXT_EC_POINT_FORMATS => 11, |
67 | EXT_SRP => 12, | |
68 | EXT_SIG_ALGS => 13, | |
69 | EXT_USE_SRTP => 14, | |
70 | EXT_ALPN => 16, | |
71 | EXT_SCT => 18, | |
72 | EXT_PADDING => 21, | |
aa474d1f EK |
73 | EXT_ENCRYPT_THEN_MAC => 22, |
74 | EXT_EXTENDED_MASTER_SECRET => 23, | |
75 | EXT_SESSION_TICKET => 35, | |
5a8e54d9 | 76 | EXT_KEY_SHARE => 40, |
9ce3ed2a MC |
77 | EXT_SUPPORTED_VERSIONS => 43, |
78 | EXT_RENEGOTIATE => 65281, | |
79 | EXT_NPN => 13172, | |
70af3d8e MC |
80 | # This extension is an unofficial extension only ever written by OpenSSL |
81 | # (i.e. not read), and even then only when enabled. We use it to test | |
82 | # handling of duplicate extensions. | |
83 | EXT_DUPLICATE_EXTENSION => 0xfde8 | |
aa474d1f EK |
84 | }; |
85 | ||
631c1206 MC |
86 | my $payload = ""; |
87 | my $messlen = -1; | |
88 | my $mt; | |
89 | my $startoffset = -1; | |
90 | my $server = 0; | |
91 | my $success = 0; | |
92 | my $end = 0; | |
93 | my @message_rec_list = (); | |
94 | my @message_frag_lens = (); | |
a1accbb1 | 95 | my $ciphersuite = 0; |
631c1206 MC |
96 | |
97 | sub clear | |
98 | { | |
99 | $payload = ""; | |
100 | $messlen = -1; | |
101 | $startoffset = -1; | |
102 | $server = 0; | |
103 | $success = 0; | |
104 | $end = 0; | |
105 | @message_rec_list = (); | |
106 | @message_frag_lens = (); | |
107 | } | |
108 | ||
109 | #Class method to extract messages from a record | |
110 | sub get_messages | |
111 | { | |
112 | my $class = shift; | |
113 | my $serverin = shift; | |
114 | my $record = shift; | |
115 | my @messages = (); | |
116 | my $message; | |
117 | ||
a1accbb1 MC |
118 | @message_frag_lens = (); |
119 | ||
631c1206 MC |
120 | if ($serverin != $server && length($payload) != 0) { |
121 | die "Changed peer, but we still have fragment data\n"; | |
122 | } | |
123 | $server = $serverin; | |
124 | ||
125 | if ($record->content_type == TLSProxy::Record::RT_CCS) { | |
126 | if ($payload ne "") { | |
127 | #We can't handle this yet | |
128 | die "CCS received before message data complete\n"; | |
129 | } | |
130 | if ($server) { | |
9970290e | 131 | TLSProxy::Record->server_encrypting(1); |
631c1206 | 132 | } else { |
9970290e | 133 | TLSProxy::Record->client_encrypting(1); |
631c1206 MC |
134 | } |
135 | } elsif ($record->content_type == TLSProxy::Record::RT_HANDSHAKE) { | |
136 | if ($record->len == 0 || $record->len_real == 0) { | |
137 | print " Message truncated\n"; | |
138 | } else { | |
139 | my $recoffset = 0; | |
140 | ||
141 | if (length $payload > 0) { | |
142 | #We are continuing processing a message started in a previous | |
143 | #record. Add this record to the list associated with this | |
144 | #message | |
145 | push @message_rec_list, $record; | |
146 | ||
147 | if ($messlen <= length($payload)) { | |
148 | #Shouldn't happen | |
149 | die "Internal error: invalid messlen: ".$messlen | |
150 | ." payload length:".length($payload)."\n"; | |
151 | } | |
152 | if (length($payload) + $record->decrypt_len >= $messlen) { | |
153 | #We can complete the message with this record | |
154 | $recoffset = $messlen - length($payload); | |
155 | $payload .= substr($record->decrypt_data, 0, $recoffset); | |
156 | push @message_frag_lens, $recoffset; | |
157 | $message = create_message($server, $mt, $payload, | |
158 | $startoffset); | |
159 | push @messages, $message; | |
160 | ||
631c1206 MC |
161 | $payload = ""; |
162 | } else { | |
163 | #This is just part of the total message | |
164 | $payload .= $record->decrypt_data; | |
165 | $recoffset = $record->decrypt_len; | |
166 | push @message_frag_lens, $record->decrypt_len; | |
167 | } | |
168 | print " Partial message data read: ".$recoffset." bytes\n"; | |
169 | } | |
170 | ||
171 | while ($record->decrypt_len > $recoffset) { | |
172 | #We are at the start of a new message | |
173 | if ($record->decrypt_len - $recoffset < 4) { | |
174 | #Whilst technically probably valid we can't cope with this | |
175 | die "End of record in the middle of a message header\n"; | |
176 | } | |
177 | @message_rec_list = ($record); | |
178 | my $lenhi; | |
179 | my $lenlo; | |
180 | ($mt, $lenhi, $lenlo) = unpack('CnC', | |
181 | substr($record->decrypt_data, | |
182 | $recoffset)); | |
183 | $messlen = ($lenhi << 8) | $lenlo; | |
184 | print " Message type: $message_type{$mt}\n"; | |
185 | print " Message Length: $messlen\n"; | |
186 | $startoffset = $recoffset; | |
187 | $recoffset += 4; | |
188 | $payload = ""; | |
189 | ||
190 | if ($recoffset < $record->decrypt_len) { | |
191 | #Some payload data is present in this record | |
192 | if ($record->decrypt_len - $recoffset >= $messlen) { | |
193 | #We can complete the message with this record | |
194 | $payload .= substr($record->decrypt_data, $recoffset, | |
195 | $messlen); | |
196 | $recoffset += $messlen; | |
197 | push @message_frag_lens, $messlen; | |
198 | $message = create_message($server, $mt, $payload, | |
199 | $startoffset); | |
200 | push @messages, $message; | |
201 | ||
631c1206 MC |
202 | $payload = ""; |
203 | } else { | |
204 | #This is just part of the total message | |
205 | $payload .= substr($record->decrypt_data, $recoffset, | |
206 | $record->decrypt_len - $recoffset); | |
207 | $recoffset = $record->decrypt_len; | |
208 | push @message_frag_lens, $recoffset; | |
209 | } | |
210 | } | |
211 | } | |
212 | } | |
213 | } elsif ($record->content_type == TLSProxy::Record::RT_APPLICATION_DATA) { | |
214 | print " [ENCRYPTED APPLICATION DATA]\n"; | |
215 | print " [".$record->decrypt_data."]\n"; | |
216 | } elsif ($record->content_type == TLSProxy::Record::RT_ALERT) { | |
8af538e5 | 217 | my ($alertlev, $alertdesc) = unpack('CC', $record->decrypt_data); |
8af538e5 MC |
218 | #A CloseNotify from the client indicates we have finished successfully |
219 | #(we assume) | |
8523288e | 220 | if (!$end && !$server && $alertlev == AL_LEVEL_WARN |
8af538e5 MC |
221 | && $alertdesc == AL_DESC_CLOSE_NOTIFY) { |
222 | $success = 1; | |
223 | } | |
8523288e DB |
224 | #All alerts end the test |
225 | $end = 1; | |
631c1206 MC |
226 | } |
227 | ||
228 | return @messages; | |
229 | } | |
230 | ||
231 | #Function to work out which sub-class we need to create and then | |
232 | #construct it | |
233 | sub create_message | |
234 | { | |
235 | my ($server, $mt, $data, $startoffset) = @_; | |
236 | my $message; | |
237 | ||
238 | #We only support ClientHello in this version...needs to be extended for | |
239 | #others | |
240 | if ($mt == MT_CLIENT_HELLO) { | |
241 | $message = TLSProxy::ClientHello->new( | |
242 | $server, | |
243 | $data, | |
244 | [@message_rec_list], | |
245 | $startoffset, | |
246 | [@message_frag_lens] | |
247 | ); | |
248 | $message->parse(); | |
a1accbb1 MC |
249 | } elsif ($mt == MT_SERVER_HELLO) { |
250 | $message = TLSProxy::ServerHello->new( | |
251 | $server, | |
252 | $data, | |
253 | [@message_rec_list], | |
254 | $startoffset, | |
255 | [@message_frag_lens] | |
256 | ); | |
9ce3ed2a MC |
257 | $message->parse(); |
258 | } elsif ($mt == MT_ENCRYPTED_EXTENSIONS) { | |
259 | $message = TLSProxy::EncryptedExtensions->new( | |
260 | $server, | |
261 | $data, | |
262 | [@message_rec_list], | |
263 | $startoffset, | |
264 | [@message_frag_lens] | |
265 | ); | |
a1accbb1 MC |
266 | $message->parse(); |
267 | } elsif ($mt == MT_SERVER_KEY_EXCHANGE) { | |
268 | $message = TLSProxy::ServerKeyExchange->new( | |
269 | $server, | |
270 | $data, | |
271 | [@message_rec_list], | |
272 | $startoffset, | |
273 | [@message_frag_lens] | |
7f6d90ac EK |
274 | ); |
275 | $message->parse(); | |
276 | } elsif ($mt == MT_NEW_SESSION_TICKET) { | |
277 | $message = TLSProxy::NewSessionTicket->new( | |
278 | $server, | |
279 | $data, | |
280 | [@message_rec_list], | |
281 | $startoffset, | |
282 | [@message_frag_lens] | |
a1accbb1 MC |
283 | ); |
284 | $message->parse(); | |
631c1206 MC |
285 | } else { |
286 | #Unknown message type | |
287 | $message = TLSProxy::Message->new( | |
288 | $server, | |
289 | $mt, | |
290 | $data, | |
291 | [@message_rec_list], | |
292 | $startoffset, | |
293 | [@message_frag_lens] | |
294 | ); | |
295 | } | |
296 | ||
297 | return $message; | |
298 | } | |
299 | ||
300 | sub end | |
301 | { | |
302 | my $class = shift; | |
303 | return $end; | |
304 | } | |
305 | sub success | |
306 | { | |
307 | my $class = shift; | |
308 | return $success; | |
309 | } | |
a1accbb1 MC |
310 | sub fail |
311 | { | |
312 | my $class = shift; | |
313 | return !$success && $end; | |
314 | } | |
631c1206 MC |
315 | sub new |
316 | { | |
317 | my $class = shift; | |
318 | my ($server, | |
319 | $mt, | |
320 | $data, | |
321 | $records, | |
322 | $startoffset, | |
323 | $message_frag_lens) = @_; | |
324 | ||
325 | my $self = { | |
326 | server => $server, | |
327 | data => $data, | |
328 | records => $records, | |
329 | mt => $mt, | |
330 | startoffset => $startoffset, | |
331 | message_frag_lens => $message_frag_lens | |
332 | }; | |
333 | ||
334 | return bless $self, $class; | |
335 | } | |
336 | ||
a1accbb1 MC |
337 | sub ciphersuite |
338 | { | |
339 | my $class = shift; | |
340 | if (@_) { | |
341 | $ciphersuite = shift; | |
342 | } | |
343 | return $ciphersuite; | |
344 | } | |
345 | ||
631c1206 MC |
346 | #Update all the underlying records with the modified data from this message |
347 | #Note: Does not currently support re-encrypting | |
348 | sub repack | |
349 | { | |
350 | my $self = shift; | |
351 | my $msgdata; | |
352 | ||
353 | my $numrecs = $#{$self->records}; | |
354 | ||
355 | $self->set_message_contents(); | |
356 | ||
357 | my $lenhi; | |
358 | my $lenlo; | |
359 | ||
360 | $lenlo = length($self->data) & 0xff; | |
361 | $lenhi = length($self->data) >> 8; | |
4deefd65 | 362 | $msgdata = pack('CnC', $self->mt, $lenhi, $lenlo).$self->data; |
631c1206 | 363 | |
631c1206 MC |
364 | if ($numrecs == 0) { |
365 | #The message is fully contained within one record | |
366 | my ($rec) = @{$self->records}; | |
367 | my $recdata = $rec->decrypt_data; | |
368 | ||
cf7f8592 EK |
369 | my $old_length; |
370 | ||
371 | # We use empty message_frag_lens to indicates that pre-repacking, | |
372 | # the message wasn't present. The first fragment length doesn't include | |
373 | # the TLS header, so we need to check and compute the right length. | |
374 | if (@{$self->message_frag_lens}) { | |
375 | $old_length = ${$self->message_frag_lens}[0] + | |
376 | TLS_MESSAGE_HEADER_LENGTH; | |
377 | } else { | |
378 | $old_length = 0; | |
631c1206 MC |
379 | } |
380 | ||
cf7f8592 EK |
381 | my $prefix = substr($recdata, 0, $self->startoffset); |
382 | my $suffix = substr($recdata, $self->startoffset + $old_length); | |
383 | ||
384 | $rec->decrypt_data($prefix.($msgdata).($suffix)); | |
385 | # TODO(openssl-team): don't keep explicit lengths. | |
386 | # (If a length override is ever needed to construct invalid packets, | |
387 | # use an explicit override field instead.) | |
388 | $rec->decrypt_len(length($rec->decrypt_data)); | |
389 | $rec->len($rec->len + length($msgdata) - $old_length); | |
390 | # Don't support re-encryption. | |
391 | $rec->data($rec->decrypt_data); | |
631c1206 MC |
392 | |
393 | #Update the fragment len in case we changed it above | |
394 | ${$self->message_frag_lens}[0] = length($msgdata) | |
395 | - TLS_MESSAGE_HEADER_LENGTH; | |
396 | return; | |
397 | } | |
398 | ||
399 | #Note we don't currently support changing a fragmented message length | |
400 | my $recctr = 0; | |
401 | my $datadone = 0; | |
402 | foreach my $rec (@{$self->records}) { | |
403 | my $recdata = $rec->decrypt_data; | |
404 | if ($recctr == 0) { | |
405 | #This is the first record | |
406 | my $remainlen = length($recdata) - $self->startoffset; | |
407 | $rec->data(substr($recdata, 0, $self->startoffset) | |
408 | .substr(($msgdata), 0, $remainlen)); | |
409 | $datadone += $remainlen; | |
410 | } elsif ($recctr + 1 == $numrecs) { | |
411 | #This is the last record | |
412 | $rec->data(substr($msgdata, $datadone)); | |
413 | } else { | |
414 | #This is a middle record | |
415 | $rec->data(substr($msgdata, $datadone, length($rec->data))); | |
416 | $datadone += length($rec->data); | |
417 | } | |
418 | $recctr++; | |
419 | } | |
420 | } | |
421 | ||
422 | #To be overridden by sub-classes | |
423 | sub set_message_contents | |
424 | { | |
425 | } | |
426 | ||
427 | #Read only accessors | |
428 | sub server | |
429 | { | |
430 | my $self = shift; | |
431 | return $self->{server}; | |
432 | } | |
433 | ||
434 | #Read/write accessors | |
435 | sub mt | |
436 | { | |
437 | my $self = shift; | |
438 | if (@_) { | |
439 | $self->{mt} = shift; | |
440 | } | |
441 | return $self->{mt}; | |
442 | } | |
443 | sub data | |
444 | { | |
445 | my $self = shift; | |
446 | if (@_) { | |
447 | $self->{data} = shift; | |
448 | } | |
449 | return $self->{data}; | |
450 | } | |
451 | sub records | |
452 | { | |
453 | my $self = shift; | |
454 | if (@_) { | |
455 | $self->{records} = shift; | |
456 | } | |
457 | return $self->{records}; | |
458 | } | |
459 | sub startoffset | |
460 | { | |
461 | my $self = shift; | |
462 | if (@_) { | |
463 | $self->{startoffset} = shift; | |
464 | } | |
465 | return $self->{startoffset}; | |
466 | } | |
467 | sub message_frag_lens | |
468 | { | |
469 | my $self = shift; | |
470 | if (@_) { | |
471 | $self->{message_frag_lens} = shift; | |
472 | } | |
473 | return $self->{message_frag_lens}; | |
474 | } | |
cf7f8592 EK |
475 | sub encoded_length |
476 | { | |
477 | my $self = shift; | |
478 | return TLS_MESSAGE_HEADER_LENGTH + length($self->data); | |
479 | } | |
631c1206 MC |
480 | |
481 | 1; |