1 /*
2  * Copyright (c) 2003, 2014, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 
26 package sun.security.provider;
27 
28 import java.security.MessageDigestSpi;
29 import java.security.DigestException;
30 import java.security.ProviderException;
31 import java.util.Arrays;
32 import java.util.Objects;
33 
34 import jdk.internal.HotSpotIntrinsicCandidate;
35 
36 /**
37  * Common base message digest implementation for the Sun provider.
38  * It implements all the JCA methods as suitable for a Java message digest
39  * implementation of an algorithm based on a compression function (as all
40  * commonly used algorithms are). The individual digest subclasses only need to
41  * implement the following methods:
42  *
43  *  . abstract void implCompress(byte[] b, int ofs);
44  *  . abstract void implDigest(byte[] out, int ofs);
45  *  . abstract void implReset();
46  *
47  * See the inline documentation for details.
48  *
49  * @since   1.5
50  * @author  Andreas Sterbenz
51  */
52 abstract class DigestBase extends MessageDigestSpi implements Cloneable {
53 
54     // one element byte array, temporary storage for update(byte)
55     private byte[] oneByte;
56 
57     // algorithm name to use in the exception message
58     private final String algorithm;
59     // length of the message digest in bytes
60     private final int digestLength;
61 
62     // size of the input to the compression function in bytes
63     private final int blockSize;
64     // buffer to store partial blocks, blockSize bytes large
65     // Subclasses should not access this array directly except possibly in their
66     // implDigest() method. See MD5.java as an example.
67     byte[] buffer;
68     // offset into buffer
69     private int bufOfs;
70 
71     // number of bytes processed so far. subclasses should not modify
72     // this value.
73     // also used as a flag to indicate reset status
74     // -1: need to call engineReset() before next call to update()
75     //  0: is already reset
76     long bytesProcessed;
77 
78     /**
79      * Main constructor.
80      */
DigestBase(String algorithm, int digestLength, int blockSize)81     DigestBase(String algorithm, int digestLength, int blockSize) {
82         super();
83         this.algorithm = algorithm;
84         this.digestLength = digestLength;
85         this.blockSize = blockSize;
86         buffer = new byte[blockSize];
87     }
88 
89     // return digest length. See JCA doc.
engineGetDigestLength()90     protected final int engineGetDigestLength() {
91         return digestLength;
92     }
93 
94     // single byte update. See JCA doc.
engineUpdate(byte b)95     protected final void engineUpdate(byte b) {
96         if (oneByte == null) {
97             oneByte = new byte[1];
98         }
99         oneByte[0] = b;
100         engineUpdate(oneByte, 0, 1);
101     }
102 
103     // array update. See JCA doc.
engineUpdate(byte[] b, int ofs, int len)104     protected final void engineUpdate(byte[] b, int ofs, int len) {
105         if (len == 0) {
106             return;
107         }
108         if ((ofs < 0) || (len < 0) || (ofs > b.length - len)) {
109             throw new ArrayIndexOutOfBoundsException();
110         }
111         if (bytesProcessed < 0) {
112             engineReset();
113         }
114         bytesProcessed += len;
115         // if buffer is not empty, we need to fill it before proceeding
116         if (bufOfs != 0) {
117             int n = Math.min(len, blockSize - bufOfs);
118             System.arraycopy(b, ofs, buffer, bufOfs, n);
119             bufOfs += n;
120             ofs += n;
121             len -= n;
122             if (bufOfs >= blockSize) {
123                 // compress completed block now
124                 implCompress(buffer, 0);
125                 bufOfs = 0;
126             }
127         }
128         // compress complete blocks
129         if (len >= blockSize) {
130             int limit = ofs + len;
131             ofs = implCompressMultiBlock(b, ofs, limit - blockSize);
132             len = limit - ofs;
133         }
134         // copy remainder to buffer
135         if (len > 0) {
136             System.arraycopy(b, ofs, buffer, 0, len);
137             bufOfs = len;
138         }
139     }
140 
141     // compress complete blocks
implCompressMultiBlock(byte[] b, int ofs, int limit)142     private int implCompressMultiBlock(byte[] b, int ofs, int limit) {
143         implCompressMultiBlockCheck(b, ofs, limit);
144         return implCompressMultiBlock0(b, ofs, limit);
145     }
146 
147     @HotSpotIntrinsicCandidate
implCompressMultiBlock0(byte[] b, int ofs, int limit)148     private int implCompressMultiBlock0(byte[] b, int ofs, int limit) {
149         for (; ofs <= limit; ofs += blockSize) {
150             implCompress(b, ofs);
151         }
152         return ofs;
153     }
154 
implCompressMultiBlockCheck(byte[] b, int ofs, int limit)155     private void implCompressMultiBlockCheck(byte[] b, int ofs, int limit) {
156         if (limit < 0) {
157             return;  // not an error because implCompressMultiBlockImpl won't execute if limit < 0
158                      // and an exception is thrown if ofs < 0.
159         }
160 
161         Objects.requireNonNull(b);
162 
163         if (ofs < 0 || ofs >= b.length) {
164             throw new ArrayIndexOutOfBoundsException(ofs);
165         }
166 
167         int endIndex = (limit / blockSize) * blockSize  + blockSize - 1;
168         if (endIndex >= b.length) {
169             throw new ArrayIndexOutOfBoundsException(endIndex);
170         }
171     }
172 
173     // reset this object. See JCA doc.
engineReset()174     protected final void engineReset() {
175         if (bytesProcessed == 0) {
176             // already reset, ignore
177             return;
178         }
179         implReset();
180         bufOfs = 0;
181         bytesProcessed = 0;
182         Arrays.fill(buffer, (byte) 0x00);
183     }
184 
185     // return the digest. See JCA doc.
engineDigest()186     protected final byte[] engineDigest() {
187         byte[] b = new byte[digestLength];
188         try {
189             engineDigest(b, 0, b.length);
190         } catch (DigestException e) {
191             throw (ProviderException)
192                 new ProviderException("Internal error").initCause(e);
193         }
194         return b;
195     }
196 
197     // return the digest in the specified array. See JCA doc.
engineDigest(byte[] out, int ofs, int len)198     protected final int engineDigest(byte[] out, int ofs, int len)
199             throws DigestException {
200         if (len < digestLength) {
201             throw new DigestException("Length must be at least "
202                 + digestLength + " for " + algorithm + "digests");
203         }
204         if ((ofs < 0) || (len < 0) || (ofs > out.length - len)) {
205             throw new DigestException("Buffer too short to store digest");
206         }
207         if (bytesProcessed < 0) {
208             engineReset();
209         }
210         implDigest(out, ofs);
211         bytesProcessed = -1;
212         return digestLength;
213     }
214 
215     /**
216      * Core compression function. Processes blockSize bytes at a time
217      * and updates the state of this object.
218      */
implCompress(byte[] b, int ofs)219     abstract void implCompress(byte[] b, int ofs);
220 
221     /**
222      * Return the digest. Subclasses do not need to reset() themselves,
223      * DigestBase calls implReset() when necessary.
224      */
implDigest(byte[] out, int ofs)225     abstract void implDigest(byte[] out, int ofs);
226 
227     /**
228      * Reset subclass specific state to their initial values. DigestBase
229      * calls this method when necessary.
230      */
implReset()231     abstract void implReset();
232 
clone()233     public Object clone() throws CloneNotSupportedException {
234         DigestBase copy = (DigestBase) super.clone();
235         copy.buffer = copy.buffer.clone();
236         return copy;
237     }
238 
239     // padding used for the MD5, and SHA-* message digests
240     static final byte[] padding;
241 
242     static {
243         // we need 128 byte padding for SHA-384/512
244         // and an additional 8 bytes for the high 8 bytes of the 16
245         // byte bit counter in SHA-384/512
246         padding = new byte[136];
247         padding[0] = (byte)0x80;
248     }
249 }
250