/* * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. * The ASF licenses this file to You under the Apache License, Version 2.0 * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.harmony.security.provider.crypto; import java.security.DigestException; import java.security.MessageDigestSpi; import java.util.Arrays; /** * This class extends the MessageDigestSpi class implementing all its abstract methods; * it overrides the "Object clone()" and "int engineGetDigestLength()" methods. <BR> * The class implements the Cloneable interface. */ public class SHA1_MessageDigestImpl extends MessageDigestSpi implements Cloneable, SHA1_Data { private int[] buffer; // buffer has the following structure: // - 0-16 - frame for accumulating a message // - 17-79 - for SHA1Impl methods // - 80 - unused // - 81 - to store length of the message // - 82-86 - frame for current message digest private byte[] oneByte; // one byte buffer needed to use in engineUpdate(byte) // having buffer as private field is just optimization private long messageLength; // total length of bytes supplied by user /** * The constructor creates needed buffers and sets the engine at initial state */ public SHA1_MessageDigestImpl() { // BYTES_OFFSET +6 is minimal length required by methods in SHA1Impl buffer = new int[BYTES_OFFSET +6]; oneByte = new byte[1]; engineReset(); } /** * The method performs final actions and invokes the "computeHash(int[])" method. * In case if there is no enough words in current frame * after processing its data, extra frame is prepared and * the "computeHash(int[])" method is invoked second time. <BR> * * After processing, the method resets engine's state * * @param * digest - byte array * @param * offset - offset in digest */ private void processDigest(byte[] digest, int offset) { int i, j; // implementation variables int lastWord; // long nBits = messageLength <<3 ; // length has to be calculated before padding engineUpdate( (byte) 0x80 ); // beginning byte in padding i = 0; // i contains number of beginning word for following loop lastWord = (buffer[BYTES_OFFSET] + 3)>>2 ; // computing of # of full words by shifting // # of bytes // possible cases: // // - buffer[BYTES_OFFSET] == 0 - buffer frame is empty, // padding byte was 64th in previous frame // current frame should contain only message's length // // - lastWord < 14 - two last, these are 14 & 15, words in 16 word frame are free; // no extra frame needed // - lastWord = 14 - only one last, namely 15-th, word in frame doesn't contain bytes; // extra frame is needed // - lastWord > 14 - last word in frame is not full; // extra frame is needed if ( buffer[BYTES_OFFSET] != 0 ) { if ( lastWord < 15 ) { i = lastWord; } else { if ( lastWord == 15 ) { buffer[15] = 0; // last word in frame is set to "0" } SHA1Impl.computeHash(buffer); i = 0; } } Arrays.fill(buffer, i, 14, 0); buffer[14] = (int)( nBits >>>32 ); buffer[15] = (int)( nBits & 0xFFFFFFFF ); SHA1Impl.computeHash(buffer); // converting 5-word frame into 20 bytes j = offset; for ( i = HASH_OFFSET; i < HASH_OFFSET +5; i++ ) { int k = buffer[i]; digest[j ] = (byte) ( k >>>24 ); // getting first byte from left digest[j+1] = (byte) ( k >>>16 ); // getting second byte from left digest[j+2] = (byte) ( k >>> 8 ); // getting third byte from left digest[j+3] = (byte) ( k ); // getting fourth byte from left j += 4; } engineReset(); } // methods specified in java.security.MessageDigestSpi /** * Returns a "deep" copy of this SHA1MDImpl object. <BR> * * The method overrides "clone()" in class Object. <BR> * * @return * a clone of this object */ public Object clone() throws CloneNotSupportedException { SHA1_MessageDigestImpl cloneObj = (SHA1_MessageDigestImpl) super.clone(); cloneObj.buffer = buffer.clone(); cloneObj.oneByte = oneByte.clone(); return cloneObj; } /** * Computes a message digest value. <BR> * * The method resets the engine. <BR> * * The method overrides "engineDigest()" in class MessageDigestSpi. <BR> * * @return * byte array containing message digest value */ protected byte[] engineDigest() { byte[] hash = new byte[DIGEST_LENGTH]; processDigest(hash, 0); return hash; } /** * Computes message digest value. * Upon return, the value is stored in "buf" buffer beginning "offset" byte. <BR> * * The method resets the engine. <BR> * * The method overrides "engineDigest(byte[],int,int) in class MessageDigestSpi. * * @param * buf byte array to store a message digest returned * @param * offset a position in the array for first byte of the message digest * @param * len number of bytes within buffer allotted for the message digest; * as this implementation doesn't provide partial digests, * len should be >= 20, DigestException is thrown otherwise * @return * the length of the message digest stored in the "buf" buffer; * in this implementation the length=20 * * @throws IllegalArgumentException * if null is passed to the "buf" argument <BR> * if offset + len > buf.length <BR> * if offset > buf.length or len > buf.length * * @throws DigestException * if len < 20 * * @throws ArrayIndexOutOfBoundsException * if offset < 0 */ protected int engineDigest(byte[] buf, int offset, int len) throws DigestException { if (buf == null) { throw new IllegalArgumentException("buf == null"); } if (offset > buf.length || len > buf.length || (len + offset) > buf.length) { throw new IllegalArgumentException(); } if (len < DIGEST_LENGTH) { throw new DigestException("len < DIGEST_LENGTH"); } if (offset < 0) { throw new ArrayIndexOutOfBoundsException(offset); } processDigest(buf, offset); return DIGEST_LENGTH; } /** * Returns a message digest length. <BR> * * The method overrides "engineGetDigestLength()" in class MessageDigestSpi. <BR> * * @return * total length of current message digest as an int value */ protected int engineGetDigestLength() { return DIGEST_LENGTH; } /** * Resets the engine. <BR> * * The method overrides "engineReset()" in class MessageDigestSpi. <BR> */ protected void engineReset() { messageLength = 0; buffer[BYTES_OFFSET] = 0; buffer[HASH_OFFSET ] = H0; buffer[HASH_OFFSET +1] = H1; buffer[HASH_OFFSET +2] = H2; buffer[HASH_OFFSET +3] = H3; buffer[HASH_OFFSET +4] = H4; } /** * Supplements a byte to current message. <BR> * * The method overrides "engineUpdate(byte)" in class MessageDigestSpi. <BR> * * @param * input byte to add to current message */ protected void engineUpdate(byte input) { oneByte[0] = input; SHA1Impl.updateHash( buffer, oneByte, 0, 0 ); messageLength++; } /** * Updates current message. <BR> * * The method overrides "engineUpdate(byte[],int,int)" in class MessageDigestSpi. <BR> * * The method silently returns if "len" <= 0. * * @param * input a byte array * @param * offset a number of first byte in the "input" array to use for updating * @param * len a number of bytes to use * * @throws NullPointerException * if null is passed to the "buf" argument * * @throws IllegalArgumentException * if offset > buf.length or len > buf.length or * (len + offset) > buf.length * @throws ArrayIndexOutOfBoundsException * offset < 0 */ protected void engineUpdate(byte[] input, int offset, int len) { if (input == null) { throw new IllegalArgumentException("input == null"); } if (len <= 0) { return; } if (offset < 0) { throw new ArrayIndexOutOfBoundsException(offset); } if (offset > input.length || len > input.length || (len + offset) > input.length) { throw new IllegalArgumentException(); } SHA1Impl.updateHash(buffer, input, offset, offset + len -1 ); messageLength += len; } }