History

My first initial commit with all of mine experimental changes.

Dmitry Kabak (userdima committed 13 years ago

Commit d37b88d1 parent 3f5b46618352 files · +2598093 −-8690Browse files

Showing the changes to java/pkg/sun/security/ssl/HandshakeHash.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/ssl/HandshakeHash.java+459−0
@@ -0,0 +1,459 @@
1/*
2 * Copyright (c) 2002, 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
27package sun.security.ssl;
28
29import java.io.ByteArrayOutputStream;
30import java.security.*;
31import java.util.Arrays;
32import java.util.LinkedList;
33import java.util.List;
34import java.util.Locale;
35import java.util.Set;
36
37/**
38 * Abstraction for the SSL/TLS hash of all handshake messages that is
39 * maintained to verify the integrity of the negotiation. Internally,
40 * it consists of an MD5 and an SHA1 digest. They are used in the client
41 * and server finished messages and in certificate verify messages (if sent).
42 *
43 * This class transparently deals with cloneable and non-cloneable digests.
44 *
45 * This class now supports TLS 1.2 also. The key difference for TLS 1.2
46 * is that you cannot determine the hash algorithms for CertificateVerify
47 * at a early stage. On the other hand, it's simpler than TLS 1.1 (and earlier)
48 * that there is no messy MD5+SHA1 digests.
49 *
50 * You need to obey these conventions when using this class:
51 *
52 * 1. protocolDetermined(version) should be called when the negotiated
53 * protocol version is determined.
54 *
55 * 2. Before protocolDetermined() is called, only update(), reset(),
56 * restrictCertificateVerifyAlgs(), setFinishedAlg(), and
57 * setCertificateVerifyAlg() can be called.
58 *
59 * 3. After protocolDetermined() is called, reset() cannot be called.
60 *
61 * 4. After protocolDetermined() is called, if the version is pre-TLS 1.2,
62 * getFinishedHash() and getCertificateVerifyHash() cannot be called. Otherwise,
63 * getMD5Clone() and getSHAClone() cannot be called.
64 *
65 * 5. getMD5Clone() and getSHAClone() can only be called after
66 * protocolDetermined() is called and version is pre-TLS 1.2.
67 *
68 * 6. getFinishedHash() and getCertificateVerifyHash() can only be called after
69 * all protocolDetermined(), setCertificateVerifyAlg() and setFinishedAlg()
70 * have been called and the version is TLS 1.2. If a CertificateVerify message
71 * is to be used, call setCertificateVerifyAlg() with the hash algorithm as the
72 * argument. Otherwise, you still must call setCertificateVerifyAlg(null) before
73 * calculating any hash value.
74 *
75 * Suggestions: Call protocolDetermined(), restrictCertificateVerifyAlgs(),
76 * setFinishedAlg(), and setCertificateVerifyAlg() as early as possible.
77 *
78 * Example:
79 * <pre>
80 * HandshakeHash hh = new HandshakeHash(...)
81 * hh.protocolDetermined(ProtocolVersion.TLS12);
82 * hh.update(clientHelloBytes);
83 * hh.setFinishedAlg("SHA-256");
84 * hh.update(serverHelloBytes);
85 * ...
86 * hh.setCertificateVerifyAlg("SHA-384");
87 * hh.update(CertificateVerifyBytes);
88 * byte[] cvDigest = hh.getCertificateVerifyHash();
89 * ...
90 * hh.update(finished1);
91 * byte[] finDigest1 = hh.getFinishedHash();
92 * hh.update(finished2);
93 * byte[] finDigest2 = hh.getFinishedHash();
94 * </pre>
95 * If no CertificateVerify message is to be used, call
96 * <pre>
97 * hh.setCertificateVerifyAlg(null);
98 * </pre>
99 * This call can be made once you are certain that this message
100 * will never be used.
101 */
102final class HandshakeHash {
103
104 // Common
105
106 // -1: unknown
107 // 1: <=TLS 1.1
108 // 2: TLS 1.2
109 private int version = -1;
110 private ByteArrayOutputStream data = new ByteArrayOutputStream();
111 private final boolean isServer;
112
113 // For TLS 1.1
114 private MessageDigest md5, sha;
115 private final int clonesNeeded; // needs to be saved for later use
116
117 // For TLS 1.2
118 // cvAlgDetermined == true means setCertificateVerifyAlg() is called
119 private boolean cvAlgDetermined = false;
120 private String cvAlg;
121 private MessageDigest finMD;
122
123 /**
124 * Create a new HandshakeHash. needCertificateVerify indicates whether
125 * a hash for the certificate verify message is required. The argument
126 * algs is a set of all possible hash algorithms that might be used in
127 * TLS 1.2. If the caller is sure that TLS 1.2 won't be used or no
128 * CertificateVerify message will be used, leave it null or empty.
129 */
130 HandshakeHash(boolean isServer, boolean needCertificateVerify,
131 Set<String> algs) {
132 this.isServer = isServer;
133 clonesNeeded = needCertificateVerify ? 3 : 2;
134 }
135
136 void update(byte[] b, int offset, int len) {
137 switch (version) {
138 case 1:
139 md5.update(b, offset, len);
140 sha.update(b, offset, len);
141 break;
142 default:
143 if (finMD != null) {
144 finMD.update(b, offset, len);
145 }
146 data.write(b, offset, len);
147 break;
148 }
149 }
150
151 /**
152 * Reset the remaining digests. Note this does *not* reset the number of
153 * digest clones that can be obtained. Digests that have already been
154 * cloned and are gone remain gone.
155 */
156 void reset() {
157 if (version != -1) {
158 throw new RuntimeException(
159 "reset() can be only be called before protocolDetermined");
160 }
161 data.reset();
162 }
163
164
165 void protocolDetermined(ProtocolVersion pv) {
166
167 // Do not set again, will ignore
168 if (version != -1) return;
169
170 version = pv.compareTo(ProtocolVersion.TLS12) >= 0 ? 2 : 1;
171 switch (version) {
172 case 1:
173 // initiate md5, sha and call update on saved array
174 try {
175 md5 = CloneableDigest.getDigest("MD5", clonesNeeded);
176 sha = CloneableDigest.getDigest("SHA", clonesNeeded);
177 } catch (NoSuchAlgorithmException e) {
178 throw new RuntimeException
179 ("Algorithm MD5 or SHA not available", e);
180 }
181 byte[] bytes = data.toByteArray();
182 update(bytes, 0, bytes.length);
183 break;
184 case 2:
185 break;
186 }
187 }
188
189 /////////////////////////////////////////////////////////////
190 // Below are old methods for pre-TLS 1.1
191 /////////////////////////////////////////////////////////////
192
193 /**
194 * Return a new MD5 digest updated with all data hashed so far.
195 */
196 MessageDigest getMD5Clone() {
197 if (version != 1) {
198 throw new RuntimeException(
199 "getMD5Clone() can be only be called for TLS 1.1");
200 }
201 return cloneDigest(md5);
202 }
203
204 /**
205 * Return a new SHA digest updated with all data hashed so far.
206 */
207 MessageDigest getSHAClone() {
208 if (version != 1) {
209 throw new RuntimeException(
210 "getSHAClone() can be only be called for TLS 1.1");
211 }
212 return cloneDigest(sha);
213 }
214
215 private static MessageDigest cloneDigest(MessageDigest digest) {
216 try {
217 return (MessageDigest)digest.clone();
218 } catch (CloneNotSupportedException e) {
219 // cannot occur for digests generated via CloneableDigest
220 throw new RuntimeException("Could not clone digest", e);
221 }
222 }
223
224 /////////////////////////////////////////////////////////////
225 // Below are new methods for TLS 1.2
226 /////////////////////////////////////////////////////////////
227
228 private static String normalizeAlgName(String alg) {
229 alg = alg.toUpperCase(Locale.US);
230 if (alg.startsWith("SHA")) {
231 if (alg.length() == 3) {
232 return "SHA-1";
233 }
234 if (alg.charAt(3) != '-') {
235 return "SHA-" + alg.substring(3);
236 }
237 }
238 return alg;
239 }
240 /**
241 * Specifies the hash algorithm used in Finished. This should be called
242 * based in info in ServerHello.
243 * Can be called multiple times.
244 */
245 void setFinishedAlg(String s) {
246 if (s == null) {
247 throw new RuntimeException(
248 "setFinishedAlg's argument cannot be null");
249 }
250
251 // Can be called multiple times, but only set once
252 if (finMD != null) return;
253
254 try {
255 finMD = CloneableDigest.getDigest(normalizeAlgName(s), 2);
256 } catch (NoSuchAlgorithmException e) {
257 throw new Error(e);
258 }
259 finMD.update(data.toByteArray());
260 }
261
262 /**
263 * Restricts the possible algorithms for the CertificateVerify. Called by
264 * the server based on info in CertRequest. The argument must be a subset
265 * of the argument with the same name in the constructor. The method can be
266 * called multiple times. If the caller is sure that no CertificateVerify
267 * message will be used, leave this argument null or empty.
268 */
269 void restrictCertificateVerifyAlgs(Set<String> algs) {
270 if (version == 1) {
271 throw new RuntimeException(
272 "setCertificateVerifyAlg() cannot be called for TLS 1.1");
273 }
274 // Not used yet
275 }
276
277 /**
278 * Specifies the hash algorithm used in CertificateVerify.
279 * Can be called multiple times.
280 */
281 void setCertificateVerifyAlg(String s) {
282
283 // Can be called multiple times, but only set once
284 if (cvAlgDetermined) return;
285
286 cvAlg = s == null ? null : normalizeAlgName(s);
287 cvAlgDetermined = true;
288 }
289
290 byte[] getAllHandshakeMessages() {
291 return data.toByteArray();
292 }
293
294 /**
295 * Calculates the hash in the CertificateVerify. Must be called right
296 * after setCertificateVerifyAlg()
297 */
298 /*byte[] getCertificateVerifyHash() {
299 throw new Error("Do not call getCertificateVerifyHash()");
300 }*/
301
302 /**
303 * Calculates the hash in Finished. Must be called after setFinishedAlg().
304 * This method can be called twice, for Finished messages of the server
305 * side and client side respectively.
306 */
307 byte[] getFinishedHash() {
308 try {
309 return cloneDigest(finMD).digest();
310 } catch (Exception e) {
311 throw new Error("BAD");
312 }
313 }
314}
315
316/**
317 * A wrapper for MessageDigests that simulates cloning of non-cloneable
318 * digests. It uses the standard MessageDigest API and therefore can be used
319 * transparently in place of a regular digest.
320 *
321 * Note that we extend the MessageDigest class directly rather than
322 * MessageDigestSpi. This works because MessageDigest was originally designed
323 * this way in the JDK 1.1 days which allows us to avoid creating an internal
324 * provider.
325 *
326 * It can be "cloned" a limited number of times, which is specified at
327 * construction time. This is achieved by internally maintaining n digests
328 * in parallel. Consequently, it is only 1/n-th times as fast as the original
329 * digest.
330 *
331 * Example:
332 * MessageDigest md = CloneableDigest.getDigest("SHA", 2);
333 * md.update(data1);
334 * MessageDigest md2 = (MessageDigest)md.clone();
335 * md2.update(data2);
336 * byte[] d1 = md2.digest(); // digest of data1 || data2
337 * md.update(data3);
338 * byte[] d2 = md.digest(); // digest of data1 || data3
339 *
340 * This class is not thread safe.
341 *
342 */
343final class CloneableDigest extends MessageDigest implements Cloneable {
344
345 /**
346 * The individual MessageDigests. Initially, all elements are non-null.
347 * When clone() is called, the non-null element with the maximum index is
348 * returned and the array element set to null.
349 *
350 * All non-null element are always in the same state.
351 */
352 private final MessageDigest[] digests;
353
354 private CloneableDigest(MessageDigest digest, int n, String algorithm)
355 throws NoSuchAlgorithmException {
356 super(algorithm);
357 digests = new MessageDigest[n];
358 digests[0] = digest;
359 for (int i = 1; i < n; i++) {
360 digests[i] = JsseJce.getMessageDigest(algorithm);
361 }
362 }
363
364 /**
365 * Return a MessageDigest for the given algorithm that can be cloned the
366 * specified number of times. If the default implementation supports
367 * cloning, it is returned. Otherwise, an instance of this class is
368 * returned.
369 */
370 static MessageDigest getDigest(String algorithm, int n)
371 throws NoSuchAlgorithmException {
372 MessageDigest digest = JsseJce.getMessageDigest(algorithm);
373 try {
374 digest.clone();
375 // already cloneable, use it
376 return digest;
377 } catch (CloneNotSupportedException e) {
378 return new CloneableDigest(digest, n, algorithm);
379 }
380 }
381
382 /**
383 * Check if this object is still usable. If it has already been cloned the
384 * maximum number of times, there are no digests left and this object can no
385 * longer be used.
386 */
387 private void checkState() {
388 // XXX handshaking currently doesn't stop updating hashes...
389 // if (digests[0] == null) {
390 // throw new IllegalStateException("no digests left");
391 // }
392 }
393
394 protected int engineGetDigestLength() {
395 checkState();
396 return digests[0].getDigestLength();
397 }
398
399 protected void engineUpdate(byte b) {
400 checkState();
401 for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
402 digests[i].update(b);
403 }
404 }
405
406 protected void engineUpdate(byte[] b, int offset, int len) {
407 checkState();
408 for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
409 digests[i].update(b, offset, len);
410 }
411 }
412
413 protected byte[] engineDigest() {
414 checkState();
415 byte[] digest = digests[0].digest();
416 digestReset();
417 return digest;
418 }
419
420 protected int engineDigest(byte[] buf, int offset, int len)
421 throws DigestException {
422 checkState();
423 int n = digests[0].digest(buf, offset, len);
424 digestReset();
425 return n;
426 }
427
428 /**
429 * Reset all digests after a digest() call. digests[0] has already been
430 * implicitly reset by the digest() call and does not need to be reset
431 * again.
432 */
433 private void digestReset() {
434 for (int i = 1; (i < digests.length) && (digests[i] != null); i++) {
435 digests[i].reset();
436 }
437 }
438
439 protected void engineReset() {
440 checkState();
441 for (int i = 0; (i < digests.length) && (digests[i] != null); i++) {
442 digests[i].reset();
443 }
444 }
445
446 public Object clone() {
447 checkState();
448 for (int i = digests.length - 1; i >= 0; i--) {
449 if (digests[i] != null) {
450 MessageDigest digest = digests[i];
451 digests[i] = null;
452 return digest;
453 }
454 }
455 // cannot occur
456 throw new InternalError();
457 }
458
459}