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 }