File Coverage

blib/lib/Authen/Challenge/Basic.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             #
2             # Authen::Challenge::Basic: Provides a trivial challenge/response protocol
3             # to assist in authentication tasks. It provides for time-window
4             # challenge/response sessions.
5             #
6             # Using this module, it's possible to autenticate both endpoints of
7             # a transaction provided that a shared-secret was exchanged prior to
8             # the session among the endpoints. As timestamps are part of the
9             # protocol, some restrictions can be applied to the timing, to help
10             # prevent hijacked connections.
11             #
12             # This is free software. You can use at will provided that proper
13             # credit is given to the author(s). This module requires MD5.
14             #
15             # lem@cantv.net, 19980713 - Initial release
16             #
17             #############
18              
19             package Authen::Challenge::Basic;
20             $VERSION='0.1';
21             require 5.000;
22              
23             =head1 NAME
24              
25             Authen::Challenge::Basic - A Basic challenge/response authentication scheme.
26              
27             =head1 SYNOPSIS
28              
29             use Authen::Challenge::Basic;
30              
31             $server = Authen::Challenge->new ('Secret' => 'known2us',
32             'Timeout' => 30,
33             'Sync' => 10);
34              
35             $client = Authen::Challenge->new ('Secret' => 'known2us',
36             'Timeout' => 30,
37             'Sync' => 10);
38              
39             $challenge = $server->Challenge;
40             $response = $client->Response($challenge);
41              
42             if ($server->Validate($challenge, $response)) {
43             print "Hi master\n";
44             }
45             else {
46             print "Impostor!\n";
47             }
48              
49             =head1 DESCRIPTION
50              
51             Authen::Challenge::Basic provides a simple MD5-based challenge/response
52             protocol allowing for mutual peer authentication in a session. The
53             protocol includes timing information, so it is possible to introduce
54             time constraints in the session to help prevent attacks that rely on
55             adjusting the clock in one of the peers.
56              
57             This protocol requires a shared secret to be known only to the peers.
58             The compromise of this secret also compromises this protocol, so
59             it should be treated as a trusted password for that matter.
60              
61             Challenge/response sessions are not 'replayable' provided that the
62             attacker ignores the shared secret. The sessions are also associated
63             to the instance that produced the challenge. This means that it can
64             only be Validate()'d by the instance that produced the challenge in
65             the first place.
66              
67             The built-in random number generator from perl is used in this module.
68             Hooks for better random number generators are planned soon to increase
69             the relative strength of this protocol. In any case, the main security
70             dependencies for this module are MD5 itself and the secrecy of the shared
71             secret.
72              
73             The following functions are provided by this class.
74              
75             new()
76              
77             Creates a new instance of a challenge/response endpoint. It has three
78             parameters that influence its behavior. Those can be seen next
79              
80             $server = Authen::Challenge::Basic->new ('Secret' => 'known2us',
81             'Timeout' => 30,
82             'Sync' => 10);
83              
84             'Secret' is used to indicate the shared secret to use in this session.
85              
86             'Timeout' specifies the lifespan, in seconds, for this transaction.
87             This means that a succesful Validate() must occur within this many
88             seconds to have a chance to be acknowledged. Reducing this value
89             too much can cause problems as a slight ammount of load can make the
90             session fail. 30 seconds is a reasonable default for many uses. If
91             left unspecified, no timeout is enforced.
92              
93             'Sync' indicates how strict are we with regard to the clock in our
94             peer. The parameter contains the maximum offset in seconds between the
95             clocks of the peers. The more strict we are (ie, the smaller the number),
96             the less tolerant we are. If left unspecified, we'll allow any ammount
97             of drift. It's specified in seconds.
98              
99             Challenge()
100              
101             Issues a new challenge. The object creating this challenge stores
102             information about it that allows for later recognition and validation.
103             The Challenge() is a typical function of the server, though a double
104             Challenge/Response scenario allows mutual authentication such as
105             in the following scheme:
106              
107             Client Server
108             (1) C1 ---------------------->
109             (2) <---------------------- R(C1), C2
110             (3) R(C2) ---------------------->
111              
112             On stage (2), the Client is authenticated to the server. On stage (3),
113             both are mutually authenticated.
114              
115             This method is usually invoked like
116              
117             $challenge = $server->Challenge;
118              
119             $challenge will contain a printable string that must be passed to the
120             peer in order for a response to be received.
121              
122             Response()
123              
124             This method takes a challenge and generates the required response. This
125             is usually invoked as
126              
127             $response = $client->Response($challenge);
128              
129             Where $challenge contains a Challenge generated by a call to Challenge().
130             $response will contain a printable string that must be returned to the
131             peer in order for it to be validated.
132              
133             Validate()
134              
135             This method verifies the correctness of the Challenge/Response session by
136             insuring that:
137              
138             (a) The challenge was indeed generated from this instance
139             and is pending validation
140             (b) The time interval for the validation is acceptable
141             (c) The shared secret was used by the peer to create the
142             response
143              
144             Usually this method is invoked like this
145              
146             $r = $server->Validate($challenge, $response);
147              
148             It returns a true value to indicate a correct session where (a), (b) and
149             (c) hold true or false otherwise.
150              
151             =head1 CAVEATS
152              
153             Note that this module helps insure that the peers were who they said they
154             where provided that the shared secret is not known to any third parties.
155             If this is not true, then anything could happen.
156              
157             Also, after the initial authentication, a network connection can be
158             stolen or hijacked, rendering all of the tests useless.
159              
160             =head1 AUTHOR
161              
162             Luis E. Munoz
163              
164             =cut
165              
166 1     1   2482 use MD5;
  0            
  0            
