File Coverage

blib/lib/Apache/CryptHash.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Apache::CryptHash;
2              
3             #require 5.005_62;
4 1     1   622 use strict;
  1         1  
  1         39  
5             #use warnings;
6              
7             BEGIN {
8             # use Apache;
9 1     1   670 use MIME::Base64;
  1         711  
  1         56  
10 1     1   1279 use Crypt::CapnMidNite;
  0            
  0            
11             use vars qw($VERSION);
12             $VERSION = do { my @r = (q$Revision: 3.03 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r };
13             }
14              
15              
16             sub init() {
17             my ($proto, $crypt) = @_;
18             my $class = ref($proto) || $proto;
19             my $self = {};
20             $self->{NAME} = 'Secret'; # default header name
21             $self->{CRYPT} = $crypt || do { # default password is hostname
22             require Sys::Hostname; # 'no, NO' turns encryption off
23             &Sys::Hostname::hostname;
24             };
25             bless ($self, $class);
26             return $self;
27             }
28              
29             sub name {
30             &_readNset(\shift->{NAME},@_);
31             }
32              
33             sub passcode {
34             &_readNset(\shift->{CRYPT},@_);
35             }
36              
37             sub _readNset {
38             my($var,$new) = @_;
39             my $rv = $$var;
40             $$var = $new if defined $new;
41             return $rv;
42             }
43              
44             #####################################################
45             # md5_hex
46             #
47             # input: string
48             # returns: md5 hex hash of string
49             #
50             sub md5_hex($$) {
51             my ($self, $string) = @_;
52             return Crypt::CapnMidNite->new->md5_hex($string);
53             }
54              
55             #####################################################
56             # md5_b64
57             #
58             # input: string
59             # returns: md5 base 64 of string
60             #
61             sub md5_b64($$) {
62             my ($self, $string) = @_;
63             return Crypt::CapnMidNite->new->md5_base64($string);
64             }
65              
66             #####################################################
67             # encode
68             # create an encrypted cookie from data values passed in hash
69             # input: pointer to hash, # \%p
70             # (optional) pointer to keys # \@k
71             # (array) of values to include in MAC
72             # these must be invarient and will
73             # fail to decrypt otherwise
74             #
75             sub encode($$$) {
76             my ( $self, $state, $k ) = @_; # get my self
77             &_MAC($self, $state, $k, 'generate'); # add MAC to state
78             my $cipher = Crypt::CapnMidNite->new_md5_rc4($self->{CRYPT});
79             my %s = %$state;
80             foreach (keys %s) {
81             $s{$_} =~ s/:/%58/g;
82             }
83             my $cook = $self->{NAME};
84             if ( $self->{CRYPT} =~ /^no$/i ) {
85             $cook .= '.Debug:' . join ':', %s;
86             } else {
87             $cook .= ':' . MIME::Base64::encode($cipher->encrypt(join ':', %s),"");
88             }
89             $cook =~ tr/=/$/;
90             return $cook;
91             }
92              
93             #####################################
94             #
95             # input: pointer to cookie value # \$string
96             # pointer to state hash # \%state to fill
97             # pointer key arrau in MAC # \@keys
98             # return: true or undef, fill hash with state values if true
99             #
100             sub decode ($$$) {
101             my ($self, $cook, $state, $ck) = @_;
102             my %s;
103             $$cook =~ tr/$/=/;
104             my $rv = &_decrypt($self, $cook, \%s, $ck);
105             return undef unless $rv;
106             %$state = %s;
107             $rv;
108             }
109              
110             sub _decrypt {
111             my ($self, $cook, $state, $ck) = @_;
112             my $cipher = Crypt::CapnMidNite->new_md5_rc4($self->{CRYPT});
113             my ($flag, $realcook) = split(':', $$cook, 2);
114             $realcook =~ tr/$/=/;
115             if ( $flag =~ /.Debug$/ ) {
116             %$state = &_evensplit(':', $realcook);
117             } else {
118             %$state = &_evensplit(':',$cipher->decrypt(MIME::Base64::decode($realcook)));
119             }
120             return undef unless exists ${$state}{MAC}; # punt if decode failure
121             foreach (keys %$state) {
122             ${$state}{$_} =~ s/%58/:/g;
123             }
124             # invalid if the cookie was tampered with
125            
126             return undef unless &_MAC($self, $state, $ck, 'check');
127             foreach ( @$ck ) {
128             return undef unless exists ${$state}{$_};
129             }
130             $flag; # return true
131             }
132              
133             sub checkMAC {
134             my ( $self, $s, $k ) = @_;
135             return _MAC($self, $s, $k, 'check');
136             }
137              
138             sub _MAC {
139             my ( $self, $s, $k, $action ) = @_;
140             @_ = ($k) ? sort @$k : ();
141             my @fields = @{$s}{@_};
142             my $md5 = Crypt::CapnMidNite->new_md5;
143             my $newmac = $md5->md5_base64($self->{CRYPT} .
144             $md5->md5_base64(join '', $self->{CRYPT}, @fields));
145             return $s->{MAC} = $newmac if $action eq 'generate';
146             return 1 if ($newmac eq $s->{MAC} && $action eq 'check');
147             return undef;
148             }
149              
150             # split to an even number of fields
151             # this will split to a hash when the trailing value is null
152             #
153             sub _evensplit {
154             my ( $m, $s ) = @_;
155             @_ = split(/$m/, $s, -1);
156             push ( @_, '') if @_ % 2;
157             @_;
158             }
159              
160             1;
161             __END__