1 /*
   2  * Copyright (c) 2003, 2012, 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-224, 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 implements Cloneable {
  53 
  54     /* fields initialized, no session acquired */
  55     private final static int S_BLANK    = 1;
  56 
  57     /* data in buffer, session acquired, but digest not initialized */
  58     private final static int S_BUFFERED = 2;
  59 
  60     /* session initialized for digesting */
  61     private final static int S_INIT     = 3;
  62 
  63     private final static int BUFFER_SIZE = 96;
  64 
  65     // token instance
  66     private final Token token;
  67 
  68     // algorithm name
  69     private final String algorithm;
  70 
  71     // mechanism id object
  72     private final CK_MECHANISM mechanism;
  73 
  74     // length of the digest in bytes
  75     private final int digestLength;
  76 
  77     // associated session, if any
  78     private Session session;
  79 
  80     // current state, one of S_* above
  81     private int state;
  82 
  83     // buffer to reduce number of JNI calls
  84     private byte[] buffer;
  85 
  86     // offset into the buffer
  87     private int bufOfs;
  88 
  89     P11Digest(Token token, String algorithm, long mechanism) {
  90         super();
  91         this.token = token;
  92         this.algorithm = algorithm;
  93         this.mechanism = new CK_MECHANISM(mechanism);
  94         switch ((int)mechanism) {
  95         case (int)CKM_MD2:
  96         case (int)CKM_MD5:
  97             digestLength = 16;
  98             break;
  99         case (int)CKM_SHA_1:
 100             digestLength = 20;
 101             break;
 102         case (int)CKM_SHA224:
 103             digestLength = 28;
 104             break;
 105         case (int)CKM_SHA256:
 106             digestLength = 32;
 107             break;
 108         case (int)CKM_SHA384:
 109             digestLength = 48;
 110             break;
 111         case (int)CKM_SHA512:
 112             digestLength = 64;
 113             break;
 114         default:
 115             throw new ProviderException("Unknown mechanism: " + mechanism);
 116         }
 117         buffer = new byte[BUFFER_SIZE];
 118         state = S_BLANK;
 119     }
 120 
 121     // see JCA spec
 122     protected int engineGetDigestLength() {
 123         return digestLength;
 124     }
 125 
 126     private void fetchSession() {
 127         token.ensureValid();
 128         if (state == S_BLANK) {
 129             try {
 130                 session = token.getOpSession();
 131                 state = S_BUFFERED;
 132             } catch (PKCS11Exception e) {
 133                 throw new ProviderException("No more session available", e);
 134             }
 135         }
 136     }
 137 
 138     // see JCA spec
 139     protected void engineReset() {
 140         token.ensureValid();
 141 
 142         if (session != null) {
 143             if (state == S_INIT && token.explicitCancel == true) {
 144                 session = token.killSession(session);
 145             } else {
 146                 session = token.releaseSession(session);
 147             }
 148         }
 149         state = S_BLANK;
 150         bufOfs = 0;
 151     }
 152 
 153     // see JCA spec
 154     protected byte[] engineDigest() {
 155         try {
 156             byte[] digest = new byte[digestLength];
 157             int n = engineDigest(digest, 0, digestLength);
 158             return digest;
 159         } catch (DigestException e) {
 160             throw new ProviderException("internal error", e);
 161         }
 162     }
 163 
 164     // see JCA spec
 165     protected int engineDigest(byte[] digest, int ofs, int len)
 166             throws DigestException {
 167         if (len < digestLength) {
 168             throw new DigestException("Length must be at least " +
 169                     digestLength);
 170         }
 171 
 172         fetchSession();
 173         try {
 174             int n;
 175             if (state == S_BUFFERED) {
 176                 n = token.p11.C_DigestSingle(session.id(), mechanism, buffer, 0,
 177                         bufOfs, digest, ofs, len);
 178                 bufOfs = 0;
 179             } else {
 180                 if (bufOfs != 0) {
 181                     token.p11.C_DigestUpdate(session.id(), 0, buffer, 0,
 182                             bufOfs);
 183                     bufOfs = 0;
 184                 }
 185                 n = token.p11.C_DigestFinal(session.id(), digest, ofs, len);
 186             }
 187             if (n != digestLength) {
 188                 throw new ProviderException("internal digest length error");
 189             }
 190             return n;
 191         } catch (PKCS11Exception e) {
 192             throw new ProviderException("digest() failed", e);
 193         } finally {
 194             engineReset();
 195         }
 196     }
 197 
 198     // see JCA spec
 199     protected void engineUpdate(byte in) {
 200         byte[] temp = { in };
 201         engineUpdate(temp, 0, 1);
 202     }
 203 
 204     // see JCA spec
 205     protected void engineUpdate(byte[] in, int ofs, int len) {
 206         if (len <= 0) {
 207             return;
 208         }
 209 
 210         fetchSession();
 211         try {
 212             if (state == S_BUFFERED) {
 213                 token.p11.C_DigestInit(session.id(), mechanism);
 214                 state = S_INIT;
 215             }
 216             if ((bufOfs != 0) && (bufOfs + len > buffer.length)) {
 217                 // process the buffered data
 218                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
 219                 bufOfs = 0;
 220             }
 221             if (bufOfs + len > buffer.length) {
 222                 // process the new data
 223                 token.p11.C_DigestUpdate(session.id(), 0, in, ofs, len);
 224              } else {
 225                 // buffer the new data
 226                 System.arraycopy(in, ofs, buffer, bufOfs, len);
 227                 bufOfs += len;
 228             }
 229         } catch (PKCS11Exception e) {
 230             engineReset();
 231             throw new ProviderException("update() failed", e);
 232         }
 233     }
 234 
 235     // Called by SunJSSE via reflection during the SSL 3.0 handshake if
 236     // the master secret is sensitive. We may want to consider making this
 237     // method public in a future release.
 238     protected void implUpdate(SecretKey key) throws InvalidKeyException {
 239 
 240         // SunJSSE calls this method only if the key does not have a RAW
 241         // encoding, i.e. if it is sensitive. Therefore, no point in calling
 242         // SecretKeyFactory to try to convert it. Just verify it ourselves.
 243         if (key instanceof P11Key == false) {
 244             throw new InvalidKeyException("Not a P11Key: " + key);
 245         }
 246         P11Key p11Key = (P11Key)key;
 247         if (p11Key.token != token) {
 248             throw new InvalidKeyException("Not a P11Key of this provider: " +
 249                     key);
 250         }
 251 
 252         fetchSession();
 253         try {
 254             if (state == S_BUFFERED) {
 255                 token.p11.C_DigestInit(session.id(), mechanism);
 256                 state = S_INIT;
 257             }
 258 
 259             if (bufOfs != 0) {
 260                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
 261                 bufOfs = 0;
 262             }
 263             token.p11.C_DigestKey(session.id(), p11Key.keyID);
 264         } catch (PKCS11Exception e) {
 265             engineReset();
 266             throw new ProviderException("update(SecretKey) failed", e);
 267         }
 268     }
 269 
 270     // see JCA spec
 271     protected void engineUpdate(ByteBuffer byteBuffer) {
 272         int len = byteBuffer.remaining();
 273         if (len <= 0) {
 274             return;
 275         }
 276 
 277         if (byteBuffer instanceof DirectBuffer == false) {
 278             super.engineUpdate(byteBuffer);
 279             return;
 280         }
 281 
 282         fetchSession();
 283         long addr = ((DirectBuffer)byteBuffer).address();
 284         int ofs = byteBuffer.position();
 285         try {
 286             if (state == S_BUFFERED) {
 287                 token.p11.C_DigestInit(session.id(), mechanism);
 288                 state = S_INIT;
 289             }
 290             if (bufOfs != 0) {
 291                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
 292                 bufOfs = 0;
 293             }
 294             token.p11.C_DigestUpdate(session.id(), addr + ofs, null, 0, len);
 295             byteBuffer.position(ofs + len);
 296         } catch (PKCS11Exception e) {
 297             engineReset();
 298             throw new ProviderException("update() failed", e);
 299         }
 300     }
 301 
 302     public Object clone() throws CloneNotSupportedException {
 303         P11Digest copy = (P11Digest) super.clone();
 304         copy.buffer = buffer.clone();
 305         try {
 306             if (session != null) {
 307                 copy.session = copy.token.getOpSession();
 308             }
 309             if (state == S_INIT) {
 310                 byte[] stateValues =
 311                     token.p11.C_GetOperationState(session.id());
 312                 token.p11.C_SetOperationState(copy.session.id(),
 313                                               stateValues, 0, 0);
 314             }
 315         } catch (PKCS11Exception e) {
 316             throw (CloneNotSupportedException)
 317                 (new CloneNotSupportedException(algorithm).initCause(e));
 318         }
 319         return copy;
 320     }
 321 }