author | chegar |
Mon, 14 Dec 2015 19:24:33 +0000 | |
changeset 34687 | d302ed125dc9 |
parent 32649 | 2ee9017c7597 |
child 41820 | 3d8c88d00c9f |
permissions | -rw-r--r-- |
30904 | 1 |
/* |
2 |
* Copyright (c) 2015, 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 |
package sun.security.ssl; |
|
27 |
||
28 |
import java.io.*; |
|
29 |
import java.nio.*; |
|
30 |
import java.util.*; |
|
31 |
import javax.crypto.BadPaddingException; |
|
32 |
||
33 |
import javax.net.ssl.*; |
|
34 |
||
34687
d302ed125dc9
8144995: Move sun.misc.HexDumpEncoder to sun.security.util
chegar
parents:
32649
diff
changeset
|
35 |
import sun.security.util.HexDumpEncoder; |
30904 | 36 |
import static sun.security.ssl.HandshakeMessage.*; |
37 |
||
38 |
/** |
|
39 |
* DTLS {@code InputRecord} implementation for {@code SSLEngine}. |
|
40 |
*/ |
|
41 |
final class DTLSInputRecord extends InputRecord implements DTLSRecord { |
|
42 |
||
43 |
private DTLSReassembler reassembler = null; |
|
44 |
||
45 |
// Cache the session identifier for the detection of session-resuming |
|
46 |
// handshake. |
|
47 |
byte[] prevSessionID = new byte[0]; |
|
48 |
||
49 |
int readEpoch; |
|
50 |
||
51 |
int prevReadEpoch; |
|
52 |
Authenticator prevReadAuthenticator; |
|
53 |
CipherBox prevReadCipher; |
|
54 |
||
55 |
DTLSInputRecord() { |
|
56 |
this.readEpoch = 0; |
|
57 |
this.readAuthenticator = new MAC(true); |
|
58 |
||
59 |
this.prevReadEpoch = 0; |
|
60 |
this.prevReadCipher = CipherBox.NULL; |
|
61 |
this.prevReadAuthenticator = new MAC(true); |
|
62 |
} |
|
63 |
||
64 |
@Override |
|
65 |
void changeReadCiphers(Authenticator readAuthenticator, |
|
66 |
CipherBox readCipher) { |
|
67 |
||
68 |
prevReadCipher.dispose(); |
|
69 |
||
70 |
this.prevReadAuthenticator = this.readAuthenticator; |
|
71 |
this.prevReadCipher = this.readCipher; |
|
72 |
this.prevReadEpoch = this.readEpoch; |
|
73 |
||
74 |
this.readAuthenticator = readAuthenticator; |
|
75 |
this.readCipher = readCipher; |
|
76 |
this.readEpoch++; |
|
77 |
} |
|
78 |
||
79 |
@Override |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
30904
diff
changeset
|
80 |
public synchronized void close() throws IOException { |
30904 | 81 |
if (!isClosed) { |
82 |
prevReadCipher.dispose(); |
|
83 |
super.close(); |
|
84 |
} |
|
85 |
} |
|
86 |
||
87 |
@Override |
|
88 |
boolean isEmpty() { |
|
89 |
return ((reassembler == null) || reassembler.isEmpty()); |
|
90 |
} |
|
91 |
||
92 |
@Override |
|
93 |
int estimateFragmentSize(int packetSize) { |
|
94 |
int macLen = 0; |
|
95 |
if (readAuthenticator instanceof MAC) { |
|
96 |
macLen = ((MAC)readAuthenticator).MAClen(); |
|
97 |
} |
|
98 |
||
99 |
if (packetSize > 0) { |
|
100 |
return readCipher.estimateFragmentSize( |
|
101 |
packetSize, macLen, headerSize); |
|
102 |
} else { |
|
103 |
return Record.maxDataSize; |
|
104 |
} |
|
105 |
} |
|
106 |
||
107 |
@Override |
|
108 |
void expectingFinishFlight() { |
|
109 |
if (reassembler != null) { |
|
110 |
reassembler.expectingFinishFlight(); |
|
111 |
} |
|
112 |
} |
|
113 |
||
114 |
@Override |
|
115 |
Plaintext acquirePlaintext() { |
|
116 |
if (reassembler != null) { |
|
117 |
Plaintext plaintext = reassembler.acquirePlaintext(); |
|
118 |
if (reassembler.finished()) { |
|
119 |
// discard all buffered unused message. |
|
120 |
reassembler = null; |
|
121 |
} |
|
122 |
||
123 |
return plaintext; |
|
124 |
} |
|
125 |
||
126 |
return null; |
|
127 |
} |
|
128 |
||
129 |
@Override |
|
130 |
Plaintext decode(ByteBuffer packet) { |
|
131 |
||
132 |
if (isClosed) { |
|
133 |
return null; |
|
134 |
} |
|
135 |
||
136 |
if (debug != null && Debug.isOn("packet")) { |
|
137 |
Debug.printHex( |
|
138 |
"[Raw read]: length = " + packet.remaining(), packet); |
|
139 |
} |
|
140 |
||
141 |
// The caller should have validated the record. |
|
142 |
int srcPos = packet.position(); |
|
143 |
int srcLim = packet.limit(); |
|
144 |
||
145 |
byte contentType = packet.get(); // pos: 0 |
|
146 |
byte majorVersion = packet.get(); // pos: 1 |
|
147 |
byte minorVersion = packet.get(); // pos: 2 |
|
148 |
byte[] recordEnS = new byte[8]; // epoch + seqence |
|
149 |
packet.get(recordEnS); |
|
150 |
int recordEpoch = ((recordEnS[0] & 0xFF) << 8) | |
|
151 |
(recordEnS[1] & 0xFF); // pos: 3, 4 |
|
152 |
long recordSeq = Authenticator.toLong(recordEnS); |
|
153 |
int contentLen = ((packet.get() & 0xFF) << 8) | |
|
154 |
(packet.get() & 0xFF); // pos: 11, 12 |
|
155 |
||
156 |
if (debug != null && Debug.isOn("record")) { |
|
157 |
System.out.println(Thread.currentThread().getName() + |
|
158 |
", READ: " + |
|
159 |
ProtocolVersion.valueOf(majorVersion, minorVersion) + |
|
160 |
" " + Record.contentName(contentType) + ", length = " + |
|
161 |
contentLen); |
|
162 |
} |
|
163 |
||
164 |
int recLim = srcPos + DTLSRecord.headerSize + contentLen; |
|
165 |
if (this.readEpoch > recordEpoch) { |
|
166 |
// Discard old records delivered before this epoch. |
|
167 |
||
168 |
// Reset the position of the packet buffer. |
|
169 |
packet.position(recLim); |
|
170 |
return null; |
|
171 |
} |
|
172 |
||
173 |
if (this.readEpoch < recordEpoch) { |
|
174 |
if (contentType != Record.ct_handshake) { |
|
175 |
// just discard it if not a handshake message |
|
176 |
packet.position(recLim); |
|
177 |
return null; |
|
178 |
} |
|
179 |
||
180 |
// Not ready to decrypt this record, may be encrypted Finished |
|
181 |
// message, need to buffer it. |
|
182 |
if (reassembler == null) { |
|
183 |
reassembler = new DTLSReassembler(); |
|
184 |
} |
|
185 |
||
186 |
byte[] fragment = new byte[contentLen]; |
|
187 |
packet.get(fragment); // copy the fragment |
|
188 |
RecordFragment buffered = new RecordFragment(fragment, contentType, |
|
189 |
majorVersion, minorVersion, |
|
190 |
recordEnS, recordEpoch, recordSeq, true); |
|
191 |
||
192 |
reassembler.queueUpFragment(buffered); |
|
193 |
||
194 |
// consume the full record in the packet buffer. |
|
195 |
packet.position(recLim); |
|
196 |
||
197 |
Plaintext plaintext = reassembler.acquirePlaintext(); |
|
198 |
if (reassembler.finished()) { |
|
199 |
// discard all buffered unused message. |
|
200 |
reassembler = null; |
|
201 |
} |
|
202 |
||
203 |
return plaintext; |
|
204 |
} |
|
205 |
||
206 |
if (this.readEpoch == recordEpoch) { |
|
207 |
// decrypt the fragment |
|
208 |
packet.limit(recLim); |
|
209 |
packet.position(srcPos + DTLSRecord.headerSize); |
|
210 |
||
211 |
ByteBuffer plaintextFragment; |
|
212 |
try { |
|
213 |
plaintextFragment = decrypt(readAuthenticator, |
|
214 |
readCipher, contentType, packet, recordEnS); |
|
215 |
} catch (BadPaddingException bpe) { |
|
216 |
if (debug != null && Debug.isOn("ssl")) { |
|
217 |
System.out.println(Thread.currentThread().getName() + |
|
218 |
" discard invalid record: " + bpe); |
|
219 |
} |
|
220 |
||
221 |
// invalid, discard this record [section 4.1.2.7, RFC 6347] |
|
222 |
return null; |
|
223 |
} finally { |
|
224 |
// comsume a complete record |
|
225 |
packet.limit(srcLim); |
|
226 |
packet.position(recLim); |
|
227 |
} |
|
228 |
||
229 |
if (contentType != Record.ct_change_cipher_spec && |
|
230 |
contentType != Record.ct_handshake) { // app data or alert |
|
231 |
// no retransmission |
|
232 |
return new Plaintext(contentType, majorVersion, minorVersion, |
|
233 |
recordEpoch, recordSeq, plaintextFragment); |
|
234 |
} |
|
235 |
||
236 |
if (contentType == Record.ct_change_cipher_spec) { |
|
237 |
if (reassembler == null) { |
|
238 |
// handshake has not started, should be an |
|
239 |
// old handshake message, discard it. |
|
240 |
return null; |
|
241 |
} |
|
242 |
||
243 |
reassembler.queueUpFragment( |
|
244 |
new RecordFragment(plaintextFragment, contentType, |
|
245 |
majorVersion, minorVersion, |
|
246 |
recordEnS, recordEpoch, recordSeq, false)); |
|
247 |
} else { // handshake record |
|
248 |
// One record may contain 1+ more handshake messages. |
|
249 |
while (plaintextFragment.remaining() > 0) { |
|
250 |
||
251 |
HandshakeFragment hsFrag = parseHandshakeMessage( |
|
252 |
contentType, majorVersion, minorVersion, |
|
253 |
recordEnS, recordEpoch, recordSeq, plaintextFragment); |
|
254 |
||
255 |
if (hsFrag == null) { |
|
256 |
// invalid, discard this record |
|
257 |
return null; |
|
258 |
} |
|
259 |
||
260 |
if ((reassembler == null) && |
|
261 |
isKickstart(hsFrag.handshakeType)) { |
|
262 |
reassembler = new DTLSReassembler(); |
|
263 |
} |
|
264 |
||
265 |
if (reassembler != null) { |
|
266 |
reassembler.queueUpHandshake(hsFrag); |
|
267 |
} // else, just ignore the message. |
|
268 |
} |
|
269 |
} |
|
270 |
||
271 |
// Completed the read of the full record. Acquire the reassembled |
|
272 |
// messages. |
|
273 |
if (reassembler != null) { |
|
274 |
Plaintext plaintext = reassembler.acquirePlaintext(); |
|
275 |
if (reassembler.finished()) { |
|
276 |
// discard all buffered unused message. |
|
277 |
reassembler = null; |
|
278 |
} |
|
279 |
||
280 |
return plaintext; |
|
281 |
} |
|
282 |
} |
|
283 |
||
284 |
return null; // make the complier happy |
|
285 |
} |
|
286 |
||
287 |
@Override |
|
288 |
int bytesInCompletePacket(ByteBuffer packet) throws SSLException { |
|
289 |
||
290 |
// DTLS length field is in bytes 11/12 |
|
291 |
if (packet.remaining() < headerSize) { |
|
292 |
return -1; |
|
293 |
} |
|
294 |
||
295 |
// Last sanity check that it's not a wild record |
|
296 |
int pos = packet.position(); |
|
297 |
||
298 |
// Check the content type of the record. |
|
299 |
byte contentType = packet.get(pos); |
|
300 |
if (!Record.isValidContentType(contentType)) { |
|
301 |
throw new SSLException( |
|
302 |
"Unrecognized SSL message, plaintext connection?"); |
|
303 |
} |
|
304 |
||
305 |
// Check the protocol version of the record. |
|
306 |
ProtocolVersion recordVersion = |
|
307 |
ProtocolVersion.valueOf(packet.get(pos + 1), packet.get(pos + 2)); |
|
308 |
checkRecordVersion(recordVersion, false); |
|
309 |
||
310 |
// Get the fragment length of the record. |
|
311 |
int fragLen = ((packet.get(pos + 11) & 0xFF) << 8) + |
|
312 |
(packet.get(pos + 12) & 0xFF) + headerSize; |
|
313 |
if (fragLen > Record.maxFragmentSize) { |
|
314 |
throw new SSLException( |
|
315 |
"Record overflow, fragment length (" + fragLen + |
|
316 |
") MUST not exceed " + Record.maxFragmentSize); |
|
317 |
} |
|
318 |
||
319 |
return fragLen; |
|
320 |
} |
|
321 |
||
322 |
@Override |
|
323 |
void checkRecordVersion(ProtocolVersion recordVersion, |
|
324 |
boolean allowSSL20Hello) throws SSLException { |
|
325 |
||
326 |
if (!recordVersion.maybeDTLSProtocol()) { |
|
327 |
throw new SSLException( |
|
328 |
"Unrecognized record version " + recordVersion + |
|
329 |
" , plaintext connection?"); |
|
330 |
} |
|
331 |
} |
|
332 |
||
333 |
private static boolean isKickstart(byte handshakeType) { |
|
334 |
return (handshakeType == HandshakeMessage.ht_client_hello) || |
|
335 |
(handshakeType == HandshakeMessage.ht_hello_request) || |
|
336 |
(handshakeType == HandshakeMessage.ht_hello_verify_request); |
|
337 |
} |
|
338 |
||
339 |
private static HandshakeFragment parseHandshakeMessage( |
|
340 |
byte contentType, byte majorVersion, byte minorVersion, |
|
341 |
byte[] recordEnS, int recordEpoch, long recordSeq, |
|
342 |
ByteBuffer plaintextFragment) { |
|
343 |
||
344 |
int remaining = plaintextFragment.remaining(); |
|
345 |
if (remaining < handshakeHeaderSize) { |
|
346 |
if (debug != null && Debug.isOn("ssl")) { |
|
347 |
System.out.println( |
|
348 |
Thread.currentThread().getName() + |
|
349 |
" discard invalid record: " + |
|
350 |
"too small record to hold a handshake fragment"); |
|
351 |
} |
|
352 |
||
353 |
// invalid, discard this record [section 4.1.2.7, RFC 6347] |
|
354 |
return null; |
|
355 |
} |
|
356 |
||
357 |
byte handshakeType = plaintextFragment.get(); // pos: 0 |
|
358 |
int messageLength = |
|
359 |
((plaintextFragment.get() & 0xFF) << 16) | |
|
360 |
((plaintextFragment.get() & 0xFF) << 8) | |
|
361 |
(plaintextFragment.get() & 0xFF); // pos: 1-3 |
|
362 |
int messageSeq = |
|
363 |
((plaintextFragment.get() & 0xFF) << 8) | |
|
364 |
(plaintextFragment.get() & 0xFF); // pos: 4/5 |
|
365 |
int fragmentOffset = |
|
366 |
((plaintextFragment.get() & 0xFF) << 16) | |
|
367 |
((plaintextFragment.get() & 0xFF) << 8) | |
|
368 |
(plaintextFragment.get() & 0xFF); // pos: 6-8 |
|
369 |
int fragmentLength = |
|
370 |
((plaintextFragment.get() & 0xFF) << 16) | |
|
371 |
((plaintextFragment.get() & 0xFF) << 8) | |
|
372 |
(plaintextFragment.get() & 0xFF); // pos: 9-11 |
|
373 |
if ((remaining - handshakeHeaderSize) < fragmentLength) { |
|
374 |
if (debug != null && Debug.isOn("ssl")) { |
|
375 |
System.out.println( |
|
376 |
Thread.currentThread().getName() + |
|
377 |
" discard invalid record: " + |
|
378 |
"not a complete handshake fragment in the record"); |
|
379 |
} |
|
380 |
||
381 |
// invalid, discard this record [section 4.1.2.7, RFC 6347] |
|
382 |
return null; |
|
383 |
} |
|
384 |
||
385 |
byte[] fragment = new byte[fragmentLength]; |
|
386 |
plaintextFragment.get(fragment); |
|
387 |
||
388 |
return new HandshakeFragment(fragment, contentType, |
|
389 |
majorVersion, minorVersion, |
|
390 |
recordEnS, recordEpoch, recordSeq, |
|
391 |
handshakeType, messageLength, |
|
392 |
messageSeq, fragmentOffset, fragmentLength); |
|
393 |
} |
|
394 |
||
395 |
// buffered record fragment |
|
396 |
private static class RecordFragment implements Comparable<RecordFragment> { |
|
397 |
boolean isCiphertext; |
|
398 |
||
399 |
byte contentType; |
|
400 |
byte majorVersion; |
|
401 |
byte minorVersion; |
|
402 |
int recordEpoch; |
|
403 |
long recordSeq; |
|
404 |
byte[] recordEnS; |
|
405 |
byte[] fragment; |
|
406 |
||
407 |
RecordFragment(ByteBuffer fragBuf, byte contentType, |
|
408 |
byte majorVersion, byte minorVersion, byte[] recordEnS, |
|
409 |
int recordEpoch, long recordSeq, boolean isCiphertext) { |
|
410 |
this((byte[])null, contentType, majorVersion, minorVersion, |
|
411 |
recordEnS, recordEpoch, recordSeq, isCiphertext); |
|
412 |
||
413 |
this.fragment = new byte[fragBuf.remaining()]; |
|
414 |
fragBuf.get(this.fragment); |
|
415 |
} |
|
416 |
||
417 |
RecordFragment(byte[] fragment, byte contentType, |
|
418 |
byte majorVersion, byte minorVersion, byte[] recordEnS, |
|
419 |
int recordEpoch, long recordSeq, boolean isCiphertext) { |
|
420 |
this.isCiphertext = isCiphertext; |
|
421 |
||
422 |
this.contentType = contentType; |
|
423 |
this.majorVersion = majorVersion; |
|
424 |
this.minorVersion = minorVersion; |
|
425 |
this.recordEpoch = recordEpoch; |
|
426 |
this.recordSeq = recordSeq; |
|
427 |
this.recordEnS = recordEnS; |
|
428 |
this.fragment = fragment; // The caller should have cloned |
|
429 |
// the buffer if necessary. |
|
430 |
} |
|
431 |
||
432 |
@Override |
|
433 |
public int compareTo(RecordFragment o) { |
|
434 |
return Long.compareUnsigned(this.recordSeq, o.recordSeq); |
|
435 |
} |
|
436 |
} |
|
437 |
||
438 |
// buffered handshake message |
|
439 |
private static final class HandshakeFragment extends RecordFragment { |
|
440 |
||
441 |
byte handshakeType; // handshake msg_type |
|
442 |
int messageSeq; // message_seq |
|
443 |
int messageLength; // Handshake body length |
|
444 |
int fragmentOffset; // fragment_offset |
|
445 |
int fragmentLength; // fragment_length |
|
446 |
||
447 |
HandshakeFragment(byte[] fragment, byte contentType, |
|
448 |
byte majorVersion, byte minorVersion, byte[] recordEnS, |
|
449 |
int recordEpoch, long recordSeq, |
|
450 |
byte handshakeType, int messageLength, |
|
451 |
int messageSeq, int fragmentOffset, int fragmentLength) { |
|
452 |
||
453 |
super(fragment, contentType, majorVersion, minorVersion, |
|
454 |
recordEnS, recordEpoch , recordSeq, false); |
|
455 |
||
456 |
this.handshakeType = handshakeType; |
|
457 |
this.messageSeq = messageSeq; |
|
458 |
this.messageLength = messageLength; |
|
459 |
this.fragmentOffset = fragmentOffset; |
|
460 |
this.fragmentLength = fragmentLength; |
|
461 |
} |
|
462 |
||
463 |
@Override |
|
464 |
public int compareTo(RecordFragment o) { |
|
465 |
if (o instanceof HandshakeFragment) { |
|
466 |
HandshakeFragment other = (HandshakeFragment)o; |
|
467 |
if (this.messageSeq != other.messageSeq) { |
|
468 |
// keep the insertion order for the same message |
|
469 |
return this.messageSeq - other.messageSeq; |
|
470 |
} |
|
471 |
} |
|
472 |
||
473 |
return Long.compareUnsigned(this.recordSeq, o.recordSeq); |
|
474 |
} |
|
475 |
} |
|
476 |
||
477 |
private static final class HoleDescriptor { |
|
478 |
int offset; // fragment_offset |
|
479 |
int limit; // fragment_offset + fragment_length |
|
480 |
||
481 |
HoleDescriptor(int offset, int limit) { |
|
482 |
this.offset = offset; |
|
483 |
this.limit = limit; |
|
484 |
} |
|
485 |
} |
|
486 |
||
487 |
final class DTLSReassembler { |
|
488 |
TreeSet<RecordFragment> bufferedFragments = new TreeSet<>(); |
|
489 |
||
490 |
HashMap<Byte, List<HoleDescriptor>> holesMap = new HashMap<>(5); |
|
491 |
||
492 |
// Epoch, sequence number and handshake message sequence of the |
|
493 |
// beginning message of a flight. |
|
494 |
byte flightType = (byte)0xFF; |
|
495 |
||
496 |
int flightTopEpoch = 0; |
|
497 |
long flightTopRecordSeq = -1; |
|
498 |
int flightTopMessageSeq = 0; |
|
499 |
||
500 |
// Epoch, sequence number and handshake message sequence of the |
|
501 |
// next message acquisition of a flight. |
|
502 |
int nextRecordEpoch = 0; // next record epoch |
|
503 |
long nextRecordSeq = 0; // next record sequence number |
|
504 |
int nextMessageSeq = 0; // next handshake message number |
|
505 |
||
506 |
// Expect ChangeCipherSpec and Finished messages for the final flight. |
|
507 |
boolean expectCCSFlight = false; |
|
508 |
||
509 |
// Ready to process this flight if received all messages of the flight. |
|
510 |
boolean flightIsReady = false; |
|
511 |
boolean needToCheckFlight = false; |
|
512 |
||
513 |
// Is it a session-resuming abbreviated handshake.? |
|
514 |
boolean isAbbreviatedHandshake = false; |
|
515 |
||
516 |
// The handshke fragment with the biggest record sequence number |
|
517 |
// in a flight, not counting the Finished message. |
|
518 |
HandshakeFragment lastHandshakeFragment = null; |
|
519 |
||
520 |
// Is handshake (intput) finished? |
|
521 |
boolean handshakeFinished = false; |
|
522 |
||
523 |
DTLSReassembler() { |
|
524 |
// blank |
|
525 |
} |
|
526 |
||
527 |
boolean finished() { |
|
528 |
return handshakeFinished; |
|
529 |
} |
|
530 |
||
531 |
void expectingFinishFlight() { |
|
532 |
expectCCSFlight = true; |
|
533 |
} |
|
534 |
||
535 |
void queueUpHandshake(HandshakeFragment hsf) { |
|
536 |
||
537 |
if ((nextRecordEpoch > hsf.recordEpoch) || |
|
538 |
(nextRecordSeq > hsf.recordSeq) || |
|
539 |
(nextMessageSeq > hsf.messageSeq)) { |
|
540 |
// too old, discard this record |
|
541 |
return; |
|
542 |
} |
|
543 |
||
544 |
// Is it the first message of next flight? |
|
545 |
if ((flightTopMessageSeq == hsf.messageSeq) && |
|
546 |
(hsf.fragmentOffset == 0) && (flightTopRecordSeq == -1)) { |
|
547 |
||
548 |
flightType = hsf.handshakeType; |
|
549 |
flightTopEpoch = hsf.recordEpoch; |
|
550 |
flightTopRecordSeq = hsf.recordSeq; |
|
551 |
||
552 |
if (hsf.handshakeType == HandshakeMessage.ht_server_hello) { |
|
553 |
// Is it a session-resuming handshake? |
|
554 |
try { |
|
555 |
isAbbreviatedHandshake = |
|
556 |
isSessionResuming(hsf.fragment, prevSessionID); |
|
557 |
} catch (SSLException ssle) { |
|
558 |
if (debug != null && Debug.isOn("ssl")) { |
|
559 |
System.out.println( |
|
560 |
Thread.currentThread().getName() + |
|
561 |
" discard invalid record: " + ssle); |
|
562 |
} |
|
563 |
||
564 |
// invalid, discard it [section 4.1.2.7, RFC 6347] |
|
565 |
return; |
|
566 |
} |
|
567 |
||
568 |
if (!isAbbreviatedHandshake) { |
|
569 |
prevSessionID = getSessionID(hsf.fragment); |
|
570 |
} |
|
571 |
} |
|
572 |
} |
|
573 |
||
574 |
boolean fragmented = false; |
|
575 |
if ((hsf.fragmentOffset) != 0 || |
|
576 |
(hsf.fragmentLength != hsf.messageLength)) { |
|
577 |
||
578 |
fragmented = true; |
|
579 |
} |
|
580 |
||
581 |
List<HoleDescriptor> holes = holesMap.get(hsf.handshakeType); |
|
582 |
if (holes == null) { |
|
583 |
if (!fragmented) { |
|
584 |
holes = Collections.emptyList(); |
|
585 |
} else { |
|
586 |
holes = new LinkedList<HoleDescriptor>(); |
|
587 |
holes.add(new HoleDescriptor(0, hsf.messageLength)); |
|
588 |
} |
|
589 |
holesMap.put(hsf.handshakeType, holes); |
|
590 |
} else if (holes.isEmpty()) { |
|
591 |
// Have got the full handshake message. This record may be |
|
592 |
// a handshake message retransmission. Discard this record. |
|
593 |
// |
|
594 |
// It's OK to discard retransmission as the handshake hash |
|
595 |
// is computed as if each handshake message had been sent |
|
596 |
// as a single fragment. |
|
597 |
// |
|
598 |
// Note that ClientHello messages are delivered twice in |
|
599 |
// DTLS handshaking. |
|
600 |
if ((hsf.handshakeType != HandshakeMessage.ht_client_hello && |
|
601 |
hsf.handshakeType != ht_hello_verify_request) || |
|
602 |
(nextMessageSeq != hsf.messageSeq)) { |
|
603 |
return; |
|
604 |
} |
|
605 |
||
606 |
if (fragmented) { |
|
607 |
holes = new LinkedList<HoleDescriptor>(); |
|
608 |
holes.add(new HoleDescriptor(0, hsf.messageLength)); |
|
609 |
} |
|
610 |
holesMap.put(hsf.handshakeType, holes); |
|
611 |
} |
|
612 |
||
613 |
if (fragmented) { |
|
614 |
int fragmentLimit = hsf.fragmentOffset + hsf.fragmentLength; |
|
615 |
for (int i = 0; i < holes.size(); i++) { |
|
616 |
||
617 |
HoleDescriptor hole = holes.get(i); |
|
618 |
if ((hole.limit <= hsf.fragmentOffset) || |
|
619 |
(hole.offset >= fragmentLimit)) { |
|
620 |
// Also discard overlapping handshake retransmissions. |
|
621 |
continue; |
|
622 |
} |
|
623 |
||
624 |
// The ranges SHOULD NOT overlap. |
|
625 |
if (((hole.offset > hsf.fragmentOffset) && |
|
626 |
(hole.offset < fragmentLimit)) || |
|
627 |
((hole.limit > hsf.fragmentOffset) && |
|
628 |
(hole.limit < fragmentLimit))) { |
|
629 |
||
630 |
if (debug != null && Debug.isOn("ssl")) { |
|
631 |
System.out.println( |
|
632 |
Thread.currentThread().getName() + |
|
633 |
" discard invalid record: " + |
|
634 |
"handshake fragment ranges are overlapping"); |
|
635 |
} |
|
636 |
||
637 |
// invalid, discard it [section 4.1.2.7, RFC 6347] |
|
638 |
return; |
|
639 |
} |
|
640 |
||
641 |
// This record interacts with this hole, fill the hole. |
|
642 |
holes.remove(i); |
|
643 |
// i--; |
|
644 |
||
645 |
if (hsf.fragmentOffset > hole.offset) { |
|
646 |
holes.add(new HoleDescriptor( |
|
647 |
hole.offset, hsf.fragmentOffset)); |
|
648 |
// i++; |
|
649 |
} |
|
650 |
||
651 |
if (fragmentLimit < hole.limit) { |
|
652 |
holes.add(new HoleDescriptor( |
|
653 |
fragmentLimit, hole.limit)); |
|
654 |
// i++; |
|
655 |
} |
|
656 |
||
657 |
// As no ranges overlap, no interact with other holes. |
|
658 |
break; |
|
659 |
} |
|
660 |
} |
|
661 |
||
662 |
// append this fragment |
|
663 |
bufferedFragments.add(hsf); |
|
664 |
||
665 |
if ((lastHandshakeFragment == null) || |
|
666 |
(lastHandshakeFragment.compareTo(hsf) < 0)) { |
|
667 |
||
668 |
lastHandshakeFragment = hsf; |
|
669 |
} |
|
670 |
||
671 |
if (flightIsReady) { |
|
672 |
flightIsReady = false; |
|
673 |
} |
|
674 |
needToCheckFlight = true; |
|
675 |
} |
|
676 |
||
677 |
// queue up change_cipher_spec or encrypted message |
|
678 |
void queueUpFragment(RecordFragment rf) { |
|
679 |
if ((nextRecordEpoch > rf.recordEpoch) || |
|
680 |
(nextRecordSeq > rf.recordSeq)) { |
|
681 |
// too old, discard this record |
|
682 |
return; |
|
683 |
} |
|
684 |
||
685 |
// Is it the first message of next flight? |
|
686 |
if (expectCCSFlight && |
|
687 |
(rf.contentType == Record.ct_change_cipher_spec)) { |
|
688 |
||
689 |
flightType = (byte)0xFE; |
|
690 |
flightTopEpoch = rf.recordEpoch; |
|
691 |
flightTopRecordSeq = rf.recordSeq; |
|
692 |
} |
|
693 |
||
694 |
// append this fragment |
|
695 |
bufferedFragments.add(rf); |
|
696 |
||
697 |
if (flightIsReady) { |
|
698 |
flightIsReady = false; |
|
699 |
} |
|
700 |
needToCheckFlight = true; |
|
701 |
} |
|
702 |
||
703 |
boolean isEmpty() { |
|
704 |
return (bufferedFragments.isEmpty() || |
|
705 |
(!flightIsReady && !needToCheckFlight) || |
|
706 |
(needToCheckFlight && !flightIsReady())); |
|
707 |
} |
|
708 |
||
709 |
Plaintext acquirePlaintext() { |
|
710 |
if (bufferedFragments.isEmpty()) { |
|
711 |
// reset the flight |
|
712 |
if (flightIsReady) { |
|
713 |
flightIsReady = false; |
|
714 |
needToCheckFlight = false; |
|
715 |
} |
|
716 |
||
717 |
return null; |
|
718 |
} |
|
719 |
||
720 |
if (!flightIsReady && needToCheckFlight) { |
|
721 |
// check the fligth status |
|
722 |
flightIsReady = flightIsReady(); |
|
723 |
||
724 |
// set for next flight |
|
725 |
if (flightIsReady) { |
|
726 |
flightTopMessageSeq = lastHandshakeFragment.messageSeq + 1; |
|
727 |
flightTopRecordSeq = -1; |
|
728 |
} |
|
729 |
||
730 |
needToCheckFlight = false; |
|
731 |
} |
|
732 |
||
733 |
if (!flightIsReady) { |
|
734 |
return null; |
|
735 |
} |
|
736 |
||
737 |
RecordFragment rFrag = bufferedFragments.first(); |
|
738 |
if (!rFrag.isCiphertext) { |
|
739 |
// handshake message, or ChangeCipherSpec message |
|
740 |
return acquireHandshakeMessage(); |
|
741 |
} else { |
|
742 |
// a Finished message or other ciphertexts |
|
743 |
return acquireCachedMessage(); |
|
744 |
} |
|
745 |
} |
|
746 |
||
747 |
private Plaintext acquireCachedMessage() { |
|
748 |
||
749 |
RecordFragment rFrag = bufferedFragments.first(); |
|
750 |
if (readEpoch != rFrag.recordEpoch) { |
|
751 |
if (readEpoch > rFrag.recordEpoch) { |
|
752 |
// discard old records |
|
753 |
bufferedFragments.remove(rFrag); // popup the fragment |
|
754 |
} |
|
755 |
||
756 |
// reset the flight |
|
757 |
if (flightIsReady) { |
|
758 |
flightIsReady = false; |
|
759 |
} |
|
760 |
return null; |
|
761 |
} |
|
762 |
||
763 |
bufferedFragments.remove(rFrag); // popup the fragment |
|
764 |
||
765 |
ByteBuffer fragment = ByteBuffer.wrap(rFrag.fragment); |
|
766 |
ByteBuffer plaintextFragment = null; |
|
767 |
try { |
|
768 |
plaintextFragment = decrypt(readAuthenticator, readCipher, |
|
769 |
rFrag.contentType, fragment, rFrag.recordEnS); |
|
770 |
} catch (BadPaddingException bpe) { |
|
771 |
if (debug != null && Debug.isOn("ssl")) { |
|
772 |
System.out.println(Thread.currentThread().getName() + |
|
773 |
" discard invalid record: " + bpe); |
|
774 |
} |
|
775 |
||
776 |
// invalid, discard this record [section 4.1.2.7, RFC 6347] |
|
777 |
return null; |
|
778 |
} |
|
779 |
||
780 |
// The ciphtext handshake message can only be Finished (the |
|
781 |
// end of this flight), ClinetHello or HelloRequest (the |
|
782 |
// beginning of the next flight) message. Need not to check |
|
783 |
// any ChangeCipherSpec message. |
|
784 |
if (rFrag.contentType == Record.ct_handshake) { |
|
785 |
HandshakeFragment finFrag = null; |
|
786 |
while (plaintextFragment.remaining() > 0) { |
|
787 |
HandshakeFragment hsFrag = parseHandshakeMessage( |
|
788 |
rFrag.contentType, |
|
789 |
rFrag.majorVersion, rFrag.minorVersion, |
|
790 |
rFrag.recordEnS, rFrag.recordEpoch, rFrag.recordSeq, |
|
791 |
plaintextFragment); |
|
792 |
||
793 |
if (hsFrag == null) { |
|
794 |
// invalid, discard this record |
|
795 |
return null; |
|
796 |
} |
|
797 |
||
798 |
if (hsFrag.handshakeType == HandshakeMessage.ht_finished) { |
|
799 |
finFrag = hsFrag; |
|
800 |
||
801 |
// reset for the next flight |
|
802 |
this.flightType = (byte)0xFF; |
|
803 |
this.flightTopEpoch = rFrag.recordEpoch; |
|
804 |
this.flightTopMessageSeq = hsFrag.messageSeq + 1; |
|
805 |
this.flightTopRecordSeq = -1; |
|
806 |
} else { |
|
807 |
// reset the flight |
|
808 |
if (flightIsReady) { |
|
809 |
flightIsReady = false; |
|
810 |
} |
|
811 |
queueUpHandshake(hsFrag); |
|
812 |
} |
|
813 |
} |
|
814 |
||
815 |
this.nextRecordSeq = rFrag.recordSeq + 1; |
|
816 |
this.nextMessageSeq = 0; |
|
817 |
||
818 |
if (finFrag != null) { |
|
819 |
this.nextRecordEpoch = finFrag.recordEpoch; |
|
820 |
this.nextRecordSeq = finFrag.recordSeq + 1; |
|
821 |
this.nextMessageSeq = finFrag.messageSeq + 1; |
|
822 |
||
823 |
// Finished message does not fragment. |
|
824 |
byte[] recordFrag = new byte[finFrag.messageLength + 4]; |
|
825 |
Plaintext plaintext = new Plaintext(finFrag.contentType, |
|
826 |
finFrag.majorVersion, finFrag.minorVersion, |
|
827 |
finFrag.recordEpoch, finFrag.recordSeq, |
|
828 |
ByteBuffer.wrap(recordFrag)); |
|
829 |
||
830 |
// fill the handshake fragment of the record |
|
831 |
recordFrag[0] = finFrag.handshakeType; |
|
832 |
recordFrag[1] = |
|
833 |
(byte)((finFrag.messageLength >>> 16) & 0xFF); |
|
834 |
recordFrag[2] = |
|
835 |
(byte)((finFrag.messageLength >>> 8) & 0xFF); |
|
836 |
recordFrag[3] = (byte)(finFrag.messageLength & 0xFF); |
|
837 |
||
838 |
System.arraycopy(finFrag.fragment, 0, |
|
839 |
recordFrag, 4, finFrag.fragmentLength); |
|
840 |
||
841 |
// handshake hashing |
|
842 |
handshakeHashing(finFrag, plaintext); |
|
843 |
||
844 |
// input handshake finished |
|
845 |
handshakeFinished = true; |
|
846 |
||
847 |
return plaintext; |
|
848 |
} else { |
|
849 |
return acquirePlaintext(); |
|
850 |
} |
|
851 |
} else { |
|
852 |
return new Plaintext(rFrag.contentType, |
|
853 |
rFrag.majorVersion, rFrag.minorVersion, |
|
854 |
rFrag.recordEpoch, rFrag.recordSeq, |
|
855 |
plaintextFragment); |
|
856 |
} |
|
857 |
} |
|
858 |
||
859 |
private Plaintext acquireHandshakeMessage() { |
|
860 |
||
861 |
RecordFragment rFrag = bufferedFragments.first(); |
|
862 |
if (rFrag.contentType == Record.ct_change_cipher_spec) { |
|
863 |
this.nextRecordEpoch = rFrag.recordEpoch + 1; |
|
864 |
this.nextRecordSeq = 0; |
|
865 |
// no change on next handshake message sequence number |
|
866 |
||
867 |
bufferedFragments.remove(rFrag); // popup the fragment |
|
868 |
||
869 |
// Reload if this message has been reserved for handshake hash. |
|
870 |
handshakeHash.reload(); |
|
871 |
||
872 |
return new Plaintext(rFrag.contentType, |
|
873 |
rFrag.majorVersion, rFrag.minorVersion, |
|
874 |
rFrag.recordEpoch, rFrag.recordSeq, |
|
875 |
ByteBuffer.wrap(rFrag.fragment)); |
|
876 |
} else { // rFrag.contentType == Record.ct_handshake |
|
877 |
HandshakeFragment hsFrag = (HandshakeFragment)rFrag; |
|
878 |
if ((hsFrag.messageLength == hsFrag.fragmentLength) && |
|
879 |
(hsFrag.fragmentOffset == 0)) { // no fragmentation |
|
880 |
||
881 |
bufferedFragments.remove(rFrag); // popup the fragment |
|
882 |
||
883 |
// this.nextRecordEpoch = hsFrag.recordEpoch; |
|
884 |
this.nextRecordSeq = hsFrag.recordSeq + 1; |
|
885 |
this.nextMessageSeq = hsFrag.messageSeq + 1; |
|
886 |
||
887 |
// Note: may try to avoid byte array copy in the future. |
|
888 |
byte[] recordFrag = new byte[hsFrag.messageLength + 4]; |
|
889 |
Plaintext plaintext = new Plaintext(hsFrag.contentType, |
|
890 |
hsFrag.majorVersion, hsFrag.minorVersion, |
|
891 |
hsFrag.recordEpoch, hsFrag.recordSeq, |
|
892 |
ByteBuffer.wrap(recordFrag)); |
|
893 |
||
894 |
// fill the handshake fragment of the record |
|
895 |
recordFrag[0] = hsFrag.handshakeType; |
|
896 |
recordFrag[1] = |
|
897 |
(byte)((hsFrag.messageLength >>> 16) & 0xFF); |
|
898 |
recordFrag[2] = |
|
899 |
(byte)((hsFrag.messageLength >>> 8) & 0xFF); |
|
900 |
recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF); |
|
901 |
||
902 |
System.arraycopy(hsFrag.fragment, 0, |
|
903 |
recordFrag, 4, hsFrag.fragmentLength); |
|
904 |
||
905 |
// handshake hashing |
|
906 |
handshakeHashing(hsFrag, plaintext); |
|
907 |
||
908 |
return plaintext; |
|
909 |
} else { // fragmented handshake message |
|
910 |
// the first record |
|
911 |
// |
|
912 |
// Note: may try to avoid byte array copy in the future. |
|
913 |
byte[] recordFrag = new byte[hsFrag.messageLength + 4]; |
|
914 |
Plaintext plaintext = new Plaintext(hsFrag.contentType, |
|
915 |
hsFrag.majorVersion, hsFrag.minorVersion, |
|
916 |
hsFrag.recordEpoch, hsFrag.recordSeq, |
|
917 |
ByteBuffer.wrap(recordFrag)); |
|
918 |
||
919 |
// fill the handshake fragment of the record |
|
920 |
recordFrag[0] = hsFrag.handshakeType; |
|
921 |
recordFrag[1] = |
|
922 |
(byte)((hsFrag.messageLength >>> 16) & 0xFF); |
|
923 |
recordFrag[2] = |
|
924 |
(byte)((hsFrag.messageLength >>> 8) & 0xFF); |
|
925 |
recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF); |
|
926 |
||
927 |
int msgSeq = hsFrag.messageSeq; |
|
928 |
long maxRecodeSN = hsFrag.recordSeq; |
|
929 |
HandshakeFragment hmFrag = hsFrag; |
|
930 |
do { |
|
931 |
System.arraycopy(hmFrag.fragment, 0, |
|
932 |
recordFrag, hmFrag.fragmentOffset + 4, |
|
933 |
hmFrag.fragmentLength); |
|
934 |
// popup the fragment |
|
935 |
bufferedFragments.remove(rFrag); |
|
936 |
||
937 |
if (maxRecodeSN < hmFrag.recordSeq) { |
|
938 |
maxRecodeSN = hmFrag.recordSeq; |
|
939 |
} |
|
940 |
||
941 |
// Note: may buffer retransmitted fragments in order to |
|
942 |
// speed up the reassembly in the future. |
|
943 |
||
944 |
// read the next buffered record |
|
945 |
if (!bufferedFragments.isEmpty()) { |
|
946 |
rFrag = bufferedFragments.first(); |
|
947 |
if (rFrag.contentType != Record.ct_handshake) { |
|
948 |
break; |
|
949 |
} else { |
|
950 |
hmFrag = (HandshakeFragment)rFrag; |
|
951 |
} |
|
952 |
} |
|
953 |
} while (!bufferedFragments.isEmpty() && |
|
954 |
(msgSeq == hmFrag.messageSeq)); |
|
955 |
||
956 |
// handshake hashing |
|
957 |
handshakeHashing(hsFrag, plaintext); |
|
958 |
||
959 |
this.nextRecordSeq = maxRecodeSN + 1; |
|
960 |
this.nextMessageSeq = msgSeq + 1; |
|
961 |
||
962 |
return plaintext; |
|
963 |
} |
|
964 |
} |
|
965 |
} |
|
966 |
||
967 |
boolean flightIsReady() { |
|
968 |
||
969 |
// |
|
970 |
// the ChangeCipherSpec/Finished flight |
|
971 |
// |
|
972 |
if (expectCCSFlight) { |
|
973 |
// Have the ChangeCipherSpec/Finished messages been received? |
|
974 |
return hasFinisedMessage(bufferedFragments); |
|
975 |
} |
|
976 |
||
977 |
if (flightType == (byte)0xFF) { |
|
978 |
return false; |
|
979 |
} |
|
980 |
||
981 |
if ((flightType == HandshakeMessage.ht_client_hello) || |
|
982 |
(flightType == HandshakeMessage.ht_hello_request) || |
|
983 |
(flightType == HandshakeMessage.ht_hello_verify_request)) { |
|
984 |
||
985 |
// single handshake message flight |
|
986 |
return hasCompleted(holesMap.get(flightType)); |
|
987 |
} |
|
988 |
||
989 |
// |
|
990 |
// the ServerHello flight |
|
991 |
// |
|
992 |
if (flightType == HandshakeMessage.ht_server_hello) { |
|
993 |
// Firstly, check the first flight handshake message. |
|
994 |
if (!hasCompleted(holesMap.get(flightType))) { |
|
995 |
return false; |
|
996 |
} |
|
997 |
||
998 |
// |
|
999 |
// an abbreviated handshake |
|
1000 |
// |
|
1001 |
if (isAbbreviatedHandshake) { |
|
1002 |
// Ready to use the flight if received the |
|
1003 |
// ChangeCipherSpec and Finished messages. |
|
1004 |
return hasFinisedMessage(bufferedFragments); |
|
1005 |
} |
|
1006 |
||
1007 |
// |
|
1008 |
// a full handshake |
|
1009 |
// |
|
1010 |
if (lastHandshakeFragment.handshakeType != |
|
1011 |
HandshakeMessage.ht_server_hello_done) { |
|
1012 |
// Not yet got the final message of the flight. |
|
1013 |
return false; |
|
1014 |
} |
|
1015 |
||
1016 |
// Have all handshake message been received? |
|
1017 |
return hasCompleted(bufferedFragments, |
|
1018 |
flightTopMessageSeq, lastHandshakeFragment.messageSeq); |
|
1019 |
} |
|
1020 |
||
1021 |
// |
|
1022 |
// the ClientKeyExchange flight |
|
1023 |
// |
|
1024 |
// Note: need to consider more messages in this flight if |
|
1025 |
// ht_supplemental_data and ht_certificate_url are |
|
1026 |
// suppported in the future. |
|
1027 |
// |
|
1028 |
if ((flightType == HandshakeMessage.ht_certificate) || |
|
1029 |
(flightType == HandshakeMessage.ht_client_key_exchange)) { |
|
1030 |
||
1031 |
// Firstly, check the first flight handshake message. |
|
1032 |
if (!hasCompleted(holesMap.get(flightType))) { |
|
1033 |
return false; |
|
1034 |
} |
|
1035 |
||
1036 |
if (!hasFinisedMessage(bufferedFragments)) { |
|
1037 |
// not yet got the ChangeCipherSpec/Finished messages |
|
1038 |
return false; |
|
1039 |
} |
|
1040 |
||
1041 |
if (flightType == HandshakeMessage.ht_client_key_exchange) { |
|
1042 |
// single handshake message flight |
|
1043 |
return true; |
|
1044 |
} |
|
1045 |
||
1046 |
// |
|
1047 |
// flightType == HandshakeMessage.ht_certificate |
|
1048 |
// |
|
1049 |
// We don't support certificates containing fixed |
|
1050 |
// Diffie-Hellman parameters. Therefore, CertificateVerify |
|
1051 |
// message is required if client Certificate message presents. |
|
1052 |
// |
|
1053 |
if (lastHandshakeFragment.handshakeType != |
|
1054 |
HandshakeMessage.ht_certificate_verify) { |
|
1055 |
// Not yet got the final message of the flight. |
|
1056 |
return false; |
|
1057 |
} |
|
1058 |
||
1059 |
// Have all handshake message been received? |
|
1060 |
return hasCompleted(bufferedFragments, |
|
1061 |
flightTopMessageSeq, lastHandshakeFragment.messageSeq); |
|
1062 |
} |
|
1063 |
||
1064 |
// |
|
1065 |
// Otherwise, need to receive more handshake messages. |
|
1066 |
// |
|
1067 |
return false; |
|
1068 |
} |
|
1069 |
||
1070 |
private boolean isSessionResuming( |
|
1071 |
byte[] fragment, byte[] prevSid) throws SSLException { |
|
1072 |
||
1073 |
// As the first fragment of ServerHello should be big enough |
|
1074 |
// to hold the session_id field, need not to worry about the |
|
1075 |
// fragmentation here. |
|
1076 |
if ((fragment == null) || (fragment.length < 38)) { |
|
1077 |
// 38: the minimal ServerHello body length |
|
1078 |
throw new SSLException( |
|
1079 |
"Invalid ServerHello message: no sufficient data"); |
|
1080 |
} |
|
1081 |
||
1082 |
int sidLen = fragment[34]; // 34: the length field |
|
1083 |
if (sidLen > 32) { // opaque SessionID<0..32> |
|
1084 |
throw new SSLException( |
|
1085 |
"Invalid ServerHello message: invalid session id"); |
|
1086 |
} |
|
1087 |
||
1088 |
if (fragment.length < 38 + sidLen) { |
|
1089 |
throw new SSLException( |
|
1090 |
"Invalid ServerHello message: no sufficient data"); |
|
1091 |
} |
|
1092 |
||
1093 |
if (sidLen != 0 && (prevSid.length == sidLen)) { |
|
1094 |
// may be a session-resuming handshake |
|
1095 |
for (int i = 0; i < sidLen; i++) { |
|
1096 |
if (prevSid[i] != fragment[35 + i]) { |
|
1097 |
// 35: the session identifier |
|
1098 |
return false; |
|
1099 |
} |
|
1100 |
} |
|
1101 |
||
1102 |
return true; |
|
1103 |
} |
|
1104 |
||
1105 |
return false; |
|
1106 |
} |
|
1107 |
||
1108 |
private byte[] getSessionID(byte[] fragment) { |
|
1109 |
// The validity has been checked in the call to isSessionResuming(). |
|
1110 |
int sidLen = fragment[34]; // 34: the sessionID length field |
|
1111 |
||
1112 |
byte[] temporary = new byte[sidLen]; |
|
1113 |
System.arraycopy(fragment, 35, temporary, 0, sidLen); |
|
1114 |
||
1115 |
return temporary; |
|
1116 |
} |
|
1117 |
||
1118 |
// Looking for the ChangeCipherSpec and Finished messages. |
|
1119 |
// |
|
1120 |
// As the cached Finished message should be a ciphertext, we don't |
|
1121 |
// exactly know a ciphertext is a Finished message or not. According |
|
1122 |
// to the spec of TLS/DTLS handshaking, a Finished message is always |
|
1123 |
// sent immediately after a ChangeCipherSpec message. The first |
|
1124 |
// ciphertext handshake message should be the expected Finished message. |
|
1125 |
private boolean hasFinisedMessage( |
|
1126 |
Set<RecordFragment> fragments) { |
|
1127 |
||
1128 |
boolean hasCCS = false; |
|
1129 |
boolean hasFin = false; |
|
1130 |
for (RecordFragment fragment : fragments) { |
|
1131 |
if (fragment.contentType == Record.ct_change_cipher_spec) { |
|
1132 |
if (hasFin) { |
|
1133 |
return true; |
|
1134 |
} |
|
1135 |
hasCCS = true; |
|
1136 |
} else if (fragment.contentType == Record.ct_handshake) { |
|
1137 |
// Finished is the first expected message of a new epoch. |
|
1138 |
if (fragment.isCiphertext) { |
|
1139 |
if (hasCCS) { |
|
1140 |
return true; |
|
1141 |
} |
|
1142 |
hasFin = true; |
|
1143 |
} |
|
1144 |
} |
|
1145 |
} |
|
1146 |
||
1147 |
return hasFin && hasCCS; |
|
1148 |
} |
|
1149 |
||
1150 |
private boolean hasCompleted(List<HoleDescriptor> holes) { |
|
1151 |
if (holes == null) { |
|
1152 |
// not yet received this kind of handshake message |
|
1153 |
return false; |
|
1154 |
} |
|
1155 |
||
1156 |
return holes.isEmpty(); // no fragment hole for complete message |
|
1157 |
} |
|
1158 |
||
1159 |
private boolean hasCompleted( |
|
1160 |
Set<RecordFragment> fragments, |
|
1161 |
int presentMsgSeq, int endMsgSeq) { |
|
1162 |
||
1163 |
// The caller should have checked the completion of the first |
|
1164 |
// present handshake message. Need not to check it again. |
|
1165 |
for (RecordFragment rFrag : fragments) { |
|
1166 |
if ((rFrag.contentType != Record.ct_handshake) || |
|
1167 |
rFrag.isCiphertext) { |
|
1168 |
break; |
|
1169 |
} |
|
1170 |
||
1171 |
HandshakeFragment hsFrag = (HandshakeFragment)rFrag; |
|
1172 |
if (hsFrag.messageSeq == presentMsgSeq) { |
|
1173 |
continue; |
|
1174 |
} else if (hsFrag.messageSeq == (presentMsgSeq + 1)) { |
|
1175 |
// check the completion of the handshake message |
|
1176 |
if (!hasCompleted(holesMap.get(hsFrag.handshakeType))) { |
|
1177 |
return false; |
|
1178 |
} |
|
1179 |
||
1180 |
presentMsgSeq = hsFrag.messageSeq; |
|
1181 |
} else { |
|
1182 |
// not yet got handshake message next to presentMsgSeq |
|
1183 |
break; |
|
1184 |
} |
|
1185 |
} |
|
1186 |
||
1187 |
return (presentMsgSeq >= endMsgSeq); |
|
1188 |
// false: if not yet got all messages of the flight. |
|
1189 |
} |
|
1190 |
||
1191 |
private void handshakeHashing( |
|
1192 |
HandshakeFragment hsFrag, Plaintext plaintext) { |
|
1193 |
||
1194 |
byte hsType = hsFrag.handshakeType; |
|
1195 |
if ((hsType == HandshakeMessage.ht_hello_request) || |
|
1196 |
(hsType == HandshakeMessage.ht_hello_verify_request)) { |
|
1197 |
||
1198 |
// omitted from handshake hash computation |
|
1199 |
return; |
|
1200 |
} |
|
1201 |
||
1202 |
if ((hsFrag.messageSeq == 0) && |
|
1203 |
(hsType == HandshakeMessage.ht_client_hello)) { |
|
1204 |
||
1205 |
// omit initial ClientHello message |
|
1206 |
// |
|
1207 |
// 4: handshake header |
|
1208 |
// 2: ClientHello.client_version |
|
1209 |
// 32: ClientHello.random |
|
1210 |
int sidLen = plaintext.fragment.get(38); |
|
1211 |
||
1212 |
if (sidLen == 0) { // empty session_id, initial handshake |
|
1213 |
return; |
|
1214 |
} |
|
1215 |
} |
|
1216 |
||
1217 |
// calculate the DTLS header |
|
1218 |
byte[] temporary = new byte[12]; // 12: handshake header size |
|
1219 |
||
1220 |
// Handshake.msg_type |
|
1221 |
temporary[0] = hsFrag.handshakeType; |
|
1222 |
||
1223 |
// Handshake.length |
|
1224 |
temporary[1] = (byte)((hsFrag.messageLength >> 16) & 0xFF); |
|
1225 |
temporary[2] = (byte)((hsFrag.messageLength >> 8) & 0xFF); |
|
1226 |
temporary[3] = (byte)(hsFrag.messageLength & 0xFF); |
|
1227 |
||
1228 |
// Handshake.message_seq |
|
1229 |
temporary[4] = (byte)((hsFrag.messageSeq >> 8) & 0xFF); |
|
1230 |
temporary[5] = (byte)(hsFrag.messageSeq & 0xFF); |
|
1231 |
||
1232 |
// Handshake.fragment_offset |
|
1233 |
temporary[6] = 0; |
|
1234 |
temporary[7] = 0; |
|
1235 |
temporary[8] = 0; |
|
1236 |
||
1237 |
// Handshake.fragment_length |
|
1238 |
temporary[9] = temporary[1]; |
|
1239 |
temporary[10] = temporary[2]; |
|
1240 |
temporary[11] = temporary[3]; |
|
1241 |
||
1242 |
plaintext.fragment.position(4); // ignore the TLS header |
|
1243 |
if ((hsType != HandshakeMessage.ht_finished) && |
|
1244 |
(hsType != HandshakeMessage.ht_certificate_verify)) { |
|
1245 |
||
1246 |
if (handshakeHash == null) { |
|
1247 |
// used for cache only |
|
1248 |
handshakeHash = new HandshakeHash(false); |
|
1249 |
} |
|
1250 |
handshakeHash.update(temporary, 0, 12); |
|
1251 |
handshakeHash.update(plaintext.fragment); |
|
1252 |
} else { |
|
1253 |
// Reserve until this handshake message has been processed. |
|
1254 |
if (handshakeHash == null) { |
|
1255 |
// used for cache only |
|
1256 |
handshakeHash = new HandshakeHash(false); |
|
1257 |
} |
|
1258 |
handshakeHash.reserve(temporary, 0, 12); |
|
1259 |
handshakeHash.reserve(plaintext.fragment); |
|
1260 |
} |
|
1261 |
plaintext.fragment.position(0); // restore the position |
|
1262 |
} |
|
1263 |
} |
|
1264 |
} |
|
1265 |