1 /* 2 * Copyright (c) 2003, 2010, 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.pkcs11.wrapper.*; 38 import 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 */ 52 final 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 }