P11Digest.java revision 16554:ccf1ccb7adf9
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
26package sun.security.pkcs11;
27
28import java.util.*;
29import java.nio.ByteBuffer;
30
31import java.security.*;
32
33import javax.crypto.SecretKey;
34
35import sun.nio.ch.DirectBuffer;
36
37import sun.security.util.MessageDigestSpi2;
38
39import sun.security.pkcs11.wrapper.*;
40import 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 */
54final 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}
326