blob: e9e6964fd479c38e0a2d6d26e9373523079a1c99 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2003-2005 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26package sun.security.pkcs11;
27
28import java.util.*;
29import java.nio.ByteBuffer;
30
31import java.security.*;
32
33import javax.crypto.SecretKey;
34
35import sun.nio.ch.DirectBuffer;
36
37import sun.security.pkcs11.wrapper.*;
38import static sun.security.pkcs11.wrapper.PKCS11Constants.*;
39
40/**
41 * MessageDigest implementation class. This class currently supports
42 * MD2, MD5, SHA-1, SHA-256, SHA-384, and SHA-512.
43 *
44 * Note that many digest operations are on fairly small amounts of data
45 * (less than 100 bytes total). For example, the 2nd hashing in HMAC or
46 * the PRF in TLS. In order to speed those up, we use some buffering to
47 * minimize number of the Java->native transitions.
48 *
49 * @author Andreas Sterbenz
50 * @since 1.5
51 */
52final class P11Digest extends MessageDigestSpi {
53
54 /* unitialized, fields uninitialized, no session acquired */
55 private final static int S_BLANK = 1;
56
57 // data in buffer, all fields valid, session acquired
58 // but digest not initialized
59 private final static int S_BUFFERED = 2;
60
61 /* session initialized for digesting */
62 private final static int S_INIT = 3;
63
64 private final static int BUFFER_SIZE = 96;
65
66 // token instance
67 private final Token token;
68
69 // algorithm name
70 private final String algorithm;
71
72 // mechanism id
73 private final long mechanism;
74
75 // length of the digest in bytes
76 private final int digestLength;
77
78 // associated session, if any
79 private Session session;
80
81 // current state, one of S_* above
82 private int state;
83
84 // one byte buffer for the update(byte) method, initialized on demand
85 private byte[] oneByte;
86
87 // buffer to reduce number of JNI calls
88 private final byte[] buffer;
89
90 // offset into the buffer
91 private int bufOfs;
92
93 P11Digest(Token token, String algorithm, long mechanism) {
94 super();
95 this.token = token;
96 this.algorithm = algorithm;
97 this.mechanism = mechanism;
98 switch ((int)mechanism) {
99 case (int)CKM_MD2:
100 case (int)CKM_MD5:
101 digestLength = 16;
102 break;
103 case (int)CKM_SHA_1:
104 digestLength = 20;
105 break;
106 case (int)CKM_SHA256:
107 digestLength = 32;
108 break;
109 case (int)CKM_SHA384:
110 digestLength = 48;
111 break;
112 case (int)CKM_SHA512:
113 digestLength = 64;
114 break;
115 default:
116 throw new ProviderException("Unknown mechanism: " + mechanism);
117 }
118 buffer = new byte[BUFFER_SIZE];
119 state = S_BLANK;
120 engineReset();
121 }
122
123 // see JCA spec
124 protected int engineGetDigestLength() {
125 return digestLength;
126 }
127
128 private void cancelOperation() {
129 token.ensureValid();
130 if (session == null) {
131 return;
132 }
133 if ((state != S_INIT) || (token.explicitCancel == false)) {
134 return;
135 }
136 // need to explicitly "cancel" active op by finishing it
137 try {
138 token.p11.C_DigestFinal(session.id(), buffer, 0, buffer.length);
139 } catch (PKCS11Exception e) {
140 throw new ProviderException("cancel() failed", e);
141 } finally {
142 state = S_BUFFERED;
143 }
144 }
145
146 private void fetchSession() {
147 token.ensureValid();
148 if (state == S_BLANK) {
149 engineReset();
150 }
151 }
152
153 // see JCA spec
154 protected void engineReset() {
155 try {
156 cancelOperation();
157 bufOfs = 0;
158 if (session == null) {
159 session = token.getOpSession();
160 }
161 state = S_BUFFERED;
162 } catch (PKCS11Exception e) {
163 state = S_BLANK;
164 throw new ProviderException("reset() failed, ", e);
165 }
166 }
167
168 // see JCA spec
169 protected byte[] engineDigest() {
170 try {
171 byte[] digest = new byte[digestLength];
172 int n = engineDigest(digest, 0, digestLength);
173 return digest;
174 } catch (DigestException e) {
175 throw new ProviderException("internal error", e);
176 }
177 }
178
179 // see JCA spec
180 protected int engineDigest(byte[] digest, int ofs, int len)
181 throws DigestException {
182 if (len < digestLength) {
183 throw new DigestException("Length must be at least " + digestLength);
184 }
185 fetchSession();
186 try {
187 int n;
188 if (state == S_BUFFERED) {
189 n = token.p11.C_DigestSingle(session.id(),
190 new CK_MECHANISM(mechanism),
191 buffer, 0, bufOfs, digest, ofs, len);
192 } else {
193 if (bufOfs != 0) {
194 doUpdate(buffer, 0, bufOfs);
195 }
196 n = token.p11.C_DigestFinal(session.id(), digest, ofs, len);
197 }
198 if (n != digestLength) {
199 throw new ProviderException("internal digest length error");
200 }
201 return n;
202 } catch (PKCS11Exception e) {
203 throw new ProviderException("digest() failed", e);
204 } finally {
205 state = S_BLANK;
206 bufOfs = 0;
207 session = token.releaseSession(session);
208 }
209 }
210
211 // see JCA spec
212 protected void engineUpdate(byte in) {
213 if (oneByte == null) {
214 oneByte = new byte[1];
215 }
216 oneByte[0] = in;
217 engineUpdate(oneByte, 0, 1);
218 }
219
220 // see JCA spec
221 protected void engineUpdate(byte[] in, int ofs, int len) {
222 fetchSession();
223 if (len <= 0) {
224 return;
225 }
226 if ((bufOfs != 0) && (bufOfs + len > buffer.length)) {
227 doUpdate(buffer, 0, bufOfs);
228 bufOfs = 0;
229 }
230 if (bufOfs + len > buffer.length) {
231 doUpdate(in, ofs, len);
232 } else {
233 System.arraycopy(in, ofs, buffer, bufOfs, len);
234 bufOfs += len;
235 }
236 }
237
238 // Called by SunJSSE via reflection during the SSL 3.0 handshake if
239 // the master secret is sensitive. We may want to consider making this
240 // method public in a future release.
241 protected void implUpdate(SecretKey key) throws InvalidKeyException {
242 fetchSession();
243 if (bufOfs != 0) {
244 doUpdate(buffer, 0, bufOfs);
245 bufOfs = 0;
246 }
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: " + key);
256 }
257 try {
258 if (state == S_BUFFERED) {
259 token.p11.C_DigestInit(session.id(), new CK_MECHANISM(mechanism));
260 state = S_INIT;
261 }
262 token.p11.C_DigestKey(session.id(), p11Key.keyID);
263 } catch (PKCS11Exception e) {
264 throw new ProviderException("update(SecretKey) failed", e);
265 }
266 }
267
268 // see JCA spec
269 protected void engineUpdate(ByteBuffer byteBuffer) {
270 fetchSession();
271 int len = byteBuffer.remaining();
272 if (len <= 0) {
273 return;
274 }
275 if (byteBuffer instanceof DirectBuffer == false) {
276 super.engineUpdate(byteBuffer);
277 return;
278 }
279 long addr = ((DirectBuffer)byteBuffer).address();
280 int ofs = byteBuffer.position();
281 try {
282 if (state == S_BUFFERED) {
283 token.p11.C_DigestInit(session.id(), new CK_MECHANISM(mechanism));
284 state = S_INIT;
285 if (bufOfs != 0) {
286 doUpdate(buffer, 0, bufOfs);
287 bufOfs = 0;
288 }
289 }
290 token.p11.C_DigestUpdate(session.id(), addr + ofs, null, 0, len);
291 byteBuffer.position(ofs + len);
292 } catch (PKCS11Exception e) {
293 throw new ProviderException("update() failed", e);
294 }
295 }
296
297 private void doUpdate(byte[] in, int ofs, int len) {
298 if (len <= 0) {
299 return;
300 }
301 try {
302 if (state == S_BUFFERED) {
303 token.p11.C_DigestInit(session.id(), new CK_MECHANISM(mechanism));
304 state = S_INIT;
305 }
306 token.p11.C_DigestUpdate(session.id(), 0, in, ofs, len);
307 } catch (PKCS11Exception e) {
308 throw new ProviderException("update() failed", e);
309 }
310 }
311
312 protected void finalize() throws Throwable {
313 try {
314 if ((session != null) && token.isValid()) {
315 cancelOperation();
316 session = token.releaseSession(session);
317 }
318 } finally {
319 super.finalize();
320 }
321 }
322
323}