1/*
2 *  Licensed to the Apache Software Foundation (ASF) under one or more
3 *  contributor license agreements.  See the NOTICE file distributed with
4 *  this work for additional information regarding copyright ownership.
5 *  The ASF licenses this file to You under the Apache License, Version 2.0
6 *  (the "License"); you may not use this file except in compliance with
7 *  the License.  You may obtain a copy of the License at
8 *
9 *     http://www.apache.org/licenses/LICENSE-2.0
10 *
11 *  Unless required by applicable law or agreed to in writing, software
12 *  distributed under the License is distributed on an "AS IS" BASIS,
13 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 *  See the License for the specific language governing permissions and
15 *  limitations under the License.
16 */
17
18
19package org.apache.harmony.security.provider.crypto;
20
21
22import java.security.MessageDigestSpi;
23import java.security.DigestException;
24
25import java.util.Arrays;
26
27import org.apache.harmony.security.internal.nls.Messages;
28import org.apache.harmony.security.provider.crypto.SHA1_Data;
29import org.apache.harmony.security.provider.crypto.SHA1Impl;
30
31
32/**
33 * This class extends the MessageDigestSpi class implementing all its abstract methods;
34 * it overrides the "Object clone()" and "int engineGetDigestLength()" methods. <BR>
35 * The class implements the Cloneable interface.
36 */
37
38
39public class SHA1_MessageDigestImpl extends MessageDigestSpi
40                                    implements Cloneable, SHA1_Data {
41
42
43    private  int buffer[];       // buffer has the following structure:
44                                 // -  0-16 - frame for accumulating a message
45                                 // - 17-79 - for SHA1Impl methods
46                                 // - 80    - unused
47                                 // - 81    - to store length of the message
48                                 // - 82-86 - frame for current message digest
49
50    private byte oneByte[];      // one byte buffer needed to use in engineUpdate(byte)
51                                 // having buffer as private field is just optimization
52
53    private int messageLength;   // total length of bytes supplied by user
54
55
56    /**
57     *  The constructor creates needed buffers and sets the engine at initial state
58     */
59    public SHA1_MessageDigestImpl() {
60
61        // BYTES_OFFSET +6 is minimal length required by methods in SHA1Impl
62        buffer  = new int[BYTES_OFFSET +6];
63
64        oneByte = new byte[1];
65
66        engineReset();
67    }
68
69
70    /**
71     * The method performs final actions and invokes the "computeHash(int[])" method.
72     * In case if there is no enough words in current frame
73     * after processing its data, extra frame is prepared and
74     * the "computeHash(int[])" method is invoked second time. <BR>
75     *
76     * After processing, the method resets engine's state
77     *
78     * @param
79     *       digest - byte array
80     * @param
81     *       offset - offset in digest
82     */
83    private void processDigest(byte[] digest, int offset) {
84
85        int i, j;         // implementation variables
86        int lastWord;     //
87
88        long nBits = messageLength <<3 ;  // length has to be calculated before padding
89
90        engineUpdate( (byte) 0x80 );      // beginning byte in padding
91
92        i = 0;                     // i contains number of beginning word for following loop
93
94        lastWord = (buffer[BYTES_OFFSET] + 3)>>2 ;  // computing of # of full words by shifting
95                                                    // # of bytes
96
97        // possible cases:
98        //
99        // - buffer[BYTES_OFFSET] == 0 - buffer frame is empty,
100        //                         padding byte was 64th in previous frame
101        //                         current frame should contain only message's length
102        //
103        // - lastWord < 14 - two last, these are 14 & 15, words in 16 word frame are free;
104        //                   no extra frame needed
105        // - lastWord = 14 - only one last, namely 15-th, word in frame doesn't contain bytes;
106        //                   extra frame is needed
107        // - lastWord > 14 - last word in frame is not full;
108        //                   extra frame is needed
109
110        if ( buffer[BYTES_OFFSET] != 0 ) {
111
112            if ( lastWord < 15 ) {
113                i = lastWord;
114            } else {
115                if ( lastWord == 15 ) {
116                    buffer[15] = 0;       // last word in frame is set to "0"
117                }
118                SHA1Impl.computeHash(buffer);
119                i = 0;
120            }
121        }
122        Arrays.fill(buffer, i, 14, 0);
123
124        buffer[14] = (int)( nBits >>>32 );
125        buffer[15] = (int)( nBits & 0xFFFFFFFF );
126        SHA1Impl.computeHash(buffer);
127
128        // converting 5-word frame into 20 bytes
129        j = offset;
130        for ( i = HASH_OFFSET; i < HASH_OFFSET +5; i++ ) {
131            int k = buffer[i];
132            digest[j  ] = (byte) ( k >>>24 );   // getting first  byte from left
133            digest[j+1] = (byte) ( k >>>16 );   // getting second byte from left
134            digest[j+2] = (byte) ( k >>> 8 );   // getting third  byte from left
135            digest[j+3] = (byte) ( k       );   // getting fourth byte from left
136            j += 4;
137        }
138
139        engineReset();
140    }
141
142    //  methods specified in java.security.MessageDigestSpi
143
144    /**
145     * Returns a "deep" copy of this SHA1MDImpl object. <BR>
146     *
147     * The method overrides "clone()" in class Object. <BR>
148     *
149     * @return
150     *       a clone of this object
151     */
152    public Object clone() throws CloneNotSupportedException {
153
154        SHA1_MessageDigestImpl cloneObj = (SHA1_MessageDigestImpl) super.clone();
155
156        cloneObj.buffer  = ( int[])buffer.clone();
157        cloneObj.oneByte = (byte[])oneByte.clone();
158
159        return cloneObj;
160    }
161
162
163    /**
164     * Computes a message digest value. <BR>
165     *
166     * The method resets the engine. <BR>
167     *
168     * The method overrides "engineDigest()" in class MessageDigestSpi. <BR>
169     *
170     * @return
171     *       byte array containing message digest value
172     */
173    protected byte[] engineDigest() {
174
175        byte[] hash = new byte[DIGEST_LENGTH];
176
177        processDigest(hash, 0);
178        return hash;
179    }
180
181
182    /**
183     * Computes message digest value.
184     * Upon return, the value is stored in "buf" buffer beginning "offset" byte. <BR>
185     *
186     * The method resets the engine. <BR>
187     *
188     * The method overrides "engineDigest(byte[],int,int) in class MessageDigestSpi.
189     *
190     * @param
191     *       buf    byte array to store a message digest returned
192     * @param
193     *       offset a position in the array for first byte of the message digest
194     * @param
195     *       len    number of bytes within buffer allotted for the message digest;
196     *                as this implementation doesn't provide partial digests,
197     *                len should be >= 20, DigestException is thrown otherwise
198     * @return
199     *       the length of the message digest stored in the "buf" buffer;
200     *       in this implementation the length=20
201     *
202     * @throws IllegalArgumentException
203     *               if null is passed to the "buf" argument <BR>
204     *               if offset + len > buf.length  <BR>
205     *               if offset > buf.length or len > buf.length
206     *
207     * @throws DigestException
208     *               if len < 20
209     *
210     * @throws  ArrayIndexOutOfBoundsException
211     *               if offset < 0
212     */
213    protected int engineDigest(byte[] buf, int offset, int len) throws DigestException {
214
215        if ( buf == null ) {
216            throw new IllegalArgumentException(Messages.getString("security.162"));  //$NON-NLS-1$
217        }
218        if ( offset > buf.length || len > buf.length || (len + offset) > buf.length ) {
219            throw new IllegalArgumentException(
220               Messages.getString("security.163")); //$NON-NLS-1$
221        }
222        if ( len < DIGEST_LENGTH ) {
223            throw new DigestException(Messages.getString("security.164")); //$NON-NLS-1$
224        }
225        if ( offset < 0 ) {
226            throw new ArrayIndexOutOfBoundsException(Messages.getString("security.165", offset)); //$NON-NLS-1$
227        }
228
229        processDigest(buf, offset);
230
231        return DIGEST_LENGTH;
232    }
233
234
235    /**
236     * Returns a message digest length. <BR>
237     *
238     * The method overrides "engineGetDigestLength()" in class MessageDigestSpi. <BR>
239     *
240     * @return
241     *        total length of current message digest as an int value
242     */
243    protected int engineGetDigestLength() {
244        return DIGEST_LENGTH;
245    }
246
247
248    /**
249     * Resets the engine. <BR>
250     *
251     * The method overrides "engineReset()" in class MessageDigestSpi. <BR>
252     */
253    protected void engineReset() {
254
255        messageLength = 0;
256
257        buffer[BYTES_OFFSET] = 0;
258        buffer[HASH_OFFSET   ] = H0;
259        buffer[HASH_OFFSET +1] = H1;
260        buffer[HASH_OFFSET +2] = H2;
261        buffer[HASH_OFFSET +3] = H3;
262        buffer[HASH_OFFSET +4] = H4;
263    }
264
265
266    /**
267     * Supplements a byte to current message. <BR>
268     *
269     * The method overrides "engineUpdate(byte)" in class MessageDigestSpi. <BR>
270     *
271     * @param
272     *       input byte to add to current message
273     */
274    protected void engineUpdate(byte input) {
275
276        oneByte[0] = input;
277        SHA1Impl.updateHash( buffer, oneByte, 0, 0 );
278        messageLength++;
279    }
280
281
282    /**
283     * Updates current message. <BR>
284     *
285     * The method overrides "engineUpdate(byte[],int,int)" in class MessageDigestSpi. <BR>
286     *
287     * The method silently returns if "len" <= 0.
288     *
289     * @param
290     *       input  a byte array
291     * @param
292     *       offset a number of first byte in the "input" array to use for updating
293     * @param
294     *       len    a number of bytes to use
295     *
296     * @throws NullPointerException
297     *                if null is passed to the "buf" argument
298     *
299     * @throws IllegalArgumentException
300     *                if offset > buf.length or len > buf.length or
301     *                (len + offset) > buf.length
302     * @throws ArrayIndexOutOfBoundsException
303     *                offset < 0
304     */
305    protected void engineUpdate(byte[] input, int offset, int len) {
306
307        if ( input == null ) {
308            throw new IllegalArgumentException(Messages.getString("security.166"));  //$NON-NLS-1$
309        }
310        if ( len <= 0 ) {
311            return;
312        }
313        if ( offset < 0 ) {
314            throw new ArrayIndexOutOfBoundsException(Messages.getString("security.165", offset)); //$NON-NLS-1$
315        }
316        if ( offset > input.length || len > input.length || (len + offset) > input.length ) {
317            throw new IllegalArgumentException(
318               Messages.getString("security.167")); //$NON-NLS-1$
319        }
320
321        SHA1Impl.updateHash(buffer, input, offset, offset + len -1 );
322        messageLength += len;
323    }
324
325}
326