# -*-Perl-*-
################################################################
###
### MD5.pm
###
### Author: Internet Message Group
### Created: Apr 23, 1997
### Revised: Jul 4, 2004
###
my $PM_VERSION = "IM::MD5.pm version 20031028(IM146)";
package IM::MD5;
require 5.003;
require Exporter;
#no integer;
use strict;
use vars qw(@ISA @EXPORT);
@ISA = qw(Exporter);
@EXPORT = qw(md5_str);
use vars qw($MD5_S11 $MD5_S12 $MD5_S13 $MD5_S14
$MD5_S21 $MD5_S22 $MD5_S23 $MD5_S24
$MD5_S31 $MD5_S32 $MD5_S33 $MD5_S34
$MD5_S41 $MD5_S42 $MD5_S43 $MD5_S44
@MD5_PADDING);
################
# MD5 routines #
################
sub md5_str($) {
my($str) = @_;
my($len);
my(@context_count, @context_state, @context_buffer);
my(@digest);
$len = length($str);
my @string = unpack('C*', $str);
&MD5Init(\@context_count, \@context_state, \@context_buffer);
&MD5Update(\@context_count, \@context_state, \@context_buffer,
\@string, $len);
&MD5Final(\@digest, \@context_count, \@context_state, \@context_buffer);
return unpack('H*', pack('C*', @digest));
}
###############################################################################
# F, G, H and I are basic MD5 functions.
sub MD5_F {my($x, $y, $z) = @_; ((($x) & ($y)) | (&MD5_trunc(~$x) & ($z))); }
sub MD5_G {my($x, $y, $z) = @_; ((($x) & ($z)) | (($y) & &MD5_trunc(~$z))); }
sub MD5_H {my($x, $y, $z) = @_; (($x) ^ ($y) ^ ($z)); }
sub MD5_I {my($x, $y, $z) = @_; (($y) ^ (($x) | &MD5_trunc(~$z))); }
# ROTATE_LEFT rotates x left n bits.
sub MD5_ROTATE_LEFT {
my($x, $n) = @_;
if ($n != 0) {
$x = (($x & (0x7fffffff >> ($n-1))) << $n)
| (($x >> (32-$n)) & (0x7fffffff >> (31-$n)));
}
return $x;
}
# FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4.
# Rotation is separate from addition to prevent recomputation.
sub MD5_FF {
my($a, $b, $c, $d, $x, $s, $ac) = @_;
$a = &MD5_trunc($a + &MD5_F($b, $c, $d) + $x + $ac);
$a = &MD5_ROTATE_LEFT($a, $s);
$a = &MD5_trunc($a + $b);
}
sub MD5_GG {
my($a, $b, $c, $d, $x, $s, $ac) = @_;
$a = &MD5_trunc($a + &MD5_G($b, $c, $d) + $x + $ac);
$a = &MD5_ROTATE_LEFT($a, $s);
$a = &MD5_trunc($a + $b);
}
sub MD5_HH {
my($a, $b, $c, $d, $x, $s, $ac) = @_;
$a = &MD5_trunc($a + &MD5_H($b, $c, $d) + $x + $ac);
$a = &MD5_ROTATE_LEFT($a, $s);
$a = &MD5_trunc($a + $b);
}
sub MD5_II {
my($a, $b, $c, $d, $x, $s, $ac) = @_;
$a = &MD5_trunc($a + &MD5_I($b, $c, $d) + $x + $ac);
$a = &MD5_ROTATE_LEFT($a, $s);
$a = &MD5_trunc($a + $b);
return $a;
}
# Truncate to 32bits-wide integer
sub MD5_trunc {
my($x) = @_;
if (($x | 0) == $x) {
$x &= 0xffffffff;
} else {
while ($x >= 4294967296) {
$x -= 4294967296;
}
}
return $x;
}
# MD5 initialization. Begins an MD5 operation, writing a new context.
sub MD5Init($$$) {
my($context_count, $context_state, $context_buffer) = @_;
# Constants for MD5Transform routine.
$MD5_S11 = 7;
$MD5_S12 = 12;
$MD5_S13 = 17;
$MD5_S14 = 22;
$MD5_S21 = 5;
$MD5_S22 = 9;
$MD5_S23 = 14;
$MD5_S24 = 20;
$MD5_S31 = 4;
$MD5_S32 = 11;
$MD5_S33 = 16;
$MD5_S34 = 23;
$MD5_S41 = 6;
$MD5_S42 = 10;
$MD5_S43 = 15;
$MD5_S44 = 21;
@MD5_PADDING = (
0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
);
$context_count->[0] = $context_count->[1] = 0;
# Load magic initialization constants.
$context_state->[0] = 0x67452301;
$context_state->[1] = 0xefcdab89;
$context_state->[2] = 0x98badcfe;
$context_state->[3] = 0x10325476;
@{$context_buffer} = ((0) x 64);
}
# MD5 block update operation. Continues an MD5 message-digest
# operation, processing another message block, and updating the
# context.
sub MD5Update($$$$$) {
my($context_count, $context_state, $context_buffer,
$input, $inputLen) = @_;
my(@arg1, @arg2);
my($i, $index, $partLen);
# Compute number of bytes mod 64
$index = (($context_count->[0] >> 3) & 0x3F);
# Update number of bits
if (($context_count->[0] += ($inputLen << 3)) < ($inputLen << 3)) {
$context_count->[1]++;
}
$context_count->[1] += ($inputLen >> 29);
$partLen = 64 - $index;
# Transform as many times as possible.
if ($inputLen >= $partLen) {
@arg1 = @{$context_buffer}[$index .. $#{$context_buffer}];
&MD5_memcpy(\@arg1, $input, $partLen);
splice(@{$context_buffer}, $index, $#arg1, @arg1);
&MD5Transform($context_state, $context_buffer);
for ($i = $partLen; $i + 63 < $inputLen; $i += 64) {
@arg2 = $input->[$i .. $#{$input}];
&MD5Transform($context_state, \@arg2);
}
$index = 0;
}
else
{
$i = 0;
}
# Buffer remaining input
@arg1 = @{$context_buffer}[$index .. $#{$context_buffer}];
@arg2 = @{$input}[$i .. $#{$input}];
&MD5_memcpy(\@arg1, \@arg2, $inputLen-$i);
splice(@{$context_buffer}, $index, $#arg1, @arg1);
}
# MD5 finalization. Ends an MD5 message-digest operation, writing the
# the message digest and zeroizing the context.
sub MD5Final($$$$) {
my($digest, $context_count, $context_state, $context_buffer) = @_;
my(@bits);
my($index, $padLen);
@bits = ((0) x 8);
# Save number of bits
&MD5_Encode(\@bits, $context_count, 8);
# Pad out to 56 mod 64.
$index = (($context_count->[0] >> 3) & 0x3f);
$padLen = ($index < 56) ? (56 - $index) : (120 - $index);
&MD5Update($context_count, $context_state, $context_buffer,
\@MD5_PADDING, $padLen);
# Append length (before padding)
&MD5Update($context_count, $context_state, $context_buffer, \@bits, 8);
# Store state in digest
&MD5_Encode($digest, $context_state, 16);
# Zeroize sensitive information.
&MD5_memset($context_state, 0, 4);
&MD5_memset($context_count, 0, 2);
&MD5_memset($context_buffer, 0, 64);
}
# MD5 basic transformation. Transforms state based on block.
sub MD5Transform($$) {
my($state, $block) = @_;
my($a) = $state->[0];
my($b) = $state->[1];
my($c) = $state->[2];
my($d) = $state->[3];
my(@x);
&MD5_Decode(\@x, $block, 64);
# Round 1
$a = &MD5_FF($a, $b, $c, $d, $x[ 0], $MD5_S11, 0xd76aa478); # 1
$d = &MD5_FF($d, $a, $b, $c, $x[ 1], $MD5_S12, 0xe8c7b756); # 2
$c = &MD5_FF($c, $d, $a, $b, $x[ 2], $MD5_S13, 0x242070db); # 3
$b = &MD5_FF($b, $c, $d, $a, $x[ 3], $MD5_S14, 0xc1bdceee); # 4
$a = &MD5_FF($a, $b, $c, $d, $x[ 4], $MD5_S11, 0xf57c0faf); # 5
$d = &MD5_FF($d, $a, $b, $c, $x[ 5], $MD5_S12, 0x4787c62a); # 6
$c = &MD5_FF($c, $d, $a, $b, $x[ 6], $MD5_S13, 0xa8304613); # 7
$b = &MD5_FF($b, $c, $d, $a, $x[ 7], $MD5_S14, 0xfd469501); # 8
$a = &MD5_FF($a, $b, $c, $d, $x[ 8], $MD5_S11, 0x698098d8); # 9
$d = &MD5_FF($d, $a, $b, $c, $x[ 9], $MD5_S12, 0x8b44f7af); # 10
$c = &MD5_FF($c, $d, $a, $b, $x[10], $MD5_S13, 0xffff5bb1); # 11
$b = &MD5_FF($b, $c, $d, $a, $x[11], $MD5_S14, 0x895cd7be); # 12
$a = &MD5_FF($a, $b, $c, $d, $x[12], $MD5_S11, 0x6b901122); # 13
$d = &MD5_FF($d, $a, $b, $c, $x[13], $MD5_S12, 0xfd987193); # 14
$c = &MD5_FF($c, $d, $a, $b, $x[14], $MD5_S13, 0xa679438e); # 15
$b = &MD5_FF($b, $c, $d, $a, $x[15], $MD5_S14, 0x49b40821); # 16
# Round 2
$a = &MD5_GG($a, $b, $c, $d, $x[ 1], $MD5_S21, 0xf61e2562); # 17
$d = &MD5_GG($d, $a, $b, $c, $x[ 6], $MD5_S22, 0xc040b340); # 18
$c = &MD5_GG($c, $d, $a, $b, $x[11], $MD5_S23, 0x265e5a51); # 19
$b = &MD5_GG($b, $c, $d, $a, $x[ 0], $MD5_S24, 0xe9b6c7aa); # 20
$a = &MD5_GG($a, $b, $c, $d, $x[ 5], $MD5_S21, 0xd62f105d); # 21
$d = &MD5_GG($d, $a, $b, $c, $x[10], $MD5_S22, 0x2441453); # 22
$c = &MD5_GG($c, $d, $a, $b, $x[15], $MD5_S23, 0xd8a1e681); # 23
$b = &MD5_GG($b, $c, $d, $a, $x[ 4], $MD5_S24, 0xe7d3fbc8); # 24
$a = &MD5_GG($a, $b, $c, $d, $x[ 9], $MD5_S21, 0x21e1cde6); # 25
$d = &MD5_GG($d, $a, $b, $c, $x[14], $MD5_S22, 0xc33707d6); # 26
$c = &MD5_GG($c, $d, $a, $b, $x[ 3], $MD5_S23, 0xf4d50d87); # 27
$b = &MD5_GG($b, $c, $d, $a, $x[ 8], $MD5_S24, 0x455a14ed); # 28
$a = &MD5_GG($a, $b, $c, $d, $x[13], $MD5_S21, 0xa9e3e905); # 29
$d = &MD5_GG($d, $a, $b, $c, $x[ 2], $MD5_S22, 0xfcefa3f8); # 30
$c = &MD5_GG($c, $d, $a, $b, $x[ 7], $MD5_S23, 0x676f02d9); # 31
$b = &MD5_GG($b, $c, $d, $a, $x[12], $MD5_S24, 0x8d2a4c8a); # 32
# Round 3
$a = &MD5_HH($a, $b, $c, $d, $x[ 5], $MD5_S31, 0xfffa3942); # 33
$d = &MD5_HH($d, $a, $b, $c, $x[ 8], $MD5_S32, 0x8771f681); # 34
$c = &MD5_HH($c, $d, $a, $b, $x[11], $MD5_S33, 0x6d9d6122); # 35
$b = &MD5_HH($b, $c, $d, $a, $x[14], $MD5_S34, 0xfde5380c); # 36
$a = &MD5_HH($a, $b, $c, $d, $x[ 1], $MD5_S31, 0xa4beea44); # 37
$d = &MD5_HH($d, $a, $b, $c, $x[ 4], $MD5_S32, 0x4bdecfa9); # 38
$c = &MD5_HH($c, $d, $a, $b, $x[ 7], $MD5_S33, 0xf6bb4b60); # 39
$b = &MD5_HH($b, $c, $d, $a, $x[10], $MD5_S34, 0xbebfbc70); # 40
$a = &MD5_HH($a, $b, $c, $d, $x[13], $MD5_S31, 0x289b7ec6); # 41
$d = &MD5_HH($d, $a, $b, $c, $x[ 0], $MD5_S32, 0xeaa127fa); # 42
$c = &MD5_HH($c, $d, $a, $b, $x[ 3], $MD5_S33, 0xd4ef3085); # 43
$b = &MD5_HH($b, $c, $d, $a, $x[ 6], $MD5_S34, 0x4881d05); # 44
$a = &MD5_HH($a, $b, $c, $d, $x[ 9], $MD5_S31, 0xd9d4d039); # 45
$d = &MD5_HH($d, $a, $b, $c, $x[12], $MD5_S32, 0xe6db99e5); # 46
$c = &MD5_HH($c, $d, $a, $b, $x[15], $MD5_S33, 0x1fa27cf8); # 47
$b = &MD5_HH($b, $c, $d, $a, $x[ 2], $MD5_S34, 0xc4ac5665); # 48
# Round 4
$a = &MD5_II($a, $b, $c, $d, $x[ 0], $MD5_S41, 0xf4292244); # 49
$d = &MD5_II($d, $a, $b, $c, $x[ 7], $MD5_S42, 0x432aff97); # 50
$c = &MD5_II($c, $d, $a, $b, $x[14], $MD5_S43, 0xab9423a7); # 51
$b = &MD5_II($b, $c, $d, $a, $x[ 5], $MD5_S44, 0xfc93a039); # 52
$a = &MD5_II($a, $b, $c, $d, $x[12], $MD5_S41, 0x655b59c3); # 53
$d = &MD5_II($d, $a, $b, $c, $x[ 3], $MD5_S42, 0x8f0ccc92); # 54
$c = &MD5_II($c, $d, $a, $b, $x[10], $MD5_S43, 0xffeff47d); # 55
$b = &MD5_II($b, $c, $d, $a, $x[ 1], $MD5_S44, 0x85845dd1); # 56
$a = &MD5_II($a, $b, $c, $d, $x[ 8], $MD5_S41, 0x6fa87e4f); # 57
$d = &MD5_II($d, $a, $b, $c, $x[15], $MD5_S42, 0xfe2ce6e0); # 58
$c = &MD5_II($c, $d, $a, $b, $x[ 6], $MD5_S43, 0xa3014314); # 59
$b = &MD5_II($b, $c, $d, $a, $x[13], $MD5_S44, 0x4e0811a1); # 60
$a = &MD5_II($a, $b, $c, $d, $x[ 4], $MD5_S41, 0xf7537e82); # 61
$d = &MD5_II($d, $a, $b, $c, $x[11], $MD5_S42, 0xbd3af235); # 62
$c = &MD5_II($c, $d, $a, $b, $x[ 2], $MD5_S43, 0x2ad7d2bb); # 63
$b = &MD5_II($b, $c, $d, $a, $x[ 9], $MD5_S44, 0xeb86d391); # 64
$state->[0] = &MD5_trunc($state->[0] + $a);
$state->[1] = &MD5_trunc($state->[1] + $b);
$state->[2] = &MD5_trunc($state->[2] + $c);
$state->[3] = &MD5_trunc($state->[3] + $d);
# Zeroize sensitive information.
&MD5_memset(\@x, 0, 64);
}
# Encodes input (UINT4) into output (unsigned char). Assumes len is
# a multiple of 4.
sub MD5_Encode($$$) {
my($output, $input, $len) = @_;
my($i, $j);
for ($i = 0, $j = 0; $j < $len; $i++, $j += 4) {
$output->[$j] = ($input->[$i] & 0xff);
$output->[$j+1] = (($input->[$i] >> 8) & 0xff);
$output->[$j+2] = (($input->[$i] >> 16) & 0xff);
$output->[$j+3] = (($input->[$i] >> 24) & 0xff);
}
}
# Decodes input (unsigned char) into output (UINT4). Assumes len is
# a multiple of 4.
sub MD5_Decode($$$) {
my($output, $input, my $len) = @_;
my($i, $j);
for ($i = 0, $j = 0; $j < $len; $i++, $j += 4) {
$output->[$i] = ($input->[$j]) | (($input->[$j+1]) << 8) |
(($input->[$j+2]) << 16) | (($input->[$j+3]) << 24);
}
}
# Note: Replace "for loop" with standard memcpy if possible.
sub MD5_memcpy($$$) {
my($output, $input, $len) = @_;
my($i);
for ($i = 0; $i < $len; $i++) {
$output->[$i] = $input->[$i];
}
}
# Note: Replace "for loop" with standard memset if possible.
sub MD5_memset($$$) {
my($output, $value, $len) = @_;
my($i);
for ($i = 0; $i < $len; $i++) {
$output->[$i] = $value;
}
}
use SelfLoader;
1;
__DATA__
sub MD5_CHECK {
my($str, $should) = @_;
my($r) = &md5_str($str);
printf "MD5 (\"%s\") = %s", $str, $r;
if ($r eq $should) {
print ", ok\n";
} else {
print ", ERROR should be $should\n";
}
}
sub MD5_TEST {
my(%v);
$v{""} = "d41d8cd98f00b204e9800998ecf8427e";
$v{"a"} = "0cc175b9c0f1b6a831c399e269772661";
$v{"abc"} = "900150983cd24fb0d6963f7d28e17f72";
$v{"message digest"} = "f96b697d7cb7938d525a2f31aaf161d0";
$v{"abcdefghijklmnopqrstuvwxyz"} = "c3fcd3d76192e4007dfb496cca67e13b";
$v{"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"} = "d174ab98d277d9f5a5611c2c9f419d9f";
$v{"12345678901234567890123456789012345678901234567890123456789012345678901234567890"} = "57edf4a22be3c955ac49da2e2107b67a";
print "MD5 test suite:\n";
foreach (keys %v) {
&MD5_CHECK($_, $v{$_});
}
}
1;
__END__
=head1 NAME
IM::MD5 - MD5 message-digesting
=head1 SYNOPSIS
use IM::MD5;
$digest = &md5_str($text);
=head1 DESCRIPTION
The I module handles MD5 message-digest algorithm.
This modules is provided by IM (Internet Message).
=head1 EXAMPLES
&md5_str("") returns d41d8cd98f00b204e9800998ecf8427e.
&md5_str("a") returns 0cc175b9c0f1b6a831c399e269772661.
&md5_str("abc") returns 900150983cd24fb0d6963f7d28e17f72.
&md5_str("message digest") returns f96b697d7cb7938d525a2f31aaf161d0.
&md5_str("abcdefghijklmnopqrstuvwxyz") returns c3fcd3d76192e4007dfb496cca67e13b.
&md5_str("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") returns d174ab98d277d9f5a5611c2c9f419d9f.
&md5_str("12345678901234567890123456789012345678901234567890123456789012345678901234567890") returns 57edf4a22be3c955ac49da2e2107b67a.
% perl -MIM::MD5 -e 'IM::MD5::MD5_TEST()'
=head1 COPYRIGHT
This modules is derived from md5.pl copyrighted by NAKAMURA, Motonori
. It is converted to Perl4 from C version
derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm.
IM (Internet Message) is copyrighted by IM developing team.
You can redistribute it and/or modify it under the modified BSD
license. See the copyright file for more details.
=cut
### md5.pl -- MD5 message-digest algorithm converted to Perl4 from C version
###
### Copyright (C) 1996 by NAKAMURA, Motonori .
### All rights reserved.
### [August 16, 1996]
###
### MD5C.C - RSA Data Security, Inc., MD5 message-digest algorithm
###
### Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All
### rights reserved.
###
### License to copy and use this software is granted provided that it
### is identified as the "RSA Data Security, Inc. MD5 Message-Digest
### Algorithm" in all material mentioning or referencing this software
### or this function.
###
### License is also granted to make and use derivative works provided
### that such works are identified as "derived from the RSA Data
### Security, Inc. MD5 Message-Digest Algorithm" in all material
### mentioning or referencing the derived work.
###
### RSA Data Security, Inc. makes no representations concerning either
### the merchantability of this software or the suitability of this
### software for any particular purpose. It is provided "as is"
### without express or implied warranty of any kind.
###
### These notices must be retained in any copies of any part of this
### documentation and/or software.
### Copyright (C) 1997, 1998, 1999 IM developing team
### All rights reserved.
###
### Redistribution and use in source and binary forms, with or without
### modification, are permitted provided that the following conditions
### are met:
###
### 1. Redistributions of source code must retain the above copyright
### notice, this list of conditions and the following disclaimer.
### 2. Redistributions in binary form must reproduce the above copyright
### notice, this list of conditions and the following disclaimer in the
### documentation and/or other materials provided with the distribution.
### 3. Neither the name of the team nor the names of its contributors
### may be used to endorse or promote products derived from this software
### without specific prior written permission.
###
### THIS SOFTWARE IS PROVIDED BY THE TEAM AND CONTRIBUTORS ``AS IS'' AND
### ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
### IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
### PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TEAM OR CONTRIBUTORS BE
### LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
### CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
### SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
### BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
### WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
### OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
### IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.