PX : code

SHA by Chris Monson
Download this code


<?php
////////////////////////////////////////////////////////////////////////////
// SHA implementation  v1.0
// Based on the SHA algorithm as given in "Applied Cryptography"
// Code written by Chris Monson (chris@bouncingchairs.net)
// Most recent version available on http://bouncingchairs.net
// Licensed under the GPL (http://www.gnu.org/copyleft/gpl.html)
// April 11, 2000
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
// USAGE:
//------------------------------------------------------------------------
//      
//      Simple text hash:
//      
//      sha = new SHA;
//      hasharray = sha.hash_text( 'hash me!' );
//
//      This returns an array of 5 32-bit integers.
//      The SHA.hash_bytes function does the same thing, but requires
//      an array of bytes as input.  Note that the input values will be
//      truncated if they are larger than 8 bits.
//
//------------------------------------------------------------------------
//
//      There are also some hash to string conversion functions.  The
//      naming convention admittedly could be better, but it works :).
//
//      sha.hash_to_string( hasharray )
//      
//      Converts the hash array to an uppercase hex string.
//
//------------------------------------------------------------------------
//
//      Hashing very large blocks a piece at a time:
//
//      sha = new SHA;
//      sha.init();
//      while (blocks_to_process) {
//          sha.update( next_byte_array )
//      }
//      hasharray = sha.finalize()
//      
////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////
// NOTES:
//      This is basically a rip off of SHAPerl.pm, which I also wrote.
//      I discovered, much to my chagrin, that PHP does not have even
//      the crappy 32-bit int support that Perl has, so I had to employ
//      some funny tricks in the code to get it to use all 32 bits.
//      One of the most obvious of these is using an 'add' method instead
//      of just adding numbers together.  Any numbers over 32 bits don't get
//      bit-truncated.  They get corralled, which is not what I wanted.
//      Another trick I had to employ was splitting large numeric constants
//      into two pieces.  Apparently, you can't specify 0xffffffff.  It gets
//      set to 0.  Everything up to 0x7fffffff works fine.  So, I used
//      some shifting and bitwise operators to get the needed constants.
//
//      A word on optimization: it isn't optimized.  My chief concern was
//      to get it working, and it is fast enough for my needs.  If, however,
//      you intend to try to brute force some hash values with this, either
//      it will need some serious optimizations done, or you should be
//      using one of the freely available C implementations.
//
////////////////////////////////////////////////////////////////////////////
class SHA {
    var 
$A$B$C$D$E// result variables
    
var $ta$tb$tc$td$te// temp variables
    
var $K0_19$K20_39$K40_59$K60_79;

    var 
$buffer;
    var 
$buffsize;
    var 
$totalsize;

    function 
SHA () {
        
$this->init();
    }

    function 
init () {
        
// The long constants can't be used for some dumb reason.
    
$this->0x6745 << 16 0x2301;
    
$this->0xefcd << 16 0xab89;
    
$this->0x98ba << 16 0xdcfe;
    
$this->0x1032 << 16 0x5476;
    
$this->0xc3d2 << 16 0xe1f0;
    
$this->ta $this->A;
    
$this->tb $this->B;
    
$this->tc $this->C;
    
$this->td $this->D;
    
$this->te $this->E;
    
$this->K0_19 0x5a82 << 16 0x7999;
    
$this->K20_39 0x6ed9 << 16 0xeba1;
    
$this->K40_59 0x8f1b << 16 0xbcdc;
    
$this->K60_79 0xca62 << 16 0xc1d6;

        
$this->buffer = array();
        
$this->buffsize 0;
        
$this->totalsize 0;
    }

    function 
bytes_to_words$block ) {
        
$nblk = array();
        for( 
$i=0$i<16; ++$i) {
            
$index $i 4;
            
$nblk[$i] = 0;
            
$nblk[$i] |= ($block[$index] & 0xff) << 24;
            
$nblk[$i] |= ($block[$index+1] & 0xff) << 16;
            
$nblk[$i] |= ($block[$index+2] & 0xff) << 8;
            
$nblk[$i] |= ($block[$index+3] & 0xff);
        }
        return 
$nblk;
    }

    function 
pad_block$block$size ) {
        
// Returns a block that is a multiple of 512 bits long
        
$blksize sizeof$block );
        
$bits $size 8;

        
// Always pad with 0x80, then add as many zeros as necessary to
        // make the message 64 bits short of 512.  Then add the 64-bit size.
        
$newblock $block;
        
$newblock[] = 0x80// push 0x80 onto the end
        // Add the zeros
        
while((sizeof($newblock) % 64) != 56) {
            
$newblock[] = 0;
        }
        
// Add the size
        
for ($i=0$i<8; ++$i) {
            
$newblock[] = ($i<4) ? : ($bits >> ((7-$i)*8)) & 0xff;
        }

        return 
$newblock;
    }

    function 
circ_shl$num$amt ) {
        
$leftmask 0xffff | (0xffff << 16);
        
$leftmask <<= 32 $amt;
        
$rightmask 0xffff | (0xffff << 16);
        
$rightmask <<= $amt;
        
$rightmask = ~$rightmask;

        
$remains $num $leftmask;
        
$remains >>= 32 $amt;
        
$remains &= $rightmask;

        
$res = ($num << $amt) | $remains;

        return 
$res;
    }

    function 
f0_19$x$y$z ) {
        return (
$x $y) | (~$x $z);
    }

    function 
f20_39$x$y$z ) {
        return (
$x $y $z);
    }

    function 
f40_59$x$y$z ) {
        return (
$x $y) | ($x $z) | ($y $z);
    }

    function 
