23 * questions. |
23 * questions. |
24 */ |
24 */ |
25 |
25 |
26 package sun.security.ssl; |
26 package sun.security.ssl; |
27 |
27 |
28 import java.io.*; |
28 import java.io.ByteArrayInputStream; |
29 import java.nio.*; |
29 import java.io.IOException; |
30 import java.util.Arrays; |
30 import java.io.OutputStream; |
31 |
31 import java.nio.ByteBuffer; |
32 import javax.net.ssl.SSLException; |
|
33 import javax.net.ssl.SSLHandshakeException; |
32 import javax.net.ssl.SSLHandshakeException; |
34 import sun.security.util.HexDumpEncoder; |
33 |
35 |
34 import sun.security.ssl.KeyUpdate.KeyUpdateMessage; |
|
35 import sun.security.ssl.KeyUpdate.KeyUpdateRequest; |
36 |
36 |
37 /** |
37 /** |
38 * {@code OutputRecord} implementation for {@code SSLSocket}. |
38 * {@code OutputRecord} implementation for {@code SSLSocket}. |
39 */ |
39 */ |
40 final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord { |
40 final class SSLSocketOutputRecord extends OutputRecord implements SSLRecord { |
41 private OutputStream deliverStream = null; |
41 private OutputStream deliverStream = null; |
42 |
42 |
43 SSLSocketOutputRecord() { |
43 SSLSocketOutputRecord(HandshakeHash handshakeHash) { |
44 this.writeAuthenticator = MAC.TLS_NULL; |
44 this(handshakeHash, null); |
45 |
45 } |
|
46 |
|
47 SSLSocketOutputRecord(HandshakeHash handshakeHash, |
|
48 TransportContext tc) { |
|
49 super(handshakeHash, SSLCipher.SSLWriteCipher.nullTlsWriteCipher()); |
|
50 this.tc = tc; |
46 this.packetSize = SSLRecord.maxRecordSize; |
51 this.packetSize = SSLRecord.maxRecordSize; |
47 this.protocolVersion = ProtocolVersion.DEFAULT_TLS; |
52 this.protocolVersion = ProtocolVersion.NONE; |
48 } |
53 } |
49 |
54 |
50 @Override |
55 @Override |
51 void encodeAlert(byte level, byte description) throws IOException { |
56 void encodeAlert(byte level, byte description) throws IOException { |
52 // use the buf of ByteArrayOutputStream |
57 // use the buf of ByteArrayOutputStream |
53 int position = headerSize + writeCipher.getExplicitNonceSize(); |
58 int position = headerSize + writeCipher.getExplicitNonceSize(); |
54 count = position; |
59 count = position; |
55 |
60 |
56 write(level); |
61 write(level); |
57 write(description); |
62 write(description); |
58 |
63 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
59 if (debug != null && Debug.isOn("record")) { |
64 SSLLogger.fine("WRITE: " + protocolVersion + |
60 System.out.println(Thread.currentThread().getName() + |
65 " " + ContentType.ALERT.name + |
61 ", WRITE: " + protocolVersion + |
|
62 " " + Record.contentName(Record.ct_alert) + |
|
63 ", length = " + (count - headerSize)); |
66 ", length = " + (count - headerSize)); |
64 } |
67 } |
65 |
68 |
66 // Encrypt the fragment and wrap up a record. |
69 // Encrypt the fragment and wrap up a record. |
67 encrypt(writeAuthenticator, writeCipher, |
70 encrypt(writeCipher, ContentType.ALERT.id, headerSize); |
68 Record.ct_alert, headerSize); |
|
69 |
71 |
70 // deliver this message |
72 // deliver this message |
71 deliverStream.write(buf, 0, count); // may throw IOException |
73 deliverStream.write(buf, 0, count); // may throw IOException |
72 deliverStream.flush(); // may throw IOException |
74 deliverStream.flush(); // may throw IOException |
73 |
75 |
74 if (debug != null && Debug.isOn("packet")) { |
76 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
75 Debug.printHex( |
77 SSLLogger.fine("Raw write", |
76 "[Raw write]: length = " + count, buf, 0, count); |
78 (new ByteArrayInputStream(buf, 0, count))); |
77 } |
79 } |
78 |
80 |
79 // reset the internal buffer |
81 // reset the internal buffer |
80 count = 0; |
82 count = 0; |
81 } |
83 } |
82 |
84 |
83 @Override |
85 @Override |
84 void encodeHandshake(byte[] source, |
86 void encodeHandshake(byte[] source, |
85 int offset, int length) throws IOException { |
87 int offset, int length) throws IOException { |
86 |
|
87 if (firstMessage) { |
88 if (firstMessage) { |
88 firstMessage = false; |
89 firstMessage = false; |
89 |
90 |
90 if ((helloVersion == ProtocolVersion.SSL20Hello) && |
91 if ((helloVersion == ProtocolVersion.SSL20Hello) && |
91 (source[offset] == HandshakeMessage.ht_client_hello) && |
92 (source[offset] == SSLHandshake.CLIENT_HELLO.id) && |
92 // 5: recode header size |
93 // 5: recode header size |
93 (source[offset + 4 + 2 + 32] == 0)) { |
94 (source[offset + 4 + 2 + 32] == 0)) { |
94 // V3 session ID is empty |
95 // V3 session ID is empty |
95 // 4: handshake header size |
96 // 4: handshake header size |
96 // 2: client_version in ClientHello |
97 // 2: client_version in ClientHello |
99 ByteBuffer v2ClientHello = encodeV2ClientHello( |
100 ByteBuffer v2ClientHello = encodeV2ClientHello( |
100 source, (offset + 4), (length - 4)); |
101 source, (offset + 4), (length - 4)); |
101 |
102 |
102 byte[] record = v2ClientHello.array(); // array offset is zero |
103 byte[] record = v2ClientHello.array(); // array offset is zero |
103 int limit = v2ClientHello.limit(); |
104 int limit = v2ClientHello.limit(); |
104 handshakeHash.update(record, 2, (limit - 2)); |
105 handshakeHash.deliver(record, 2, (limit - 2)); |
105 |
106 |
106 if (debug != null && Debug.isOn("record")) { |
107 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
107 System.out.println(Thread.currentThread().getName() + |
108 SSLLogger.fine( |
108 ", WRITE: SSLv2 ClientHello message" + |
109 "WRITE: SSLv2 ClientHello message" + |
109 ", length = " + limit); |
110 ", length = " + limit); |
110 } |
111 } |
111 |
112 |
112 // deliver this message |
113 // deliver this message |
113 // |
114 // |
114 // Version 2 ClientHello message should be plaintext. |
115 // Version 2 ClientHello message should be plaintext. |
115 // |
116 // |
116 // No max fragment length negotiation. |
117 // No max fragment length negotiation. |
117 deliverStream.write(record, 0, limit); |
118 deliverStream.write(record, 0, limit); |
118 deliverStream.flush(); |
119 deliverStream.flush(); |
119 |
120 |
120 if (debug != null && Debug.isOn("packet")) { |
121 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
121 Debug.printHex( |
122 SSLLogger.fine("Raw write", |
122 "[Raw write]: length = " + count, record, 0, limit); |
123 (new ByteArrayInputStream(record, 0, limit))); |
123 } |
124 } |
124 |
125 |
125 return; |
126 return; |
126 } |
127 } |
127 } |
128 } |
128 |
129 |
129 byte handshakeType = source[0]; |
130 byte handshakeType = source[0]; |
130 if (handshakeType != HandshakeMessage.ht_hello_request) { |
131 if (handshakeHash.isHashable(handshakeType)) { |
131 handshakeHash.update(source, offset, length); |
132 handshakeHash.deliver(source, offset, length); |
132 } |
133 } |
133 |
134 |
134 int fragLimit = getFragLimit(); |
135 int fragLimit = getFragLimit(); |
135 int position = headerSize + writeCipher.getExplicitNonceSize(); |
136 int position = headerSize + writeCipher.getExplicitNonceSize(); |
136 if (count == 0) { |
137 if (count == 0) { |
151 write(source, offset, fragLen); |
152 write(source, offset, fragLen); |
152 if (remains < fragLimit) { |
153 if (remains < fragLimit) { |
153 return; |
154 return; |
154 } |
155 } |
155 |
156 |
156 if (debug != null && Debug.isOn("record")) { |
157 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
157 System.out.println(Thread.currentThread().getName() + |
158 SSLLogger.fine( |
158 ", WRITE: " + protocolVersion + |
159 "WRITE: " + protocolVersion + |
159 " " + Record.contentName(Record.ct_handshake) + |
160 " " + ContentType.HANDSHAKE.name + |
160 ", length = " + (count - headerSize)); |
161 ", length = " + (count - headerSize)); |
161 } |
162 } |
162 |
163 |
163 // Encrypt the fragment and wrap up a record. |
164 // Encrypt the fragment and wrap up a record. |
164 encrypt(writeAuthenticator, writeCipher, |
165 encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize); |
165 Record.ct_handshake, headerSize); |
|
166 |
166 |
167 // deliver this message |
167 // deliver this message |
168 deliverStream.write(buf, 0, count); // may throw IOException |
168 deliverStream.write(buf, 0, count); // may throw IOException |
169 deliverStream.flush(); // may throw IOException |
169 deliverStream.flush(); // may throw IOException |
170 |
170 |
171 if (debug != null && Debug.isOn("packet")) { |
171 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
172 Debug.printHex( |
172 SSLLogger.fine("Raw write", |
173 "[Raw write]: length = " + count, buf, 0, count); |
173 (new ByteArrayInputStream(buf, 0, count))); |
174 } |
174 } |
175 |
175 |
176 // reset the offset |
176 // reset the offset |
177 offset += fragLen; |
177 offset += fragLen; |
178 |
178 |
188 int position = headerSize + writeCipher.getExplicitNonceSize(); |
188 int position = headerSize + writeCipher.getExplicitNonceSize(); |
189 count = position; |
189 count = position; |
190 |
190 |
191 write((byte)1); // byte 1: change_cipher_spec( |
191 write((byte)1); // byte 1: change_cipher_spec( |
192 |
192 |
193 if (debug != null && Debug.isOn("record")) { |
|
194 System.out.println(Thread.currentThread().getName() + |
|
195 ", WRITE: " + protocolVersion + |
|
196 " " + Record.contentName(Record.ct_change_cipher_spec) + |
|
197 ", length = " + (count - headerSize)); |
|
198 } |
|
199 |
|
200 // Encrypt the fragment and wrap up a record. |
193 // Encrypt the fragment and wrap up a record. |
201 encrypt(writeAuthenticator, writeCipher, |
194 encrypt(writeCipher, ContentType.CHANGE_CIPHER_SPEC.id, headerSize); |
202 Record.ct_change_cipher_spec, headerSize); |
|
203 |
195 |
204 // deliver this message |
196 // deliver this message |
205 deliverStream.write(buf, 0, count); // may throw IOException |
197 deliverStream.write(buf, 0, count); // may throw IOException |
206 // deliverStream.flush(); // flush in Finished |
198 // deliverStream.flush(); // flush in Finished |
207 |
199 |
208 if (debug != null && Debug.isOn("packet")) { |
200 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
209 Debug.printHex( |
201 SSLLogger.fine("Raw write", |
210 "[Raw write]: length = " + count, buf, 0, count); |
202 (new ByteArrayInputStream(buf, 0, count))); |
211 } |
203 } |
212 |
204 |
213 // reset the internal buffer |
205 // reset the internal buffer |
214 count = 0; |
206 count = 0; |
215 } |
207 } |
219 int position = headerSize + writeCipher.getExplicitNonceSize(); |
211 int position = headerSize + writeCipher.getExplicitNonceSize(); |
220 if (count <= position) { |
212 if (count <= position) { |
221 return; |
213 return; |
222 } |
214 } |
223 |
215 |
224 if (debug != null && Debug.isOn("record")) { |
216 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
225 System.out.println(Thread.currentThread().getName() + |
217 SSLLogger.fine( |
226 ", WRITE: " + protocolVersion + |
218 "WRITE: " + protocolVersion + |
227 " " + Record.contentName(Record.ct_handshake) + |
219 " " + ContentType.HANDSHAKE.name + |
228 ", length = " + (count - headerSize)); |
220 ", length = " + (count - headerSize)); |
229 } |
221 } |
230 |
222 |
231 // Encrypt the fragment and wrap up a record. |
223 // Encrypt the fragment and wrap up a record. |
232 encrypt(writeAuthenticator, writeCipher, |
224 encrypt(writeCipher, ContentType.HANDSHAKE.id, headerSize); |
233 Record.ct_handshake, headerSize); |
|
234 |
225 |
235 // deliver this message |
226 // deliver this message |
236 deliverStream.write(buf, 0, count); // may throw IOException |
227 deliverStream.write(buf, 0, count); // may throw IOException |
237 deliverStream.flush(); // may throw IOException |
228 deliverStream.flush(); // may throw IOException |
238 |
229 |
239 if (debug != null && Debug.isOn("packet")) { |
230 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
240 Debug.printHex( |
231 SSLLogger.fine("Raw write", |
241 "[Raw write]: length = " + count, buf, 0, count); |
232 (new ByteArrayInputStream(buf, 0, count))); |
242 } |
233 } |
243 |
234 |
244 // reset the internal buffer |
235 // reset the internal buffer |
245 count = 0; // DON'T use position |
236 count = 0; // DON'T use position |
246 } |
237 } |
247 |
238 |
248 @Override |
239 @Override |
249 void deliver(byte[] source, int offset, int length) throws IOException { |
240 void deliver(byte[] source, int offset, int length) throws IOException { |
250 |
241 if (writeCipher.authenticator.seqNumOverflow()) { |
251 if (writeAuthenticator.seqNumOverflow()) { |
242 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
252 if (debug != null && Debug.isOn("ssl")) { |
243 SSLLogger.fine( |
253 System.out.println(Thread.currentThread().getName() + |
244 "sequence number extremely close to overflow " + |
254 ", sequence number extremely close to overflow " + |
|
255 "(2^64-1 packets). Closing connection."); |
245 "(2^64-1 packets). Closing connection."); |
256 } |
246 } |
257 |
247 |
258 throw new SSLHandshakeException("sequence number overflow"); |
248 throw new SSLHandshakeException("sequence number overflow"); |
259 } |
249 } |
260 |
250 |
261 boolean isFirstRecordOfThePayload = true; |
251 boolean isFirstRecordOfThePayload = true; |
262 for (int limit = (offset + length); offset < limit;) { |
252 for (int limit = (offset + length); offset < limit;) { |
263 int macLen = 0; |
|
264 if (writeAuthenticator instanceof MAC) { |
|
265 macLen = ((MAC)writeAuthenticator).MAClen(); |
|
266 } |
|
267 |
|
268 int fragLen; |
253 int fragLen; |
269 if (packetSize > 0) { |
254 if (packetSize > 0) { |
270 fragLen = Math.min(maxRecordSize, packetSize); |
255 fragLen = Math.min(maxRecordSize, packetSize); |
271 fragLen = writeCipher.calculateFragmentSize( |
256 fragLen = |
272 fragLen, macLen, headerSize); |
257 writeCipher.calculateFragmentSize(fragLen, headerSize); |
273 |
258 |
274 fragLen = Math.min(fragLen, Record.maxDataSize); |
259 fragLen = Math.min(fragLen, Record.maxDataSize); |
275 } else { |
260 } else { |
276 fragLen = Record.maxDataSize; |
261 fragLen = Record.maxDataSize; |
277 } |
262 } |
290 // use the buf of ByteArrayOutputStream |
275 // use the buf of ByteArrayOutputStream |
291 int position = headerSize + writeCipher.getExplicitNonceSize(); |
276 int position = headerSize + writeCipher.getExplicitNonceSize(); |
292 count = position; |
277 count = position; |
293 write(source, offset, fragLen); |
278 write(source, offset, fragLen); |
294 |
279 |
295 if (debug != null && Debug.isOn("record")) { |
280 if (SSLLogger.isOn && SSLLogger.isOn("record")) { |
296 System.out.println(Thread.currentThread().getName() + |
281 SSLLogger.fine( |
297 ", WRITE: " + protocolVersion + |
282 "WRITE: " + protocolVersion + |
298 " " + Record.contentName(Record.ct_application_data) + |
283 " " + ContentType.APPLICATION_DATA.name + |
299 ", length = " + (count - headerSize)); |
284 ", length = " + (count - position)); |
300 } |
285 } |
301 |
286 |
302 // Encrypt the fragment and wrap up a record. |
287 // Encrypt the fragment and wrap up a record. |
303 encrypt(writeAuthenticator, writeCipher, |
288 encrypt(writeCipher, ContentType.APPLICATION_DATA.id, headerSize); |
304 Record.ct_application_data, headerSize); |
|
305 |
289 |
306 // deliver this message |
290 // deliver this message |
307 deliverStream.write(buf, 0, count); // may throw IOException |
291 deliverStream.write(buf, 0, count); // may throw IOException |
308 deliverStream.flush(); // may throw IOException |
292 deliverStream.flush(); // may throw IOException |
309 |
293 |
310 if (debug != null && Debug.isOn("packet")) { |
294 if (SSLLogger.isOn && SSLLogger.isOn("packet")) { |
311 Debug.printHex( |
295 SSLLogger.fine("Raw write", |
312 "[Raw write]: length = " + count, buf, 0, count); |
296 (new ByteArrayInputStream(buf, 0, count))); |
313 } |
297 } |
314 |
298 |
315 // reset the internal buffer |
299 // reset the internal buffer |
316 count = 0; |
300 count = 0; |
317 |
301 |
318 if (isFirstAppOutputRecord) { |
302 if (isFirstAppOutputRecord) { |
319 isFirstAppOutputRecord = false; |
303 isFirstAppOutputRecord = false; |
320 } |
304 } |
321 |
305 |
322 offset += fragLen; |
306 offset += fragLen; |
|
307 |
|
308 // atKeyLimit() inactive when limits not checked, tc set when limits |
|
309 // are active. |
|
310 if (writeCipher.atKeyLimit()) { |
|
311 if (SSLLogger.isOn && SSLLogger.isOn("ssl")) { |
|
312 SSLLogger.fine("KeyUpdate: triggered, write side."); |
|
313 } |
|
314 |
|
315 PostHandshakeContext p = new PostHandshakeContext(tc); |
|
316 KeyUpdate.handshakeProducer.produce(p, |
|
317 new KeyUpdateMessage(p, KeyUpdateRequest.REQUESTED)); |
|
318 } |
323 } |
319 } |
324 } |
320 } |
325 |
321 |
326 @Override |
322 @Override |
327 void setDeliverStream(OutputStream outputStream) { |
323 void setDeliverStream(OutputStream outputStream) { |
356 writeCipher.isCBCMode() && !isFirstAppOutputRecord && |
352 writeCipher.isCBCMode() && !isFirstAppOutputRecord && |
357 Record.enableCBCProtection; |
353 Record.enableCBCProtection; |
358 } |
354 } |
359 |
355 |
360 private int getFragLimit() { |
356 private int getFragLimit() { |
361 int macLen = 0; |
|
362 if (writeAuthenticator instanceof MAC) { |
|
363 macLen = ((MAC)writeAuthenticator).MAClen(); |
|
364 } |
|
365 |
|
366 int fragLimit; |
357 int fragLimit; |
367 if (packetSize > 0) { |
358 if (packetSize > 0) { |
368 fragLimit = Math.min(maxRecordSize, packetSize); |
359 fragLimit = Math.min(maxRecordSize, packetSize); |
369 fragLimit = writeCipher.calculateFragmentSize( |
360 fragLimit = |
370 fragLimit, macLen, headerSize); |
361 writeCipher.calculateFragmentSize(fragLimit, headerSize); |
371 |
362 |
372 fragLimit = Math.min(fragLimit, Record.maxDataSize); |
363 fragLimit = Math.min(fragLimit, Record.maxDataSize); |
373 } else { |
364 } else { |
374 fragLimit = Record.maxDataSize; |
365 fragLimit = Record.maxDataSize; |
375 } |
366 } |