/* * File: SHA1Hash.java * Authors: Kevin R. Dixon * Company: Sandia National Laboratories * Project: Cognitive Foundry * * Copyright Jan 26, 2011, Sandia Corporation. * Under the terms of Contract DE-AC04-94AL85000, there is a non-exclusive * license for use of this work by or on behalf of the U.S. Government. * Export of this program may require a license from the United States * Government. See CopyrightHistory.txt for complete details. * */ package gov.sandia.cognition.hash; import gov.sandia.cognition.annotation.PublicationReference; import gov.sandia.cognition.annotation.PublicationReferences; import gov.sandia.cognition.annotation.PublicationType; import gov.sandia.cognition.util.ObjectUtil; import java.util.Arrays; /* * A Java implementation of the Secure Hash Algorithm, SHA-1, as defined * in FIPS PUB 180-1 * Copyright (C) Sam Ruby 2004 * All rights reserved * * Based on code Copyright (C) Paul Johnston 2000 - 2002. * See http://pajhome.org.uk/site/legal.html for details. * * Converted to Java by Russell Beattie 2004 * Base64 logic and inlining by Sam Ruby 2004 * Bug fix correcting single bit error in base64 code by John Wilson * * BSD License * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. 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. * * Neither the name of the author nor the names of its contributors may be * used to endorse or promote products derived from this software without * specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT OWNER 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. */ /** * A Java implementation of the Secure Hash Algorithm, SHA-1, as defined * in FIPS PUB 180-1. This is a 160-bit (20-byte) cryptographic * hash function. SHA1 is generally secure, though there are potential * weaknesses on the order of 2^51. This implementation is based on the * implementation due to Sam Ruby, Paul Johnson, and John Wilson. * @author krdixon * @since 3.4.2 */ @PublicationReferences( references={ @PublicationReference( author="Wikipedia", title="SHA-1", type=PublicationType.WebPage, year=2011, url="http://en.wikipedia.org/wiki/SHA-1" ) , @PublicationReference( author="Sam Ruby", title="SHA1.java", type=PublicationType.WebPage, year=2004, url="http://intertwingly.net/stories/2004/07/18/SHA1.java" ) } ) public class SHA1Hash extends AbstractHashFunction { /** * SHA-1 hash function output is 160-bits or 20-bytes, {@value}. */ public static final int LENGTH = 20; /** * Default seed */ protected static final byte[] DEFAULT_SEED = { (byte)0x67, (byte)0x45, (byte)0x23, (byte)0x01, (byte)0xEF, (byte)0xCD, (byte)0xAB, (byte)0x89, (byte)0x98, (byte)0xBA, (byte)0xDC, (byte)0xFE, (byte)0x10, (byte)0x32, (byte)0x54, (byte)0x76, (byte)0xC3, (byte)0xD2, (byte)0xE1, (byte)0xF0 }; /** * Default constructor */ public SHA1Hash() { super(); } /** * Bitwise rotate a 32-bit number to the left * @param num * Number to rotate * @param cnt * Count to rotate * @return * Rotate number */ private static int rol( int num, int cnt) { return (num << cnt) | (num >>> (32 - cnt)); } /** * Computes the SHA-1 representation of the given input. * @param input * Input byte array to compute the SHA-1 hash of. * @return * 160-bit (20-byte) SHA-1 representation of the input. */ public static byte[] hash( byte[] input ) { byte[] output = new byte[ LENGTH ]; hash( input, output ); return output; } /** * Computes the SHA-1 representation of the given input. * @param input * Input byte array to compute the SHA-1 hash of. * @param output * Output to store the hash into */ public static void hash( byte[] input, byte[] output ) { hash( input, output, DEFAULT_SEED ); } /** * Computes the SHA-1 representation of the given input and seed. * @param x * Input byte array to compute the SHA-1 hash of. * @param seed * Seed to use to offset the hash function * @param output * 160-bit (20-byte) SHA-1 representation of the input and seed. */ public static void hash( byte[] x, byte[] output, byte[] seed ) { if( output.length != LENGTH ) { throw new IllegalArgumentException( "Expected output to be of length: " + LENGTH ); } if( seed.length != LENGTH ) { throw new IllegalArgumentException( "Expected seed to be of length: " + LENGTH ); } // Null input is all zeros. if( x == null ) { Arrays.fill(output, (byte) 0); return; } // Convert a string to a sequence of 16-word blocks, stored as an array. // Append padding bits and the length, as described in the SHA1 standard int[] blks = new int[(((x.length + 8) >> 6) + 1) * 16]; int i; for(i = 0; i < x.length; i++) { blks[i >> 2] |= x[i] << (24 - (i % 4) * 8); } blks[i >> 2] |= 0x80 << (24 - (i % 4) * 8); blks[blks.length - 1] = x.length * 8; // calculate 160 bit SHA1 hash of the sequence of blocks int[] w = new int[80]; int a = HashFunctionUtil.toInteger(seed, 0*4); int b = HashFunctionUtil.toInteger(seed, 1*4); int c = HashFunctionUtil.toInteger(seed, 2*4); int d = HashFunctionUtil.toInteger(seed, 3*4); int e = HashFunctionUtil.toInteger(seed, 4*4); for(i = 0; i < blks.length; i += 16) { int olda = a; int oldb = b; int oldc = c; int oldd = d; int olde = e; for(int j = 0; j < 80; j++) { w[j] = (j < 16) ? blks[i + j] : (rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1)); int t = rol(a, 5) + e + w[j] + ((j < 20) ? 1518500249 + ((b & c) | ((~b) & d)) : (j < 40) ? 1859775393 + (b ^ c ^ d) : (j < 60) ? -1894007588 + ((b & c) | (b & d) | (c & d)) : -899497514 + (b ^ c ^ d)); e = d; d = c; c = rol(b, 30); b = a; a = t; } a += olda; b += oldb; c += oldc; d += oldd; e += olde; } byte[] word = new byte[4]; int offset = 0; HashFunctionUtil.toByteArray(a, word); for( i = 0; i < word.length; i++ ) { output[offset] = word[i]; offset++; } HashFunctionUtil.toByteArray(b, word); for( i = 0; i < word.length; i++ ) { output[offset] = word[i]; offset++; } HashFunctionUtil.toByteArray(c, word); for( i = 0; i < word.length; i++ ) { output[offset] = word[i]; offset++; } HashFunctionUtil.toByteArray(d, word); for( i = 0; i < word.length; i++ ) { output[offset] = word[i]; offset++; } HashFunctionUtil.toByteArray(e, word); for( i = 0; i < word.length; i++ ) { output[offset] = word[i]; offset++; } } @Override public void evaluateInto( byte[] input, byte[] output, byte[] seed) { hash( input, output, seed ); } @Override public int length() { return LENGTH; } @Override public byte[] getDefaultSeed() { return ObjectUtil.deepCopy(DEFAULT_SEED); } }