167              
168             sub new {
169             my ($class, @opt) = @_;
170             my ($r_param) = canon_params(@opt);
171             $self = {'p' => $r_param,
172             'Error' => undef,
173             'Stamp' => '',
174             'Count' => 0,
175             'Alive' => {}
176             };
177             bless $self, $class;
178             return $self;
179             }
180              
181             sub Error {
182             my ($self) = @_;
183             $self->{'Error'};
184             }
185              
186             sub Challenge {
187             my ($self) = @_;
188             my ($ctx) = new MD5;
189             my ($Count) = sprintf("%05d", $self->{'Count'}++);
190             my ($Stamp) = time;
191             # Add the shared secret...
192             $ctx->add($self->{'p'}->{'secret'});
193             # ... and a transaction counter...
194             $ctx->add($Count);
195             # ... and a perturbed timestamp, keeping it safe.
196             $ctx->add($self->{'Stamp'} = $Stamp + rand(16384));
197             my ($result) = $ctx->hexdigest;
198             $self->{'Alive'}->{$result} = $Stamp;
199             $self->{'Error'} = undef;
200             $result;
201             }
202              
203             sub Response {
204             my ($self, $challenge) = @_;
205             my ($ctx) = new MD5;
206             my ($Stamp) = time;
207             $self->{'Error'} = undef;
208             $ctx->add($challenge);
209             $ctx->add($self->{'p'}->{'secret'});
210             $ctx->add($Stamp);
211             $ctx->hexdigest . "/" . sprintf("%012d", $Stamp);
212             }
213              
214             sub Validate {
215             my ($self, $c, $r) = @_;
216             my ($Stamp) = time;
217             my ($mysignature, $this);
218              
219             # Insure the response is in a valid format...
220             my ($signature, $tstamp) = split(/\//, $r, 2);
221              
222             if (!$tstamp or !$signature) {
223             $self->{'Error'} = "Wrong response format";
224             return undef;
225             }
226              
227             # Insure this challenge was issued by this instance...
228             if (!($this = $self->{'Alive'}->{$c})) {
229             $self->{'Error'} = "This challenge doesn't belong to this instance";
230             return undef;
231             }
232              
233             undef($self->{'Alive'}->{$c});
234              
235             # Insure that this response has come on time...
236             if ($self->{'p'}->{'timeout'}
237             && $self->{'p'}->{'timeout'} + $this < $Stamp)
238             {
239             $self->{'Error'} = "Response came too late";
240             return undef;
241             }
242              
243             # Insure time sync between client and server...
244             if ($self->{'p'}->{'sync'}
245             && $self->{'p'}->{'sync'} + $this < $tstamp)
246             {
247             $self->{'Error'} = "Endpoints out of time-sync";
248             return undef;
249             }
250              
251             # Insure a correct signature...
252             $mysignature = new MD5;
253             $mysignature->add($c);
254             $mysignature->add($self->{'p'}->{'secret'});
255             $mysignature->add(int($tstamp));
256             if ($mysignature->hexdigest ne $signature) {
257             $self->{'Error'} = "Wrong signature for this response";
258             return undef;
259             }
260             $c;
261             }
262              
263             sub canon_params {
264             my(@param) = @_;
265             my(%param) = @param;
266             my ($k, $n);
267              
268             foreach $k (keys %param) {
269             $n = $k;
270             $n =~ tr/A-Z/a-z/;
271             $param{$n} = $param{$k};
272             undef($param{$k}) unless $n eq $k;
273             }
274              
275             \%param;
276              
277             }
278              
279             1;
280              
281              
282              
283