f60_79$x$y$z ) {
        return 
$this->f20_39$x$y$z );
    }

    function 
expand_block$block ) {
        
$nblk $block;
        for( 
$i=16$i<80; ++$i ) {
            
$nblk[$i] = $this->circ_shl(
                    
$nblk[$i-3] ^ $nblk[$i-8] ^ $nblk[$i-14] ^ $nblk[$i-16], 1
                
);
        }

        return 
$nblk;
    }

    function 
print_bytes$bytes ) {
        
$len sizeof$bytes );
        for( 
$i=0$i<$len; ++$i) {
            
$str[] = sprintf"%02x"$bytes[$i] );
        }

        print( 
join", "$str ) . "\n" );
    }

    function 
wordstr$word ) {
        return 
sprintf
            
"%04x%04x", ($word >> 16) & 0xffff$word 0xffff
            
);
    }

    function 
print_words$words ) {
        
$len sizeof$words );
        for( 
$i=0$i<$len; ++$i) {
            
$str[] = $this->wordstr$words[$i] );
        }
        
        print( 
join", "$str ) . "\n" );
    }

    function 
hash_to_string$hash ) {
        
$len sizeof$hash );
        for (
$i=0$i<$len; ++$i) {
            
$astr[] = $this->wordstr$hash[$i] );
        }
        return 
join""$astr );
    }

    
// Add simply adds two numbers.  It is provided for compatibility on
    // platforms that only support a 31 bit add (there are a few, apparently)
    
function add$a$b ) {
        
$ma = ($a >> 16) & 0xffff;
        
$la = ($a) & 0xffff;
        
$mb = ($b >> 16) & 0xffff;
        
$lb = ($b) & 0xffff;

        
$ls $la $lb;
        
// Carry
        
if ($ls 0xffff) {
            
$ma += 1;
            
$ls &= 0xffff;
        }

        
// MS add
        
$ms $ma $mb;
        
$ms &= 0xffff;

        
// Works because the bitwise operators are 32 bit
        
$result = ($ms << 16) | $ls;
        return 
$result;
    }

    function 
process_block$blk ) {
        
$blk $this->expand_block$blk );

        for( 
$i=0$i<80; ++$i ) {
            
$temp $this->circ_shl$this->ta);
            if (
$i<20) {
                
$f $this->f0_19$this->tb$this->tc$this->td );
                
$k $this->K0_19;
            }
            elseif (
$i<40) {
                
$f $this->f20_39$this->tb$this->tc$this->td );
                
$k $this->K20_39;
            }
            elseif (
$i<60) {
                
$f $this->f40_59$this->tb$this->tc$this->td );
                
$k $this->K40_59;
            }
            else {
                
$f $this->f60_79$this->tb$this->tc$this->td );
                
$k $this->K60_79;
            }

            
$temp $this->add$temp$f );
            
$temp $this->add$temp$this->te );
            
$temp $this->add$temp$blk[$i] );
            
$temp $this->add$temp$k );

            
$this->te $this->td;
            
$this->td $this->tc;
            
$this->tc $this->circ_shl$this->tb30 );
            
$this->tb $this->ta;
            
$this->ta $temp;
        }

        
$this->$this->add$this->A$this->ta );
        
$this->$this->add$this->B$this->tb );
        
$this->$this->add$this->C$this->tc );
        
$this->$this->add$this->D$this->td );
        
$this->$this->add$this->E$this->te );
    }

    function 
update $bytes ) {
        
$length sizeof$bytes );
        
$index 0;

        
// Process each full block
        
while (($length $index) + $this->buffsize >= 64) {
            for( 
$i=$this->buffsize$i<64; ++$i) {
                
$this->buffer[$i] = $bytes[$index $i $this->buffsize];
            }
            
$this->process_block$this->bytes_to_words$this->buffer ) );
            
$index += 64;
            
$this->buffsize 0;
        }

        
// Any remaining bytes that do not make up a full block need to be'
        // added into the buffer for the next update (or final)
        
$remaining $length $index;
        for( 
$i=0$i<$remaining; ++$i) {
            
$this->buffer[$this->buffisze $i] = $bytes[$index $i];
        }
        
$this->buffsize += $remaining;
        
$this->totalsize += $length;
    }

    function final() {
        
// Pad and process the buffer
        
for( $i=0$i<$this->buffsize; ++$i) {
            
$last_block[$i] = $this->buffer[$i];
        }
        
$this->buffsize 0;
        
// Pad the block
        
$last_block $this->pad_block$last_block$this->totalsize );
        
// Process the last one (or two) block(s)
        
$index 0;
        
$length sizeof$last_block );
        while( 
$index $length )
        {
            
$block = array();
            for( 
$i=0$i<64; ++$i) {
                
$block[$i] = $last_block[$i $index];
            }
            
$this->process_block$this->bytes_to_words$block ) );
            
$index += 64;
        }

        
$result[0] = $this->A;
        
$result[1] = $this->B;
        
$result[2] = $this->C;
        
$result[3] = $this->D;
        
$result[4] = $this->E;

        return 
$result;
    }

    function 
hash_bytes$bytes ) {
        
$this->init();
        
$this->update$bytes );
        return 
$this->final();
    }

    function 
hash_string$str ) {
        
$len strlen$str );
        for(
$i=0$i<$len; ++$i) {
            
$bytes[] = ord$str[$i] ) & 0xff;
        }
        return 
$this->hash_bytes$bytes );
    }
}
?>

Comments or questions?
PX is running PHP 5.2.17
Thanks to Miranda Productions for hosting and bandwidth.
Use of any code from PX is at your own risk.