# # RSA's SHA-1 in perl5 - "Fast" version. # # Usage: # $sha = sha1($data); # # Test Case: # $sha = sha1("squeamish ossifrage\n"); # print $sha; # 820550664cf296792b38d1647a4d8c0e1966af57 # # This code is written for perl5, specifically any perl version after 5.002. # # This version has been somewhat optimized for speed, and gets about # 10 KB per second on a PPC604-120 42T workstation running AIX. Still # pitiful compared with C. Feel free to improve it if you can. # # Disowner: # This original perl implementation of RSADSI's SHA-1 was written by # John L. Allen, allen@gateway.grumman.com on 03/08/97. No copyright # or property rights are claimed or implied. You may use, copy, modify # and re-distribute it in any way you see fit, for personal or business # use, for inclusion in any free or for-profit product, royalty-free # and with no further obligation to the author. # # Copyright (C) 2002 Daniel Quinlan # (adapted public domain code into a module) # # This program is free software; you can redistribute it and/or modify # it under the terms of either the Artistic License or the GNU General # Public License as published by the Free Software Foundation; either # version 2 of the License, or (at your option) any later version. package Mail::SpamAssassin::SHA1; require 5.002; use strict; use bytes; use integer; use vars qw( @ISA @EXPORT ); require Exporter; @ISA = qw(Exporter); @EXPORT = qw(sha1); use constant HAS_DIGEST_SHA1 => eval { require Digest::SHA1; }; sub sha1 { my ($data) = @_; if (HAS_DIGEST_SHA1) { # this is about 40x faster than the below perl version return Digest::SHA1::sha1_hex($data); } else { return SHA1($data); } } sub SHA1($) { local $^W = 0; local $_; my @a = (16..19); my @b = (20..39); my @c = (40..59); my @d = (60..79); my $data = $_[0]; my $aa = 0x67452301; my $bb = 0xefcdab89; my $cc = 0x98badcfe; my $dd = 0x10325476; my $ee = 0xc3d2e1f0; my ($a, $b, $c, $d, $e, $t, $l, $r, $p) = (0)x9; my @W; do { $_ = substr $data, $l, 64; $l += ($r = length); $r++, $_.="\x80" if ($r<64 && !$p++); # handle padding, but once only ($p) @W = unpack "N16", $_."\0"x7; # unpack block into array of 16 ints $W[15] = $l*8 if ($r<57); # bit length of file in final block # initialize working vars from the accumulators $a=$aa, $b=$bb, $c=$cc, $d=$dd, $e=$ee; # the meat of SHA is 80 iterations applied to the working vars for(@W){ $t = ($b&($c^$d)^$d) + $e + $_ + 0x5a827999 + ($a<<5|31&$a>>27); $e = $d; $d = $c; $c = $b<<30 | 0x3fffffff & $b>>2; $b = $a; $a = $t; } for(@a){ $t = $W[$_-3]^$W[$_-8]^$W[$_-14]^$W[$_-16]; $W[$_] = $t = ($t<<1|1&$t>>31); $t += ($b&($c^$d)^$d) + $e + 0x5a827999 + ($a<<5|31&$a>>27); $e = $d; $d = $c; $c = $b<<30 | 0x3fffffff & $b>>2; $b = $a; $a = $t; } for(@b){ $t = $W[$_-3]^$W[$_-8]^$W[$_-14]^$W[$_-16]; $W[$_] = $t = ($t<<1|1&$t>>31); $t += ($b^$c^$d) + $e + 0x6ed9eba1 + ($a<<5|31&$a>>27); $e = $d; $d = $c; $c = $b<<30 | 0x3fffffff & $b>>2; $b = $a; $a = $t; } for(@c){ $t = $W[$_-3]^$W[$_-8]^$W[$_-14]^$W[$_-16]; $W[$_] = $t = ($t<<1|1&$t>>31); $t += ($b&$c|($b|$c)&$d) + $e + 0x8f1bbcdc + ($a<<5|31&$a>>27); $e = $d; $d = $c; $c = $b<<30 | 0x3fffffff & $b>>2; $b = $a; $a = $t; } for(@d){ $t = $W[$_-3]^$W[$_-8]^$W[$_-14]^$W[$_-16]; $W[$_] = $t = ($t<<1|1&$t>>31); $t += ($b^$c^$d) + $e + 0xca62c1d6 + ($a<<5|31&$a>>27); $e = $d; $d = $c; $c = $b<<30 | 0x3fffffff & $b>>2; $b = $a; $a = $t; } # add in the working vars to the accumulators, modulo 2**32 $aa+=$a, $bb+=$b, $cc+=$c, $dd+=$d, $ee+=$e; } while $r>56; sprintf "%.8x%.8x%.8x%.8x%.8x", $aa & 0xffffffff, $bb & 0xffffffff, $cc & 0xffffffff, $dd & 0xffffffff, $ee & 0xffffffff; } 1;