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