1e1051a39Sopenharmony_ci# Copyright 2016 The OpenSSL Project Authors. All Rights Reserved.
2e1051a39Sopenharmony_ci#
3e1051a39Sopenharmony_ci# Licensed under the Apache License 2.0 (the "License").  You may not use
4e1051a39Sopenharmony_ci# this file except in compliance with the License.  You can obtain a copy
5e1051a39Sopenharmony_ci# in the file LICENSE in the source distribution or at
6e1051a39Sopenharmony_ci# https://www.openssl.org/source/license.html
7e1051a39Sopenharmony_ci
8e1051a39Sopenharmony_ciuse strict;
9e1051a39Sopenharmony_ci
10e1051a39Sopenharmony_cipackage TLSProxy::ClientHello;
11e1051a39Sopenharmony_ci
12e1051a39Sopenharmony_ciuse vars '@ISA';
13e1051a39Sopenharmony_cipush @ISA, 'TLSProxy::Message';
14e1051a39Sopenharmony_ci
15e1051a39Sopenharmony_cisub new
16e1051a39Sopenharmony_ci{
17e1051a39Sopenharmony_ci    my $class = shift;
18e1051a39Sopenharmony_ci    my ($server,
19e1051a39Sopenharmony_ci        $data,
20e1051a39Sopenharmony_ci        $records,
21e1051a39Sopenharmony_ci        $startoffset,
22e1051a39Sopenharmony_ci        $message_frag_lens) = @_;
23e1051a39Sopenharmony_ci
24e1051a39Sopenharmony_ci    my $self = $class->SUPER::new(
25e1051a39Sopenharmony_ci        $server,
26e1051a39Sopenharmony_ci        1,
27e1051a39Sopenharmony_ci        $data,
28e1051a39Sopenharmony_ci        $records,
29e1051a39Sopenharmony_ci        $startoffset,
30e1051a39Sopenharmony_ci        $message_frag_lens);
31e1051a39Sopenharmony_ci
32e1051a39Sopenharmony_ci    $self->{client_version} = 0;
33e1051a39Sopenharmony_ci    $self->{random} = [];
34e1051a39Sopenharmony_ci    $self->{session_id_len} = 0;
35e1051a39Sopenharmony_ci    $self->{session} = "";
36e1051a39Sopenharmony_ci    $self->{ciphersuite_len} = 0;
37e1051a39Sopenharmony_ci    $self->{ciphersuites} = [];
38e1051a39Sopenharmony_ci    $self->{comp_meth_len} = 0;
39e1051a39Sopenharmony_ci    $self->{comp_meths} = [];
40e1051a39Sopenharmony_ci    $self->{extensions_len} = 0;
41e1051a39Sopenharmony_ci    $self->{extension_data} = "";
42e1051a39Sopenharmony_ci
43e1051a39Sopenharmony_ci    return $self;
44e1051a39Sopenharmony_ci}
45e1051a39Sopenharmony_ci
46e1051a39Sopenharmony_cisub parse
47e1051a39Sopenharmony_ci{
48e1051a39Sopenharmony_ci    my $self = shift;
49e1051a39Sopenharmony_ci    my $ptr = 2;
50e1051a39Sopenharmony_ci    my ($client_version) = unpack('n', $self->data);
51e1051a39Sopenharmony_ci    my $random = substr($self->data, $ptr, 32);
52e1051a39Sopenharmony_ci    $ptr += 32;
53e1051a39Sopenharmony_ci    my $session_id_len = unpack('C', substr($self->data, $ptr));
54e1051a39Sopenharmony_ci    $ptr++;
55e1051a39Sopenharmony_ci    my $session = substr($self->data, $ptr, $session_id_len);
56e1051a39Sopenharmony_ci    $ptr += $session_id_len;
57e1051a39Sopenharmony_ci    my $ciphersuite_len = unpack('n', substr($self->data, $ptr));
58e1051a39Sopenharmony_ci    $ptr += 2;
59e1051a39Sopenharmony_ci    my @ciphersuites = unpack('n*', substr($self->data, $ptr,
60e1051a39Sopenharmony_ci                                           $ciphersuite_len));
61e1051a39Sopenharmony_ci    $ptr += $ciphersuite_len;
62e1051a39Sopenharmony_ci    my $comp_meth_len = unpack('C', substr($self->data, $ptr));
63e1051a39Sopenharmony_ci    $ptr++;
64e1051a39Sopenharmony_ci    my @comp_meths = unpack('C*', substr($self->data, $ptr, $comp_meth_len));
65e1051a39Sopenharmony_ci    $ptr += $comp_meth_len;
66e1051a39Sopenharmony_ci    my $extensions_len = unpack('n', substr($self->data, $ptr));
67e1051a39Sopenharmony_ci    $ptr += 2;
68e1051a39Sopenharmony_ci    #For now we just deal with this as a block of data. In the future we will
69e1051a39Sopenharmony_ci    #want to parse this
70e1051a39Sopenharmony_ci    my $extension_data = substr($self->data, $ptr);
71e1051a39Sopenharmony_ci
72e1051a39Sopenharmony_ci    if (length($extension_data) != $extensions_len) {
73e1051a39Sopenharmony_ci        die "Invalid extension length\n";
74e1051a39Sopenharmony_ci    }
75e1051a39Sopenharmony_ci    my %extensions = ();
76e1051a39Sopenharmony_ci    while (length($extension_data) >= 4) {
77e1051a39Sopenharmony_ci        my ($type, $size) = unpack("nn", $extension_data);
78e1051a39Sopenharmony_ci        my $extdata = substr($extension_data, 4, $size);
79e1051a39Sopenharmony_ci        $extension_data = substr($extension_data, 4 + $size);
80e1051a39Sopenharmony_ci        $extensions{$type} = $extdata;
81e1051a39Sopenharmony_ci    }
82e1051a39Sopenharmony_ci
83e1051a39Sopenharmony_ci    $self->client_version($client_version);
84e1051a39Sopenharmony_ci    $self->random($random);
85e1051a39Sopenharmony_ci    $self->session_id_len($session_id_len);
86e1051a39Sopenharmony_ci    $self->session($session);
87e1051a39Sopenharmony_ci    $self->ciphersuite_len($ciphersuite_len);
88e1051a39Sopenharmony_ci    $self->ciphersuites(\@ciphersuites);
89e1051a39Sopenharmony_ci    $self->comp_meth_len($comp_meth_len);
90e1051a39Sopenharmony_ci    $self->comp_meths(\@comp_meths);
91e1051a39Sopenharmony_ci    $self->extensions_len($extensions_len);
92e1051a39Sopenharmony_ci    $self->extension_data(\%extensions);
93e1051a39Sopenharmony_ci
94e1051a39Sopenharmony_ci    $self->process_extensions();
95e1051a39Sopenharmony_ci
96e1051a39Sopenharmony_ci    print "    Client Version:".$client_version."\n";
97e1051a39Sopenharmony_ci    print "    Session ID Len:".$session_id_len."\n";
98e1051a39Sopenharmony_ci    print "    Ciphersuite len:".$ciphersuite_len."\n";
99e1051a39Sopenharmony_ci    print "    Compression Method Len:".$comp_meth_len."\n";
100e1051a39Sopenharmony_ci    print "    Extensions Len:".$extensions_len."\n";
101e1051a39Sopenharmony_ci}
102e1051a39Sopenharmony_ci
103e1051a39Sopenharmony_ci#Perform any actions necessary based on the extensions we've seen
104e1051a39Sopenharmony_cisub process_extensions
105e1051a39Sopenharmony_ci{
106e1051a39Sopenharmony_ci    my $self = shift;
107e1051a39Sopenharmony_ci    my %extensions = %{$self->extension_data};
108e1051a39Sopenharmony_ci
109e1051a39Sopenharmony_ci    #Clear any state from a previous run
110e1051a39Sopenharmony_ci    TLSProxy::Record->etm(0);
111e1051a39Sopenharmony_ci
112e1051a39Sopenharmony_ci    if (exists $extensions{TLSProxy::Message::EXT_ENCRYPT_THEN_MAC}) {
113e1051a39Sopenharmony_ci        TLSProxy::Record->etm(1);
114e1051a39Sopenharmony_ci    }
115e1051a39Sopenharmony_ci}
116e1051a39Sopenharmony_ci
117e1051a39Sopenharmony_cisub extension_contents
118e1051a39Sopenharmony_ci{
119e1051a39Sopenharmony_ci    my $self = shift;
120e1051a39Sopenharmony_ci    my $key = shift;
121e1051a39Sopenharmony_ci    my $extension = "";
122e1051a39Sopenharmony_ci
123e1051a39Sopenharmony_ci    my $extdata = ${$self->extension_data}{$key};
124e1051a39Sopenharmony_ci    $extension .= pack("n", $key);
125e1051a39Sopenharmony_ci    $extension .= pack("n", length($extdata));
126e1051a39Sopenharmony_ci    $extension .= $extdata;
127e1051a39Sopenharmony_ci    return $extension;
128e1051a39Sopenharmony_ci}
129e1051a39Sopenharmony_ci
130e1051a39Sopenharmony_ci#Reconstruct the on-the-wire message data following changes
131e1051a39Sopenharmony_cisub set_message_contents
132e1051a39Sopenharmony_ci{
133e1051a39Sopenharmony_ci    my $self = shift;
134e1051a39Sopenharmony_ci    my $data;
135e1051a39Sopenharmony_ci    my $extensions = "";
136e1051a39Sopenharmony_ci
137e1051a39Sopenharmony_ci    $data = pack('n', $self->client_version);
138e1051a39Sopenharmony_ci    $data .= $self->random;
139e1051a39Sopenharmony_ci    $data .= pack('C', $self->session_id_len);
140e1051a39Sopenharmony_ci    $data .= $self->session;
141e1051a39Sopenharmony_ci    $data .= pack('n', $self->ciphersuite_len);
142e1051a39Sopenharmony_ci    $data .= pack("n*", @{$self->ciphersuites});
143e1051a39Sopenharmony_ci    $data .= pack('C', $self->comp_meth_len);
144e1051a39Sopenharmony_ci    $data .= pack("C*", @{$self->comp_meths});
145e1051a39Sopenharmony_ci
146e1051a39Sopenharmony_ci    foreach my $key (keys %{$self->extension_data}) {
147e1051a39Sopenharmony_ci        next if ($key == TLSProxy::Message::EXT_PSK);
148e1051a39Sopenharmony_ci        $extensions .= $self->extension_contents($key);
149e1051a39Sopenharmony_ci        #Add extension twice if we are duplicating that extension
150e1051a39Sopenharmony_ci        $extensions .= $self->extension_contents($key) if ($key == $self->dupext);
151e1051a39Sopenharmony_ci    }
152e1051a39Sopenharmony_ci    #PSK extension always goes last...
153e1051a39Sopenharmony_ci    if (defined ${$self->extension_data}{TLSProxy::Message::EXT_PSK}) {
154e1051a39Sopenharmony_ci        $extensions .= $self->extension_contents(TLSProxy::Message::EXT_PSK);
155e1051a39Sopenharmony_ci    }
156e1051a39Sopenharmony_ci    #unless we have EXT_FORCE_LAST
157e1051a39Sopenharmony_ci    if (defined ${$self->extension_data}{TLSProxy::Message::EXT_FORCE_LAST}) {
158e1051a39Sopenharmony_ci        $extensions .= $self->extension_contents(TLSProxy::Message::EXT_FORCE_LAST);
159e1051a39Sopenharmony_ci    }
160e1051a39Sopenharmony_ci
161e1051a39Sopenharmony_ci    $data .= pack('n', length($extensions));
162e1051a39Sopenharmony_ci    $data .= $extensions;
163e1051a39Sopenharmony_ci
164e1051a39Sopenharmony_ci    $self->data($data);
165e1051a39Sopenharmony_ci}
166e1051a39Sopenharmony_ci
167e1051a39Sopenharmony_ci#Read/write accessors
168e1051a39Sopenharmony_cisub client_version
169e1051a39Sopenharmony_ci{
170e1051a39Sopenharmony_ci    my $self = shift;
171e1051a39Sopenharmony_ci    if (@_) {
172e1051a39Sopenharmony_ci      $self->{client_version} = shift;
173e1051a39Sopenharmony_ci    }
174e1051a39Sopenharmony_ci    return $self->{client_version};
175e1051a39Sopenharmony_ci}
176e1051a39Sopenharmony_cisub random
177e1051a39Sopenharmony_ci{
178e1051a39Sopenharmony_ci    my $self = shift;
179e1051a39Sopenharmony_ci    if (@_) {
180e1051a39Sopenharmony_ci      $self->{random} = shift;
181e1051a39Sopenharmony_ci    }
182e1051a39Sopenharmony_ci    return $self->{random};
183e1051a39Sopenharmony_ci}
184e1051a39Sopenharmony_cisub session_id_len
185e1051a39Sopenharmony_ci{
186e1051a39Sopenharmony_ci    my $self = shift;
187e1051a39Sopenharmony_ci    if (@_) {
188e1051a39Sopenharmony_ci      $self->{session_id_len} = shift;
189e1051a39Sopenharmony_ci    }
190e1051a39Sopenharmony_ci    return $self->{session_id_len};
191e1051a39Sopenharmony_ci}
192e1051a39Sopenharmony_cisub session
193e1051a39Sopenharmony_ci{
194e1051a39Sopenharmony_ci    my $self = shift;
195e1051a39Sopenharmony_ci    if (@_) {
196e1051a39Sopenharmony_ci      $self->{session} = shift;
197e1051a39Sopenharmony_ci    }
198e1051a39Sopenharmony_ci    return $self->{session};
199e1051a39Sopenharmony_ci}
200e1051a39Sopenharmony_cisub ciphersuite_len
201e1051a39Sopenharmony_ci{
202e1051a39Sopenharmony_ci    my $self = shift;
203e1051a39Sopenharmony_ci    if (@_) {
204e1051a39Sopenharmony_ci      $self->{ciphersuite_len} = shift;
205e1051a39Sopenharmony_ci    }
206e1051a39Sopenharmony_ci    return $self->{ciphersuite_len};
207e1051a39Sopenharmony_ci}
208e1051a39Sopenharmony_cisub ciphersuites
209e1051a39Sopenharmony_ci{
210e1051a39Sopenharmony_ci    my $self = shift;
211e1051a39Sopenharmony_ci    if (@_) {
212e1051a39Sopenharmony_ci      $self->{ciphersuites} = shift;
213e1051a39Sopenharmony_ci    }
214e1051a39Sopenharmony_ci    return $self->{ciphersuites};
215e1051a39Sopenharmony_ci}
216e1051a39Sopenharmony_cisub comp_meth_len
217e1051a39Sopenharmony_ci{
218e1051a39Sopenharmony_ci    my $self = shift;
219e1051a39Sopenharmony_ci    if (@_) {
220e1051a39Sopenharmony_ci      $self->{comp_meth_len} = shift;
221e1051a39Sopenharmony_ci    }
222e1051a39Sopenharmony_ci    return $self->{comp_meth_len};
223e1051a39Sopenharmony_ci}
224e1051a39Sopenharmony_cisub comp_meths
225e1051a39Sopenharmony_ci{
226e1051a39Sopenharmony_ci    my $self = shift;
227e1051a39Sopenharmony_ci    if (@_) {
228e1051a39Sopenharmony_ci      $self->{comp_meths} = shift;
229e1051a39Sopenharmony_ci    }
230e1051a39Sopenharmony_ci    return $self->{comp_meths};
231e1051a39Sopenharmony_ci}
232e1051a39Sopenharmony_cisub extensions_len
233e1051a39Sopenharmony_ci{
234e1051a39Sopenharmony_ci    my $self = shift;
235e1051a39Sopenharmony_ci    if (@_) {
236e1051a39Sopenharmony_ci      $self->{extensions_len} = shift;
237e1051a39Sopenharmony_ci    }
238e1051a39Sopenharmony_ci    return $self->{extensions_len};
239e1051a39Sopenharmony_ci}
240e1051a39Sopenharmony_cisub extension_data
241e1051a39Sopenharmony_ci{
242e1051a39Sopenharmony_ci    my $self = shift;
243e1051a39Sopenharmony_ci    if (@_) {
244e1051a39Sopenharmony_ci      $self->{extension_data} = shift;
245e1051a39Sopenharmony_ci    }
246e1051a39Sopenharmony_ci    return $self->{extension_data};
247e1051a39Sopenharmony_ci}
248e1051a39Sopenharmony_cisub set_extension
249e1051a39Sopenharmony_ci{
250e1051a39Sopenharmony_ci    my ($self, $ext_type, $ext_data) = @_;
251e1051a39Sopenharmony_ci    $self->{extension_data}{$ext_type} = $ext_data;
252e1051a39Sopenharmony_ci}
253e1051a39Sopenharmony_cisub delete_extension
254e1051a39Sopenharmony_ci{
255e1051a39Sopenharmony_ci    my ($self, $ext_type) = @_;
256e1051a39Sopenharmony_ci    delete $self->{extension_data}{$ext_type};
257e1051a39Sopenharmony_ci}
258e1051a39Sopenharmony_ci1;
259