File Coverage

blib/lib/Protocol/TLS/Client.pm
Criterion Covered Total %
statement 76 78 97.4
branch 7 12 58.3
condition n/a
subroutine 14 14 100.0
pod 0 2 0.0
total 97 106 91.5


line stmt bran cond sub pod time code
1             package Protocol::TLS::Client;
2 1     1   121909 use strict;
  1         2  
  1         39  
3 1     1   5 use warnings;
  1         1  
  1         25  
4 1     1   4 use Carp;
  1         1  
  1         66  
5 1     1   320 use Protocol::TLS::Trace qw(tracer bin2hex);
  1         1  
  1         50  
6 1     1   425 use Protocol::TLS::Context;
  1         2  
  1         30  
7 1     1   360 use Protocol::TLS::Connection;
  1         3  
  1         28  
8 1         990 use Protocol::TLS::Constants qw(const_name :state_types :end_types :c_types
9 1     1   5 :versions :hs_types :ciphers cipher_type :alert_desc);
  1         1  
10              
11             sub new {
12 2     2 0 30903 my ( $class, %opts ) = @_;
13 2         22 my $self = bless { %opts, sid => {}, }, $class;
14             }
15              
16             sub new_connection {
17 3     3 0 3254 my ( $self, $server_name, %opts ) = @_;
18 3 50       17 croak "Specify server name of host" unless defined $server_name;
19              
20 3         37 my $ctx = Protocol::TLS::Context->new( type => CLIENT );
21 3         36 my $con = Protocol::TLS::Connection->new($ctx);
22              
23             # Grab random session_id from cache (if exists)
24 3 100       16 if ( exists $self->{sid}->{$server_name} ) {
25 1         7 my $s = $self->{sid}->{$server_name};
26 1         7 my $sid = ( keys %$s )[0];
27              
28 1         10 $ctx->{proposed} = {
29             session_id => $sid,
30             tls_version => $s->{$sid}->{tls_version},
31             ciphers => [ $s->{$sid}->{cipher} ],
32             compression => [ $s->{$sid}->{compression} ],
33             };
34             }
35             else {
36 2         21 $ctx->{proposed} = {
37             session_id => '',
38             ciphers => [
39             TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_NULL_SHA256,
40             TLS_RSA_WITH_NULL_SHA,
41             ],
42             tls_version => TLS_v12,
43             compression => [0],
44             };
45             }
46              
47 3 50       13 if ( exists $opts{on_data} ) {
48 3         8 $ctx->{on_data} = $opts{on_data};
49             }
50              
51 3         41 $ctx->enqueue( [ CTYPE_HANDSHAKE, HSTYPE_CLIENT_HELLO, $ctx->{proposed} ] );
52              
53             $ctx->{on_change_state} = sub {
54 11     11   19 my ( $ctx, $prev_state, $new_state ) = @_;
55 11         30 tracer->debug( "State changed from "
56             . const_name( 'state_types', $prev_state ) . " to "
57             . const_name( 'state_types', $new_state ) );
58 3         32 };
59              
60             # New session
61             $ctx->state_cb(
62             STATE_HS_HALF,
63             sub {
64 2     2   4 my $ctx = shift;
65 2         5 my $p = $ctx->{pending};
66 2         3 my $pro = $ctx->{proposed};
67 2         4 my $sp = $p->{securityParameters};
68 2         6 my $crypto = $ctx->crypto;
69              
70             # Server invalidate our session
71 2 50       8 if ( $pro->{session_id} ne '' ) {
72 0         0 delete $self->{sid}->{$server_name}->{ $pro->{session_id} };
73             }
74              
75 2         15 my $pub_key = $crypto->cert_pubkey( $p->{cert}->[0] );
76              
77 2         80 my ( $da, $ca, $mac ) = cipher_type( $p->{cipher} );
78              
79 2 50       11 if ( $da eq 'RSA' ) {
80 2         18 my $preMasterSecret =
81             pack( "n", $p->{tls_version} ) . $crypto->random(46);
82              
83 2         83 $sp->{master_secret} = $crypto->PRF(
84             $preMasterSecret,
85             "master secret",
86             $sp->{client_random} . $sp->{server_random}, 48
87             );
88              
89 2         7 my $encoded =
90             $crypto->rsa_encrypt( $pub_key, $preMasterSecret );
91 2         427 $ctx->enqueue(
92             [ CTYPE_HANDSHAKE, HSTYPE_CLIENT_KEY_EXCHANGE, $encoded ] );
93             }
94             else {
95 0         0 die "not implemented";
96             }
97              
98 2         32 $ctx->enqueue( [CTYPE_CHANGE_CIPHER_SPEC],
99             [ CTYPE_HANDSHAKE, HSTYPE_FINISHED, $ctx->finished ] );
100             }
101 3         42 );
102              
103             $ctx->state_cb( STATE_SESS_RESUME,
104             sub {
105 1     1   4 my $ctx = shift;
106 1         8 my $p = $ctx->{pending};
107              
108             #my $pro = $ctx->{proposed};
109 1         4 my $sp = $p->{securityParameters};
110              
111 1         4 my $s = $self->{sid}->{$server_name}->{ $p->{session_id} };
112 1         2 $p->{tls_version} = $s->{tls_version};
113 1         3 $p->{cipher} = $s->{cipher};
114 1         9 $sp->{$_} = $s->{securityParameters}->{$_}
115 1         1 for keys %{ $s->{securityParameters} },
116              
117             tracer->debug( "Resume session: " . bin2hex( $p->{session_id} ) );
118             }
119 3         22 );
120              
121             $ctx->state_cb( STATE_HS_RESUME,
122             sub {
123 1     1   2 my $ctx = shift;
124 1         5 $ctx->enqueue( [CTYPE_CHANGE_CIPHER_SPEC],
125             [ CTYPE_HANDSHAKE, HSTYPE_FINISHED, $ctx->finished ] );
126             }
127 3         16 );
128              
129             $ctx->state_cb( STATE_OPEN,
130             sub {
131 3     3   5 my $ctx = shift;
132 3         5 my $p = $ctx->{pending};
133              
134             # add sid to client's cache
135 3         14 $self->{sid}->{$server_name}->{ $p->{session_id} } =
136             $ctx->copy_pending;
137 3         11 tracer->debug( "Saved sid: " . bin2hex( $p->{session_id} ) );
138 3         43 $ctx->{session_id} = $p->{session_id};
139 3         7 $ctx->{tls_version} = $p->{tls_version};
140 3         10 $ctx->clear_pending;
141              
142             # Handle callbacks
143 3 50       10 if ( exists $opts{on_handshake_finish} ) {
144 3         11 $opts{on_handshake_finish}->($ctx);
145             }
146             }
147 3         24 );
148              
149 3         8 $con;
150             }
151              
152             1
153             __END__