blob: 271b63d6d5f4a630486a7e1de399bb04147f61c3 [file] [log] [blame]
J. Duke319a3b92007-12-01 00:00:00 +00001/*
2 * Copyright 2002-2007 Sun Microsystems, Inc. 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. Sun designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
22 * CA 95054 USA or visit www.sun.com if you need additional information or
23 * have any questions.
24 */
25
26
27package sun.security.ssl;
28
29import java.security.*;
30
31/**
32 * Abstraction for the SSL/TLS hash of all handshake messages that is
33 * maintained to verify the integrity of the negotiation. Internally,
34 * it consists of an MD5 and an SHA1 digest. They are used in the client
35 * and server finished messages and in certificate verify messages (if sent).
36 *
37 * This class transparently deals with cloneable and non-cloneable digests.
38 *
39 */
40final class HandshakeHash {
41
42 private final MessageDigest md5, sha;
43
44 /**
45 * Create a new HandshakeHash. needCertificateVerify indicates whether
46 * a hash for the certificate verify message is required.
47 */
48 HandshakeHash(boolean needCertificateVerify) {
49 int n = needCertificateVerify ? 3 : 2;
50 try {
51 md5 = CloneableDigest.getDigest("MD5", n);
52 sha = CloneableDigest.getDigest("SHA", n);
53 } catch (NoSuchAlgorithmException e) {
54 throw new RuntimeException
55 ("Algorithm MD5 or SHA not available", e);
56
57 }
58 }
59
60 void update(byte b) {
61 md5.update(b);
62 sha.update(b);
63 }
64
65 void update(byte[] b, int offset, int len) {
66 md5.update(b, offset, len);
67 sha.update(b, offset, len);
68 }
69
70 /**
71 * Reset the remaining digests. Note this does *not* reset the numbe of
72 * digest clones that can be obtained. Digests that have already been
73 * cloned and are gone remain gone.
74 */
75 void reset() {
76 md5.reset();
77 sha.reset();
78 }
79
80 /**
81 * Return a new MD5 digest updated with all data hashed so far.
82 */
83 MessageDigest getMD5Clone() {
84 return cloneDigest(md5);
85 }
86
87 /**
88 * Return a new SHA digest updated with all data hashed so far.
89 */
90 MessageDigest getSHAClone() {
91 return cloneDigest(sha);
92 }
93
94 private static MessageDigest cloneDigest(MessageDigest digest) {
95 try {
96 return (MessageDigest)digest.clone();
97 } catch (CloneNotSupportedException e) {
98 // cannot occur for digests generated via CloneableDigest
99 throw new RuntimeException("Could not clone digest", e);
100 }
101 }
102
103}
104
105/**
106 * A wrapper for MessageDigests that simulates cloning of non-cloneable
107 * digests. It uses the standard MessageDigest API and therefore can be used
108 * transparently in place of a regular digest.
109 *
110 * Note that we extend the MessageDigest class directly rather than
111 * MessageDigestSpi. This works because MessageDigest was originally designed
112 * this way in the JDK 1.1 days which allows us to avoid creating an internal
113 * provider.
114 *
115 * It can be "cloned" a limited number of times, which is specified at
116 * construction time. This is achieved by internally maintaining n digests
117 * in parallel. Consequently, it is only 1/n-th times as fast as the original
118 * digest.
119 *
120 * Example:
121 * MessageDigest md = CloneableDigest.getDigest("SHA", 2);
122 * md.update(data1);
123 * MessageDigest md2 = (MessageDigest)md.clone();
124 * md2.update(data2);
125 * byte[] d1 = md2.digest(); // digest of data1 || data2
126 * md.update(data3);
127 * byte[] d2 = md.digest(); // digest of data1 || data3
128 *
129 * This class is not thread safe.
130 *
131 */
132final class CloneableDigest extends MessageDigest implements Cloneable {
133
134 /**
135 * The individual MessageDigests. Initially, all elements are non-null.
136 * When clone() is called, the non-null element with the maximum index is
137 * returned and the array element set to null.
138 *
139 * All non-null element are always in the same state.
140 */
141 private final MessageDigest[] digests;
142
143 private CloneableDigest(MessageDigest digest, int n, String algorithm)
144 throws NoSuchAlgorithmException {
145 super(algorithm);
146 digests = new MessageDigest[n];
147 digests[0] = digest;
148 for (int i = 1; i < n; i++) {
149 digests[i] = JsseJce.getMessageDigest(algorithm);
150 }
151 }
152
153 /**
154 * Return a MessageDigest for the given algorithm that can be cloned the
155 * specified number of times. If the default implementation supports
156 * cloning, it is returned. Otherwise, an instance of this class is
157 * returned.
158 */
159 static MessageDigest getDigest(String algorithm, int n)
160 throws NoSuchAlgorithmException {
161 MessageDigest digest = JsseJce.getMessageDigest(algorithm);
162 try {
163 digest.clone();
164 // already cloneable, use it
165 return digest;
166 } catch (CloneNotSupportedException e) {
167 return new CloneableDigest(digest, n, algorithm);
168 }
169 }
170
171 /**
172 * Check if this object is still usable. If it has already been cloned the
173 * maximum number of times, there are no digests left and this object can no
174 * longer be used.
175 */
176 private void checkState() {
177 // XXX handshaking currently doesn't stop updating hashes...
178 // if (digests[0] == null) {
179 // throw new IllegalStateException("no digests left");
180 // }
181 }
182
183 protected int engineGetDigestLength() {
184 checkState();
185 return digests[0].getDigestLength();
186 }
187
188 protected void engineUpdate(byte b) {
189 checkState();
190 for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
191 digests[i].update(b);
192 }
193 }
194
195 protected void engineUpdate(byte[] b, int offset, int len) {
196 checkState();
197 for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
198 digests[i].update(b, offset, len);
199 }
200 }
201
202 protected byte[] engineDigest() {
203 checkState();
204 byte[] digest = digests[0].digest();
205 digestReset();
206 return digest;
207 }
208
209 protected int engineDigest(byte[] buf, int offset, int len)
210 throws DigestException {
211 checkState();
212 int n = digests[0].digest(buf, offset, len);
213 digestReset();
214 return n;
215 }
216
217 /**
218 * Reset all digests after a digest() call. digests[0] has already been
219 * implicitly reset by the digest() call and does not need to be reset
220 * again.
221 */
222 private void digestReset() {
223 for (int i = 1; (i < digests.length) && (digests[i] != null); i++) {
224 digests[i].reset();
225 }
226 }
227
228 protected void engineReset() {
229 checkState();
230 for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
231 digests[i].reset();
232 }
233 }
234
235 public Object clone() {
236 checkState();
237 for (int i = digests.length - 1; i >= 0; i--) {
238 if (digests[i] != null) {
239 MessageDigest digest = digests[i];
240 digests[i] = null;
241 return digest;
242 }
243 }
244 // cannot occur
245 throw new InternalError();
246 }
247
248}