1package Net::SSL::Handshake;
2
3use Moose;
4
5use English qw( -no_match_vars );
6use IO::Socket::INET;
7use IO::Socket::Timeout qw(IO::Socket::INET);
8use Net::IDN::Encode qw(domain_to_ascii);
9use Readonly;
10use Carp qw(croak);
11
12use 5.010;
13
14use Net::SSL::CipherSuites;
15use Net::SSL::Handshake::Extensions::ServerName;
16use Net::SSL::Handshake::Extensions::EllipticCurves;
17use Net::SSL::Handshake::Extensions::ECPointFormats;
18
19use Exporter qw(import);
20
21my @ssl_versions = qw($SSLv2 $SSLv3 $TLSv1 $TLSv11 $TLSv12);
22
23our @EXPORT_OK = ( @ssl_versions, qw(%CONTENT_TYPE) );
24our %EXPORT_TAGS = ( all => \@EXPORT_OK, ssl_versions => \@ssl_versions, );
25
26Readonly my $MT_CLIENT_HELLO => 1;
27Readonly my $MT_SERVER_HELLO => 4;
28
29
30Readonly our $SSLv2  => 0x0002;
31Readonly our $SSLv3  => 0x0300;
32Readonly our $TLSv1  => 0x0301;
33Readonly our $TLSv11 => 0x0302;
34Readonly our $TLSv12 => 0x0303;
35
36Readonly our %CONTENT_TYPE => (
37                               change_cipher_spec => 20,
38                               alert              => 21,
39                               handshake          => 22,
40                               application_data   => 23,
41                               early_handshake    => 25,
42                             );
43
44
45Readonly our %HANDSHAKE_TYPE => (
46                                 client_hello         => 1,
47                                 server_hello         => 2,
48                                 session_ticket       => 4,
49                                 hello_retry_request  => 6,
50                                 encrypted_extensions => 8,
51                                 certificate          => 11,
52                                 certificate_request  => 13,
53                                 certificate_verify   => 15,
54                                 server_configuration => 17,
55                                 finished             => 20,
56                               );
57
58
59=encoding utf8
60
61=head1 NAME
62
63Net::SSL::Handshake - SSL Handshake on an existing connection or open a new one
64
65=head1 VERSION
66
67Version 0.1.x, $Revision: 646 $
68
69=cut
70
71use version; our $VERSION = qv( "v0.1." . ( sprintf "%d", q$Revision: 646 $ =~ /(\d+)/xg ) );
72
73
74
75=head1 SYNOPSIS
76
77=encoding utf8
78
79 my $handshake = Net::SSL::Handshake->new(
80   socket   => $socket,
81   timeout  => $timeout,
82   host     => $hostname,
83   port     => $port,
84   ciphers  => $ciphers,
85   );
86 $handshake->hello;
87
88
89
90<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
91=head1 DESCRIPTION
92
93
94Attributes:
95
96  Tieouts for IO::Socket::Timeout (read, write)
97  Default: read from socket obj???? or 30 seconds? or whatelse!
98
99  socket
100
101  version
102
103  random and other parameters to ssl etc (default random)
104
105+++ IDN via Net::IDN::Encode (for SNI)
106
107Modules:
108
109  Net::SSL::StartTLS::SMTP, ...
110
111
112
113 Peter Mosman openssl: https://github.com/PeterMosmans/openssl/
114
115
116# Build OpenSSL TEST!
117
118  git clone https://github.com/PeterMosmans/openssl.git --depth 1 -b 1.0.2-chacha openssl-chacha
119
120  # config, make & Inst
121  CFLAGS="-O3 -fno-strict-aliasing -pipe -march=native -mtune=native  -fstack-protector"  ./Configure darwin64-x86_64-cc --prefix=/Users/alvar/Documents/Code/externes/openssl-chacha/installdir --openssldir=/Users/alvar/Documents/Code/externes/openssl-chacha/installdir/openssl   enable-asm threads shared zlib enable-ssl2 enable-ssl3 enable-md2 enable-rc5 no-gmp no-rfc3779 enable-ec_nistp_64_gcc_128 zlib no-shared experimental-jpake enable-md2 enable-rc5 enable-rfc3779 enable-gost enable-static-engine
122  make depend && make && make test && make report && make install
123
124  # Via: https://github.com/jvehent/cipherscan
125
126# cert list
127
128  openssl ciphers -l -V ALL:eNULL:aNULL
129  /Users/alvar/Documents/Code/externes/openssl-chacha/installdir/bin/openssl ciphers -l -V ALL:eNULL:aNULL
130
131
132
133# Self singned cert:
134openssl req -new -newkey rsa:2048 -days 36500 -nodes -x509 -keyout server.pem -out server.pem
135
136# Server: man s_server
137
138# Install openssl with SSLv2 and SSLv3 etc
139
140  CFLAGS="-O3 -fno-strict-aliasing -pipe -march=native -mtune=native  -fstack-protector"  ./Configure darwin64-x86_64-cc --prefix=/Users/alvar/Documents/Code/externes/openssl-1.0.2d/installdir --openssldir=/Users/alvar/Documents/Code/externes/openssl-1.0.2d/installdir/openssl   enable-asm threads shared zlib zlib-dynamic  enable-ssl2 enable-ssl3 enable-md2 enable-rc5 no-gmp no-rfc3779 enable-ec_nistp_64_gcc_128
141  make depend
142  make && make test && make install
143
144
145# Start server
146
147  Users/alvar/Documents/Code/externes/openssl-1.0.2d/installdir/bin/openssl s_server -HTTP -accept 443
148
149# oder mit www
150
151
152
153# Standard TLS/SSL handshake
154handshake_pkts = {
155"TLS v1.3": '\x80\x2c\x01\x03\x04\x00\x03\x00\x00\x00\x20',
156"TLS v1.2": '\x80\x2c\x01\x03\x03\x00\x03\x00\x00\x00\x20',
157"TLS v1.1": '\x80\x2c\x01\x03\x02\x00\x03\x00\x00\x00\x20',
158"TLS v1.0": '\x80\x2c\x01\x03\x01\x00\x03\x00\x00\x00\x20',
159"SSL v3.0": '\x80\x2c\x01\x03\x00\x00\x03\x00\x00\x00\x20',
160"SSL v2.0": '\x80\x2c\x01\x00\x02\x00\x03\x00\x00\x00\x20'
161}
162
163https://github.com/iphelix/sslmap/blob/master/sslmap.py
164
165=head2 SSL Handshake
166
167
168https://github.com/iphelix/sslmap/blob/master/sslmap.py
169
170https://labs.portcullis.co.uk/tools/ssl-cipher-suite-enum/
171
172https://github.com/drwetter/testssl.sh
173https://testssl.sh
174
175
176pack types:
177
178  C unsigned 8 bit char
179  n unsigned short, 16 bit, network order
180  a binary string, NULL padded
181
182
183
184
185
186=head3 SSLv2
187
188
189Client sends
190
191
192Client hello max 256 bytes for F5! (Bug) https://code.google.com/p/chromium/issues/detail?id=245500
193Fixed at least since 09/29/2011
194
195
196Client-Hello SSLv2:
197
198  #  Header
199  n  Message len | 0x8000
200
201  # Data. len: message len
202  C  Message Type: SSL_MT_CLIENT_HELLO
203  n  Client-Version
204  n  Cipher spec len
205  n  Session-ID len   => 0
206  n  challenge len
207  a* cipher spec data
208  a* session id data  => empty
209  a* challenge data
210
211alternative header (3 bytes):
212
213  #  Header
214  n  Message len
215  C  Padding (number of bytes added at the end of data part!)
216
217
218
219=head3 SSLv3 and TLS
220
221
222
223
224
225  C     record Type      / SSL record type = 22 (SSL3_RT_HANDSHAKE)
226  n     SSL Version
227  n     Record len
228
229  # Record:
230  C     Message Type     / Handshake type
231  C     0x00             / Length of data to follow in this record (3 Bytes!)
232  n     Message len      / Length rest
233
234  ## Data
235  n     SSL/TLS Version
236  a[32] challenge
237  C     session ID len
238  n     cipher spec len
239  a*    cipher spec
240  C     compression method len (1)
241  C*    compression method  (0x00)
242  n     length extensions
243  a*    extensions data
244
245
246  ## Extensions: SNI,
247
248
249
250
251  # Hello Extensions format:
252
253  n  extension type
254  n  Length extension data
255  a* data
256
257
258  # data for hello extension sni:
259
260  n  len of list (bytes)
261  C  Nametype (host_name: 0x00)
262  n  len host name
263  a* hostname (IDN!)
264
265
266      $clientHello_extensions = pack(
267                                      "n n n C n a[$clientHello{'extension_sni_len'}]",
268                                      $clientHello{'extension_type_server_name'},          #n
269                                      $clientHello{'extension_len'},            #n
270                                      $clientHello{'extension_sni_list_len'},   #n
271                                      $clientHello{'extension_sni_type'},       #C
272                                      $clientHello{'extension_sni_len'},        #n
273                                      $clientHello{'extension_sni_name'},       #a[$clientHello{'extension_sni_len'}]
274                                    );
275
276
277
278
279
280
281
282
283         "n a[32] C n a[$clientHello{'cipher_spec_len'}] C C[$clientHello{'compression_method_len'}] a[$clientHello{'extensions_total_len'}]",
284         $clientHello{'version'},                  # n
285         $clientHello{'challenge'},                # A[32] = gmt + random [4] + [28] Bytes
286         $clientHello{'session_id_len'},           # C
287         $clientHello{'cipher_spec_len'},          # n
288         $clientHello{'cipher_spec'},              # A[$clientHello{'cipher_spec_len'}]
289         $clientHello{'compression_method_len'},   # C (0x01)
290         $clientHello{'compression_method'},       # C[len] (0x00)
291         $clientHello_extensions                   # optional
292                             );
293
294
295
296https://www-01.ibm.com/support/knowledgecenter/#!/SSB23S_1.1.0.10/com.ibm.ztpf-ztpfdf.doc_put.10/gtps5/s5rcd.html?cp=SSB23S_1.1.0.10%2F0-1-8-2-3
297
298
299possible handshake types:
300
301   SSL3_MT_HELLO_REQUEST            0   (x'00')
302   SSL3_MT_CLIENT_HELLO             1   (x'01')
303   SSL3_MT_SERVER_HELLO             2   (x'02')
304   SSL3_MT_CERTIFICATE             11   (x'0B')
305   SSL3_MT_SERVER_KEY_EXCHANGE     12   (x'0C')
306   SSL3_MT_CERTIFICATE_REQUEST     13   (x'0D')
307   SSL3_MT_SERVER_DONE             14   (x'0E')
308   SSL3_MT_CERTIFICATE_VERIFY      15   (x'0F')
309   SSL3_MT_CLIENT_KEY_EXCHANGE     16   (x'10')
310   SSL3_MT_FINISHED                20   (x'14')
311
312
313
314
315
316      $clientHello{'msg_len'}    = length($clientHello_tmp);
317      $clientHello{'record_len'} = $clientHello{'msg_len'} + 4;
318
319      $clientHello = pack(
320                           "C n n C C n a*",
321                           $clientHello{'record_type'},              # C
322                           $clientHello{'record_version'},           # n
323                           $clientHello{'record_len'},               # n
324                           $clientHello{'msg_type'},                 # C
325                           0x00,                                     # C (0x00)
326                           $clientHello{'msg_len'},                  # n
327                           $clientHello_tmp                          # a
328                         );
329
330
331
332
333
334Server-Hello:
335
336
337
338
339
340
341The SSL Handshake Protocol defines the following errors:
342
343NO-CIPHER-ERROR
344This error is returned by the client to the server when it cannot find a cipher or key size that it supports that is also supported by the server.
345This error is not recoverable.
346
347
348
349=cut
350
351
352has record_template => (
353                         is      => "ro",
354                         isa     => "Str",
355                         traits  => ['String'],
356                         default => "",
357                         handles => { add_record_template => "append", clear_record_template => "clear", }
358                       );
359has _record_data => (
360                      is      => "ro",
361                      isa     => "ArrayRef",
362                      traits  => ['Array'],
363                      default => sub { [] },
364                      handles => { add_record_data => "push", clear_record_data => "clear", record_data => "elements", }
365                    );
366
367#<<<
368
369
370has socket           => ( is => "ro", isa => "Object",                 handles => [qw(send recv )], lazy => 1, builder => "_build_socket", clearer => "close", );
371has host             => ( is => "ro", isa => "Str", );
372has port             => ( is => "ro", isa => "Int",                    default => 443, );
373has error            => ( is => "rw", isa => "Int",                    default => 0, );
374has ciphers          => ( is => "ro", isa => "Net::SSL::CipherSuites", required => 1, );
375has accepted_ciphers => ( is => "rw", isa => "Net::SSL::CipherSuites", default => sub { Net::SSL::CipherSuites->new });
376has timeout          => ( is => "ro", isa => "Int",                    default => 60, );
377has ssl_version      => ( is => "ro", isa => "Int",                    default => $TLSv12);
378has sni              => ( is => "ro", isa => "Bool",                   default => 1);
379
380# Server messages
381has server_version   => ( is => "rw", isa => "Int", );
382has server_cert      => ( is => "rw", isa => "Str", );
383
384has ok               => (is => "ro",  isa => "Bool", writer => "_ok", );
385
386
387# TODO: readonly with private writer!
388has alert            => (is => "rw",  isa => "Bool", );
389has no_cipher_found  => (is => "rw",  isa => "Bool", );
390
391
392#>>>
393
394sub _build_socket
395   {
396   my $self = shift;
397
398   die __PACKAGE__ . ": need parameter socket or host!\n" unless $self->host;
399
400   my $idn_host = domain_to_ascii( $self->host );
401
402   my $socket = IO::Socket::INET->new( Timeout => $self->timeout, PeerAddr => $idn_host, PeerPort => $self->port )
403      // die __PACKAGE__ . ": Can't connect to ${ \$self->host }:${ \$self->port }: $OS_ERROR\n";
404
405   IO::Socket::Timeout->enable_timeouts_on($socket);
406   $socket->read_timeout( $self->timeout );
407   $socket->write_timeout( $self->timeout );
408
409   return $socket;
410   }
411
412# for some Debug
413#sub _to_hex
414#   {
415#   return join( " ", map { sprintf "%02X", $ARG } unpack( "C*", shift ) );
416#   }
417#
418
419=head2 send_record
420
421sends the record to the server
422
423=cut
424
425sub send_record
426   {
427   my $self = shift;
428   my $content_type = shift // $CONTENT_TYPE{handshake};
429
430   # say _to_hex( $self->record_as_string($content_type) );
431
432   $self->send( $self->record_as_string($content_type) ) // croak "Error while sending data";
433
434   # TODO: TImeout check!
435
436   $self->clear_record;                            # oder beim aufruf?
437
438   return;
439   }
440
441
442=head2 add_to_record
443
444adds a template and some data to a record
445
446
447=cut
448
449sub add_to_record
450   {
451   my $self    = shift;
452   my $pattern = shift;
453   my @data    = @ARG;
454
455   $self->add_record_template($pattern);
456   $self->add_record_data(@data);
457
458   return $self;
459   }
460
461
462=head2 record_as_string
463
464returns the record as a string; checks for SSLv2/ SSLv3 / TLS
465
466=cut
467
468
469sub record_as_string
470   {
471   my $self         = shift;
472   my $content_type = shift;
473
474   my $record_data = pack( $self->record_template, $self->record_data );
475
476   my $record_header;
477
478   if ( $self->ssl_version == $SSLv2 )
479      {
480      $record_header = pack( "n", length($record_data) | 0x8000 );
481      }
482   else
483      {
484      $record_header = pack( "C n n", $content_type, $self->ssl_version == $SSLv3 ? $SSLv3 : $TLSv1, length($record_data), );
485      }
486
487   return $record_header . $record_data;
488
489   } ## end sub record_as_string
490
491=head2 clear_record
492
493clears the template etc
494
495=cut
496
497sub clear_record
498   {
499   my $self = shift;
500   $self->clear_record_template;
501   $self->clear_record_data;
502   return $self;
503   }
504
505=head2 challenge
506
507generate some random ...
508
509=cut
510
511sub challenge
512   {
513   return pack( "NC[28]", time, ( map { int( rand(256) ) } ( 1 .. 28 ) ) );
514   }
515
516
517=head2 close_notify
518
519send a "close notify" alert
520
521=cut
522
523sub close_notify
524   {
525   my $self = shift;
526
527   $self->add_to_record( "CC", 1, 0 );
528   $self->send_record( $CONTENT_TYPE{alert} );
529
530   return $self;
531   }
532
533=head2 ->hello
534
535Send client hello, receive and parse server hello.
536
537...
538
539=cut
540
541sub hello
542   {
543   my $self = shift;
544
545   $self->build_client_hello;
546   $self->send_record;
547   $self->receive_record;
548
549   $self->close_notify unless $self->ssl_version == $SSLv2;
550   $self->close;
551
552   return $self;
553   }
554
555
556=head2 build_client_hello
557
558build client hello message
559
560=cut
561
562sub build_client_hello
563   {
564   my $self = shift;
565
566   my $cipher_spec = $self->ciphers->cipher_spec( $self->ssl_version );
567
568   if ( $self->ssl_version == $SSLv2 )
569      {
570      #  C  Message Type: SSL_MT_CLIENT_HELLO
571      #  n  Client-Version
572      #  n  Cipher spec len
573      #  n  Session-ID len   => 0
574      #  n  challenge len
575      #  a* cipher spec data
576      #  a* session id data  => empty
577      #  a* challenge data
578
579      $self->add_to_record(
580                            "Cnnnna*a[0]a[32]", $MT_CLIENT_HELLO, $self->ssl_version, length($cipher_spec),
581                            0,                  32,               $cipher_spec,       "",
582                            $self->challenge
583                          );
584      }
585   else
586      {
587
588      #   uint8 CipherSuite[2];    /* Cryptographic suite selector */
589      #
590      #   enum { null(0), (255) } CompressionMethod;
591      #
592      #   struct {
593      #       ProtocolVersion client_version = { 3, 4 };    /* TLS v1.3 */
594      #       Random random;
595      #       SessionID session_id;
596      #       CipherSuite cipher_suites<2..2^16-2>;
597      #       CompressionMethod compression_methods<1..2^8-1>;
598      #       Extension extensions<0..2^16-1>;
599      #   } ClientHello;
600      #
601
602      # von außen: erst record, dann handshare-record, dann client-hello.
603
604      # build ClientHello
605      my $cipher_spec_len = length($cipher_spec);
606
607      my $extensions = $self->build_extensions;
608      my $client_hello = pack(
609                               "n a[32] C n a[$cipher_spec_len] C C a*",
610                               $self->ssl_version,
611                               $self->challenge,
612                               0,
613                               length($cipher_spec),
614                               $cipher_spec,
615                               1,                                        # compression len
616                               0,                                        # compression type
617                               $extensions,
618                             );
619
620      #
621      #         struct {
622      #       HandshakeType msg_type;    /* handshake type */
623      #       uint24 length;             /* bytes in message */
624      #       select (HandshakeType) {
625      #           case client_hello:        ClientHello;
626      #           case server_hello:        ServerHello;
627      #           case hello_retry_request: HelloRetryRequest;
628      #           case encrypted_extensions: EncryptedExtensions;
629      #           case server_configuration:ServerConfiguration;
630      #           case certificate:         Certificate;
631      #           case certificate_request: CertificateRequest;
632      #           case certificate_verify:  CertificateVerify;
633      #           case finished:            Finished;
634      #           case session_ticket:      NewSessionTicket;
635      #       } body;
636      #   } Handshake;
637
638
639      $self->add_to_record(
640                            "C C n a*",
641                            $HANDSHAKE_TYPE{client_hello},
642                            0,                                        # first byte length (type C)
643                            length($client_hello),
644                            $client_hello,
645                          );
646
647
648      } ## end else [ if ( $self->ssl_version...)]
649
650   return $self;
651   } ## end sub build_client_hello
652
653
654=head2 ->build_extensions
655
656Builds the hello extensions
657
658=cut
659
660sub build_extensions
661   {
662   my $self = shift;
663
664   my $extensions = "";
665
666   $extensions .= Net::SSL::Handshake::Extensions::ServerName->new( hostname => $self->host )->data if $self->sni;
667   $extensions .= Net::SSL::Handshake::Extensions::ECPointFormats->new()->data;
668   $extensions .= Net::SSL::Handshake::Extensions::EllipticCurves->new()->data;
669
670   # $extensions .= ...
671
672   return pack( "n a*", length($extensions), $extensions );
673   }
674
675
676
677=head2 receive_record
678
679receive and parse server record ...
680
681=cut
682
683sub receive_record
684   {
685   my $self = shift;
686
687   my $data;
688   $self->recv( $data, 1 );
689   croak "Nothing received!" unless length($data);
690
691   my $content_type = unpack( "C", $data );
692
693   return $self->sslv2_server_hello($data) if $content_type & 0x80;    # = $MT_SERVER_HELLO;
694
695   #   say "v3+";
696   #
697   #   die "jkgkjhgkjhg";
698   #
699   #   #use Data::Dumper;
700   #   #   say Dumper( [ $msg, $session_id_hit, $cert_type, $server_version, $cert_len, $cipher_spec_len, $connection_id_len ] ),
701   #   #      "\n", _to_hex($cert_data), "\n", _to_hex( $cipher_spec), "\n", _to_hex("$connection_id");
702   #
703   #
704   #
705   #   $content_type &= 0x7f;
706   #   return $self->sslv2_server_hello($content_type) if $content_type == $MT_SERVER_HELLO;
707
708   undef $data;
709   $self->recv( $data, 4 );
710   croak "no protocol-version/length received" unless length($data);
711
712   my ( $protocol_version, $record_lenght ) = unpack( "nn", $data );
713
714   $self->server_version($protocol_version);
715
716   undef $data;
717   $self->recv( $data, $record_lenght ) // die "FATAL: No data!\n";
718
719   return $self->parse_handshake($data) if $content_type == $CONTENT_TYPE{handshake};
720   return $self->parse_alert($data)     if $content_type == $CONTENT_TYPE{alert};
721   die "Ups, the Server sent a NOT IMPLEMENTED ContentType: $content_type\n";
722   } ## end sub receive_record
723
724
725=head2 parse_handshake($data)
726
727Parse SSLv3+ Handshake
728
729
730=cut
731
732
733sub parse_handshake
734   {
735   my $self = shift;
736   my $data = shift;
737
738   my ( $msg_type, $zero, $length, $rest ) = unpack( "C C n a*", $data );
739
740   die "Expected server_hello, got $msg_type\n" if $msg_type != $HANDSHAKE_TYPE{server_hello};
741
742
743
744   # TLS 1.3!!!
745   #  struct {
746   #       ProtocolVersion server_version;
747   #       Random random;
748   #       CipherSuite cipher_suite;
749   #       select (extensions_present) {
750   #           case false:
751   #               struct {};
752   #           case true:
753   #               Extension extensions<0..2^16-1>;
754   #       };
755   #   } ServerHello;
756
757   # SSLv3 ==> TLS 1.2:
758   #      struct {
759   #          ProtocolVersion server_version;
760   #          Random random;
761   #          SessionID session_id;
762   #          CipherSuite cipher_suite;
763   #          CompressionMethod compression_method;
764   #          select (extensions_present) {
765   #              case false:
766   #                  struct {};
767   #              case true:
768   #                  Extension extensions<0..2^16-1>;
769   #          };
770   #      } ServerHello;
771
772   my ( $server_version, $random, $session_id_len, $rest2 ) = unpack( "n a[32] C a*", $rest );
773   my ( $session_id, $cipher_suite, $compression_method, $extensions ) = unpack( "a[$session_id_len] a[2] C a*", $rest2 );
774
775   $self->server_version($server_version);
776
777   if ( length($cipher_suite) )
778      {
779      $self->accepted_ciphers->add( Net::SSL::CipherSuites->new_by_cipher_spec($cipher_suite) );
780      $self->_ok(1);
781      }
782
783
784   return $self;
785   } ## end sub parse_handshake
786
787
788=head2 sslv2_server_hello
789
790
791SERVER-HELLO (Phase 1; Sent in the clear)
792
793   0 char MSG-SERVER-HELLO
794   1 char SESSION-ID-HIT
795   2 char CERTIFICATE-TYPE
796   3 char SERVER-VERSION-MSB
797   4 char SERVER-VERSION-LSB
798   5 char CERTIFICATE-LENGTH-MSB
799   6 char CERTIFICATE-LENGTH-LSB
800   7 char CIPHER-SPECS-LENGTH-MSB
801   8 char CIPHER-SPECS-LENGTH-LSB
802   9 char CONNECTION-ID-LENGTH-MSB
803  10 char CONNECTION-ID-LENGTH-LSB
804    char CERTIFICATE-DATA[MSB<<8|LSB]
805    char CIPHER-SPECS-DATA[MSB<<8|LSB]
806    char CONNECTION-ID-DATA[MSB<<8|LSB]
807
808
809=cut
810
811Readonly my $SERVER_HELLO_HEAD_LEN => 11;
812
813sub sslv2_server_hello
814   {
815   my $self       = shift;
816   my $first_byte = shift;
817
818
819   my $data;
820   $self->recv( $data, 1 );
821
822   my $record_len = unpack( "n", "$first_byte$data" ) & 0x7fff;
823
824   undef $data;
825   $self->recv( $data, $record_len );
826
827   die "Not enough Data for SSLv2 record!\n" unless length($data);
828
829   #
830   # all received; parse data ...
831   #
832
833   my ( $msg, $session_id_hit, $cert_type, $server_version, $cert_len, $cipher_spec_len, $connection_id_len, $rest )
834      = unpack( "C C C n n n n a*", $data );
835
836   die "Got no cert len at unpack SSLv2 server hello; got not enough data?\n" unless $cert_len;
837
838   my ( $cert_data, $cipher_spec, $connection_id ) = unpack( "a[$cert_len] a[$cipher_spec_len] a[$connection_id_len]", $rest );
839
840
841   $self->server_version($server_version);
842   $self->server_cert($cert_data);
843
844   die "Wrong Server Version: $server_version\n" if $server_version != $SSLv2;
845
846   if ( length($cipher_spec) )
847      {
848      $self->accepted_ciphers->add( Net::SSL::CipherSuites->new_by_cipher_spec_sslv2($cipher_spec) );
849      $self->_ok(1);
850      }
851
852   return $self;
853   } ## end sub sslv2_server_hello
854
855
856=head2 parse_alert
857
858parse alert message
859
860=cut
861
862
863sub parse_alert
864   {
865   my $self = shift;
866   my $data = shift;
867
868   $self->alert(1);
869
870   $self->no_cipher_found(1) if $data eq "\x02\x28";
871
872   # say "ALERT-ZEUG:  " . _to_hex($data);
873
874   return $self;
875   }
876
877
8781;
879
880