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