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+
27+package sun.security.ssl;
28+
29+import java.io.ByteArrayOutputStream;
30+import java.security.*;
31+import java.util.Arrays;
32+import java.util.LinkedList;
33+import java.util.List;
34+import java.util.Locale;
35+import 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+ */
102+final 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+ */
343+final 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+}