1 /*
2 * Copyright (c) 2003, 2016, 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
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 digestLength = 28;
107 break;
108 case (int)CKM_SHA256:
109 digestLength = 32;
110 break;
111 case (int)CKM_SHA384:
112 digestLength = 48;
113 break;
114 case (int)CKM_SHA512:
115 digestLength = 64;
116 break;
117 default:
118 throw new ProviderException("Unknown mechanism: " + mechanism);
119 }
120 buffer = new byte[BUFFER_SIZE];
121 state = S_BLANK;
122 }
123
124 // see JCA spec
125 protected int engineGetDigestLength() {
126 return digestLength;
127 }
128
129 private void fetchSession() {
130 token.ensureValid();
131 if (state == S_BLANK) {
132 try {
133 session = token.getOpSession();
134 state = S_BUFFERED;
135 } catch (PKCS11Exception e) {
136 throw new ProviderException("No more session available", e);
137 }
138 }
139 }
140
141 // see JCA spec
142 protected void engineReset() {
143 token.ensureValid();
144
145 if (session != null) {
146 if (state == S_INIT && token.explicitCancel == true) {
147 session = token.killSession(session);
148 } else {
149 session = token.releaseSession(session);
150 }
151 }
152 state = S_BLANK;
153 bufOfs = 0;
154 }
155
156 // see JCA spec
157 protected byte[] engineDigest() {
158 try {
159 byte[] digest = new byte[digestLength];
160 int n = engineDigest(digest, 0, digestLength);
161 return digest;
162 } catch (DigestException e) {
163 throw new ProviderException("internal error", e);
164 }
165 }
166
167 // see JCA spec
168 protected int engineDigest(byte[] digest, int ofs, int len)
169 throws DigestException {
170 if (len < digestLength) {
171 throw new DigestException("Length must be at least " +
172 digestLength);
173 }
174
175 fetchSession();
176 try {
177 int n;
178 if (state == S_BUFFERED) {
179 n = token.p11.C_DigestSingle(session.id(), mechanism, buffer, 0,
180 bufOfs, digest, ofs, len);
181 bufOfs = 0;
182 } else {
183 if (bufOfs != 0) {
184 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0,
185 bufOfs);
186 bufOfs = 0;
187 }
188 n = token.p11.C_DigestFinal(session.id(), digest, ofs, len);
189 }
190 if (n != digestLength) {
191 throw new ProviderException("internal digest length error");
192 }
193 return n;
194 } catch (PKCS11Exception e) {
195 throw new ProviderException("digest() failed", e);
196 } finally {
197 engineReset();
198 }
199 }
200
201 // see JCA spec
202 protected void engineUpdate(byte in) {
203 byte[] temp = { in };
204 engineUpdate(temp, 0, 1);
205 }
206
207 // see JCA spec
208 protected void engineUpdate(byte[] in, int ofs, int len) {
209 if (len <= 0) {
210 return;
211 }
212
213 fetchSession();
214 try {
215 if (state == S_BUFFERED) {
216 token.p11.C_DigestInit(session.id(), mechanism);
217 state = S_INIT;
218 }
219 if ((bufOfs != 0) && (bufOfs + len > buffer.length)) {
220 // process the buffered data
221 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
222 bufOfs = 0;
223 }
224 if (bufOfs + len > buffer.length) {
225 // process the new data
226 token.p11.C_DigestUpdate(session.id(), 0, in, ofs, len);
227 } else {
228 // buffer the new data
229 System.arraycopy(in, ofs, buffer, bufOfs, len);
230 bufOfs += len;
231 }
232 } catch (PKCS11Exception e) {
233 engineReset();
234 throw new ProviderException("update() failed", e);
235 }
236 }
237
238 // Called by SunJSSE via reflection during the SSL 3.0 handshake if
239 // the master secret is sensitive.
240 // Note: Change to protected after this method is moved from
241 // sun.security.util.MessageSpi2 interface to
242 // java.security.MessageDigestSpi class
243 public void engineUpdate(SecretKey key) throws InvalidKeyException {
244 // SunJSSE calls this method only if the key does not have a RAW
245 // encoding, i.e. if it is sensitive. Therefore, no point in calling
246 // SecretKeyFactory to try to convert it. Just verify it ourselves.
247 if (key instanceof P11Key == false) {
248 throw new InvalidKeyException("Not a P11Key: " + key);
249 }
250 P11Key p11Key = (P11Key)key;
251 if (p11Key.token != token) {
252 throw new InvalidKeyException("Not a P11Key of this provider: " +
253 key);
254 }
255
256 fetchSession();
257 try {
258 if (state == S_BUFFERED) {
259 token.p11.C_DigestInit(session.id(), mechanism);
260 state = S_INIT;
261 }
262
263 if (bufOfs != 0) {
264 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
265 bufOfs = 0;
266 }
267 token.p11.C_DigestKey(session.id(), p11Key.keyID);
268 } catch (PKCS11Exception e) {
269 engineReset();
270 throw new ProviderException("update(SecretKey) failed", e);
271 }
272 }
273
274 // see JCA spec
275 protected void engineUpdate(ByteBuffer byteBuffer) {
276 int len = byteBuffer.remaining();
277 if (len <= 0) {
278 return;
279 }
280
281 if (byteBuffer instanceof DirectBuffer == false) {
282 super.engineUpdate(byteBuffer);
283 return;
284 }
285
286 fetchSession();
287 long addr = ((DirectBuffer)byteBuffer).address();
288 int ofs = byteBuffer.position();
289 try {
290 if (state == S_BUFFERED) {
291 token.p11.C_DigestInit(session.id(), mechanism);
292 state = S_INIT;
293 }
294 if (bufOfs != 0) {
295 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
296 bufOfs = 0;
297 }
298 token.p11.C_DigestUpdate(session.id(), addr + ofs, null, 0, len);
299 byteBuffer.position(ofs + len);
300 } catch (PKCS11Exception e) {
301 engineReset();
302 throw new ProviderException("update() failed", e);
303 }
304 }
305
306 public Object clone() throws CloneNotSupportedException {
307 P11Digest copy = (P11Digest) super.clone();
308 copy.buffer = buffer.clone();
309 try {
310 if (session != null) {
311 copy.session = copy.token.getOpSession();
312 }
313 if (state == S_INIT) {
314 byte[] stateValues =
315 token.p11.C_GetOperationState(session.id());
316 token.p11.C_SetOperationState(copy.session.id(),
317 stateValues, 0, 0);
318 }
319 } catch (PKCS11Exception e) {
320 throw (CloneNotSupportedException)
321 (new CloneNotSupportedException(algorithm).initCause(e));
322 }
323 return copy;
324 }
325 }
--- EOF ---