File Coverage

blib/lib/Argon/SecureChannel.pm
Criterion Covered Total %
statement 28 33 84.8
branch 2 4 50.0
condition n/a
subroutine 10 10 100.0
pod 2 3 66.6
total 42 50 84.0


line stmt bran cond sub pod time code
1             package Argon::SecureChannel;
2             # ABSTRACT: Encrypted Argon::Channel
3             $Argon::SecureChannel::VERSION = '0.18';
4              
5 3     3   27 use strict;
  3         9  
  3         120  
6 3     3   20 use warnings;
  3         7  
  3         119  
7 3     3   18 use Carp;
  3         8  
  3         241  
8 3     3   25 use Moose;
  3         6  
  3         27  
9 3     3   26815 use Argon::Log;
  3         10  
  3         391  
10 3     3   28 use Argon::Constants qw(:commands);
  3         9  
  3         2470  
11              
12             extends qw(Argon::Channel);
13             with qw(Argon::Encryption);
14              
15              
16             has on_ready => (
17             is => 'rw',
18             isa => 'Ar::Callback',
19             default => sub { sub{} },
20             );
21              
22              
23             has remote => (
24             is => 'rw',
25             isa => 'Maybe[Str]',
26             );
27              
28              
29             sub BUILD {
30 8     8 0 23 my ($self, $args) = @_;
31 8         37 $self->identify;
32             }
33              
34             around encode_msg => sub {
35             my ($orig, $self, $msg) = @_;
36             $self->encrypt($self->$orig($msg));
37             };
38              
39             around decode_msg => sub {
40             my ($orig, $self, $line) = @_;
41             $self->$orig($self->decrypt($line));
42             };
43              
44             around send => sub {
45             my ($orig, $self, $msg) = @_;
46             $msg->token($self->token);
47             $self->$orig($msg);
48             };
49              
50             around recv => sub {
51             my ($orig, $self, $msg) = @_;
52              
53             if ($msg->cmd eq $ID) {
54             if ($self->is_ready) {
55             my $error = $self->remote ne $msg->token
56             ? 'Remote channel ID did not match expected value'
57             : 'Remote channel ID received out of sequence';
58              
59             log_error $error;
60             $self->send($msg->error($error));
61             }
62             else {
63             log_trace 'remote host identified as %s', $msg->token;
64             $self->remote($msg->token);
65             $self->on_ready->();
66             }
67             }
68             elsif ($self->_validate($msg)) {
69             $self->$orig($msg);
70             }
71             };
72              
73              
74             sub is_ready {
75 11     11 1 37 my $self = shift;
76 11         465 defined $self->remote;
77             }
78              
79             sub _validate {
80 7     7   26 my ($self, $msg) = @_;
81 7 50       273 return 1 unless defined $self->remote;
82 7 50       233 return 1 if $self->remote eq $msg->token;
83 0         0 log_error 'token mismatch';
84 0         0 log_error 'expected %s', $self->remote;
85 0         0 log_error ' actual %s', $msg->token;
86 0         0 $self->disconnect;
87 0         0 return;
88             }
89              
90              
91             sub identify {
92 8     8 1 23 my $self = shift;
93 8         221 log_trace 'sending identity %s', $self->token;
94 8         1018 $self->send(Argon::Message->new(cmd => $ID));
95             }
96              
97             __PACKAGE__->meta->make_immutable;
98             1;
99              
100             __END__
101              
102             =pod
103              
104             =encoding UTF-8
105              
106             =head1 NAME
107              
108             Argon::SecureChannel - Encrypted Argon::Channel
109              
110             =head1 VERSION
111              
112             version 0.18
113              
114             =head1 DESCRIPTION
115              
116             An L<Argon::Channel> which implements L<Argon::Encryption> to encrypt all
117             messages sent. Additionally adds a unique identifier for the channel to assist
118             with the tracking of message circuits in the Ar network.
119              
120             =head1 ATTRIBUTES
121              
122             =head2 on_ready
123              
124             C<SecureChannel> adds an additional setup step during initialization. The
125             C<on_ready> callback is triggered once that setup has completed and the channel
126             is ready for use.
127              
128             =head2 remote
129              
130             Holds the identifier for the speaker on the remote end of the channel. If not
131             provided, the channel will not be ready (see L</on_ready>) until the remote
132             side has identified itself. Any received messages whose L<Argon::Message/token>
133             does not match the expected value are rejected.
134              
135             =head1 METHODS
136              
137             =head2 is_ready
138              
139             Returns true once the remote side has identified itself (C<remote> has been
140             set).
141              
142             =head2 identify
143              
144             Identifies this side of the channel to the remote end by sending its
145             L<Argon::Encryption/token>.
146              
147             =head1 AUTHOR
148              
149             Jeff Ober <sysread@fastmail.fm>
150              
151             =head1 COPYRIGHT AND LICENSE
152              
153             This software is copyright (c) 2017 by Jeff Ober.
154              
155             This is free software; you can redistribute it and/or modify it under
156             the same terms as the Perl 5 programming language system itself.
157              
158             =cut