1 /*
2  * Copyright (c) 2003, 2019, 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.pkcs11;
27 
28 import java.util.*;
29 import java.nio.ByteBuffer;
30 
31 import java.security.*;
32 
33 import javax.crypto.SecretKey;
34 
35 import sun.nio.ch.DirectBuffer;
36 
37 import sun.security.util.MessageDigestSpi2;
38 
39 import sun.security.pkcs11.wrapper.*;
40 import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
41 
42 /**
43  * MessageDigest implementation class. This class currently supports
44  * MD2, MD5, SHA-1, SHA-224, SHA-256, SHA-384, and SHA-512.
45  *
46  * Note that many digest operations are on fairly small amounts of data
47  * (less than 100 bytes total). For example, the 2nd hashing in HMAC or
48  * the PRF in TLS. In order to speed those up, we use some buffering to
49  * minimize number of the Java->native transitions.
50  *
51  * @author  Andreas Sterbenz
52  * @since   1.5
53  */
54 final class P11Digest extends MessageDigestSpi implements Cloneable,
55     MessageDigestSpi2 {
56 
57     /* fields initialized, no session acquired */
58     private final static int S_BLANK    = 1;
59 
60     /* data in buffer, session acquired, but digest not initialized */
61     private final static int S_BUFFERED = 2;
62 
63     /* session initialized for digesting */
64     private final static int S_INIT     = 3;
65 
66     private final static int BUFFER_SIZE = 96;
67 
68     // token instance
69     private final Token token;
70 
71     // algorithm name
72     private final String algorithm;
73 
74     // mechanism id object
75     private final CK_MECHANISM mechanism;
76 
77     // length of the digest in bytes
78     private final int digestLength;
79 
80     // associated session, if any
81     private Session session;
82 
83     // current state, one of S_* above
84     private int state;
85 
86     // buffer to reduce number of JNI calls
87     private byte[] buffer;
88 
89     // offset into the buffer
90     private int bufOfs;
91 
P11Digest(Token token, String algorithm, long mechanism)92     P11Digest(Token token, String algorithm, long mechanism) {
93         super();
94         this.token = token;
95         this.algorithm = algorithm;
96         this.mechanism = new CK_MECHANISM(mechanism);
97         switch ((int)mechanism) {
98         case (int)CKM_MD2:
99         case (int)CKM_MD5:
100             digestLength = 16;
101             break;
102         case (int)CKM_SHA_1:
103             digestLength = 20;
104             break;
105         case (int)CKM_SHA224:
106         case (int)CKM_SHA512_224:
107             digestLength = 28;
108             break;
109         case (int)CKM_SHA256:
110         case (int)CKM_SHA512_256:
111             digestLength = 32;
112             break;
113         case (int)CKM_SHA384:
114             digestLength = 48;
115             break;
116         case (int)CKM_SHA512:
117             digestLength = 64;
118             break;
119         default:
120             throw new ProviderException("Unknown mechanism: " + mechanism);
121         }
122         buffer = new byte[BUFFER_SIZE];
123         state = S_BLANK;
124     }
125 
126     // see JCA spec
engineGetDigestLength()127     protected int engineGetDigestLength() {
128         return digestLength;
129     }
130 
fetchSession()131     private void fetchSession() {
132         token.ensureValid();
133         if (state == S_BLANK) {
134             try {
135                 session = token.getOpSession();
136                 state = S_BUFFERED;
137             } catch (PKCS11Exception e) {
138                 throw new ProviderException("No more session available", e);
139             }
140         }
141     }
142 
143     // see JCA spec
engineReset()144     protected void engineReset() {
145         token.ensureValid();
146 
147         if (session != null) {
148             if (state == S_INIT && token.explicitCancel == true
149                     && session.hasObjects() == false) {
150                 session = token.killSession(session);
151             } else {
152                 session = token.releaseSession(session);
153             }
154         }
155         state = S_BLANK;
156         bufOfs = 0;
157     }
158 
159     // see JCA spec
engineDigest()160     protected byte[] engineDigest() {
161         try {
162             byte[] digest = new byte[digestLength];
163             int n = engineDigest(digest, 0, digestLength);
164             return digest;
165         } catch (DigestException e) {
166             throw new ProviderException("internal error", e);
167         }
168     }
169 
170     // see JCA spec
engineDigest(byte[] digest, int ofs, int len)171     protected int engineDigest(byte[] digest, int ofs, int len)
172             throws DigestException {
173         if (len < digestLength) {
174             throw new DigestException("Length must be at least " +
175                     digestLength);
176         }
177 
178         fetchSession();
179         try {
180             int n;
181             if (state == S_BUFFERED) {
182                 n = token.p11.C_DigestSingle(session.id(), mechanism, buffer, 0,
183                         bufOfs, digest, ofs, len);
184                 bufOfs = 0;
185             } else {
186                 if (bufOfs != 0) {
187                     token.p11.C_DigestUpdate(session.id(), 0, buffer, 0,
188                             bufOfs);
189                     bufOfs = 0;
190                 }
191                 n = token.p11.C_DigestFinal(session.id(), digest, ofs, len);
192             }
193             if (n != digestLength) {
194                 throw new ProviderException("internal digest length error");
195             }
196             return n;
197         } catch (PKCS11Exception e) {
198             throw new ProviderException("digest() failed", e);
199         } finally {
200             engineReset();
201         }
202     }
203 
204     // see JCA spec
engineUpdate(byte in)205     protected void engineUpdate(byte in) {
206         byte[] temp = { in };
207         engineUpdate(temp, 0, 1);
208     }
209 
210     // see JCA spec
engineUpdate(byte[] in, int ofs, int len)211     protected void engineUpdate(byte[] in, int ofs, int len) {
212         if (len <= 0) {
213             return;
214         }
215 
216         fetchSession();
217         try {
218             if (state == S_BUFFERED) {
219                 token.p11.C_DigestInit(session.id(), mechanism);
220                 state = S_INIT;
221             }
222             if ((bufOfs != 0) && (bufOfs + len > buffer.length)) {
223                 // process the buffered data
224                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
225                 bufOfs = 0;
226             }
227             if (bufOfs + len > buffer.length) {
228                 // process the new data
229                 token.p11.C_DigestUpdate(session.id(), 0, in, ofs, len);
230              } else {
231                 // buffer the new data
232                 System.arraycopy(in, ofs, buffer, bufOfs, len);
233                 bufOfs += len;
234             }
235         } catch (PKCS11Exception e) {
236             engineReset();
237             throw new ProviderException("update() failed", e);
238         }
239     }
240 
241     // Called by SunJSSE via reflection during the SSL 3.0 handshake if
242     // the master secret is sensitive.
243     // Note: Change to protected after this method is moved from
244     // sun.security.util.MessageSpi2 interface to
245     // java.security.MessageDigestSpi class
engineUpdate(SecretKey key)246     public void engineUpdate(SecretKey key) throws InvalidKeyException {
247         // SunJSSE calls this method only if the key does not have a RAW
248         // encoding, i.e. if it is sensitive. Therefore, no point in calling
249         // SecretKeyFactory to try to convert it. Just verify it ourselves.
250         if (key instanceof P11Key == false) {
251             throw new InvalidKeyException("Not a P11Key: " + key);
252         }
253         P11Key p11Key = (P11Key)key;
254         if (p11Key.token != token) {
255             throw new InvalidKeyException("Not a P11Key of this provider: " +
256                     key);
257         }
258 
259         fetchSession();
260         long p11KeyID = p11Key.getKeyID();
261         try {
262             if (state == S_BUFFERED) {
263                 token.p11.C_DigestInit(session.id(), mechanism);
264                 state = S_INIT;
265             }
266 
267             if (bufOfs != 0) {
268                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
269                 bufOfs = 0;
270             }
271             token.p11.C_DigestKey(session.id(), p11KeyID);
272         } catch (PKCS11Exception e) {
273             engineReset();
274             throw new ProviderException("update(SecretKey) failed", e);
275         } finally {
276             p11Key.releaseKeyID();
277         }
278     }
279 
280     // see JCA spec
engineUpdate(ByteBuffer byteBuffer)281     protected void engineUpdate(ByteBuffer byteBuffer) {
282         int len = byteBuffer.remaining();
283         if (len <= 0) {
284             return;
285         }
286 
287         if (byteBuffer instanceof DirectBuffer == false) {
288             super.engineUpdate(byteBuffer);
289             return;
290         }
291 
292         fetchSession();
293         long addr = ((DirectBuffer)byteBuffer).address();
294         int ofs = byteBuffer.position();
295         try {
296             if (state == S_BUFFERED) {
297                 token.p11.C_DigestInit(session.id(), mechanism);
298                 state = S_INIT;
299             }
300             if (bufOfs != 0) {
301                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
302                 bufOfs = 0;
303             }
304             token.p11.C_DigestUpdate(session.id(), addr + ofs, null, 0, len);
305             byteBuffer.position(ofs + len);
306         } catch (PKCS11Exception e) {
307             engineReset();
308             throw new ProviderException("update() failed", e);
309         }
310     }
311 
clone()312     public Object clone() throws CloneNotSupportedException {
313         P11Digest copy = (P11Digest) super.clone();
314         copy.buffer = buffer.clone();
315         try {
316             if (session != null) {
317                 copy.session = copy.token.getOpSession();
318             }
319             if (state == S_INIT) {
320                 byte[] stateValues =
321                     token.p11.C_GetOperationState(session.id());
322                 token.p11.C_SetOperationState(copy.session.id(),
323                                               stateValues, 0, 0);
324             }
325         } catch (PKCS11Exception e) {
326             throw (CloneNotSupportedException)
327                 (new CloneNotSupportedException(algorithm).initCause(e));
328         }
329         return copy;
330     }
331 }
332