File Coverage

blib/lib/Digest/Auth.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             package Digest::Auth;
2            
3             # --- Required Packages
4             #use Carp; # Generate better errors with more context
5 1     1   28622 use DBI; # Database connector
  0            
  0            
6             use CGI; # For the cookie handeling and output tools.
7             use strict; # Enforce restictions on good programming
8            
9             # --- Headers ---
10             my $PACKAGE = 'Auth';
11             my $VERSION = '0.01_2';
12             use vars qw($VERSION);
13            
14             # Routines
15             sub new {
16             my $package = shift;
17             my %opts = @_;
18             my $class = ref($package) || $package;
19            
20             my(%DM) = (
21             session => {
22             table => "session",
23             ip => "ip",
24             cert => "cert",
25             hash => "hash",
26             userid => "userid",
27             firstactive => "firstactive",
28             lastactive => "lastactive",
29             },
30             sessionlocks => {
31             table => "sessionlocks",
32             ip => "ip",
33             stamp => "stamp",
34             cert => "cert",
35             hash => "hash",
36             userid => "userid",
37             },
38             sessionattempts => {
39             table => "sessionattempts",
40             ip => "ip",
41             stamp => "stamp",
42             cert => "cert",
43             hash => "hash",
44             userid => "userid",
45             password => "password",
46             },
47             user => {
48             table => "user",
49             userid => "userid",
50             password => "password",
51             },
52             );
53            
54             # Options hash init
55             my($self) = {
56             dbh => undef, # data base handel
57             debug => 0, # level of debug: 0=off,1=Error,2=Warn,3=Verbose
58             debuginfo => "", # store debug information here.
59             cookiename => "SessionCert", # Cookie name to use for authorization
60             domain => ".", # Domain name for cookie
61             usecookies => 1, # Use cookies to maintain sessions.
62             digest => "Digest::SHA1", # Hash Digest to use
63             connection => 86400, # Maximum length of a authorized session. Default 24 hours
64             validation => 600, # Maximum amout of time for validation process. Default 10 minutes
65             idletime => 3600, # Amount of time you can be inactive before being logged out. Default 60 minutes
66             initcertretry => 5, # number of times to retry intitializing a session if the key is a duplicate
67             forgiverate => 86400, # Forgive bad login attempts after this much time or good login
68             maxconperip => 10, # Max connections/sessions per ip address
69             maxconperuser => 1, # Max connections/sessions per user name
70             maxbadpass => 5, # Max number of times a user can enter a bad password
71             compatmode => 0, # WARNING: THIS REDUCES SECURITY: Change to 1 to enable compatibility for non-javascript browsers.
72             locklength => [300,900,3600,86400,-1], # Amount of time a user is locked/banned for a rule violation
73             datamapping => \%DM, # Data Mapping to modify the fieldnames for the database.
74             };
75             bless($self, $class);
76             $self->DebugAdd($PACKAGE."->new()::Initialized",3);
77             my($bitbucket) = $self->Put(%opts);
78             return $self;
79             }
80            
81             sub Get{
82             my($self,$opt1,$opt2,$opt3) = @_;
83             my $result;
84             $self->DebugAdd($PACKAGE."->Get($opt1,$opt2,$opt3)::Initialized",3);
85            
86             $opt1 =~ s/[^\w]//g;
87             $opt1 = lc($opt1);
88            
89             $opt2 =~ s/[^\w]//g;
90             $opt2 = lc($opt2);
91            
92             $opt3 =~ s/[^\w]//g;
93             $opt3 = lc($opt3);
94            
95             if(exists $self->{$opt1}){
96             if($opt1 eq "datamapping"){
97             if(exists $self->{datamapping}{$opt2}){
98             if(exists $self->{$opt1}{$opt2}{$opt3}){
99             return $self->{$opt1}{$opt2}{$opt3};
100             }else{
101             return $result; # no result
102             }
103             }else{
104             return $result; # no result
105             }
106             }else{
107             return $self->{$opt1};
108             }
109             }else{
110             return $result; # no result
111             }
112             }
113             sub Put{
114             my($self,%opt) = @_;
115             my($result)= 0;
116             $self->DebugAdd($PACKAGE."->Put()::Initialized",3);
117            
118             if(exists $opt{dbh}){
119             $result++; $self->{dbh} = $opt{dbh};
120             }
121             if(exists $opt{debug} && $self->SanitizeData($opt{debug}) && $opt{debug} >= 0 && $opt{debug} <= 3){
122             $result++; $self->{debug} = $opt{debug};
123             }
124             if(exists $opt{digest}){
125             $result++; $self->{digest} = $opt{digest};
126             }
127             if(exists $opt{cookiename} && $self->SanitizeData($opt{cookiename},1)){
128             $result++; $self->{cookiename} = $opt{cookiename};
129             }
130             if(exists $opt{domain} && $self->SanitizeData($opt{domain},4)){
131             $result++; $self->{domain} = $opt{domain};
132             }
133             if(exists $opt{usecookies} && ($opt{usecookies} == 0 || $opt{usecookies} == 1)){
134             $result++; $self->{usecookies} = $opt{usecookies};
135             }
136             if(exists $opt{connection} && $self->SanitizeData($opt{connection},3)){
137             $result++; $self->{connection} = $opt{connection};
138             }
139             if(exists $opt{validation} && $self->SanitizeData($opt{validation},3)){
140             $result++; $self->{validation} = $opt{validation};
141             }
142             if(exists $opt{idletime} && $self->SanitizeData($opt{idletime},3)){
143             $result++; $self->{idletime} = $opt{idletime};
144             }
145             if(exists $opt{initcertretry} && $self->SanitizeData($opt{initcertretry})){
146             $result++; $self->{initcertretry} = $opt{initcertretry};
147             }
148             if(exists $opt{forgiverate} && $self->SanitizeData($opt{forgiverate})){
149             $result++; $self->{forgiverate} = $opt{forgiverate};
150             }
151             if(exists $opt{maxconperip} && $self->SanitizeData($opt{maxconperip},3)){
152             $result++; $self->{maxconperip} = $opt{maxconperip};
153             }
154             if(exists $opt{maxconperuser} && $self->SanitizeData($opt{maxconperuser},3)){
155             $result++; $self->{maxconperuser} = $opt{maxconperuser};
156             }
157             if(exists $opt{maxbadpass} && $self->SanitizeData($opt{maxbadpass},3)){
158             $result++; $self->{maxbadpass} = $opt{maxbadpass};
159             }
160             if(exists $opt{compatmode} && $self->SanitizeData($opt{compatmode}) && ($opt{compatmode} == 1 || $opt{compatmode} == 0)){
161             $result++; $self->{compatmode} = $opt{compatmode};
162             }
163             if(exists $opt{locklength} && $self->SanitizeData($opt{locklength},3)){
164             $result++; $self->{locklength} = $opt{locklength};
165             }
166             if(exists $opt{datamapping}{session}{table} && $self->SanitizeData($opt{datamapping}{session}{table},2)){
167             $result++; $self->{datamapping}{session}{table} = $opt{datamapping}{session}{table};
168             }
169             if(exists $opt{datamapping}{session}{ip} && $self->SanitizeData($opt{datamapping}{session}{ip},2)){
170             $result++; $self->{datamapping}{session}{ip} = $opt{datamapping}{session}{ip};
171             }
172             if(exists $opt{datamapping}{session}{cert} && $self->SanitizeData($opt{datamapping}{session}{cert},2)){
173             $result++; $self->{datamapping}{session}{cert} = $self->{datamapping}{session}{cert};
174             }
175             if(exists $opt{datamapping}{session}{hash} && $self->SanitizeData($opt{datamapping}{session}{hash},2)){
176             $result++; $self->{datamapping}{session}{hash} = $opt{datamapping}{session}{hash};
177             }
178             if(exists $opt{datamapping}{session}{userid} && $self->SanitizeData($opt{datamapping}{session}{userid},2)){
179             $result++; $self->{datamapping}{session}{userid} = $opt{datamapping}{session}{userid};
180             }
181             if(exists $opt{datamapping}{session}{firstactive} && $self->SanitizeData($opt{datamapping}{session}{firstactive},2)){
182             $result++; $self->{datamapping}{session}{firstactive} = $opt{datamapping}{session}{firstactive};
183             }
184             if(exists $opt{datamapping}{session}{lastactive} && $self->SanitizeData($opt{datamapping}{session}{lastactive},2)){
185             $result++; $self->{datamapping}{session}{lastactive} = $opt{datamapping}{session}{lastactive};
186             }
187             if(exists $opt{datamapping}{sessionlocks}{table} && $self->SanitizeData($opt{datamapping}{sessionlocks}{table},2)){
188             $result++; $self->{datamapping}{sessionlocks}{table} = $opt{datamapping}{sessionlocks}{table};
189             }
190             if(exists $opt{datamapping}{sessionlocks}{ip} && $self->SanitizeData($opt{datamapping}{sessionlocks}{ip},2)){
191             $result++; $self->{datamapping}{sessionlocks}{ip} = $opt{datamapping}{sessionlocks}{ip};
192             }
193             if(exists $opt{datamapping}{sessionlocks}{stamp} && $self->SanitizeData($opt{datamapping}{sessionlocks}{stamp},2)){
194             $result++; $self->{datamapping}{sessionlocks}{stamp} = $opt{datamapping}{sessionlocks}{stamp};
195             }
196             if(exists $opt{datamapping}{sessionlocks}{cert} && $self->SanitizeData($opt{datamapping}{sessionlocks}{cert},2)){
197             $result++; $self->{datamapping}{sessionlocks}{cert} = $opt{datamapping}{sessionlocks}{cert};
198             }
199             if(exists $opt{datamapping}{sessionlocks}{hash} && $self->SanitizeData($opt{datamapping}{sessionlocks}{hash},2)){
200             $result++; $self->{datamapping}{sessionlocks}{hash} = $opt{datamapping}{sessionlocks}{hash};
201             }
202             if(exists $opt{datamapping}{sessionlocks}{userid} && $self->SanitizeData($opt{datamapping}{sessionlocks}{userid},2)){
203             $result++; $self->{datamapping}{sessionlocks}{userid} = $opt{datamapping}{sessionlocks}{userid};
204             }
205             if(exists $opt{datamapping}{sessionattempts}{table} && $self->SanitizeData($opt{datamapping}{sessionattempts}{table},2)){
206             $result++; $self->{datamapping}{sessionattempts}{table} = $opt{datamapping}{sessionattempts}{table};
207             }
208             if(exists $opt{datamapping}{sessionattempts}{ip} && $self->SanitizeData($opt{datamapping}{sessionattempts}{ip},2)){
209             $result++; $self->{datamapping}{sessionattempts}{ip} = $self->{datamapping}{sessionattempts}{ip};
210             }
211             if(exists $opt{datamapping}{sessionattempts}{stamp} && $self->SanitizeData($opt{datamapping}{sessionattempts}{stamp},2)){
212             $result++; $self->{datamapping}{sessionattempts}{stamp} = $opt{datamapping}{sessionattempts}{stamp};
213             }
214             if(exists $opt{datamapping}{sessionattempts}{cert} && $self->SanitizeData($opt{datamapping}{sessionattempts}{cert},2)){
215             $result++; $self->{datamapping}{sessionattempts}{cert} = $opt{datamapping}{sessionattempts}{cert};
216             }
217             if(exists $opt{datamapping}{sessionattempts}{hash} && $self->SanitizeData($opt{datamapping}{sessionattempts}{hash},2)){
218             $result++; $self->{datamapping}{sessionattempts}{hash} = $opt{datamapping}{sessionattempts}{hash};
219             }
220             if(exists $opt{datamapping}{sessionattempts}{userid} && $self->SanitizeData($opt{datamapping}{sessionattempts}{userid},2)){
221             $result++; $self->{datamapping}{sessionattempts}{userid} = $opt{datamapping}{sessionattempts}{userid};
222             }
223             if(exists $opt{datamapping}{sessionattempts}{password} && $self->SanitizeData($opt{datamapping}{sessionattempts}{password},2)){
224             $result++; $self->{datamapping}{sessionattempts}{password} = $opt{datamapping}{sessionattempts}{password};
225             }
226             if(exists $opt{datamapping}{user}{table} && $self->SanitizeData($opt{datamapping}{user}{table},2)){
227             $result++; $self->{datamapping}{user}{table} = $opt{datamapping}{user}{table};
228             }
229             if(exists $opt{datamapping}{user}{userid} && $self->SanitizeData($opt{datamapping}{user}{userid},2)){
230             $result++; $self->{datamapping}{user}{userid} = $opt{datamapping}{user}{userid};
231             }
232             if(exists $opt{datamapping}{user}{password} && $self->SanitizeData($opt{datamapping}{user}{password},2)){
233             $result++; $self->{datamapping}{user}{password} = $opt{datamapping}{user}{password};
234             }
235            
236             return $result;
237             }
238             sub Keygen {
239             # Creates a somewhat random key of $QueKeyLength size using numbers
240             # and uppercase and lowercase letters based on $complexity.
241             my($self,
242             $QueKeyLength,
243             $complexity)=@_;
244             my(@keys);
245             my($key) = "";
246             $self->DebugAdd($PACKAGE."->Keygen()::Initialized",3);
247            
248             if($QueKeyLength < 1 || $QueKeyLength > 128 ){ $QueKeyLength = 10; }
249             if($complexity == 1){
250             @keys = ('A'..'Z');
251             }elsif($complexity == 2){
252             @keys = ('A'..'Z','0'..'9');
253             }else{
254             @keys = ('A'..'Z','a'..'z','0'..'9');
255             }
256             for(my($a)=1; $a <= $QueKeyLength; $a++){ $key .= $keys[ int( rand($#keys)) ]; }
257             return $key;
258             }
259             sub SanitizeData{
260             # do a test to see if there are any special characters
261             my($self,$data1,$opt) = @_;
262             my($result)=0;
263             my($data2) = $data1;
264             if($opt == 1){ # letters and numbers with underscore "_"
265             $data2 =~ s/[^a-zA-Z0-9_]//g;
266             }elsif($opt == 2){ # only letters and numbers
267             $data2 =~ s/[^a-zA-Z0-9]//g;
268             }elsif($opt == 3){ # positive or neg numbers
269             $data2 =~ s/[^0-9\-]//g;
270             }elsif($opt == 4){ # lower case letters numbers and dots + dashes
271             $data2 =~ s/[^a-z0-9\.\-]//g;
272             }else{ # positive numbers only
273             $data2 =~ s/[^0-9]//g;
274             }
275             if($data1 eq $data2){
276             $result++;}
277             return $result
278             }
279             sub AutoAuth{
280             # Automatically Authorize the user.
281             my($self,%opts) = @_;
282             my($result)=0;
283             $self->DebugAdd($PACKAGE."->AutoAuth()::Initialized",3);
284            
285             if( defined($self->{user}) && defined($self->{hash})){
286             $result = "Authorize";
287             # $self->Authorize(%opts);
288            
289             }elsif( defined($opts{cert}) ){
290             $result = "Validate";
291             # $self->Validate(%opts);
292            
293             }else{
294             $result = "Initialize";
295             # $self->Initialize(%opts);
296            
297             }
298            
299             return $result;
300             }
301             sub UserAdd{
302             # insert data into user table for login credentials
303             my($self, %opts) = @_;
304             my($redflag) = 0;
305             my($result) = 0;
306             my($hits) = 0;
307             $self->DebugAdd($PACKAGE."->UserAdd()::Initialized",3);
308            
309             if(! $self->SanitizeData($opts{user},1)){$redflag++;}
310            
311             # Check for user in the system
312             # We have two methods to sanitize databases with.
313             # We are using DBI::quote since we want to keep this extensible.
314             # on the front end after the DBH is created if it is a SQL database it will be the
315             # programmers responisbility to first parse it with method no. 1. We will try to
316             # keep things neat using method no. 2.
317             # 1. mysql_real_escape_string();
318             # 2. $self->{dbh}->quote();
319             my($sql)='SELECT COUNT(*) AS HITS FROM '
320             . $self->{datamapping}{user}{table}
321             . ' WHERE '
322             . $self->{datamapping}{user}{userid}
323             . ' = '
324             . $self->{dbh}->quote( $opts{user} );
325            
326             my($sth) = $self->{dbh}->prepare($sql);
327             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->UserAdd()::Check",$sql, $!),1);
328             while (my $ref = $sth->fetchrow_hashref()){
329             $hits = $ref->{'HITS'};
330             }
331             $sth->finish();
332            
333             # if user is not in the system, add them, otherwise fail gracefully
334             if($hits == 0){
335             # create user, they are not in the system
336             my($sql)='INSERT INTO '
337             . $self->{datamapping}{user}{table}
338             . ' ('
339             . $self->{datamapping}{user}{userid}
340             . ', '
341             . $self->{datamapping}{user}{password}
342             . ') VALUES ('
343             . ' '
344             . $self->{dbh}->quote($opts{user})
345             . ', '
346             . $self->{dbh}->quote($opts{password})
347             . ')';
348             my($sth) = $self->{dbh}->prepare($sql);
349             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->UserAdd()::Add",$sql, $!),1);
350             $sth->finish();
351             $result=2;
352             }else{
353             # user in the system already
354             $self->DebugAdd($PACKAGE."->UserAdd()::User Exists{".$opts{user}."}",2);
355             $result=1;
356             }
357             return $result;
358             }
359            
360             sub Initialize{
361             my($self) = @_;
362             my($result) = 0;
363             my($cert);
364             my($ip) = $ENV{'REMOTE_ADDR'};
365             my($stamp) = time();
366            
367             $self->DebugAdd($PACKAGE."->Initialize()::Initialized",3);
368             # insert data into sessions table to establish a login session
369            
370             my($login) = 0; # toggles to 1 when successfull
371             my($max) = 0; # max number of trys for system safety when testing
372             my($hits) = 0;
373            
374             while ($login <= 0 && $max < $self->{initcertretry}){ # make sure it is a unique entry
375            
376             $cert = $self->Keygen(10,2);
377            
378             my($sql)="SELECT COUNT(*) AS HITS FROM "
379             . $self->{datamapping}{session}{table}
380             . " WHERE "
381             . $self->{datamapping}{session}{ip}
382             . " = "
383             . $self->{dbh}->quote($ip)
384             . " AND "
385             . $self->{datamapping}{session}{cert}
386             . " = "
387             . $self->{dbh}->quote($cert);
388            
389             my($sth) = $self->{dbh}->prepare($sql);
390             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Initialize()",$sql, $!),1);
391             while (my $ref = $sth->fetchrow_hashref()){
392             $hits = $ref->{'HITS'};
393             }
394             $sth->finish();
395             if($hits == 0){
396             $login++;
397             }else{
398             $max++;
399             $self->DebugAdd($self->SQL_Die("Initialize()::CreateCert::CertCollision",$sql,"Debug"),2);
400             }
401             }
402             if($hits == 0){
403             # initialize the session transaction.
404             my($sql) = "INSERT INTO "
405             . $self->{datamapping}{session}{table}
406             . " ("
407             . $self->{datamapping}{session}{cert}
408             . ","
409             . $self->{datamapping}{session}{ip}
410             . ","
411             . $self->{datamapping}{session}{firstactive}
412             . ","
413             . $self->{datamapping}{session}{lastactive}
414             . ") VALUES("
415             . $self->{dbh}->quote($cert)
416             . ","
417             . $self->{dbh}->quote($ip)
418             . ","
419             . $self->{dbh}->quote($stamp)
420             . ","
421             . $self->{dbh}->quote($stamp)
422             . ")";
423             my($sth) = $self->{dbh}->prepare($sql);
424             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Initialize()",$sql, $!),1);
425             $sth->finish();
426             $result++;
427             }
428             # temp measure cert, ip, stamp should not be global var's
429             # $self->{cert} = $cert;
430             # $self->{ip} = $ip
431             # $self->{stamp} = $stamp
432             return (cert => $cert,
433             ip => $ip,
434             stamp => $stamp);
435             }
436            
437             sub Validate{
438             # ops{userid, cert, hash, pass}
439             # userid, cert, (hash || password) required
440             # + use only hash when self->compatmode = 0
441             # + use only pass when self->compatmode = 1
442            
443             my($self,%opts) = @_;
444             $self->DebugAdd($PACKAGE."->Validate()::Initialized",3);
445            
446             my($hash);
447             my($cert);
448             my($ip) = $ENV{'REMOTE_ADDR'};
449             my($stamp) = time();
450             my($result) = 0;
451             my($hits) = 0;
452             my($cookiedata)="";
453            
454             # TODO: Need to build validation for this
455            
456             # Find all matching record for the primary key (Key,IP Address)
457             my(%SQL_SESSION);
458             my($sql) = "SELECT * FROM "
459             . $self->{datamapping}{session}{table}
460             . " WHERE "
461             . $self->{datamapping}{session}{cert}
462             . " = "
463             . $self->{dbh}->quote($opts{cert})
464             . " AND "
465             . $self->{datamapping}{session}{ip}
466             . " = "
467             . $self->{dbh}->quote($ip)
468             . " LIMIT 0,1";
469             my($sth) = $self->{dbh}->prepare($sql);
470             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Validate()",$sql, $!),1);
471             while (my $ref = $sth->fetchrow_hashref()){
472             $SQL_SESSION{firstactive} = $ref->{$self->{datamapping}{session}{firstactive}};
473             }
474             $sth->finish();
475            
476             if( ! $SQL_SESSION{firstactive} ){
477             # no: stop / log attempt; (Key,IP Address) not found in sessions. Log to sessionattempts
478             # ops{user, cert, hash, pass}
479             $self->DebugAdd($PACKAGE."->Validate()::Active(Key,IP Address) Not Found",2);
480             my($sql) = "INSERT INTO "
481             . $self->{datamapping}{sessionattempt}{table}
482             . " ("
483             . $self->{datamapping}{sessionattempt}{cert}
484             . ","
485             . $self->{datamapping}{sessionattempt}{ip}
486             . ","
487             . $self->{datamapping}{sessionattempt}{stamp}
488             . ","
489             . $self->{datamapping}{sessionattempt}{userid}
490             . ","
491             . $self->{datamapping}{sessionattempt}{hash}
492             . ","
493             . $self->{datamapping}{sessionattempt}{password}
494             . ") VALUES("
495             . $self->{dbh}->quote($opts{cert})
496             . ","
497             . $self->{dbh}->quote($ip)
498             . ","
499             . $self->{dbh}->quote($stamp)
500             . ","
501             . $self->{dbh}->quote($opts{userid})
502             . ","
503             . $self->{dbh}->quote($opts{hash})
504             . ","
505             . $self->{dbh}->quote($opts{password})
506             . ")";
507             my($sth) = $self->{dbh}->prepare($sql);
508             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Initialize()",$sql, $!),1);
509             $sth->finish();
510             }else{
511             # Yes: continue
512             # Get the specified Username given from the client from the user table
513             my(%SQL_USER);
514             my($sql) = "SELECT * FROM "
515             . $self->{datamapping}{user}{table}
516             . " WHERE "
517             . $self->{datamapping}{user}{userid}
518             . " = "
519             . $self->{dbh}->quote($opts{userid});
520             my($sth) = $self->{dbh}->prepare($sql);
521             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Validate()",$sql, $!),1);
522             while (my $ref = $sth->fetchrow_hashref()){
523             $SQL_USER{password} = $ref->{$self->{datamapping}{user}{password}};
524             $SQL_USER{userid} = $ref->{$self->{datamapping}{user}{userid}};
525             }
526             $sth->finish();
527            
528             # if(Is username Found?){Yes: continue}else{no: stop}
529             # -----TODO
530            
531             # Create a secure 1 way Hash of the Username, Key, and Password
532            
533             my($digestdata) = ($opts{userid} . $opts{cert} . $SQL_USER{password} . $SQL_SESSION{firstactive} );
534            
535             $hash = $self->DigestMake(data => $digestdata);
536            
537             # if compat mode, ignore hash arg and build the hash without javascript
538             if($self->{compatmode} != 0){
539             $self->DebugAdd($PACKAGE."->Validate()::CompatMode=1",3);
540             my($digestdata2) = ($opts{userid} . $opts{cert} . $opts{password} . $SQL_SESSION{firstactive});
541             $opts{hash} = $self->DigestMake(data => $digestdata);
542             }
543            
544            
545            
546             # Test the hashes against eachother on a time sensitive manner
547             if( $hash eq $opts{hash} &&
548             $stamp <= ($SQL_SESSION{firstactive} + $self->{validation})
549             ){
550            
551             # -----TODO
552             # this deletes anything, but we have to make it match our prefrences for the max number of user logins
553             # we do need some sort of cleanup though here.. this SQL statement needs revision
554             # my($sql) = "DELETE * FROM "
555             # . $self->{datamapping}{session}{table}
556             # . " WHERE NOT "
557             # . $self->{datamapping}{session}{userid}
558             # . " = "
559             # . $self->{dbh}->quote($opts{userid})
560             # . " AND "
561             # . $self->{datamapping}{session}{cert}
562             # . " = "
563             # . $self->{dbh}->quote($opts{cert})
564             # . " AND "
565             # . $self->{datamapping}{session}{ip}
566             # . " = "
567             # . $self->{dbh}->quote($ip);
568             # print $self->SQL_Die("Validate()",$sql,"Debug");
569             # # $self->DebugAdd( $self->SQL_Die("Validate()",$sql,"Debug") );
570             # my($sth) = $self->{dbh}->prepare($sql);
571             # $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Validate()",$sql, $!),1);
572             # $sth->finish();
573            
574             my($sql) = "UPDATE "
575             . $self->{datamapping}{session}{table}
576             . " SET "
577             . $self->{datamapping}{session}{lastactive}
578             . " = "
579             . $self->{dbh}->quote($stamp)
580             . ", "
581             . $self->{datamapping}{session}{hash}
582             . " = "
583             . $self->{dbh}->quote($hash)
584             . ", "
585             . $self->{datamapping}{session}{userid}
586             . " = "
587             . $self->{dbh}->quote($opts{userid})
588             . " WHERE "
589             . $self->{datamapping}{session}{cert}
590             . " = "
591             . $self->{dbh}->quote($opts{cert})
592             . " AND "
593             . $self->{datamapping}{session}{ip}
594             . " = "
595             . $self->{dbh}->quote($ip);
596             # $self->DebugAdd( $self->SQL_Die("Validate()",$sql,"Debug") );
597             my($sth) = $self->{dbh}->prepare($sql);
598             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Validate()",$sql, $!),1);
599             $sth->finish();
600             # TODO: actions:
601             # delete all previous sessions for username (1 session per user)
602            
603             $result = 1;
604             }else{
605             # failed to validate
606             # print "'$hash' is ne '".$opts{hash}."'
OR
" .$stamp ." is not <= ". ($SQL_SESSION{firstactive} + $self->{validation})."
";
607             }
608             }
609             if($self->{usecookies}){
610             $cookiedata = $self->CookieSet(data => "userid:".$opts{userid}.",hash:".$hash);
611             }
612             return (result => $result,
613             userid => $opts{userid},
614             hash => $hash,
615             cookie => $cookiedata);
616             }
617            
618             sub Authorize{
619             my($self,%opts) = @_;
620             # ops{userid, hash}
621            
622             my($ip) = $ENV{'REMOTE_ADDR'};
623             my($stamp) = time();
624             my($result) = 0;
625             my($hits) = 0;
626             $self->DebugAdd($PACKAGE."->Authorize()::Initialized",3);
627            
628             # rules:
629             # CERT{hash} eq SQL.session{hash}
630             # CERT{userid} eq SQL.session{userid}
631             # Parsed{ip} eq SQL.session{ip}
632             # ($SQL_SESSION{firstactive} + $connection) = $stamp &&
633             # ($SQL_SESSION{lastactive} + $idletime) >= $stamp
634             # actions:
635             # session{lastactive} = Parsed{time}
636            
637             my(%SQL_SESSION);
638             my($sql)="SELECT * FROM "
639             . $self->{datamapping}{session}{table}
640             . " WHERE "
641             . $self->{datamapping}{session}{hash}
642             . " = "
643             . $self->{dbh}->quote($opts{hash})
644             . " AND "
645             . $self->{datamapping}{session}{userid}
646             . " = "
647             . $self->{dbh}->quote($opts{userid})
648             . " AND "
649             . $self->{datamapping}{session}{ip}
650             . " = "
651             . $self->{dbh}->quote($ip)
652             . " LIMIT 0,1";
653             my($sth) = $self->{dbh}->prepare($sql);
654             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Authorize()",$sql, $!),1);
655             while (my $ref = $sth->fetchrow_hashref()){
656             $SQL_SESSION{firstactive} = $ref->{ $self->{datamapping}{session}{firstactive} };
657             $SQL_SESSION{lastactive} = $ref->{ $self->{datamapping}{session}{lastactive} };
658             }
659             $sth->finish();
660            
661             if( (($SQL_SESSION{firstactive} + $self->{connection}) >= $stamp) &&
662             (($SQL_SESSION{lastactive} + $self->{idletime}) >= $stamp)
663             ){
664             # Is the user session active valid? if it is update session table;
665             my($sql) = "UPDATE "
666             . $self->{datamapping}{session}{table}
667             . " SET "
668             . $self->{datamapping}{session}{lastactive}
669             . " = "
670             . $self->{dbh}->quote($stamp)
671             . " WHERE "
672             . $self->{datamapping}{session}{hash}
673             . " = "
674             . $self->{dbh}->quote($opts{hash})
675             . " AND "
676             . $self->{datamapping}{session}{userid}
677             . " = "
678             . $self->{dbh}->quote($opts{userid})
679             . " AND "
680             . $self->{datamapping}{session}{ip}
681             . " = "
682             . $self->{dbh}->quote($ip);
683             my($sth) = $self->{dbh}->prepare($sql);
684             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->Authorize()",$sql, $!),1);
685             $sth->finish();
686             $result++;
687             }else{
688             # Note the invalid attempt
689             }
690             return $result;
691             }
692            
693             sub DigestMake{
694             my($self,%opts) = @_;
695             $self->DebugAdd($PACKAGE."->Authorize()::DigestMake",3);
696            
697             my($DIGEST); my($HASH);
698            
699             if($self->{digest} eq "Digest::SHA1"){
700             use Digest::SHA1;
701             $DIGEST = Digest::SHA1->new;
702             $DIGEST->add( $opts{data} );
703             $HASH = $DIGEST->hexdigest;
704             }elsif($self->{digest} eq "MD5"){
705             use Digest::MD5;
706             $DIGEST = Digest::MD5->new;
707             $DIGEST->add( $opts{data} );
708             $HASH = $DIGEST->hexdigest;
709             }else{
710             # Might do this way instead:
711             ##################################################
712             # use Package::Alias DIGESTNAME => $self->digest;
713             # use DIGESTNAME;
714             # $DIGEST = DIGESTNAME->new;
715             # $DIGEST->add($opts{data});
716             # $HASH = $DIGEST->hexdigest;
717             ##################################################
718            
719             eval ('use '.$self->digest);
720             eval ('$DIGEST = '.$self->digest.'->new');
721             $DIGEST->add($opts{data});
722             $HASH = $DIGEST->hexdigest;
723             }
724             return $HASH;
725             }
726            
727             sub DeleteExpiredSessions {
728             # scrubs any expired sessions
729             my($self,%opts) = @_;
730             my($result) = 0;
731            
732             return $result;
733             }
734            
735             sub TableMake{
736             my($self, %opts) = @_;
737             my($result) = 0;
738             $self->DebugAdd($PACKAGE."->TableMake()::Initialized",3);
739            
740             if(defined($opts{'table'}) && (lc($opts{'table'}) eq 'all'|| lc($opts{'table'}) eq 'session')){
741             $self->DebugAdd($PACKAGE."->TableMake()::CreatingTable::Session",3);
742             my($sql)= qq^CREATE TABLE IF NOT EXISTS ^ . $self->{datamapping}{session}{table}
743             . qq^ (^ . $self->{datamapping}{session}{ip}
744             . qq^ VARCHAR(15) NOT NULL, ^ . $self->{datamapping}{session}{cert}
745             . qq^ VARCHAR(10) NOT NULL, ^ . $self->{datamapping}{session}{hash}
746             . qq^ VARCHAR(45), ^ . $self->{datamapping}{session}{userid}
747             . qq^ VARCHAR(45), ^ . $self->{datamapping}{session}{firstactive}
748             . qq^ VARCHAR(45), ^ . $self->{datamapping}{session}{lastactive}
749             . qq^ VARCHAR(45), PRIMARY KEY(^ . $self->{datamapping}{session}{ip}
750             . qq^, ^ . $self->{datamapping}{session}{cert}
751             . qq^))^;
752             my($sth) = $self->{dbh}->prepare($sql);
753             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->TableMake(table => '".$opts{'table'}."')::Create Table::Session::Execute",$sql, $!),1);
754             $sth->finish();
755             $result++;
756             }
757            
758             if(defined($opts{'table'}) && (lc($opts{'table'}) eq 'all'|| lc($opts{'table'}) eq 'sessionattempts')){
759             $self->DebugAdd($PACKAGE."->TableMake()::CreatingTable::SessionAttempts",3);
760             my($sql)= qq^CREATE TABLE IF NOT EXISTS ^.$self->{datamapping}{sessionattempts}{table}
761             .qq^ (^.$self->{datamapping}{sessionattempts}{ip}
762             .qq^ varchar(15) NOT NULL, ^.$self->{datamapping}{sessionattempts}{stamp}
763             .qq^ varchar(10) NOT NULL, ^.$self->{datamapping}{sessionattempts}{hash}
764             .qq^ varchar(45) default NULL, ^.$self->{datamapping}{sessionattempts}{userid}
765             .qq^ varchar(45) default NULL, ^.$self->{datamapping}{sessionattempts}{cert}
766             .qq^ varchar(45) default NULL, ^.$self->{datamapping}{sessionattempts}{password}
767             .qq^ varchar(45) default NULL, PRIMARY KEY(^.$self->{datamapping}{sessionattempts}{ip}
768             .qq^, ^.$self->{datamapping}{sessionattempts}{stamp}
769             .qq^))^;
770             my($sth) = $self->{dbh}->prepare($sql);
771             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->TableMake(table => '".$opts{'table'}."')::Create Table::SessionAttempts::Execute",$sql, $!),1);
772             $sth->finish();
773             $result++;
774             }
775            
776             if(defined($opts{'table'}) && (lc($opts{'table'}) eq 'all'|| lc($opts{'table'}) eq 'sessionlocks')){
777             $self->DebugAdd($PACKAGE."->TableMake()::CreatingTable::SessionLocks",3);
778             my($sql)= qq^CREATE TABLE IF NOT EXISTS ^.$self->{datamapping}{sessionlocks}{table}
779             .qq^ (^.$self->{datamapping}{sessionlocks}{ip}
780             .qq^ varchar(15) NOT NULL, ^.$self->{datamapping}{sessionlocks}{stamp}
781             .qq^ varchar(10) NOT NULL, ^.$self->{datamapping}{sessionlocks}{hash}
782             .qq^ varchar(45) default NULL, ^.$self->{datamapping}{sessionlocks}{userid}
783             .qq^ varchar(45) default NULL, ^.$self->{datamapping}{sessionlocks}{cert}
784             .qq^ varchar(45) default NULL, PRIMARY KEY(^.$self->{datamapping}{sessionlocks}{ip}
785             .qq^,^.$self->{datamapping}{sessionlocks}{stamp}
786             .qq^))^;
787             my($sth) = $self->{dbh}->prepare($sql);
788             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->TableMake(table => '".$opts{'table'}."')::Create Table::SessionLocks::Execute",$sql, $!),1);
789             $sth->finish();
790             $result++;
791             }
792            
793             if(defined($opts{'table'}) && (lc($opts{'table'}) eq 'all'|| lc($opts{'table'}) eq 'user')){
794             $self->DebugAdd($PACKAGE."->TableMake()::CreatingTable::User",3);
795             my($sql)= qq^CREATE TABLE IF NOT EXISTS ^.$self->{datamapping}{user}{table}
796             .qq^ (^.$self->{datamapping}{user}{userid}
797             .qq^ varchar(50) NOT NULL,^.$self->{datamapping}{user}{password}
798             .qq^ varchar(50) default NULL, PRIMARY KEY(^.$self->{datamapping}{user}{userid}
799             .qq^))^;
800             my($sth) = $self->{dbh}->prepare($sql);
801             $sth->execute() or $self->DebugShow($self->SQL_Die("Auth->TableMake(table => '".$opts{'table'}."')::Create Table::User::Execute",$sql, $!),1);
802             $sth->finish();
803             $result++;
804             }
805             return $result; # Number of SQL statements processed successfully.
806             }
807            
808             sub SQL_Die{
809             # Deguging tool: Helps neatly print SQL statements out when module for debug messages.
810             my($self, $subroutine, $sql, $err) = @_;
811             my(@keys)=("FROM","WHERE","VALUES","SET","OR","AND","LIMIT","ORDER","BETWEEN","IN", "AS", "UNION", "INTO", "IF");
812             my($cnt)= "\n
";
 
813             for(my($a) = 0; $a <= $#keys; $a++){
814             my($b) = " " . $keys[$a] . " ";
815             my($c) = "\n" . $b;
816             $sql =~ s/$b/$c/gi;
817             }
818             $sql =~ s/, /,\n /g;
819             return ("\n
\nCan not execute SQL statement $PACKAGE->". $subroutine ."". $cnt . $sql .";\n$err\n
");
820             }
821             sub DebugAdd{
822             # Deguging tool: Appends debugging information to the main module.
823             my($self,$data,$level) = @_;
824             if($self->{debug} >= $level){
825             my($leveltext)="";
826             if($level == 1){
827             $leveltext = "Error";
828             }elsif($level == 2){
829             $leveltext = "Warning";
830             }elsif($level == 3){
831             $leveltext = "Infomation";
832             }else{
833             $leveltext = "Unknown";
834             }
835             $self->{debuginfo} .= "
".$leveltext.": Time: " . localtime() . " [" . time() . "]
" . $data;
836             }
837             }
838             sub DebugShow{
839             my($self,$data,$level) = @_;
840             $self->DebugAdd($data,$level);
841             if($self->{debug} > 0){
842             print $self->{debuginfo};
843             }
844             }
845            
846             sub CookieGet {
847             # move cookies to a hash
848             my($self,%opts) = @_;
849             $self->DebugAdd($PACKAGE."->CookieGet()::Initialized",3);
850             my(%result);
851             my($nm) = ( defined($opts{name}) )? $opts{name} : $self->{cookiename};
852             my($query) = new CGI;
853             my(@cookies) = split(/;/, $query->cookie($nm));
854            
855             for (my($a)=0; $a <= $#cookies; $a++){
856             my @pairs = split(/,/,$cookies[$a]);
857             for (my($b)=0; $b <= $#pairs; $b++){
858             my($n,$v) = split(/:/, $pairs[$b]) ;
859             $result{$n} = $v;
860             }
861             last;
862             }
863             return (%result);
864             }
865            
866             sub CookieSet {
867             # move data to a cookie
868             my($self,%opts) = @_;
869             $self->DebugAdd($PACKAGE."->CookieWrite()::Initialized",3);
870            
871             my($query) = new CGI;
872             my($nm) = ( defined($opts{name}) )? $opts{name} : $self->{cookiename};
873             my($domain)= ( defined($opts{domain}) )? $opts{domain} : $self->{domain};
874             my($path) = ( defined($opts{path}) )? $opts{path} : "/";
875             my($gmt) = ( defined($opts{gmt}) )? $opts{gmt} : gmtime(time() + $self->{idletime})." GMT;";
876             my($cookie)= $query->cookie(-domain => $domain,
877             -name => $nm,
878             -value => $opts{data},
879             -expires => $gmt,
880             -path => $path);
881            
882             print "Set-Cookie: $cookie\n";
883             $self->DebugAdd($PACKAGE."->CookieWrite()::Cookie Written".$cookie,3);
884            
885             return $cookie;
886             }
887            
888             sub WriteScript{
889             # print a javascript
890             my($self,$script) = @_;
891             if($script eq undef){ $script = $self->JS_Digest(); }
892             my($result)= qq^\n\n^;
893             return $result;
894             }
895            
896             sub JS_Digest{
897             my($self,%opts) = @_;
898            
899             if($self->{digest} eq "MD5"){
900            
901             return <<"ENDTAG"
902             /*
903             * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
904             * Digest Algorithm, as defined in RFC 1321.
905             * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
906             * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
907             * Distributed under the BSD License
908             * See http://pajhome.org.uk/crypt/md5 for more info.
909             */
910            
911             /*
912             * Configurable variables. You may need to tweak these to be compatible with
913             * the server-side, but the defaults work in most cases.
914             */
915             var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
916             var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
917             var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
918            
919             /*
920             * These are the functions you will usually want to call
921             * They take string arguments and return either hex or base-64 encoded strings
922             */
923             function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
924             function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
925             function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
926             function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
927             function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
928             function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
929            
930             /*
931             * Perform a simple self-test to see if the VM is working
932             */
933             function md5_vm_test()
934             {
935             return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
936             }
937            
938             /*
939             * Calculate the MD5 of an array of little-endian words, and a bit length
940             */
941             function core_md5(x, len)
942             {
943             /* append padding */
944             x[len >> 5] |= 0x80 << ((len) % 32);
945             x[(((len + 64) >>> 9) << 4) + 14] = len;
946            
947             var a = 1732584193;
948             var b = -271733879;
949             var c = -1732584194;
950             var d = 271733878;
951            
952             for(var i = 0; i < x.length; i += 16)
953             {
954             var olda = a;
955             var oldb = b;
956             var oldc = c;
957             var oldd = d;
958            
959             a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
960             d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
961             c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);
962             b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
963             a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
964             d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);
965             c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
966             b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
967             a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);
968             d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
969             c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
970             b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
971             a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);
972             d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
973             c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
974             b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);
975            
976             a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
977             d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
978             c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);
979             b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
980             a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
981             d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);
982             c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
983             b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
984             a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);
985             d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
986             c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
987             b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);
988             a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
989             d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
990             c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);
991             b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
992            
993             a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
994             d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
995             c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);
996             b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
997             a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
998             d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);
999             c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
1000             b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
1001             a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);
1002             d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
1003             c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
1004             b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);
1005             a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
1006             d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
1007             c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);
1008             b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
1009            
1010             a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
1011             d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);
1012             c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
1013             b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
1014             a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);
1015             d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
1016             c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
1017             b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
1018             a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);
1019             d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
1020             c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
1021             b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);
1022             a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
1023             d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
1024             c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);
1025             b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
1026            
1027             a = safe_add(a, olda);
1028             b = safe_add(b, oldb);
1029             c = safe_add(c, oldc);
1030             d = safe_add(d, oldd);
1031             }
1032             return Array(a, b, c, d);
1033            
1034             }
1035            
1036             /*
1037             * These functions implement the four basic operations the algorithm uses.
1038             */
1039             function md5_cmn(q, a, b, x, s, t)
1040             {
1041             return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
1042             }
1043             function md5_ff(a, b, c, d, x, s, t)
1044             {
1045             return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
1046             }
1047             function md5_gg(a, b, c, d, x, s, t)
1048             {
1049             return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
1050             }
1051             function md5_hh(a, b, c, d, x, s, t)
1052             {
1053             return md5_cmn(b ^ c ^ d, a, b, x, s, t);
1054             }
1055             function md5_ii(a, b, c, d, x, s, t)
1056             {
1057             return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
1058             }
1059            
1060             /*
1061             * Calculate the HMAC-MD5, of a key and some data
1062             */
1063             function core_hmac_md5(key, data)
1064             {
1065             var bkey = str2binl(key);
1066             if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
1067            
1068             var ipad = Array(16), opad = Array(16);
1069             for(var i = 0; i < 16; i++)
1070             {
1071             ipad[i] = bkey[i] ^ 0x36363636;
1072             opad[i] = bkey[i] ^ 0x5C5C5C5C;
1073             }
1074            
1075             var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
1076             return core_md5(opad.concat(hash), 512 + 128);
1077             }
1078            
1079             /*
1080             * Add integers, wrapping at 2^32. This uses 16-bit operations internally
1081             * to work around bugs in some JS interpreters.
1082             */
1083             function safe_add(x, y)
1084             {
1085             var lsw = (x & 0xFFFF) + (y & 0xFFFF);
1086             var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
1087             return (msw << 16) | (lsw & 0xFFFF);
1088             }
1089            
1090             /*
1091             * Bitwise rotate a 32-bit number to the left.
1092             */
1093             function bit_rol(num, cnt)
1094             {
1095             return (num << cnt) | (num >>> (32 - cnt));
1096             }
1097            
1098             /*
1099             * Convert a string to an array of little-endian words
1100             * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
1101             */
1102             function str2binl(str)
1103             {
1104             var bin = Array();
1105             var mask = (1 << chrsz) - 1;
1106             for(var i = 0; i < str.length * chrsz; i += chrsz)
1107             bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
1108             return bin;
1109             }
1110            
1111             /*
1112             * Convert an array of little-endian words to a string
1113             */
1114             function binl2str(bin)
1115             {
1116             var str = "";
1117             var mask = (1 << chrsz) - 1;
1118             for(var i = 0; i < bin.length * 32; i += chrsz)
1119             str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
1120             return str;
1121             }
1122            
1123             /*
1124             * Convert an array of little-endian words to a hex string.
1125             */
1126             function binl2hex(binarray)
1127             {
1128             var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
1129             var str = "";
1130             for(var i = 0; i < binarray.length * 4; i++)
1131             {
1132             str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
1133             hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);
1134             }
1135             return str;
1136             }
1137            
1138             /*
1139             * Convert an array of little-endian words to a base-64 string
1140             */
1141             function binl2b64(binarray)
1142             {
1143             var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1144             var str = "";
1145             for(var i = 0; i < binarray.length * 4; i += 3)
1146             {
1147             var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)
1148             | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
1149             | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
1150             for(var j = 0; j < 4; j++)
1151             {
1152             if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
1153             else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
1154             }
1155             }
1156             return str;
1157             }
1158             ENDTAG
1159            
1160             ####################################################################
1161             }else{ # $self->{digest} eq "Digest::SHA1"
1162             ####################################################################
1163            
1164             return <<"ENDTAG"
1165             /*
1166             * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
1167             * in FIPS PUB 180-1
1168             * Version 2.1a Copyright Paul Johnston 2000 - 2002.
1169             * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
1170             * Distributed under the BSD License
1171             * See http://pajhome.org.uk/crypt/md5 for details.
1172             */
1173            
1174             /*
1175             * Configurable variables. You may need to tweak these to be compatible with
1176             * the server-side, but the defaults work in most cases.
1177             */
1178             var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
1179             var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
1180             var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
1181            
1182             /*
1183             * These are the functions you will usually want to call
1184             * They take string arguments and return either hex or base-64 encoded strings
1185             */
1186             function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
1187             function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
1188             function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
1189             function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
1190             function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
1191             function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
1192            
1193             /*
1194             * Perform a simple self-test to see if the VM is working
1195             */
1196             function sha1_vm_test()
1197             {
1198             return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
1199             }
1200            
1201             /*
1202             * Calculate the SHA-1 of an array of big-endian words, and a bit length
1203             */
1204             function core_sha1(x, len)
1205             {
1206             /* append padding */
1207             x[len >> 5] |= 0x80 << (24 - len % 32);
1208             x[((len + 64 >> 9) << 4) + 15] = len;
1209            
1210             var w = Array(80);
1211             var a = 1732584193;
1212             var b = -271733879;
1213             var c = -1732584194;
1214             var d = 271733878;
1215             var e = -1009589776;
1216            
1217             for(var i = 0; i < x.length; i += 16)
1218             {
1219             var olda = a;
1220             var oldb = b;
1221             var oldc = c;
1222             var oldd = d;
1223             var olde = e;
1224            
1225             for(var j = 0; j < 80; j++)
1226             {
1227             if(j < 16) w[j] = x[i + j];
1228             else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
1229             var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
1230             safe_add(safe_add(e, w[j]), sha1_kt(j)));
1231             e = d;
1232             d = c;
1233             c = rol(b, 30);
1234             b = a;
1235             a = t;
1236             }
1237            
1238             a = safe_add(a, olda);
1239             b = safe_add(b, oldb);
1240             c = safe_add(c, oldc);
1241             d = safe_add(d, oldd);
1242             e = safe_add(e, olde);
1243             }
1244             return Array(a, b, c, d, e);
1245            
1246             }
1247            
1248             /*
1249             * Perform the appropriate triplet combination function for the current
1250             * iteration
1251             */
1252             function sha1_ft(t, b, c, d)
1253             {
1254             if(t < 20) return (b & c) | ((~b) & d);
1255             if(t < 40) return b ^ c ^ d;
1256             if(t < 60) return (b & c) | (b & d) | (c & d);
1257             return b ^ c ^ d;
1258             }
1259            
1260             /*
1261             * Determine the appropriate additive constant for the current iteration
1262             */
1263             function sha1_kt(t)
1264             {
1265             return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
1266             (t < 60) ? -1894007588 : -899497514;
1267             }
1268            
1269             /*
1270             * Calculate the HMAC-SHA1 of a key and some data
1271             */
1272             function core_hmac_sha1(key, data)
1273             {
1274             var bkey = str2binb(key);
1275             if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
1276            
1277             var ipad = Array(16), opad = Array(16);
1278             for(var i = 0; i < 16; i++)
1279             {
1280             ipad[i] = bkey[i] ^ 0x36363636;
1281             opad[i] = bkey[i] ^ 0x5C5C5C5C;
1282             }
1283            
1284             var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
1285             return core_sha1(opad.concat(hash), 512 + 160);
1286             }
1287            
1288             /*
1289             * Add integers, wrapping at 2^32. This uses 16-bit operations internally
1290             * to work around bugs in some JS interpreters.
1291             */
1292             function safe_add(x, y)
1293             {
1294             var lsw = (x & 0xFFFF) + (y & 0xFFFF);
1295             var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
1296             return (msw << 16) | (lsw & 0xFFFF);
1297             }
1298            
1299             /*
1300             * Bitwise rotate a 32-bit number to the left.
1301             */
1302             function rol(num, cnt)
1303             {
1304             return (num << cnt) | (num >>> (32 - cnt));
1305             }
1306            
1307             /*
1308             * Convert an 8-bit or 16-bit string to an array of big-endian words
1309             * In 8-bit function, characters >255 have their hi-byte silently ignored.
1310             */
1311             function str2binb(str)
1312             {
1313             var bin = Array();
1314             var mask = (1 << chrsz) - 1;
1315             for(var i = 0; i < str.length * chrsz; i += chrsz)
1316             bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
1317             return bin;
1318             }
1319            
1320             /*
1321             * Convert an array of big-endian words to a string
1322             */
1323             function binb2str(bin)
1324             {
1325             var str = "";
1326             var mask = (1 << chrsz) - 1;
1327             for(var i = 0; i < bin.length * 32; i += chrsz)
1328             str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
1329             return str;
1330             }
1331            
1332             /*
1333             * Convert an array of big-endian words to a hex string.
1334             */
1335             function binb2hex(binarray)
1336             {
1337             var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
1338             var str = "";
1339             for(var i = 0; i < binarray.length * 4; i++)
1340             {
1341             str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
1342             hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8 )) & 0xF);
1343             }
1344             return str;
1345             }
1346            
1347             /*
1348             * Convert an array of big-endian words to a base-64 string
1349             */
1350             function binb2b64(binarray)
1351             {
1352             var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
1353             var str = "";
1354             for(var i = 0; i < binarray.length * 4; i += 3)
1355             {
1356             var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
1357             | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
1358             | ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
1359             for(var j = 0; j < 4; j++)
1360             {
1361             if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
1362             else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
1363             }
1364             }
1365             return str;
1366             }
1367             ENDTAG
1368             ####################################################################
1369             }
1370             ####################################################################
1371             }
1372            
1373             1;
1374             __END__