📄 shahash.java
字号:
// ShaHash - the Secure Hash Algorithm
//
// Copyright (C) 1996 by Jef Poskanzer <jef@acme.com>. 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.
//
// THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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.
//
// Visit the ACME Labs Java page for up-to-date versions of this and other
// fine Java utilities: http://www.acme.com/java/
package Acme.Crypto;
import java.io.*;
/// The Secure Hash Algorithm.
// <P>
// This is surprisingly fast, processing 28000 bytes per second on a
// SPARC Ultra.
// <P>
// <A HREF="/resources/classes/Acme/Crypto/ShaHash.java">Fetch the software.</A><BR>
// <A HREF="/resources/classes/Acme.tar.gz">Fetch the entire Acme package.</A>
public class ShaHash extends Hash
{
/// Constructor.
public ShaHash()
{
super( SHA_DIGESTSIZE );
reset();
}
/// Initialize (reset) the hash.
public void reset()
{
shaInit();
}
/// Add a byte to the hash.
public void add( byte b )
{
// Just use the block add routine. Maybe add a buffer later.
byte[] data = new byte[1];
data[0] = b;
add( data, 0, 1 );
}
/// Add some bytes to the hash.
public void add( byte[] data, int off, int len )
{
shaUpdate( data, off, len );
}
/// Prepare the hash bytes for use.
protected void prepare()
{
shaFinal();
spreadIntsToBytes( digest, 0, hashBytes, 0, SHA_DIGESTSIZE/4 );
}
private static final boolean USE_MODIFIED_SHA = true;
private static final int SHA_BLOCKSIZE = 64;
private static final int SHA_DIGESTSIZE = 20;
private int[] digest = new int[SHA_DIGESTSIZE/4]; // message digest
private long bitCount; // 64-bit bit count
private byte[] dataB = new byte[SHA_BLOCKSIZE]; // SHA byte data buffer
private int[] dataI = new int[SHA_BLOCKSIZE/4]; // SHA long data buffer
// This implementation includes a change to the algorithm introduced by
// NIST at the behest of the NSA. It supposedly corrects a weakness in
// the original formulation. Bruce Schneier described it thus in a
// posting to the Cypherpunks mailing list on June 21, 1994 (as told to
// us by Steve Bellovin):
//
// This is the fix to the Secure Hash Standard, NIST FIPS PUB 180:
//
// In Section 7 of FIPS 180 (page 9), the line which reads
//
// "b) For t=16 to 79 let Wt = Wt-3 XOR Wt-8 XOR Wt-14 XOR
// Wt-16."
//
// is to be replaced by
//
// "b) For t=16 to 79 let Wt = S1(Wt-3 XOR Wt-8 XOR Wt-14 XOR
// Wt-16)."
//
// where S1 is a left circular shift by one bit as defined in
// Section 3 of FIPS 180 (page 6):
//
// S1(X) = (X<<1) OR (X>>31).
// The SHA f()-functions
// Rounds 0-19.
private static int f1( int x, int y, int z )
{
return ( x & y ) | ( ~x & z );
}
// Rounds 20-39.
private static int f2( int x, int y, int z )
{
return x ^ y ^ z;
}
// Rounds 40-59.
private static int f3( int x, int y, int z )
{
return ( x & y ) | ( x & z ) | ( y & z );
}
// Rounds 60-79.
private static int f4( int x, int y, int z )
{
return x ^ y ^ z;
}
// The SHA Mysterious Constants.
private static final int K1 = 0x5a827999; // rounds 0-19
private static final int K2 = 0x6ed9eba1; // rounds 20-39
private static final int K3 = 0x8f1bbcdc; // rounds 40-59
private static final int K4 = 0xca62c1d6; // rounds 60-79
// SHA initial values.
private static final int h0init = 0x67452301;
private static final int h1init = 0xefcdab89;
private static final int h2init = 0x98badcfe;
private static final int h3init = 0x10325476;
private static final int h4init = 0xc3d2e1f0;
// 32-bit left rotate - kludged with shifts.
private static int rotateL( int x, int n )
{
return ( x << n ) | ( x >>> ( 32 - n ) );
}
// The four SHA sub-rounds.
private void subRound1( int count )
{
int temp = rotateL( A, 5 ) + f1( B, C, D ) + E + W[count] + K1;
E = D;
D = C;
C = rotateL( B, 30 );
B = A;
A = temp;
}
private void subRound2( int count )
{
int temp = rotateL( A, 5 ) + f2( B, C, D ) + E + W[count] + K2;
E = D;
D = C;
C = rotateL( B, 30 );
B = A;
A = temp;
}
private void subRound3( int count )
{
int temp = rotateL( A, 5 ) + f3( B, C, D ) + E + W[count] + K3;
E = D;
D = C;
C = rotateL( B, 30 );
B = A;
A = temp;
}
private void subRound4( int count )
{
int temp = rotateL( A, 5 ) + f4( B, C, D ) + E + W[count] + K4;
E = D;
D = C;
C = rotateL( B, 30 );
B = A;
A = temp;
}
// The two buffers of 5 32-bit words.
private int h0, h1, h2, h3, h4;
private int A, B, C, D, E;
/// Initialize the SHA values.
private void shaInit()
{
// Set the h-vars to their initial values.
digest[0] = h0init;
digest[1] = h1init;
digest[2] = h2init;
digest[3] = h3init;
digest[4] = h4init;
// Initialise bit count.
bitCount = 0;
}
private int[] W = new int[80];
/// Perform the SHA transformation.
private void shaTransform()
{
int i;
// Step A. Copy the data buffer into the local work buffer.
for( i = 0; i < SHA_BLOCKSIZE/4; ++i )
W[i] = dataI[i];
// Step B. Expand the 16 words into 64 temporary data words.
for ( ; i < 80; ++i )
{
W[i] = W[i - 3] ^ W[i - 8] ^ W[i - 14] ^ W[i - 16];
if ( USE_MODIFIED_SHA )
W[i] = rotateL( W[i], 1 );
}
// Step C. Set up first buffer
A = digest[0];
B = digest[1];
C = digest[2];
D = digest[3];
E = digest[4];
// Step D. Serious mangling, divided into four sub-rounds.
for ( i = 0; i < 20; ++i )
subRound1( i );
for ( ; i < 40; ++i )
subRound2( i );
for ( ; i < 60; ++i )
subRound3( i );
for ( ; i < 80; ++i )
subRound4( i );
// Step E. Build message digest.
digest[0] += A;
digest[1] += B;
digest[2] += C;
digest[3] += D;
digest[4] += E;
}
/// Update SHA for a block of data. This code assumes that the buffer size
// is a multiple of SHA_BLOCKSIZE bytes long, which makes the code a lot
// more efficient since it does away with the need to handle partial blocks
// between calls to shaUpdate().
private void shaUpdate( byte[] buffer, int offset, int count )
{
// Update bitcount.
bitCount += count << 3;
// Process data in SHA_BLOCKSIZE chunks.
while ( count >= SHA_BLOCKSIZE )
{
copyBlock( buffer, offset, dataB, 0, SHA_BLOCKSIZE );
squashBytesToInts( dataB, 0, dataI, 0, SHA_BLOCKSIZE/4 );
shaTransform();
offset += SHA_BLOCKSIZE;
count -= SHA_BLOCKSIZE;
}
// Handle any remaining bytes of data. This should only happen once
// on the final lot of data.
copyBlock( buffer, offset, dataB, 0, count );
}
private void shaFinal()
{
int count;
// Compute number of bytes mod 64.
count = (int) ( bitCount >>> 3 ) & 0x3F;
// Set the first char of padding to 0x80. This is safe since there is
// always at least one byte free.
dataB[count++] = (byte) 0x80;
// Pad out to 56 mod 64.
if ( count > SHA_BLOCKSIZE - 8 )
{
// Two lots of padding: Pad the first block to 64 bytes.
fillBlock( dataB, count, (byte) 0, SHA_BLOCKSIZE - count );
squashBytesToInts( dataB, 0, dataI, 0, SHA_BLOCKSIZE/4 );
shaTransform();
// Now fill the next block with 56 bytes.
fillBlock( dataB, 0, (byte) 0, SHA_BLOCKSIZE - 8 );
}
else
// Pad block to 56 bytes.
fillBlock( dataB, count, (byte) 0, SHA_BLOCKSIZE - 8 - count );
squashBytesToInts( dataB, 0, dataI, 0, SHA_BLOCKSIZE/4 );
// Append length in bits and transform.
dataI[14] = (int) ( bitCount >>> 32 );
dataI[15] = (int) ( bitCount & 0xffffffff );
shaTransform();
}
// ----------------------------- SHA Test code ---------------------------
// Size of buffer for SHA speed test data.
private static int TEST_BLOCK_SIZE = SHA_DIGESTSIZE * 100;
// Number of bytes of test data to process.
private static int TEST_BYTES = 10000000;
private static int TEST_BLOCKS = TEST_BYTES / TEST_BLOCK_SIZE;
public static void main( String[] args )
{
ShaHash h = new ShaHash();
// Test output data (this is the only test data given in the SHA
// document, but chances are if it works for this it'll work for
// anything).
h.addASCII( "abc" );
byte[] hb = h.get();
byte[] oldCorrect = {
(byte) 0x01, (byte) 0x64, (byte) 0xb8, (byte) 0xa9,
(byte) 0x14, (byte) 0xcd, (byte) 0x2a, (byte) 0x5e,
(byte) 0x74, (byte) 0xc4, (byte) 0xf7, (byte) 0xff,
(byte) 0x08, (byte) 0x2c, (byte) 0x4d, (byte) 0x97,
(byte) 0xf1, (byte) 0xed, (byte) 0xf8, (byte) 0x80
};
byte[] newCorrect = {
(byte) 0xa9, (byte) 0x99, (byte) 0x3e, (byte) 0x36,
(byte) 0x47, (byte) 0x06, (byte) 0x81, (byte) 0x6a,
(byte) 0xba, (byte) 0x3e, (byte) 0x25, (byte) 0x71,
(byte) 0x78, (byte) 0x50, (byte) 0xc2, (byte) 0x6c,
(byte) 0x9c, (byte) 0xd0, (byte) 0xd8, (byte) 0x9d
};
byte[] correct;
if ( USE_MODIFIED_SHA )
correct = newCorrect;
else
correct = oldCorrect;
System.out.println( "Got: " + toStringBlock( hb ) );
System.out.println( "Want: " + toStringBlock( correct ) );
if ( ! equalsBlock( hb, correct ) )
{
System.err.println( "Error in SHA implementation." );
System.exit( 1 );
}
// Now perform time trial, generating MD for 10MB of data. First,
// initialize the test data.
byte[] data = new byte[TEST_BLOCK_SIZE];
int i;
fillBlock( data, (byte) 0 );
System.out.println( "SHA time trial. Processing " + TEST_BYTES + " characters..." );
// Calculate SHA message digest in TEST_BLOCK_SIZE byte blocks.
h.reset();
for ( i = TEST_BLOCKS; i > 0; i-- )
h.add( data, 0, TEST_BLOCK_SIZE );
h.get();
System.out.println( "Done." );
}
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -