|
Alan Kaminsky
|
|
•
|
|
Department of Computer Science
|
|
•
|
|
Rochester Institute of Technology
|
|
•
|
|
4486 +
2220 =
6706
|
|
Home Page
|
//******************************************************************************
//
// File: SHA256Hash.java
// Package: edu.rit.crypto.hash
// Unit: Class edu.rit.crypto.hash.SHA256Hash
//
// This Java source file is copyright (C) 2008 by Alan Kaminsky. All rights
// reserved. For further information, contact the author, Alan Kaminsky, at
// ark@cs.rit.edu.
//
// This Java source file is part of the Computer Science Course Library ("The
// Library"). The Library is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or (at your
// option) any later version.
//
// The Library 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 for more
// details.
//
// A copy of the GNU General Public License is provided in the file gpl.txt. You
// may also obtain a copy of the GNU General Public License on the World Wide
// Web at http://www.gnu.org/licenses/gpl.html.
//
//******************************************************************************
package edu.rit.crypto.hash;
import edu.rit.crypto.CryptoUtils;
// For unit test main program
// import edu.rit.util.Hex;
/**
* Class SHA256Hash provides a one-way hash function using the Secure Hash
* Algorithm with a 256-bit hash value (SHA-256). The SHA-256 specification may
* be found at
* <A HREF="http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf">http://csrc.nist.gov/publications/fips/fips180-2/fips180-2.pdf</A>.
* <P>
* To compute a hash value, create an instance of class SHA256Hash; call the
* various <TT>write...()</TT> methods to write a sequence of data items; and
* call the <TT>hash()</TT> method to get the hash value. After calling
* <TT>hash()</TT>, the SHA-256 hash function resets itself. You can also call
* <TT>reset()</TT> to reset the SHA-256 hash function explicitly. If you write
* further bytes after resetting the SHA-256 hash function, these bytes go into
* the computation of a new hash value.
* <P>
* Since it is an iterated hash function, class SHA256Hash is susceptible to a
* length extension attack. Consider using class {@link DoubleSHA256Hash
* </CODE>DoubleSHA256Hash<CODE>} instead.
* <P>
* Class SHA256Hash is limited to hashing a maximum of 2<SUP>61</SUP> bytes
* (2<SUP>64</SUP> bits).
* <P>
* <B><I>Note:</I></B> Class SHA256Hash is not multiple thread safe.
*
* @author Alan Kaminsky
* @version 30-Apr-2008
*/
public class SHA256Hash
extends OneWayHash
{
// Hidden constants.
private static final int BLOCK_LENGTH_WORDS = 16;
private static final int HASH_LENGTH_WORDS = 8;
private static final int H_LENGTH_WORDS = 8;
private static final int W_LENGTH_WORDS = 64;
// Hidden data members.
// Hash value.
private int[] H = new int [H_LENGTH_WORDS];
// Message schedule.
private int[] W = new int [W_LENGTH_WORDS];
// Exported constructors.
/**
* Construct a new SHA-256 hash function.
*/
public SHA256Hash()
{
super (BLOCK_LENGTH_WORDS*4, HASH_LENGTH_WORDS*4);
resetHashComputation();
}
// Hidden operations.
/**
* Reset the hash computation.
*/
protected void resetHashComputation()
{
H[0] = 0x6a09e667;
H[1] = 0xbb67ae85;
H[2] = 0x3c6ef372;
H[3] = 0xa54ff53a;
H[4] = 0x510e527f;
H[5] = 0x9b05688c;
H[6] = 0x1f83d9ab;
H[7] = 0x5be0cd19;
}
/**
* Accumulate the given hash block into this one-way hash function's hash
* computation. Every byte in <TT>theBlock</TT> is valid.
*
* @param theBlock Hash block.
*/
protected void accumulateHashBlock
(byte[] theBlock)
{
int i;
// Prepare the message schedule W.
for (i = 0; i < BLOCK_LENGTH_WORDS; ++ i)
{
W[i] =
((theBlock[4*i ] & 0xFF) << 24) |
((theBlock[4*i+1] & 0xFF) << 16) |
((theBlock[4*i+2] & 0xFF) << 8) |
((theBlock[4*i+3] & 0xFF) );
}
for (i = BLOCK_LENGTH_WORDS; i < W_LENGTH_WORDS; ++ i)
{
W[i] =
lowerSigmaOne (W[i-2]) +
W[i-7] +
lowerSigmaZero (W[i-15]) +
W[i-16];
}
// Initialize working variables.
int a = H[0];
int b = H[1];
int c = H[2];
int d = H[3];
int e = H[4];
int f = H[5];
int g = H[6];
int h = H[7];
// Churn working variables using message schedule.
for (i = 0; i < W_LENGTH_WORDS; ++ i)
{
int T1 = h + upperSigmaOne (e) + Ch (e, f, g) + K[i] + W[i];
int T2 = upperSigmaZero (a) + Maj (a, b, c);
h = g;
g = f;
f = e;
e = d + T1;
d = c;
c = b;
b = a;
a = T1 + T2;
}
// Update hash value.
H[0] += a;
H[1] += b;
H[2] += c;
H[3] += d;
H[4] += e;
H[5] += f;
H[6] += g;
H[7] += h;
}
/**
* Accumulate the given partial hash block into this one-way hash function's
* hash computation and compute the final hash value. Only those bytes in
* <TT>theBlock</TT> from index 0 through index <TT>theByteCount-1</TT> are
* valid.
*
* @param theBlock Hash block.
* @param theByteCount Number of valid bytes in the hash block.
* @param theDataLengthBits Total number of data bits accumulated.
* @param buf Byte array in which to store hash value.
* @param off Index at which to start storing hash value.
*/
protected void computeHashValue
(byte[] theBlock,
int theByteCount,
long theDataLengthBits,
byte[] buf,
int off)
{
// If the message block is full, accumulate it and clear it out.
if (theByteCount == 64)
{
accumulateHashBlock (theBlock);
theByteCount = 0;
}
// Store first padding byte.
theBlock[theByteCount++] = (byte) 0x80;
// If there are fewer than 8 unused bytes in the hash block, we must put
// the message length into the next hash block.
if (theByteCount > 56)
{
while (theByteCount < 64)
{
theBlock[theByteCount++] = (byte) 0;
}
accumulateHashBlock (theBlock);
theByteCount = 0;
}
// Store remaining padding bytes, leaving 8 free bytes for the message
// length.
while (theByteCount < 56)
{
theBlock[theByteCount++] = (byte) 0;
}
// Store message length and accumulate hash block.
theBlock[56] = (byte) ((theDataLengthBits >> 56) & 0xFFL);
theBlock[57] = (byte) ((theDataLengthBits >> 48) & 0xFFL);
theBlock[58] = (byte) ((theDataLengthBits >> 40) & 0xFFL);
theBlock[59] = (byte) ((theDataLengthBits >> 32) & 0xFFL);
theBlock[60] = (byte) ((theDataLengthBits >> 24) & 0xFFL);
theBlock[61] = (byte) ((theDataLengthBits >> 16) & 0xFFL);
theBlock[62] = (byte) ((theDataLengthBits >> 8) & 0xFFL);
theBlock[63] = (byte) ((theDataLengthBits ) & 0xFFL);
accumulateHashBlock (theBlock);
// Return final hash value.
int j = off;
for (int i = 0; i < HASH_LENGTH_WORDS; ++ i)
{
buf[j++] = (byte) ((H[i] >> 24) & 0xFF);
buf[j++] = (byte) ((H[i] >> 16) & 0xFF);
buf[j++] = (byte) ((H[i] >> 8) & 0xFF);
buf[j++] = (byte) ((H[i] ) & 0xFF);
}
}
/**
* Erase this one-way hash function's internal state.
*/
protected void eraseInternalState()
{
CryptoUtils.erase (H);
H = null;
CryptoUtils.erase (W);
W = null;
}
// Hidden functions and constants from the SHA-256 spec.
private static int Ch
(int x,
int y,
int z)
{
return (x & y) ^ (~x & z);
}
private static int Maj
(int x,
int y,
int z)
{
return (x & y) ^ (x & z) ^ (y & z);
}
private static int upperSigmaZero
(int x)
{
return ROTR (2, x) ^ ROTR (13, x) ^ ROTR (22, x);
}
private static int upperSigmaOne
(int x)
{
return ROTR (6, x) ^ ROTR (11, x) ^ ROTR (25, x);
}
private static int lowerSigmaZero
(int x)
{
return ROTR (7, x) ^ ROTR (18, x) ^ SHR (3, x);
}
private static int lowerSigmaOne
(int x)
{
return ROTR (17, x) ^ ROTR (19, x) ^ SHR (10, x);
}
private static int ROTR
(int n,
int x)
{
return (x >>> n) | (x << (32 - n));
}
private static int SHR
(int n,
int x)
{
return x >>> n;
}
private static int[] K = new int[]
{/* 0*/ 0x428a2f98,
/* 1*/ 0x71374491,
/* 2*/ 0xb5c0fbcf,
/* 3*/ 0xe9b5dba5,
/* 4*/ 0x3956c25b,
/* 5*/ 0x59f111f1,
/* 6*/ 0x923f82a4,
/* 7*/ 0xab1c5ed5,
/* 8*/ 0xd807aa98,
/* 9*/ 0x12835b01,
/*10*/ 0x243185be,
/*11*/ 0x550c7dc3,
/*12*/ 0x72be5d74,
/*13*/ 0x80deb1fe,
/*14*/ 0x9bdc06a7,
/*15*/ 0xc19bf174,
/*16*/ 0xe49b69c1,
/*17*/ 0xefbe4786,
/*18*/ 0x0fc19dc6,
/*19*/ 0x240ca1cc,
/*20*/ 0x2de92c6f,
/*21*/ 0x4a7484aa,
/*22*/ 0x5cb0a9dc,
/*23*/ 0x76f988da,
/*24*/ 0x983e5152,
/*25*/ 0xa831c66d,
/*26*/ 0xb00327c8,
/*27*/ 0xbf597fc7,
/*28*/ 0xc6e00bf3,
/*29*/ 0xd5a79147,
/*30*/ 0x06ca6351,
/*31*/ 0x14292967,
/*32*/ 0x27b70a85,
/*33*/ 0x2e1b2138,
/*34*/ 0x4d2c6dfc,
/*35*/ 0x53380d13,
/*36*/ 0x650a7354,
/*37*/ 0x766a0abb,
/*38*/ 0x81c2c92e,
/*39*/ 0x92722c85,
/*40*/ 0xa2bfe8a1,
/*41*/ 0xa81a664b,
/*42*/ 0xc24b8b70,
/*43*/ 0xc76c51a3,
/*44*/ 0xd192e819,
/*45*/ 0xd6990624,
/*46*/ 0xf40e3585,
/*47*/ 0x106aa070,
/*48*/ 0x19a4c116,
/*49*/ 0x1e376c08,
/*50*/ 0x2748774c,
/*51*/ 0x34b0bcb5,
/*52*/ 0x391c0cb3,
/*53*/ 0x4ed8aa4a,
/*54*/ 0x5b9cca4f,
/*55*/ 0x682e6ff3,
/*56*/ 0x748f82ee,
/*57*/ 0x78a5636f,
/*58*/ 0x84c87814,
/*59*/ 0x8cc70208,
/*60*/ 0x90befffa,
/*61*/ 0xa4506ceb,
/*62*/ 0xbef9a3f7,
/*63*/ 0xc67178f2};
// Unit test main program.
// /**
// * Unit test main program.
// */
// public static void main
// (String[] args)
// throws Exception
// {
// int i, n;
//
// OneWayHash md = new SHA256Hash();
//
// if (args.length == 0)
// {
// System.out.println ("Hashing one million 'a's");
// for (i = 0; i < 1000000; ++ i)
// {
// md.writeByte ((byte) 'a');
// }
// }
// else
// {
// System.out.println ("Hashing \"" + args[0] + "\"");
// md.writeByteArray (args[0].getBytes());
// }
//
// byte[] h = new byte [md.getHashLength()];
// md.hash (h);
// System.out.println (Hex.toString (h));
// }
}
|
Alan Kaminsky
|
|
•
|
|
Department of Computer Science
|
|
•
|
|
Rochester Institute of Technology
|
|
•
|
|
4486 +
2220 =
6706
|
|
Home Page
|
Copyright © 2011 Alan Kaminsky.
All rights reserved.
Last updated 01-Mar-2013.
Please send comments to ark@cs.rit.edu.