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 }