summaryrefslogtreecommitdiffstats
path: root/base/util/src/netscape/security/provider/SHA.java
blob: 42a6b8b90776bdd4b2633298ad36342ed9748115 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
// --- BEGIN COPYRIGHT BLOCK ---
// This program 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; version 2 of the License.
//
// 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 for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, write to the Free Software Foundation, Inc.,
// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
//
// (C) 2007 Red Hat, Inc.
// All rights reserved.
// --- END COPYRIGHT BLOCK ---
package netscape.security.provider;

import java.security.DigestException;
import java.security.MessageDigestSpi;

/**
 * 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.30 97/12/10
 * @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;

    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;

        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;
    }
}