# md5.pl - perl library for MD5 Message-Digest
# Copyright (C) 1998 Masanao Izumo <mo@goice.co.jp>
#
# This program is free software.  You can redistribute it and/or modify
# it without any warranty.  This library calculates the MD5 based on RFC1321.
# See RFC1321 for more information and algorism.
#
# Usage:
# require 'md5.pl';
# $bits128 = &md5::convert($data);

# modified by zurubon <zurubon@lycos.ne.jp> 2001/06/22
# for uploader
# Usage:
# require 'md5.pl';
# $md5 = &md5::from_file("/etc/passwd");

package md5;
$version = '1.0 mod by zurubon';

@T = map { int(4294967296.0 * abs(sin($_))) } (0..64);
@MD5round1 = ([ 0, 7, 1], [ 1,12, 2], [ 2,17, 3], [ 3,22, 4],
	      [ 4, 7, 5], [ 5,12, 6], [ 6,17, 7], [ 7,22, 8],
	      [ 8, 7, 9], [ 9,12,10], [10,17,11], [11,22,12],
	      [12, 7,13], [13,12,14], [14,17,15], [15,22,16]);
@MD5round2 = ([ 1, 5,17], [ 6, 9,18], [11,14,19], [ 0,20,20],
	      [ 5, 5,21], [10, 9,22], [15,14,23], [ 4,20,24],
	      [ 9, 5,25], [14, 9,26], [ 3,14,27], [ 8,20,28],
	      [13, 5,29], [ 2, 9,30], [ 7,14,31], [12,20,32]);
@MD5round3 = ([ 5, 4,33], [ 8,11,34], [11,16,35], [14,23,36],
	      [ 1, 4,37], [ 4,11,38], [ 7,16,39], [10,23,40],
	      [13, 4,41], [ 0,11,42], [ 3,16,43], [ 6,23,44],
	      [ 9, 4,45], [12,11,46], [15,16,47], [ 2,23,48]);
@MD5round4 = ([ 0, 6,49], [ 7,10,50], [14,15,51], [ 5,21,52],
	      [12, 6,53], [ 3,10,54], [10,15,55], [ 1,21,56],
	      [ 8, 6,57], [15,10,58], [ 6,15,59], [13,21,60],
	      [ 4, 6,61], [11,10,62], [ 2,15,63], [ 9,21,64]);
@MD5round = ([\&F, [@MD5round1]],
	     [\&G, [@MD5round2]],
	     [\&H, [@MD5round3]],
	     [\&I, [@MD5round4]]);
sub F { my($x, $y, $z) = @_; ($x & $y) | (~$x & $z); }
sub G { my($x, $y, $z) = @_; ($x & $z) | ($y & ~$z); }
sub H { my($x, $y, $z) = @_; $x ^ $y ^ $z; }
sub I { my($x, $y, $z) = @_; $y ^ ($x | ~$z); }

sub convert {
    local($data) = @_;
    local(@abcd, @x, @state, @s);
    my($len, $index, $padLen, $f, $r);

    @state = (0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
    $len = length($data);
    $index = $len & 0x3f;
    $padLen = ($index < 56) ? (56 - $index) : (120 - $index);
    if($padLen > 0) {
	$data .= "\x80" . ("\x00" x ($padLen - 1));
    }
    $data .= pack('VV', $len*8);

    @abcd = (0..3);
    while($data =~ /(.|\n){64}/g) {
	@x = unpack('V16', $&);
	@s = @state;
	for(@MD5round) {
	    $f = $_->[0];
	    $r = $_->[1];
	    for(@$r) {
		&round($f, @abcd, @$_); unshift(@abcd, pop(@abcd));
	    }
	}
	for(0..3) {
	    $state[$_] += $s[$_];
	    $state[$_] -= 4294967296 if $state[$_] > 4294967295;
	}
    }
    pack('VVVV', @state);
}

sub round {
    local($f, $a, $b, $c, $d, $k, $s, $i) = @_;
    my($t);

    $t = $s[$a] + &$f($s[$b], $s[$c], $s[$d]) + $x[$k] + $T[$i];
    $t -= 4294967296 while $t > 4294967295;
    $t = ($t<<$s) | ($t>>(32-$s));
    $t += $s[$b]; $t -= 4294967296 if $t > 4294967295;
    $s[$a] = $t;
}

sub from_file{
	my $size, $data, $bits128;

	open (FILE, shift);
	binmode (FILE);
	$size = -s FILE;
	read (FILE, $data, $size);
	close (FILE);

	$bits128 = md5::convert($data);
	return sprintf("%08x%08x%08x%08x\n", unpack('NNNN', $bits128));
}

##for test:
#$/ = undef;
#$digest = convert(<>);
#printf("%08x%08x%08x%08x\n", unpack('NNNN', $digest));
 
1;
