File Coverage

blib/lib/Net/YMSG/CRAM.pm
Criterion Covered Total %
statement 98 107 91.5
branch 16 26 61.5
condition 3 9 33.3
subroutine 13 13 100.0
pod 0 5 0.0
total 130 160 81.2


line stmt bran cond sub pod time code
1             package Net::YMSG::CRAM;
2              
3 2     2   8222 use Digest::MD5 qw(md5);
  2         4  
  2         1074  
4 2     2   12 use vars qw($VERSION);
  2         2  
  2         110  
5             $VERSION = '0.02';
6 2     2   11 use strict;
  2         4  
  2         75  
7              
8 2     2   11 use constant MD5_CRYPT_MAGIC_STRING => '$1$';
  2         40  
  2         380  
9 2     2   11 use constant I_TO_A64 => './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
  2         6  
  2         3913  
10              
11              
12             sub new
13             {
14 1     1 0 492 my $class = shift;
15 1         10 bless {
16             challenge_string => '',
17             id => '',
18             password => '',
19             }, $class;
20             }
21              
22              
23             sub set_challenge_string
24             {
25 2     2 0 798 my $self = shift;
26 2         13 $self->{challenge_string} = shift;
27             }
28              
29              
30             sub set_id
31             {
32 2     2 0 9 my $self = shift;
33 2         7 $self->{id} = shift;
34             }
35              
36              
37             sub set_password
38             {
39 2     2 0 8 my $self = shift;
40 2         7 $self->{password} = shift;
41             }
42              
43              
44             sub get_response_strings
45             {
46 2     2 0 10 my $self = shift;
47 2         5 my $id = $self->{id};
48 2         4 my $password = $self->{password};
49 2         20 my @challenge_string = split //, $self->{challenge_string};
50              
51 2 50       11 return undef unless scalar @challenge_string;
52              
53              
54 2         18 my $password_hash = _to_yahoo_base64(md5($password));
55 2         9 my $crypt_hash = _to_yahoo_base64(md5(_md5_crypt($password, '_2S43d5f')));
56              
57 2         5 my $hash_string_p;
58             my $hash_string_c;
59              
60 2         8 my $sv = ord($challenge_string[15]) % 8;
61 2 100 66     71 if ($sv == 1 || $sv == 6) {
    50 33        
    0 0        
    0          
    0          
62 1         5 my $checksum = $challenge_string[ord($challenge_string[9]) % 16];
63 1         13 $hash_string_p = sprintf '%s%s%s%s',
64             $checksum, $id, join('', @challenge_string), $password_hash;
65 1         6 $hash_string_c = sprintf '%s%s%s%s',
66             $checksum, $id, join('', @challenge_string), $crypt_hash;
67             }
68             elsif ($sv == 2 || $sv == 7) {
69 1         4 my $checksum = $challenge_string[ord($challenge_string[15]) % 16];
70 1         13 $hash_string_p = sprintf '%s%s%s%s',
71             $checksum, join('', @challenge_string), $password_hash, $id;
72 1         6 $hash_string_c = sprintf '%s%s%s%s',
73             $checksum, join('', @challenge_string), $crypt_hash, $id;
74             }
75             elsif ($sv == 3) {
76 0         0 my $checksum = $challenge_string[ord($challenge_string[1]) % 16];
77 0         0 $hash_string_p = sprintf '%s%s%s%s',
78             $checksum, $id, $password_hash, join('', @challenge_string);
79 0         0 $hash_string_c = sprintf '%s%s%s%s',
80             $checksum, $id, $crypt_hash, join('', @challenge_string);
81             }
82             elsif ($sv == 4) {
83 0         0 my $checksum = $challenge_string[ord($challenge_string[3]) % 16];
84 0         0 $hash_string_p = sprintf '%s%s%s%s',
85             $checksum, $password_hash, join('', @challenge_string), $id;
86 0         0 $hash_string_c = sprintf '%s%s%s%s',
87             $checksum, $crypt_hash, join('', @challenge_string), $id;
88             }
89             elsif ($sv == 0 || $sv == 5) {
90 0         0 my $checksum = $challenge_string[ord($challenge_string[7]) % 16];
91 0         0 $hash_string_p = sprintf '%s%s%s%s',
92             $checksum, $password_hash, $id, join('', @challenge_string);
93 0         0 $hash_string_c = sprintf '%s%s%s%s',
94             $checksum, $crypt_hash, $id, join('', @challenge_string);
95             }
96              
97 2         11 my $result6 = _to_yahoo_base64(md5($hash_string_p));
98 2         11 my $result96 = _to_yahoo_base64(md5($hash_string_c));
99 2         24 return ($result6, $result96);
100             }
101              
102              
103             sub _to_yahoo_base64
104             {
105 8     8   33 pos($_[0]) = 0;
106              
107 8         98 my $res = join '', map( pack('u',$_)=~ /^.(\S*)/, ($_[0]=~/(.{1,45})/gs));
108 8         17 $res =~ tr{` -_}{AA-Za-z0-9\._};
109              
110 8         22 my $padding = (3 - length($_[0]) % 3) % 3;
111 8 50       94 $res =~ s/.{$padding}$/'-' x $padding/e if $padding;
  8         27  
112 8         27 return $res;
113             }
114              
115              
116             sub _to64
117             {
118 12     12   19 my ($v, $n) = @_;
119 12         14 my $ret = '';
120 12         29 while (--$n >= 0) {
121 44         56 $ret .= substr(I_TO_A64, $v & 0x3f, 1);
122 44         85 $v >>= 6;
123             }
124 12         27 $ret;
125             }
126              
127              
128             sub _md5_crypt
129             {
130 2     2   3 my $pw = shift;
131 2         4 my $salt = shift;
132              
133 2         3 my $Magic = MD5_CRYPT_MAGIC_STRING;
134 2         18 $salt =~ s/^\Q$Magic//;
135 2         6 $salt =~ s/^(.*)\$.*$/$1/;
136 2         4 $salt = substr $salt, 0, 8;
137              
138 2         15 my $ctx = new Digest::MD5;
139 2         8 $ctx->add($pw);
140 2         7 $ctx->add($Magic);
141 2         6 $ctx->add($salt);
142              
143 2         9 my $final = new Digest::MD5;
144 2         6 $final->add($pw);
145 2         7 $final->add($salt);
146 2         5 $final->add($pw);
147 2         20 $final = $final->digest;
148              
149 2         26 for (my $pl = length($pw); $pl > 0; $pl -= 16) {
150 2 50       20 $ctx->add(substr($final, 0, $pl > 16 ? 16 : $pl));
151             }
152              
153              
154 2         7 for (my $i = length($pw); $i; $i >>= 1) {
155 7 100       13 if ($i & 1) {
156 3         38 $ctx->add(pack("C", 0));
157             } else {
158 4         20 $ctx->add(substr($pw, 0, 1));
159             }
160             }
161              
162 2         8 $final = $ctx->digest;
163              
164 2         8 for (my $i = 0; $i < 1000; $i++) {
165 2000         7275 my $ctx1 = new Digest::MD5;
166 2000 100       4351 if ($i & 1) {
167 1000         2442 $ctx1->add($pw);
168             } else {
169 1000         3279 $ctx1->add(substr($final, 0, 16));
170             }
171 2000 100       4361 if ($i % 3) {
172 1332         2567 $ctx1->add($salt);
173             }
174 2000 100       3609 if ($i % 7) {
175 1714         3491 $ctx1->add($pw);
176             }
177 2000 100       3278 if ($i & 1) {
178 1000         4392 $ctx1->add(substr($final, 0, 16));
179             } else {
180 1000         2083 $ctx1->add($pw);
181             }
182 2000         13705 $final = $ctx1->digest;
183             }
184              
185 2         6 my $passwd = '';
186 2         29 $passwd .= _to64(int(unpack("C", (substr($final, 0, 1))) << 16)
187             | int(unpack("C", (substr($final, 6, 1))) << 8)
188             | int(unpack("C", (substr($final, 12, 1)))), 4);
189 2         16 $passwd .= _to64(int(unpack("C", (substr($final, 1, 1))) << 16)
190             | int(unpack("C", (substr($final, 7, 1))) << 8)
191             | int(unpack("C", (substr($final, 13, 1)))), 4);
192 2         55 $passwd .= _to64(int(unpack("C", (substr($final, 2, 1))) << 16)
193             | int(unpack("C", (substr($final, 8, 1))) << 8)
194             | int(unpack("C", (substr($final, 14, 1)))), 4);
195 2         14 $passwd .= _to64(int(unpack("C", (substr($final, 3, 1))) << 16)
196             | int(unpack("C", (substr($final, 9, 1))) << 8)
197             | int(unpack("C", (substr($final, 15, 1)))), 4);
198 2         16 $passwd .= _to64(int(unpack("C", (substr($final, 4, 1))) << 16)
199             | int(unpack("C", (substr($final, 10, 1))) << 8)
200             | int(unpack("C", (substr($final, 5, 1)))), 4);
201 2         7 $passwd .= _to64(int(unpack("C", substr($final, 11, 1))), 2);
202              
203 2         30 return $Magic. $salt. '$'. $passwd;
204             }
205              
206              
207             1;
208             __END__