311 lines · 9.7 KB · Java
RawDownload
/* * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.  Oracle designates this * particular file as subject to the "Classpath" exception as provided * by Oracle in the LICENSE file that accompanied this code. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. */ package sun.security.pkcs11; import java.util.*;import java.nio.ByteBuffer; import java.security.*; import javax.crypto.SecretKey; import sun.nio.ch.DirectBuffer; import sun.security.pkcs11.wrapper.*;import static sun.security.pkcs11.wrapper.PKCS11Constants.*; /** * MessageDigest implementation class. This class currently supports * MD2, MD5, SHA-1, SHA-256, SHA-384, and SHA-512. * * Note that many digest operations are on fairly small amounts of data * (less than 100 bytes total). For example, the 2nd hashing in HMAC or * the PRF in TLS. In order to speed those up, we use some buffering to * minimize number of the Java->native transitions. * * @author  Andreas Sterbenz * @since   1.5 */final class P11Digest extends MessageDigestSpi {     /* unitialized, fields uninitialized, no session acquired */    private final static int S_BLANK    = 1;     // data in buffer, all fields valid, session acquired    // but digest not initialized    private final static int S_BUFFERED = 2;     /* session initialized for digesting */    private final static int S_INIT     = 3;     private final static int BUFFER_SIZE = 96;     // token instance    private final Token token;     // algorithm name    private final String algorithm;     // mechanism id    private final long mechanism;     // length of the digest in bytes    private final int digestLength;     // associated session, if any    private Session session;     // current state, one of S_* above    private int state;     // one byte buffer for the update(byte) method, initialized on demand    private byte[] oneByte;     // buffer to reduce number of JNI calls    private final byte[] buffer;     // offset into the buffer    private int bufOfs;     P11Digest(Token token, String algorithm, long mechanism) {        super();        this.token = token;        this.algorithm = algorithm;        this.mechanism = mechanism;        switch ((int)mechanism) {        case (int)CKM_MD2:        case (int)CKM_MD5:            digestLength = 16;            break;        case (int)CKM_SHA_1:            digestLength = 20;            break;        case (int)CKM_SHA256:            digestLength = 32;            break;        case (int)CKM_SHA384:            digestLength = 48;            break;        case (int)CKM_SHA512:            digestLength = 64;            break;        default:            throw new ProviderException("Unknown mechanism: " + mechanism);        }        buffer = new byte[BUFFER_SIZE];        state = S_BLANK;        engineReset();    }     // see JCA spec    protected int engineGetDigestLength() {        return digestLength;    }     private void cancelOperation() {        token.ensureValid();        if (session == null) {            return;        }        if ((state != S_INIT) || (token.explicitCancel == false)) {            return;        }        // need to explicitly "cancel" active op by finishing it        try {            token.p11.C_DigestFinal(session.id(), buffer, 0, buffer.length);        } catch (PKCS11Exception e) {            throw new ProviderException("cancel() failed", e);        } finally {            state = S_BUFFERED;        }    }     private void fetchSession() {        token.ensureValid();        if (state == S_BLANK) {            engineReset();        }    }     // see JCA spec    protected void engineReset() {        try {            cancelOperation();            bufOfs = 0;            if (session == null) {                session = token.getOpSession();            }            state = S_BUFFERED;        } catch (PKCS11Exception e) {            state = S_BLANK;            throw new ProviderException("reset() failed, ", e);        }    }     // see JCA spec    protected byte[] engineDigest() {        try {            byte[] digest = new byte[digestLength];            int n = engineDigest(digest, 0, digestLength);            return digest;        } catch (DigestException e) {            throw new ProviderException("internal error", e);        }    }     // see JCA spec    protected int engineDigest(byte[] digest, int ofs, int len)            throws DigestException {        if (len < digestLength) {            throw new DigestException("Length must be at least " + digestLength);        }        fetchSession();        try {            int n;            if (state == S_BUFFERED) {                n = token.p11.C_DigestSingle(session.id(),                        new CK_MECHANISM(mechanism),                        buffer, 0, bufOfs, digest, ofs, len);            } else {                if (bufOfs != 0) {                    doUpdate(buffer, 0, bufOfs);                }                n = token.p11.C_DigestFinal(session.id(), digest, ofs, len);            }            if (n != digestLength) {                throw new ProviderException("internal digest length error");            }            return n;        } catch (PKCS11Exception e) {            throw new ProviderException("digest() failed", e);        } finally {            state = S_BLANK;            bufOfs = 0;            session = token.releaseSession(session);        }    }     // see JCA spec    protected void engineUpdate(byte in) {        if (oneByte == null) {            oneByte = new byte[1];        }        oneByte[0] = in;        engineUpdate(oneByte, 0, 1);    }     // see JCA spec    protected void engineUpdate(byte[] in, int ofs, int len) {        fetchSession();        if (len <= 0) {            return;        }        if ((bufOfs != 0) && (bufOfs + len > buffer.length)) {            doUpdate(buffer, 0, bufOfs);            bufOfs = 0;        }        if (bufOfs + len > buffer.length) {            doUpdate(in, ofs, len);        } else {            System.arraycopy(in, ofs, buffer, bufOfs, len);            bufOfs += len;        }    }     // Called by SunJSSE via reflection during the SSL 3.0 handshake if    // the master secret is sensitive. We may want to consider making this    // method public in a future release.    protected void implUpdate(SecretKey key) throws InvalidKeyException {        fetchSession();        if (bufOfs != 0) {            doUpdate(buffer, 0, bufOfs);            bufOfs = 0;        }        // SunJSSE calls this method only if the key does not have a RAW        // encoding, i.e. if it is sensitive. Therefore, no point in calling        // SecretKeyFactory to try to convert it. Just verify it ourselves.        if (key instanceof P11Key == false) {            throw new InvalidKeyException("Not a P11Key: " + key);        }        P11Key p11Key = (P11Key)key;        if (p11Key.token != token) {            throw new InvalidKeyException("Not a P11Key of this provider: " + key);        }        try {            if (state == S_BUFFERED) {                token.p11.C_DigestInit(session.id(), new CK_MECHANISM(mechanism));                state = S_INIT;            }            token.p11.C_DigestKey(session.id(), p11Key.keyID);        } catch (PKCS11Exception e) {            throw new ProviderException("update(SecretKey) failed", e);        }    }     // see JCA spec    protected void engineUpdate(ByteBuffer byteBuffer) {        fetchSession();        int len = byteBuffer.remaining();        if (len <= 0) {            return;        }        if (byteBuffer instanceof DirectBuffer == false) {            super.engineUpdate(byteBuffer);            return;        }        long addr = ((DirectBuffer)byteBuffer).address();        int ofs = byteBuffer.position();        try {            if (state == S_BUFFERED) {                token.p11.C_DigestInit(session.id(), new CK_MECHANISM(mechanism));                state = S_INIT;                if (bufOfs != 0) {                    doUpdate(buffer, 0, bufOfs);                    bufOfs = 0;                }            }            token.p11.C_DigestUpdate(session.id(), addr + ofs, null, 0, len);            byteBuffer.position(ofs + len);        } catch (PKCS11Exception e) {            throw new ProviderException("update() failed", e);        }    }     private void doUpdate(byte[] in, int ofs, int len) {        if (len <= 0) {            return;        }        try {            if (state == S_BUFFERED) {                token.p11.C_DigestInit(session.id(), new CK_MECHANISM(mechanism));                state = S_INIT;            }            token.p11.C_DigestUpdate(session.id(), 0, in, ofs, len);        } catch (PKCS11Exception e) {            throw new ProviderException("update() failed", e);        }    }}