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.hasObjects() == false) {
 148                 session = token.killSession(session);
 149             } else {
 150                 session = token.releaseSession(session);
 151             }
 152         }
 153         state = S_BLANK;
 154         bufOfs = 0;
 155     }
 156 
 157     // see JCA spec
 158     protected byte[] engineDigest() {
 159         try {
 160             byte[] digest = new byte[digestLength];
 161             int n = engineDigest(digest, 0, digestLength);
 162             return digest;
 163         } catch (DigestException e) {
 164             throw new ProviderException("internal error", e);
 165         }
 166     }
 167 
 168     // see JCA spec
 169     protected int engineDigest(byte[] digest, int ofs, int len)
 170             throws DigestException {
 171         if (len < digestLength) {
 172             throw new DigestException("Length must be at least " +
 173                     digestLength);
 174         }
 175 
 176         fetchSession();
 177         try {
 178             int n;
 179             if (state == S_BUFFERED) {
 180                 n = token.p11.C_DigestSingle(session.id(), mechanism, buffer, 0,
 181                         bufOfs, digest, ofs, len);
 182                 bufOfs = 0;
 183             } else {
 184                 if (bufOfs != 0) {
 185                     token.p11.C_DigestUpdate(session.id(), 0, buffer, 0,
 186                             bufOfs);
 187                     bufOfs = 0;
 188                 }
 189                 n = token.p11.C_DigestFinal(session.id(), digest, ofs, len);
 190             }
 191             if (n != digestLength) {
 192                 throw new ProviderException("internal digest length error");
 193             }
 194             return n;
 195         } catch (PKCS11Exception e) {
 196             throw new ProviderException("digest() failed", e);
 197         } finally {
 198             engineReset();
 199         }
 200     }
 201 
 202     // see JCA spec
 203     protected void engineUpdate(byte in) {
 204         byte[] temp = { in };
 205         engineUpdate(temp, 0, 1);
 206     }
 207 
 208     // see JCA spec
 209     protected void engineUpdate(byte[] in, int ofs, int len) {
 210         if (len <= 0) {
 211             return;
 212         }
 213 
 214         fetchSession();
 215         try {
 216             if (state == S_BUFFERED) {
 217                 token.p11.C_DigestInit(session.id(), mechanism);
 218                 state = S_INIT;
 219             }
 220             if ((bufOfs != 0) && (bufOfs + len > buffer.length)) {
 221                 // process the buffered data
 222                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
 223                 bufOfs = 0;
 224             }
 225             if (bufOfs + len > buffer.length) {
 226                 // process the new data
 227                 token.p11.C_DigestUpdate(session.id(), 0, in, ofs, len);
 228              } else {
 229                 // buffer the new data
 230                 System.arraycopy(in, ofs, buffer, bufOfs, len);
 231                 bufOfs += len;
 232             }
 233         } catch (PKCS11Exception e) {
 234             engineReset();
 235             throw new ProviderException("update() failed", e);
 236         }
 237     }
 238 
 239     // Called by SunJSSE via reflection during the SSL 3.0 handshake if
 240     // the master secret is sensitive.
 241     // Note: Change to protected after this method is moved from
 242     // sun.security.util.MessageSpi2 interface to
 243     // java.security.MessageDigestSpi class
 244     public void engineUpdate(SecretKey key) throws InvalidKeyException {
 245         // SunJSSE calls this method only if the key does not have a RAW
 246         // encoding, i.e. if it is sensitive. Therefore, no point in calling
 247         // SecretKeyFactory to try to convert it. Just verify it ourselves.
 248         if (key instanceof P11Key == false) {
 249             throw new InvalidKeyException("Not a P11Key: " + key);
 250         }
 251         P11Key p11Key = (P11Key)key;
 252         if (p11Key.token != token) {
 253             throw new InvalidKeyException("Not a P11Key of this provider: " +
 254                     key);
 255         }
 256 
 257         fetchSession();
 258         try {
 259             if (state == S_BUFFERED) {
 260                 token.p11.C_DigestInit(session.id(), mechanism);
 261                 state = S_INIT;
 262             }
 263 
 264             if (bufOfs != 0) {
 265                 token.p11.C_DigestUpdate(session.id(), 0, buffer, 0, bufOfs);
 266                 bufOfs = 0;
 267             }
 268             p11Key.incNativeKeyRef();
 269             try {
 270                 token.p11.C_DigestKey(session.id(), p11Key.keyID);
 271             } finally {
 272                 p11Key.decNativeKeyRef();
 273             }
 274         } catch (PKCS11Exception e) {
 275             engineReset();
 276             throw new ProviderException("update(SecretKey) failed", e);
 277         }
 278     }
 279 
 280     // see JCA spec
 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 
 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 }