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/OutputRecord.java (1 of 18352 files). Show all

Unified · Split

java/pkg/sun/security/ssl/OutputRecord.java+450−0
@@ -0,0 +1,450 @@
1/*
2 * Copyright (c) 1996, 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.*;
30import java.nio.*;
31
32import javax.net.ssl.SSLException;
33import sun.misc.HexDumpEncoder;
34
35
36/**
37 * SSL 3.0 records, as written to a TCP stream.
38 *
39 * Each record has a message area that starts out with data supplied by the
40 * application. It may grow/shrink due to compression and will be modified
41 * in place for mac-ing and encryption.
42 *
43 * Handshake records have additional needs, notably accumulation of a set
44 * of hashes which are used to establish that handshaking was done right.
45 * Handshake records usually have several handshake messages each, and we
46 * need message-level control over what's hashed.
47 *
48 * @author David Brownell
49 */
50class OutputRecord extends ByteArrayOutputStream implements Record {
51
52 private HandshakeHash handshakeHash;
53 private int lastHashed;
54 private boolean firstMessage;
55 final private byte contentType;
56
57 // current protocol version, sent as record version
58 ProtocolVersion protocolVersion;
59
60 // version for the ClientHello message. Only relevant if this is a
61 // client handshake record. If set to ProtocolVersion.SSL20Hello,
62 // the V3 client hello is converted to V2 format.
63 private ProtocolVersion helloVersion;
64
65 /* Class and subclass dynamic debugging support */
66 static final Debug debug = Debug.getInstance("ssl");
67
68 /*
69 * Default constructor makes a record supporting the maximum
70 * SSL record size. It allocates the header bytes directly.
71 *
72 * @param type the content type for the record
73 */
74 OutputRecord(byte type, int size) {
75 super(size);
76 this.protocolVersion = ProtocolVersion.DEFAULT;
77 this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
78 firstMessage = true;
79 count = headerSize;
80 contentType = type;
81 lastHashed = count;
82 }
83
84 OutputRecord(byte type) {
85 this(type, recordSize(type));
86 }
87
88 /**
89 * Get the size of the buffer we need for records of the specified
90 * type.
91 */
92 private static int recordSize(byte type) {
93 if ((type == ct_change_cipher_spec) || (type == ct_alert)) {
94 return maxAlertRecordSize;
95 } else {
96 return maxRecordSize;
97 }
98 }
99
100 /*
101 * Updates the SSL version of this record.
102 */
103 synchronized void setVersion(ProtocolVersion protocolVersion) {
104 this.protocolVersion = protocolVersion;
105 }
106
107 /*
108 * Updates helloVersion of this record.
109 */
110 synchronized void setHelloVersion(ProtocolVersion helloVersion) {
111 this.helloVersion = helloVersion;
112 }
113
114 /*
115 * Reset the record so that it can be refilled, starting
116 * immediately after the header.
117 */
118 public synchronized void reset() {
119 super.reset();
120 count = headerSize;
121 lastHashed = count;
122 }
123
124 /*
125 * For handshaking, we need to be able to hash every byte above the
126 * record marking layer. This is where we're guaranteed to see those
127 * bytes, so this is where we can hash them.
128 */
129 void setHandshakeHash(HandshakeHash handshakeHash) {
130 assert(contentType == ct_handshake);
131 this.handshakeHash = handshakeHash;
132 }
133
134 /*
135 * We hash (the plaintext) on demand. There is one place where
136 * we want to access the hash in the middle of a record: client
137 * cert message gets hashed, and part of the same record is the
138 * client cert verify message which uses that hash. So we track
139 * how much of each record we've hashed so far.
140 */
141 void doHashes() {
142 int len = count - lastHashed;
143
144 if (len > 0) {
145 hashInternal(buf, lastHashed, len);
146 lastHashed = count;
147 }
148 }
149
150 /*
151 * Need a helper function so we can hash the V2 hello correctly
152 */
153 private void hashInternal(byte buf [], int offset, int len) {
154 if (debug != null && Debug.isOn("data")) {
155 try {
156 HexDumpEncoder hd = new HexDumpEncoder();
157
158 System.out.println("[write] MD5 and SHA1 hashes: len = "
159 + len);
160 hd.encodeBuffer(new ByteArrayInputStream(buf,
161 lastHashed, len), System.out);
162 } catch (IOException e) { }
163 }
164
165 handshakeHash.update(buf, lastHashed, len);
166 lastHashed = count;
167 }
168
169 /*
170 * Return true iff the record is empty -- to avoid doing the work
171 * of sending empty records over the network.
172 */
173 boolean isEmpty() {
174 return count == headerSize;
175 }
176
177 /*
178 * Return true if the record is of a given alert.
179 */
180 boolean isAlert(byte description) {
181 // An alert is defined with a two bytes struct,
182 // {byte level, byte description}, following after the header bytes.
183 if (count > (headerSize + 1) && contentType == ct_alert) {
184 return buf[headerSize + 1] == description;
185 }
186
187 return false;
188 }
189
190 /*
191 * Compute the MAC and append it to this record. In case we
192 * are automatically flushing a handshake stream, make sure we
193 * have hashed the message first.
194 */
195 void addMAC(MAC signer) throws IOException {
196 //
197 // when we support compression, hashing can't go here
198 // since it'll need to be done on the uncompressed data,
199 // and the MAC applies to the compressed data.
200 //
201 if (contentType == ct_handshake) {
202 doHashes();
203 }
204 if (signer.MAClen() != 0) {
205 byte[] hash = signer.compute(contentType, buf,
206 headerSize, count - headerSize);
207 write(hash);
208 }
209 }
210
211 /*
212 * Encrypt ... length may grow due to block cipher padding
213 */
214 void encrypt(CipherBox box) {
215 int len = count - headerSize;
216 count = headerSize + box.encrypt(buf, headerSize, len);
217 }
218
219
220 /*
221 * Tell how full the buffer is ... for filling it with application or
222 * handshake data.
223 */
224 final int availableDataBytes() {
225 int dataSize = count - headerSize;
226 return maxDataSize - dataSize;
227 }
228
229 /*
230 * Return the type of SSL record that's buffered here.
231 */
232 final byte contentType() {
233 return contentType;
234 }
235
236 /*
237 * Write the record out on the stream. Note that you must have (in
238 * order) compressed the data, appended the MAC, and encrypted it in
239 * order for the record to be understood by the other end. (Some of
240 * those steps will be null early in handshaking.)
241 *
242 * Note that this does no locking for the connection, it's required
243 * that synchronization be done elsewhere. Also, this does its work
244 * in a single low level write, for efficiency.
245 */
246 void write(OutputStream s) throws IOException {
247 /*
248 * Don't emit content-free records. (Even change cipher spec
249 * messages have a byte of data!)
250 */
251 if (count == headerSize) {
252 return;
253 }
254
255 int length = count - headerSize;
256 // "should" really never write more than about 14 Kb...
257 if (length < 0) {
258 throw new SSLException("output record size too small: "
259 + length);
260 }
261
262 if (debug != null
263 && (Debug.isOn("record") || Debug.isOn("handshake"))) {
264 if ((debug != null && Debug.isOn("record"))
265 || contentType() == ct_change_cipher_spec)
266 System.out.println(Thread.currentThread().getName()
267 // v3.0/v3.1 ...
268 + ", WRITE: " + protocolVersion
269 + " " + InputRecord.contentName(contentType())
270 + ", length = " + length);
271 }
272
273 /*
274 * If this is the initial ClientHello on this connection and
275 * we're not trying to resume a (V3) session then send a V2
276 * ClientHello instead so we can detect V2 servers cleanly.
277 */
278 if (firstMessage && useV2Hello()) {
279 byte[] v3Msg = new byte[length - 4];
280 System.arraycopy(buf, headerSize + 4, v3Msg, 0, v3Msg.length);
281 V3toV2ClientHello(v3Msg);
282 handshakeHash.reset();
283 lastHashed = 2;
284 doHashes();
285 if (debug != null && Debug.isOn("record")) {
286 System.out.println(
287 Thread.currentThread().getName()
288 + ", WRITE: SSLv2 client hello message"
289 + ", length = " + (count - 2)); // 2 byte SSLv2 header
290 }
291 } else {
292 /*
293 * Fill out the header, write it and the message.
294 */
295 buf[0] = contentType;
296 buf[1] = protocolVersion.major;
297 buf[2] = protocolVersion.minor;
298 buf[3] = (byte)(length >> 8);
299 buf[4] = (byte)(length);
300 }
301 firstMessage = false;
302
303 writeBuffer(s, buf, 0, count);
304 reset();
305 }
306
307 /*
308 * Actually do the write here. For SSLEngine's HS data,
309 * we'll override this method and let it take the appropriate
310 * action.
311 */
312 void writeBuffer(OutputStream s, byte [] buf, int off, int len)
313 throws IOException {
314 s.write(buf, off, len);
315 s.flush();
316
317 if (debug != null && Debug.isOn("packet")) {
318 try {
319 HexDumpEncoder hd = new HexDumpEncoder();
320 ByteBuffer bb = ByteBuffer.wrap(buf, off, len);
321
322 System.out.println("[Raw write]: length = " +
323 bb.remaining());
324 hd.encodeBuffer(bb, System.out);
325 } catch (IOException e) { }
326 }
327 }
328
329 /*
330 * Return whether the buffer contains a ClientHello message that should
331 * be converted to V2 format.
332 */
333 private boolean useV2Hello() {
334 return firstMessage
335 && (helloVersion == ProtocolVersion.SSL20Hello)
336 && (contentType == ct_handshake)
337 && (buf[5] == HandshakeMessage.ht_client_hello)
338 && (buf[headerSize + 4+2+32] == 0); // V3 session ID is empty
339 }
340
341 /*
342 * Detect "old" servers which are capable of SSL V2.0 protocol ... for
343 * example, Netscape Commerce 1.0 servers. The V3 message is in the
344 * header and the bytes passed as parameter. This routine translates
345 * the V3 message into an equivalent V2 one.
346 *
347 * Note that the translation will strip off all hello extensions as
348 * SSL V2.0 does not support hello extension.
349 */
350 private void V3toV2ClientHello(byte v3Msg []) throws SSLException {
351 int v3SessionIdLenOffset = 2 + 32; // version + nonce
352 int v3SessionIdLen = v3Msg[v3SessionIdLenOffset];
353 int v3CipherSpecLenOffset = v3SessionIdLenOffset + 1 + v3SessionIdLen;
354 int v3CipherSpecLen = ((v3Msg[v3CipherSpecLenOffset] & 0xff) << 8) +
355 (v3Msg[v3CipherSpecLenOffset + 1] & 0xff);
356 int cipherSpecs = v3CipherSpecLen / 2; // 2 bytes each in V3
357
358 /*
359 * Copy over the cipher specs. We don't care about actually translating
360 * them for use with an actual V2 server since we only talk V3.
361 * Therefore, just copy over the V3 cipher spec values with a leading
362 * 0.
363 */
364 int v3CipherSpecOffset = v3CipherSpecLenOffset + 2; // skip length
365 int v2CipherSpecLen = 0;
366 count = 11;
367 boolean containsRenegoInfoSCSV = false;
368 for (int i = 0; i < cipherSpecs; i++) {
369 byte byte1, byte2;
370
371 byte1 = v3Msg[v3CipherSpecOffset++];
372 byte2 = v3Msg[v3CipherSpecOffset++];
373 v2CipherSpecLen += V3toV2CipherSuite(byte1, byte2);
374 if (!containsRenegoInfoSCSV &&
375 byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
376 containsRenegoInfoSCSV = true;
377 }
378 }
379
380 if (!containsRenegoInfoSCSV) {
381 v2CipherSpecLen += V3toV2CipherSuite((byte)0x00, (byte)0xFF);
382 }
383
384 /*
385 * Build the first part of the V3 record header from the V2 one
386 * that's now buffered up. (Lengths are fixed up later).
387 */
388 buf[2] = HandshakeMessage.ht_client_hello;
389 buf[3] = v3Msg[0]; // major version
390 buf[4] = v3Msg[1]; // minor version
391 buf[5] = (byte)(v2CipherSpecLen >>> 8);
392 buf[6] = (byte)v2CipherSpecLen;
393 buf[7] = 0;
394 buf[8] = 0; // always no session
395 buf[9] = 0;
396 buf[10] = 32; // nonce length (always 32 in V3)
397
398 /*
399 * Copy in the nonce.
400 */
401 System.arraycopy(v3Msg, 2, buf, count, 32);
402 count += 32;
403
404 /*
405 * Set the length of the message.
406 */
407 count -= 2; // don't include length field itself
408 buf[0] = (byte)(count >>> 8);
409 buf[0] |= 0x80;
410 buf[1] = (byte)(count);
411 count += 2;
412 }
413
414 /*
415 * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
416 * This is taken from the SSL V3 specification, Appendix E.
417 */
418 private static int[] V3toV2CipherMap1 =
419 {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
420 private static int[] V3toV2CipherMap3 =
421 {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
422
423 /*
424 * See which matching pure-V2 cipher specs we need to include.
425 * We are including these not because we are actually prepared
426 * to talk V2 but because the Oracle Web Server insists on receiving
427 * at least 1 "pure V2" cipher suite that it supports and returns an
428 * illegal_parameter alert unless one is present. Rather than mindlessly
429 * claiming to implement all documented pure V2 cipher suites the code below
430 * just claims to implement the V2 cipher suite that is "equivalent"
431 * in terms of cipher algorithm & exportability with the actual V3 cipher
432 * suite that we do support.
433 */
434 private int V3toV2CipherSuite(byte byte1, byte byte2) {
435 buf[count++] = 0;
436 buf[count++] = byte1;
437 buf[count++] = byte2;
438
439 if (((byte2 & 0xff) > 0xA) ||
440 (V3toV2CipherMap1[byte2] == -1)) {
441 return 3;
442 }
443
444 buf[count++] = (byte)V3toV2CipherMap1[byte2];
445 buf[count++] = 0;
446 buf[count++] = (byte)V3toV2CipherMap3[byte2];
447
448 return 6;
449 }
450}