File Coverage

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