/*
* @(#)SHA.java 1.42 06/10/10
*
* Copyright 1990-2008 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*
*/
package sun.security.provider;
import java.security.*;
/**
* This class implements the Secure Hash Algorithm (SHA) developed by
* the National Institute of Standards and Technology along with the
* National Security Agency. This is the updated version of SHA
* fip-180 as superseded by fip-180-1.
*
* <p>It implement JavaSecurity MessageDigest, and can be used by in
* the Java Security framework, as a pluggable implementation, as a
* filter for the digest stream classes.
*
* @version 1.35 02/02/00
* @author Roger Riggs
* @author Benjamin Renaud
*/
public class SHA extends MessageDigestSpi implements Cloneable {
/* This private hookm controlled by the appropriate constructor,
causes this class to implement the first version of SHA,
as defined in FIPS 180, as opposed to FIPS 180-1. This was
useful for DSA testing. */
private int version = 1;
private static final int SHA_LENGTH = 20;
// Buffer of int's and count of characters accumulated
// 64 bytes are included in each hash block so the low order
// bits of count are used to know how to pack the bytes into ints
// and to know when to compute the block and start the next one.
private int W[] = new int[80];
private long count = 0;
private final int countmax = 64;
private final int countmask = (countmax-1);
private int AA, BB, CC, DD, EE;
/**
* Creates a SHA object.with state (for cloning) */
private SHA(SHA sha) {
this();
this.version = sha.version;
System.arraycopy(sha.W, 0, this.W, 0, W.length);
this.count = sha.count;
this.AA = sha.AA;
this.BB = sha.BB;
this.CC = sha.CC;
this.DD = sha.DD;
this.EE = sha.EE;
}
SHA(int version) {
this();
this.version = version;
}
/**
* Creates a new SHA object.
*/
public SHA() {
init();
}
/**
* Return the length of the digest in bytes
*/
protected int engineGetDigestLength() {
return (SHA_LENGTH);
}
public void engineUpdate(byte b) {
engineUpdate((int)b);
}
/**
* Update a byte.
*
* @param b the byte
*/
private void engineUpdate(int b) {
int word;
int offset;
/* compute word offset and bit offset within word the low bits
of count are inverted to make put the bytes in the write
order */
word = ((int)count & countmask) >>> 2;
offset = (~(int)count & 3) << 3;
W[word] = (W[word] & ~(0xff << offset)) | ((b & 0xff) << offset);
/* If this is the last byte of a block, compute the partial hash */
if (((int)count & countmask) == countmask) {
computeBlock();
}
count++;
}
/**
* Update a buffer.
*
* @param b the data to be updated.
* @param off the start offset in the data
* @param len the number of bytes to be updated.
*/
public void engineUpdate(byte b[], int off, int len) {
int word;
int offset;
if ((off < 0) || (len < 0) || (off + len > b.length))
throw new ArrayIndexOutOfBoundsException();
// Use single writes until integer aligned
while ((len > 0) &&
((int)count & 3) != 0) {
engineUpdate(b[off]);
off++;
len--;
}
/* Assemble groups of 4 bytes to be inserted in integer array */
for (;len >= 4; len -= 4, off += 4) {
word = ((int)count & countmask) >> 2;
W[word] = ((b[off] & 0xff) << 24) |
((b[off+1] & 0xff) << 16) |
((b[off+2] & 0xff) << 8) |
((b[off+3] & 0xff) );
count += 4;
if (((int)count & countmask) == 0) {
computeBlock();
}
}
/* Use single writes for last few bytes */
for (; len > 0; len--, off++) {
engineUpdate(b[off]);
}
}
/**
* Resets the buffers and hash value to start a new hash.
*/
public void init() {
AA = 0x67452301;
BB = 0xefcdab89;
CC = 0x98badcfe;
DD = 0x10325476;
EE = 0xc3d2e1f0;
for (int i = 0; i < 80; i++)
W[i] = 0;
count = 0;
}
/**
* Resets the buffers and hash value to start a new hash.
*/
public void engineReset() {
init();
}
/**
* Computes the final hash and returns the final value as a
* byte[20] array. The object is reset to be ready for further
* use, as specified in the JavaSecurity MessageDigest
* specification. */
public byte[] engineDigest() {
byte hashvalue[] = new byte[SHA_LENGTH];
try {
engineDigest(hashvalue, 0, hashvalue.length);
} catch (DigestException e) {
throw new InternalError("");
}
return hashvalue;
}
/**
* Computes the final hash and returns the final value as a
* byte[20] array. The object is reset to be ready for further
* use, as specified in the JavaSecurity MessageDigest
* specification. */
public int engineDigest(byte[] hashvalue, int offset, int len)
throws DigestException {
if (len < SHA_LENGTH)
throw new DigestException("partial digests not returned");
if (hashvalue.length - offset < SHA_LENGTH)
throw new DigestException("insufficient space in the output " +
"buffer to store the digest");
/* The number of bits before padding occurs */
long bits = count << 3;
engineUpdate(0x80);
/* Pad with zeros until length is a multiple of 448 (the last two
32 ints are used a holder for bits (see above). */
while ((int)(count & countmask) != 56) {
engineUpdate(0);
}
W[14] = (int)(bits >>> 32);
W[15] = (int)(bits & 0xffffffff);
count += 8;
computeBlock();
// Copy out the result
hashvalue[offset + 0] = (byte)(AA >>> 24);
hashvalue[offset + 1] = (byte)(AA >>> 16);
hashvalue[offset + 2] = (byte)(AA >>> 8);
hashvalue[offset + 3] = (byte)(AA >>> 0);
hashvalue[offset + 4] = (byte)(BB >>> 24);
hashvalue[offset + 5] = (byte)(BB >>> 16);
hashvalue[offset + 6] = (byte)(BB >>> 8);
hashvalue[offset + 7] = (byte)(BB >>> 0);
hashvalue[offset + 8] = (byte)(CC >>> 24);
hashvalue[offset + 9] = (byte)(CC >>> 16);
hashvalue[offset + 10] = (byte)(CC >>> 8);
hashvalue[offset + 11] = (byte)(CC >>> 0);
hashvalue[offset + 12] = (byte)(DD >>> 24);
hashvalue[offset + 13] = (byte)(DD >>> 16);
hashvalue[offset + 14] = (byte)(DD >>> 8);
hashvalue[offset + 15] = (byte)(DD >>> 0);
hashvalue[offset + 16] = (byte)(EE >>> 24);
hashvalue[offset + 17] = (byte)(EE >>> 16);
hashvalue[offset + 18] = (byte)(EE >>> 8);
hashvalue[offset + 19] = (byte)(EE >>> 0);
engineReset(); // remove the evidence
return SHA_LENGTH;
}
// Constants for each round
private final int round1_kt = 0x5a827999;
private final int round2_kt = 0x6ed9eba1;
private final int round3_kt = 0x8f1bbcdc;
private final int round4_kt = 0xca62c1d6;
/**
* Compute a the hash for the current block.
*
* This is in the same vein as Peter Gutmann's algorithm listed in
* the back of Applied Cryptography, Compact implementation of
* "old" NIST Secure Hash Algorithm.
*
*/
private void computeBlock() {
int temp, a, b, c, d, e;
// The first 16 ints have the byte stream, compute the rest of
// the buffer
for (int t = 16; t <= 79; t++) {
if (version == 0) {
W[t] = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16];
} else {
temp = W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16];
W[t] = ((temp << 1) | (temp >>>(32 - 1)));
}
}
a = AA;
b = BB;
c = CC;
d = DD;
e = EE;
// Round 1
for (int i = 0; i < 20; i++) {
temp = ((a<<5) | (a>>>(32-5))) +
((b&c)|((~b)&d))+ e + W[i] + round1_kt;
e = d;
d = c;
c = ((b<<30) | (b>>>(32-30)));
b = a;
a = temp;
}
// Round 2
for (int i = 20; i < 40; i++) {
temp = ((a<<5) | (a>>>(32-5))) +
(b ^ c ^ d) + e + W[i] + round2_kt;
e = d;
d = c;
c = ((b<<30) | (b>>>(32-30)));
b = a;
a = temp;
}
// Round 3
for (int i = 40; i < 60; i++) {
temp = ((a<<5) | (a>>>(32-5))) +
((b&c)|(b&d)|(c&d)) + e + W[i] + round3_kt;
e = d;
d = c;
c = ((b<<30) | (b>>>(32-30)));
b = a;
a = temp;
}
// Round 4
for (int i = 60; i < 80; i++) {
temp = ((a<<5) | (a>>>(32-5))) +
(b ^ c ^ d) + e + W[i] + round4_kt;
e = d;
d = c;
c = ((b<<30) | (b>>>(32-30)));
b = a;
a = temp;
}
AA += a;
BB += b;
CC += c;
DD += d;
EE += e;
}
/*
* Clones this object.
*/
public Object clone() {
SHA that = null;
try {
that = (SHA)super.clone();
that.W = new int[80];
System.arraycopy(this.W, 0, that.W, 0, W.length);
return that;
} catch (CloneNotSupportedException e) {
}
return that;
}
}