File Coverage

blib/lib/Protocol/TLS/Server.pm
Criterion Covered Total %
statement 68 89 76.4
branch 8 18 44.4
condition 1 3 33.3
subroutine 14 16 87.5
pod 0 2 0.0
total 91 128 71.0


line stmt bran cond sub pod time code
1             package Protocol::TLS::Server;
2 2     2   32202 use strict;
  2         5  
  2         71  
3 2     2   10 use warnings;
  2         4  
  2         48  
4 2     2   10 use Carp;
  2         3  
  2         150  
5 2     2   1841 use MIME::Base64;
  2         1564  
  2         135  
6 2     2   489 use Protocol::TLS::Trace qw(tracer bin2hex);
  2         5  
  2         162  
7 2     2   675 use Protocol::TLS::Context;
  2         5  
  2         64  
8 2     2   552 use Protocol::TLS::Connection;
  2         4  
  2         70  
9             use Protocol::TLS::Constants
10 2     2   11 qw(cipher_type const_name :versions :ciphers :c_types :end_types :hs_types :state_types :alert_desc);
  2         4  
  2         2922  
11              
12             sub new {
13 2     2 0 1693 my ( $class, %opts ) = @_;
14 2         14 my $self = bless { %opts, sid => {}, }, $class;
15 2         10 $self->_load_cert( delete $opts{cert_file} );
16 2         9 $self->_load_priv_key( delete $opts{key_file} );
17 2         8 $self;
18             }
19              
20             sub _load_cert {
21 2     2   5 my ( $self, $file ) = @_;
22 2 50       7 croak "specify cert_file path" unless defined $file;
23              
24 2         7 local $/;
25 2 50       75 open my $fh, '<', $file or croak "opening cert_file error: $!";
26              
27             # TODO: multiple certs
28 2         211 my ($cert) = (
29             <$fh> =~ /^-----BEGIN\x20CERTIFICATE-----\r?\n
30             (.+?\r?\n)
31             -----END\x20CERTIFICATE-----\r?\n/sx
32             );
33 2         22 close $fh;
34 2 50       6 croak "Certificate must be in PEM format" unless $cert;
35 2         31 $self->{cert} = decode_base64($cert);
36 2         11 ();
37             }
38              
39             sub _load_priv_key {
40 2     2   3 my ( $self, $file ) = @_;
41 2 50       6 croak "specify key_file path" unless defined $file;
42              
43 2         5 local $/;
44 2 50       52 open my $fh, '<', $file or croak "opening key_file error: $!";
45 2         174 my ($key) = (
46             <$fh> =~ /^-----BEGIN\x20RSA\x20PRIVATE\x20KEY-----\r?\n
47             (.+?\r?\n)
48             -----END\x20RSA\x20PRIVATE\x20KEY-----\r?\n/sx
49             );
50 2         12 close $fh;
51 2 50       7 croak "Private key must be in PEM format" unless $key;
52 2         13 $self->{key} = decode_base64($key);
53 2         10 ();
54             }
55              
56             sub new_connection {
57 1     1 0 2 my ( $self, %opts ) = @_;
58 1         11 my $ctx = Protocol::TLS::Context->new( type => SERVER );
59 1         4 $ctx->{key} = $self->{key};
60 1         4 $ctx->{cert} = $self->{cert};
61 1         10 $ctx->{proposed} = {
62             ciphers => [
63             TLS_RSA_WITH_AES_128_CBC_SHA, TLS_RSA_WITH_NULL_SHA256,
64             TLS_RSA_WITH_NULL_SHA,
65             ],
66             tls_version => TLS_v12,
67             compression => [0],
68             };
69 1         12 my $con = Protocol::TLS::Connection->new($ctx);
70              
71             $ctx->{on_change_state} = sub {
72 3     3   6 my ( $ctx, $prev_state, $new_state ) = @_;
73 3         9 tracer->debug( "State changed from "
74             . const_name( 'state_types', $prev_state ) . " to "
75             . const_name( 'state_types', $new_state )
76             . "\n" );
77 1         13 };
78              
79 1 50       8 if ( exists $opts{on_data} ) {
80 0         0 $ctx->{on_data} = $opts{on_data};
81             }
82              
83             $ctx->state_cb(
84             STATE_HS_START,
85             sub {
86 1     1   2 my $ctx = shift;
87 1         3 my $p = $ctx->{pending};
88 1         3 my $sp = $p->{securityParameters};
89 1         2 my $sid = $p->{session_id};
90              
91             # Resume session
92 1 50 33     10 if ( $sid ne '' && exists $self->{sid}->{$sid} ) {
93 0         0 my $s = $self->{sid}->{$sid};
94 0         0 $p->{tls_version} = $s->{tls_version};
95 0         0 $p->{cipher} = $s->{cipher};
96 0         0 $sp->{$_} = $s->{securityParameters}->{$_}
97 0         0 for keys %{ $s->{securityParameters} };
98              
99             # save sid as proposed
100 0         0 $ctx->{proposed}->{session_id} = $sid;
101 0         0 tracer->debug( "Resume session: " . bin2hex($sid) );
102              
103 0         0 $ctx->enqueue(
104             [
105             CTYPE_HANDSHAKE,
106             HSTYPE_SERVER_HELLO,
107             {
108             tls_version => $p->{tls_version},
109             server_random => $sp->{server_random},
110             session_id => $sid,
111             cipher => $p->{cipher},
112             compression => $sp->{CompressionMethod}
113             }
114             ]
115             );
116 0         0 $ctx->enqueue( [CTYPE_CHANGE_CIPHER_SPEC],
117             [ CTYPE_HANDSHAKE, HSTYPE_FINISHED, $ctx->finished ] );
118             }
119              
120             # New session
121             else {
122 1         5 $sid = $p->{session_id} = $ctx->crypto->random(32);
123 1         47 $ctx->enqueue(
124             [
125             CTYPE_HANDSHAKE,
126             HSTYPE_SERVER_HELLO,
127             {
128             tls_version => $p->{tls_version},
129             server_random => $sp->{server_random},
130             session_id => $sid,
131             cipher => $p->{cipher},
132             compression => $sp->{CompressionMethod}
133             }
134             ],
135             [ CTYPE_HANDSHAKE, HSTYPE_CERTIFICATE, $ctx->{cert} ],
136             [ CTYPE_HANDSHAKE, HSTYPE_SERVER_HELLO_DONE ]
137             );
138             }
139             }
140 1         12 );
141              
142             $ctx->state_cb( STATE_HS_FULL,
143             sub {
144 0     0   0 my $ctx = shift;
145 0         0 $ctx->enqueue( [CTYPE_CHANGE_CIPHER_SPEC],
146             [ CTYPE_HANDSHAKE, HSTYPE_FINISHED, $ctx->finished ] );
147             }
148 1         8 );
149              
150             $ctx->state_cb( STATE_OPEN,
151             sub {
152 0     0   0 my $ctx = shift;
153 0         0 my $p = $ctx->{pending};
154              
155             # add sid to server's cache
156 0         0 $self->{sid}->{ $p->{session_id} } = $ctx->copy_pending;
157 0         0 tracer->debug( "Saved sid: " . bin2hex( $p->{session_id} ) );
158 0         0 $ctx->{session_id} = $p->{session_id};
159 0         0 $ctx->{tls_version} = $p->{tls_version};
160 0         0 $ctx->clear_pending;
161              
162             # Handle callbacks
163 0 0       0 if ( exists $opts{on_handshake_finish} ) {
164 0         0 $opts{on_handshake_finish}->($ctx);
165             }
166             }
167 1         12 );
168              
169 1         4 $con;
170             }
171              
172             1
173             __END__