author | ascarpino |
Wed, 08 Feb 2017 12:08:28 -0800 | |
changeset 43701 | fe8c324ba97c |
parent 41820 | 3d8c88d00c9f |
permissions | -rw-r--r-- |
30904 | 1 |
/* |
41820 | 2 |
* Copyright (c) 2015, 2016, Oracle and/or its affiliates. All rights reserved. |
30904 | 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 |
int readEpoch; |
|
46 |
||
47 |
int prevReadEpoch; |
|
48 |
Authenticator prevReadAuthenticator; |
|
49 |
CipherBox prevReadCipher; |
|
50 |
||
51 |
DTLSInputRecord() { |
|
52 |
this.readEpoch = 0; |
|
53 |
this.readAuthenticator = new MAC(true); |
|
54 |
||
55 |
this.prevReadEpoch = 0; |
|
56 |
this.prevReadCipher = CipherBox.NULL; |
|
57 |
this.prevReadAuthenticator = new MAC(true); |
|
58 |
} |
|
59 |
||
60 |
@Override |
|
61 |
void changeReadCiphers(Authenticator readAuthenticator, |
|
62 |
CipherBox readCipher) { |
|
63 |
||
64 |
prevReadCipher.dispose(); |
|
65 |
||
66 |
this.prevReadAuthenticator = this.readAuthenticator; |
|
67 |
this.prevReadCipher = this.readCipher; |
|
68 |
this.prevReadEpoch = this.readEpoch; |
|
69 |
||
70 |
this.readAuthenticator = readAuthenticator; |
|
71 |
this.readCipher = readCipher; |
|
72 |
this.readEpoch++; |
|
73 |
} |
|
74 |
||
75 |
@Override |
|
32649
2ee9017c7597
8136583: Core libraries should use blessed modifier order
martin
parents:
30904
diff
changeset
|
76 |
public synchronized void close() throws IOException { |
30904 | 77 |
if (!isClosed) { |
78 |
prevReadCipher.dispose(); |
|
79 |
super.close(); |
|
80 |
} |
|
81 |
} |
|
82 |
||
83 |
@Override |
|
84 |
boolean isEmpty() { |
|
85 |
return ((reassembler == null) || reassembler.isEmpty()); |
|
86 |
} |
|
87 |
||
88 |
@Override |
|
89 |
int estimateFragmentSize(int packetSize) { |
|
90 |
int macLen = 0; |
|
91 |
if (readAuthenticator instanceof MAC) { |
|
92 |
macLen = ((MAC)readAuthenticator).MAClen(); |
|
93 |
} |
|
94 |
||
95 |
if (packetSize > 0) { |
|
96 |
return readCipher.estimateFragmentSize( |
|
97 |
packetSize, macLen, headerSize); |
|
98 |
} else { |
|
99 |
return Record.maxDataSize; |
|
100 |
} |
|
101 |
} |
|
102 |
||
103 |
@Override |
|
104 |
void expectingFinishFlight() { |
|
105 |
if (reassembler != null) { |
|
106 |
reassembler.expectingFinishFlight(); |
|
107 |
} |
|
108 |
} |
|
109 |
||
110 |
@Override |
|
111 |
Plaintext acquirePlaintext() { |
|
112 |
if (reassembler != null) { |
|
41820 | 113 |
return reassembler.acquirePlaintext(); |
30904 | 114 |
} |
115 |
||
116 |
return null; |
|
117 |
} |
|
118 |
||
119 |
@Override |
|
120 |
Plaintext decode(ByteBuffer packet) { |
|
121 |
||
122 |
if (isClosed) { |
|
123 |
return null; |
|
124 |
} |
|
125 |
||
126 |
if (debug != null && Debug.isOn("packet")) { |
|
127 |
Debug.printHex( |
|
128 |
"[Raw read]: length = " + packet.remaining(), packet); |
|
129 |
} |
|
130 |
||
131 |
// The caller should have validated the record. |
|
132 |
int srcPos = packet.position(); |
|
133 |
int srcLim = packet.limit(); |
|
134 |
||
135 |
byte contentType = packet.get(); // pos: 0 |
|
136 |
byte majorVersion = packet.get(); // pos: 1 |
|
137 |
byte minorVersion = packet.get(); // pos: 2 |
|
138 |
byte[] recordEnS = new byte[8]; // epoch + seqence |
|
139 |
packet.get(recordEnS); |
|
140 |
int recordEpoch = ((recordEnS[0] & 0xFF) << 8) | |
|
141 |
(recordEnS[1] & 0xFF); // pos: 3, 4 |
|
41820 | 142 |
long recordSeq = ((recordEnS[2] & 0xFFL) << 40) | |
143 |
((recordEnS[3] & 0xFFL) << 32) | |
|
144 |
((recordEnS[4] & 0xFFL) << 24) | |
|
145 |
((recordEnS[5] & 0xFFL) << 16) | |
|
146 |
((recordEnS[6] & 0xFFL) << 8) | |
|
147 |
(recordEnS[7] & 0xFFL); // pos: 5-10 |
|
148 |
||
30904 | 149 |
int contentLen = ((packet.get() & 0xFF) << 8) | |
41820 | 150 |
(packet.get() & 0xFF); // pos: 11, 12 |
30904 | 151 |
|
152 |
if (debug != null && Debug.isOn("record")) { |
|
41820 | 153 |
Debug.log("READ: " + |
30904 | 154 |
ProtocolVersion.valueOf(majorVersion, minorVersion) + |
155 |
" " + Record.contentName(contentType) + ", length = " + |
|
156 |
contentLen); |
|
157 |
} |
|
158 |
||
159 |
int recLim = srcPos + DTLSRecord.headerSize + contentLen; |
|
160 |
||
41820 | 161 |
if (this.prevReadEpoch > recordEpoch) { |
30904 | 162 |
// Reset the position of the packet buffer. |
163 |
packet.position(recLim); |
|
41820 | 164 |
if (debug != null && Debug.isOn("record")) { |
165 |
Debug.printHex("READ: discard this old record", recordEnS); |
|
166 |
} |
|
30904 | 167 |
return null; |
168 |
} |
|
169 |
||
41820 | 170 |
// Buffer next epoch message if necessary. |
30904 | 171 |
if (this.readEpoch < recordEpoch) { |
41820 | 172 |
// Discard the record younger than the current epcoh if: |
173 |
// 1. it is not a handshake message, or |
|
174 |
// 2. it is not of next epoch. |
|
175 |
if (((contentType != Record.ct_handshake) && |
|
176 |
(contentType != Record.ct_change_cipher_spec)) || |
|
177 |
(this.readEpoch < (recordEpoch - 1))) { |
|
178 |
||
30904 | 179 |
packet.position(recLim); |
41820 | 180 |
|
181 |
if (debug != null && Debug.isOn("verbose")) { |
|
182 |
Debug.log("Premature record (epoch), discard it."); |
|
183 |
} |
|
184 |
||
30904 | 185 |
return null; |
186 |
} |
|
187 |
||
41820 | 188 |
// Not ready to decrypt this record, may be an encrypted Finished |
30904 | 189 |
// message, need to buffer it. |
190 |
byte[] fragment = new byte[contentLen]; |
|
191 |
packet.get(fragment); // copy the fragment |
|
192 |
RecordFragment buffered = new RecordFragment(fragment, contentType, |
|
193 |
majorVersion, minorVersion, |
|
194 |
recordEnS, recordEpoch, recordSeq, true); |
|
195 |
||
196 |
reassembler.queueUpFragment(buffered); |
|
197 |
||
198 |
// consume the full record in the packet buffer. |
|
199 |
packet.position(recLim); |
|
200 |
||
41820 | 201 |
return reassembler.acquirePlaintext(); |
202 |
} |
|
203 |
||
204 |
// |
|
205 |
// Now, the message is of this epoch or the previous epoch. |
|
206 |
// |
|
207 |
Authenticator decodeAuthenticator; |
|
208 |
CipherBox decodeCipher; |
|
209 |
if (this.readEpoch == recordEpoch) { |
|
210 |
decodeAuthenticator = readAuthenticator; |
|
211 |
decodeCipher = readCipher; |
|
212 |
} else { // prevReadEpoch == recordEpoch |
|
213 |
decodeAuthenticator = prevReadAuthenticator; |
|
214 |
decodeCipher = prevReadCipher; |
|
215 |
} |
|
216 |
||
217 |
// decrypt the fragment |
|
218 |
packet.limit(recLim); |
|
219 |
packet.position(srcPos + DTLSRecord.headerSize); |
|
220 |
||
221 |
ByteBuffer plaintextFragment; |
|
222 |
try { |
|
223 |
plaintextFragment = decrypt(decodeAuthenticator, |
|
224 |
decodeCipher, contentType, packet, recordEnS); |
|
225 |
} catch (BadPaddingException bpe) { |
|
226 |
if (debug != null && Debug.isOn("ssl")) { |
|
227 |
Debug.log("Discard invalid record: " + bpe); |
|
228 |
} |
|
229 |
||
230 |
// invalid, discard this record [section 4.1.2.7, RFC 6347] |
|
231 |
return null; |
|
232 |
} finally { |
|
233 |
// comsume a complete record |
|
234 |
packet.limit(srcLim); |
|
235 |
packet.position(recLim); |
|
236 |
} |
|
237 |
||
238 |
if (contentType != Record.ct_change_cipher_spec && |
|
239 |
contentType != Record.ct_handshake) { // app data or alert |
|
240 |
// no retransmission |
|
241 |
// Cleanup the handshake reassembler if necessary. |
|
242 |
if ((reassembler != null) && |
|
243 |
(reassembler.handshakeEpoch < recordEpoch)) { |
|
244 |
if (debug != null && Debug.isOn("verbose")) { |
|
245 |
Debug.log("Cleanup the handshake reassembler"); |
|
246 |
} |
|
247 |
||
30904 | 248 |
reassembler = null; |
249 |
} |
|
250 |
||
41820 | 251 |
return new Plaintext(contentType, majorVersion, minorVersion, |
252 |
recordEpoch, Authenticator.toLong(recordEnS), |
|
253 |
plaintextFragment); |
|
30904 | 254 |
} |
255 |
||
41820 | 256 |
if (contentType == Record.ct_change_cipher_spec) { |
257 |
if (reassembler == null) { |
|
258 |
if (this.readEpoch != recordEpoch) { |
|
30904 | 259 |
// handshake has not started, should be an |
260 |
// old handshake message, discard it. |
|
41820 | 261 |
|
262 |
if (debug != null && Debug.isOn("verbose")) { |
|
263 |
Debug.log( |
|
264 |
"Lagging behind ChangeCipherSpec, discard it."); |
|
265 |
} |
|
266 |
||
30904 | 267 |
return null; |
268 |
} |
|
269 |
||
41820 | 270 |
reassembler = new DTLSReassembler(recordEpoch); |
271 |
} |
|
272 |
||
273 |
reassembler.queueUpChangeCipherSpec( |
|
274 |
new RecordFragment(plaintextFragment, contentType, |
|
275 |
majorVersion, minorVersion, |
|
276 |
recordEnS, recordEpoch, recordSeq, false)); |
|
277 |
} else { // handshake record |
|
278 |
// One record may contain 1+ more handshake messages. |
|
279 |
while (plaintextFragment.remaining() > 0) { |
|
280 |
||
281 |
HandshakeFragment hsFrag = parseHandshakeMessage( |
|
282 |
contentType, majorVersion, minorVersion, |
|
283 |
recordEnS, recordEpoch, recordSeq, plaintextFragment); |
|
30904 | 284 |
|
41820 | 285 |
if (hsFrag == null) { |
286 |
// invalid, discard this record |
|
287 |
if (debug != null && Debug.isOn("verbose")) { |
|
288 |
Debug.log("Invalid handshake message, discard it."); |
|
289 |
} |
|
290 |
||
291 |
return null; |
|
292 |
} |
|
30904 | 293 |
|
41820 | 294 |
if (reassembler == null) { |
295 |
if (this.readEpoch != recordEpoch) { |
|
296 |
// handshake has not started, should be an |
|
297 |
// old handshake message, discard it. |
|
298 |
||
299 |
if (debug != null && Debug.isOn("verbose")) { |
|
300 |
Debug.log( |
|
301 |
"Lagging behind handshake record, discard it."); |
|
302 |
} |
|
303 |
||
30904 | 304 |
return null; |
305 |
} |
|
306 |
||
41820 | 307 |
reassembler = new DTLSReassembler(recordEpoch); |
30904 | 308 |
} |
309 |
||
41820 | 310 |
reassembler.queueUpHandshake(hsFrag); |
30904 | 311 |
} |
312 |
} |
|
313 |
||
41820 | 314 |
// Completed the read of the full record. Acquire the reassembled |
315 |
// messages. |
|
316 |
if (reassembler != null) { |
|
317 |
return reassembler.acquirePlaintext(); |
|
318 |
} |
|
319 |
||
320 |
if (debug != null && Debug.isOn("verbose")) { |
|
321 |
Debug.log("The reassembler is not initialized yet."); |
|
322 |
} |
|
323 |
||
324 |
return null; |
|
30904 | 325 |
} |
326 |
||
327 |
@Override |
|
328 |
int bytesInCompletePacket(ByteBuffer packet) throws SSLException { |
|
329 |
||
330 |
// DTLS length field is in bytes 11/12 |
|
331 |
if (packet.remaining() < headerSize) { |
|
332 |
return -1; |
|
333 |
} |
|
334 |
||
335 |
// Last sanity check that it's not a wild record |
|
336 |
int pos = packet.position(); |
|
337 |
||
338 |
// Check the content type of the record. |
|
339 |
byte contentType = packet.get(pos); |
|
340 |
if (!Record.isValidContentType(contentType)) { |
|
341 |
throw new SSLException( |
|
342 |
"Unrecognized SSL message, plaintext connection?"); |
|
343 |
} |
|
344 |
||
345 |
// Check the protocol version of the record. |
|
346 |
ProtocolVersion recordVersion = |
|
347 |
ProtocolVersion.valueOf(packet.get(pos + 1), packet.get(pos + 2)); |
|
348 |
checkRecordVersion(recordVersion, false); |
|
349 |
||
350 |
// Get the fragment length of the record. |
|
351 |
int fragLen = ((packet.get(pos + 11) & 0xFF) << 8) + |
|
352 |
(packet.get(pos + 12) & 0xFF) + headerSize; |
|
353 |
if (fragLen > Record.maxFragmentSize) { |
|
354 |
throw new SSLException( |
|
355 |
"Record overflow, fragment length (" + fragLen + |
|
356 |
") MUST not exceed " + Record.maxFragmentSize); |
|
357 |
} |
|
358 |
||
359 |
return fragLen; |
|
360 |
} |
|
361 |
||
362 |
@Override |
|
363 |
void checkRecordVersion(ProtocolVersion recordVersion, |
|
364 |
boolean allowSSL20Hello) throws SSLException { |
|
365 |
||
366 |
if (!recordVersion.maybeDTLSProtocol()) { |
|
367 |
throw new SSLException( |
|
368 |
"Unrecognized record version " + recordVersion + |
|
369 |
" , plaintext connection?"); |
|
370 |
} |
|
371 |
} |
|
372 |
||
373 |
private static HandshakeFragment parseHandshakeMessage( |
|
374 |
byte contentType, byte majorVersion, byte minorVersion, |
|
375 |
byte[] recordEnS, int recordEpoch, long recordSeq, |
|
376 |
ByteBuffer plaintextFragment) { |
|
377 |
||
378 |
int remaining = plaintextFragment.remaining(); |
|
379 |
if (remaining < handshakeHeaderSize) { |
|
380 |
if (debug != null && Debug.isOn("ssl")) { |
|
41820 | 381 |
Debug.log("Discard invalid record: " + |
30904 | 382 |
"too small record to hold a handshake fragment"); |
383 |
} |
|
384 |
||
385 |
// invalid, discard this record [section 4.1.2.7, RFC 6347] |
|
386 |
return null; |
|
387 |
} |
|
388 |
||
389 |
byte handshakeType = plaintextFragment.get(); // pos: 0 |
|
390 |
int messageLength = |
|
391 |
((plaintextFragment.get() & 0xFF) << 16) | |
|
392 |
((plaintextFragment.get() & 0xFF) << 8) | |
|
393 |
(plaintextFragment.get() & 0xFF); // pos: 1-3 |
|
394 |
int messageSeq = |
|
395 |
((plaintextFragment.get() & 0xFF) << 8) | |
|
396 |
(plaintextFragment.get() & 0xFF); // pos: 4/5 |
|
397 |
int fragmentOffset = |
|
398 |
((plaintextFragment.get() & 0xFF) << 16) | |
|
399 |
((plaintextFragment.get() & 0xFF) << 8) | |
|
400 |
(plaintextFragment.get() & 0xFF); // pos: 6-8 |
|
401 |
int fragmentLength = |
|
402 |
((plaintextFragment.get() & 0xFF) << 16) | |
|
403 |
((plaintextFragment.get() & 0xFF) << 8) | |
|
404 |
(plaintextFragment.get() & 0xFF); // pos: 9-11 |
|
405 |
if ((remaining - handshakeHeaderSize) < fragmentLength) { |
|
406 |
if (debug != null && Debug.isOn("ssl")) { |
|
41820 | 407 |
Debug.log("Discard invalid record: " + |
30904 | 408 |
"not a complete handshake fragment in the record"); |
409 |
} |
|
410 |
||
411 |
// invalid, discard this record [section 4.1.2.7, RFC 6347] |
|
412 |
return null; |
|
413 |
} |
|
414 |
||
415 |
byte[] fragment = new byte[fragmentLength]; |
|
416 |
plaintextFragment.get(fragment); |
|
417 |
||
418 |
return new HandshakeFragment(fragment, contentType, |
|
419 |
majorVersion, minorVersion, |
|
420 |
recordEnS, recordEpoch, recordSeq, |
|
421 |
handshakeType, messageLength, |
|
422 |
messageSeq, fragmentOffset, fragmentLength); |
|
423 |
} |
|
424 |
||
425 |
// buffered record fragment |
|
426 |
private static class RecordFragment implements Comparable<RecordFragment> { |
|
427 |
boolean isCiphertext; |
|
428 |
||
429 |
byte contentType; |
|
430 |
byte majorVersion; |
|
431 |
byte minorVersion; |
|
432 |
int recordEpoch; |
|
433 |
long recordSeq; |
|
434 |
byte[] recordEnS; |
|
435 |
byte[] fragment; |
|
436 |
||
437 |
RecordFragment(ByteBuffer fragBuf, byte contentType, |
|
438 |
byte majorVersion, byte minorVersion, byte[] recordEnS, |
|
439 |
int recordEpoch, long recordSeq, boolean isCiphertext) { |
|
440 |
this((byte[])null, contentType, majorVersion, minorVersion, |
|
441 |
recordEnS, recordEpoch, recordSeq, isCiphertext); |
|
442 |
||
443 |
this.fragment = new byte[fragBuf.remaining()]; |
|
444 |
fragBuf.get(this.fragment); |
|
445 |
} |
|
446 |
||
447 |
RecordFragment(byte[] fragment, byte contentType, |
|
448 |
byte majorVersion, byte minorVersion, byte[] recordEnS, |
|
449 |
int recordEpoch, long recordSeq, boolean isCiphertext) { |
|
450 |
this.isCiphertext = isCiphertext; |
|
451 |
||
452 |
this.contentType = contentType; |
|
453 |
this.majorVersion = majorVersion; |
|
454 |
this.minorVersion = minorVersion; |
|
455 |
this.recordEpoch = recordEpoch; |
|
456 |
this.recordSeq = recordSeq; |
|
457 |
this.recordEnS = recordEnS; |
|
458 |
this.fragment = fragment; // The caller should have cloned |
|
459 |
// the buffer if necessary. |
|
460 |
} |
|
461 |
||
462 |
@Override |
|
463 |
public int compareTo(RecordFragment o) { |
|
41820 | 464 |
if (this.contentType == Record.ct_change_cipher_spec) { |
465 |
if (o.contentType == Record.ct_change_cipher_spec) { |
|
466 |
// Only one incoming ChangeCipherSpec message for an epoch. |
|
467 |
// |
|
468 |
// Ignore duplicated ChangeCipherSpec messages. |
|
469 |
return Integer.compare(this.recordEpoch, o.recordEpoch); |
|
470 |
} else if ((this.recordEpoch == o.recordEpoch) && |
|
471 |
(o.contentType == Record.ct_handshake)) { |
|
472 |
// ChangeCipherSpec is the latest message of an epoch. |
|
473 |
return 1; |
|
474 |
} |
|
475 |
} else if (o.contentType == Record.ct_change_cipher_spec) { |
|
476 |
if ((this.recordEpoch == o.recordEpoch) && |
|
477 |
(this.contentType == Record.ct_handshake)) { |
|
478 |
// ChangeCipherSpec is the latest message of an epoch. |
|
479 |
return -1; |
|
480 |
} else { |
|
481 |
// different epoch or this is not a handshake message |
|
482 |
return compareToSequence(o.recordEpoch, o.recordSeq); |
|
483 |
} |
|
484 |
} |
|
485 |
||
486 |
return compareToSequence(o.recordEpoch, o.recordSeq); |
|
487 |
} |
|
488 |
||
489 |
int compareToSequence(int epoch, long seq) { |
|
490 |
if (this.recordEpoch > epoch) { |
|
491 |
return 1; |
|
492 |
} else if (this.recordEpoch == epoch) { |
|
493 |
return Long.compare(this.recordSeq, seq); |
|
494 |
} else { |
|
495 |
return -1; |
|
496 |
} |
|
30904 | 497 |
} |
498 |
} |
|
499 |
||
500 |
// buffered handshake message |
|
501 |
private static final class HandshakeFragment extends RecordFragment { |
|
502 |
||
503 |
byte handshakeType; // handshake msg_type |
|
504 |
int messageSeq; // message_seq |
|
505 |
int messageLength; // Handshake body length |
|
506 |
int fragmentOffset; // fragment_offset |
|
507 |
int fragmentLength; // fragment_length |
|
508 |
||
509 |
HandshakeFragment(byte[] fragment, byte contentType, |
|
510 |
byte majorVersion, byte minorVersion, byte[] recordEnS, |
|
511 |
int recordEpoch, long recordSeq, |
|
512 |
byte handshakeType, int messageLength, |
|
513 |
int messageSeq, int fragmentOffset, int fragmentLength) { |
|
514 |
||
515 |
super(fragment, contentType, majorVersion, minorVersion, |
|
516 |
recordEnS, recordEpoch , recordSeq, false); |
|
517 |
||
518 |
this.handshakeType = handshakeType; |
|
519 |
this.messageSeq = messageSeq; |
|
520 |
this.messageLength = messageLength; |
|
521 |
this.fragmentOffset = fragmentOffset; |
|
522 |
this.fragmentLength = fragmentLength; |
|
523 |
} |
|
524 |
||
525 |
@Override |
|
526 |
public int compareTo(RecordFragment o) { |
|
527 |
if (o instanceof HandshakeFragment) { |
|
528 |
HandshakeFragment other = (HandshakeFragment)o; |
|
529 |
if (this.messageSeq != other.messageSeq) { |
|
41820 | 530 |
// keep the insertion order of handshake messages |
30904 | 531 |
return this.messageSeq - other.messageSeq; |
41820 | 532 |
} else if (this.fragmentOffset != other.fragmentOffset) { |
533 |
// small fragment offset was transmitted first |
|
534 |
return this.fragmentOffset - other.fragmentOffset; |
|
535 |
} else if (this.fragmentLength == other.fragmentLength) { |
|
536 |
// retransmissions, ignore duplicated messages. |
|
537 |
return 0; |
|
30904 | 538 |
} |
41820 | 539 |
|
540 |
// Should be repacked for suitable fragment length. |
|
541 |
// |
|
542 |
// Note that the acquiring processes will reassemble the |
|
543 |
// the fragments later. |
|
544 |
return compareToSequence(o.recordEpoch, o.recordSeq); |
|
30904 | 545 |
} |
546 |
||
41820 | 547 |
return super.compareTo(o); |
30904 | 548 |
} |
549 |
} |
|
550 |
||
551 |
private static final class HoleDescriptor { |
|
552 |
int offset; // fragment_offset |
|
553 |
int limit; // fragment_offset + fragment_length |
|
554 |
||
555 |
HoleDescriptor(int offset, int limit) { |
|
556 |
this.offset = offset; |
|
557 |
this.limit = limit; |
|
558 |
} |
|
559 |
} |
|
560 |
||
41820 | 561 |
private static final class HandshakeFlight implements Cloneable { |
562 |
static final byte HF_UNKNOWN = HandshakeMessage.ht_not_applicable; |
|
563 |
||
564 |
byte handshakeType; // handshake type |
|
565 |
int flightEpoch; // the epoch of the first message |
|
566 |
int minMessageSeq; // minimal message sequence |
|
567 |
||
568 |
int maxMessageSeq; // maximum message sequence |
|
569 |
int maxRecordEpoch; // maximum record sequence number |
|
570 |
long maxRecordSeq; // maximum record sequence number |
|
571 |
||
572 |
HashMap<Byte, List<HoleDescriptor>> holesMap; |
|
573 |
||
574 |
HandshakeFlight() { |
|
575 |
this.handshakeType = HF_UNKNOWN; |
|
576 |
this.flightEpoch = 0; |
|
577 |
this.minMessageSeq = 0; |
|
578 |
||
579 |
this.maxMessageSeq = 0; |
|
580 |
this.maxRecordEpoch = 0; |
|
581 |
this.maxRecordSeq = -1; |
|
582 |
||
583 |
this.holesMap = new HashMap<>(5); |
|
584 |
} |
|
585 |
||
586 |
boolean isRetransmitOf(HandshakeFlight hs) { |
|
587 |
return (hs != null) && |
|
588 |
(this.handshakeType == hs.handshakeType) && |
|
589 |
(this.minMessageSeq == hs.minMessageSeq); |
|
590 |
} |
|
591 |
||
592 |
@Override |
|
593 |
public Object clone() { |
|
594 |
HandshakeFlight hf = new HandshakeFlight(); |
|
595 |
||
596 |
hf.handshakeType = this.handshakeType; |
|
597 |
hf.flightEpoch = this.flightEpoch; |
|
598 |
hf.minMessageSeq = this.minMessageSeq; |
|
599 |
||
600 |
hf.maxMessageSeq = this.maxMessageSeq; |
|
601 |
hf.maxRecordEpoch = this.maxRecordEpoch; |
|
602 |
hf.maxRecordSeq = this.maxRecordSeq; |
|
603 |
||
604 |
hf.holesMap = new HashMap<>(this.holesMap); |
|
605 |
||
606 |
return hf; |
|
607 |
} |
|
608 |
} |
|
609 |
||
30904 | 610 |
final class DTLSReassembler { |
41820 | 611 |
// The handshake epoch. |
612 |
final int handshakeEpoch; |
|
613 |
||
614 |
// The buffered fragments. |
|
30904 | 615 |
TreeSet<RecordFragment> bufferedFragments = new TreeSet<>(); |
616 |
||
41820 | 617 |
// The handshake flight in progress. |
618 |
HandshakeFlight handshakeFlight = new HandshakeFlight(); |
|
30904 | 619 |
|
41820 | 620 |
// The preceding handshake flight. |
621 |
HandshakeFlight precedingFlight = null; |
|
30904 | 622 |
|
623 |
// Epoch, sequence number and handshake message sequence of the |
|
624 |
// next message acquisition of a flight. |
|
41820 | 625 |
int nextRecordEpoch; // next record epoch |
30904 | 626 |
long nextRecordSeq = 0; // next record sequence number |
627 |
||
628 |
// Expect ChangeCipherSpec and Finished messages for the final flight. |
|
629 |
boolean expectCCSFlight = false; |
|
630 |
||
631 |
// Ready to process this flight if received all messages of the flight. |
|
632 |
boolean flightIsReady = false; |
|
633 |
boolean needToCheckFlight = false; |
|
634 |
||
41820 | 635 |
DTLSReassembler(int handshakeEpoch) { |
636 |
this.handshakeEpoch = handshakeEpoch; |
|
637 |
this.nextRecordEpoch = handshakeEpoch; |
|
30904 | 638 |
|
41820 | 639 |
this.handshakeFlight.flightEpoch = handshakeEpoch; |
30904 | 640 |
} |
641 |
||
642 |
void expectingFinishFlight() { |
|
643 |
expectCCSFlight = true; |
|
644 |
} |
|
645 |
||
41820 | 646 |
// Queue up a handshake message. |
30904 | 647 |
void queueUpHandshake(HandshakeFragment hsf) { |
41820 | 648 |
if (!isDesirable(hsf)) { |
649 |
// Not a dedired record, discard it. |
|
30904 | 650 |
return; |
651 |
} |
|
652 |
||
41820 | 653 |
// Clean up the retransmission messages if necessary. |
654 |
cleanUpRetransmit(hsf); |
|
30904 | 655 |
|
41820 | 656 |
// Is it the first message of next flight? |
657 |
// |
|
658 |
// Note: the Finished message is handled in the final CCS flight. |
|
659 |
boolean isMinimalFlightMessage = false; |
|
660 |
if (handshakeFlight.minMessageSeq == hsf.messageSeq) { |
|
661 |
isMinimalFlightMessage = true; |
|
662 |
} else if ((precedingFlight != null) && |
|
663 |
(precedingFlight.minMessageSeq == hsf.messageSeq)) { |
|
664 |
isMinimalFlightMessage = true; |
|
665 |
} |
|
666 |
||
667 |
if (isMinimalFlightMessage && (hsf.fragmentOffset == 0) && |
|
668 |
(hsf.handshakeType != HandshakeMessage.ht_finished)) { |
|
30904 | 669 |
|
41820 | 670 |
// reset the handshake flight |
671 |
handshakeFlight.handshakeType = hsf.handshakeType; |
|
672 |
handshakeFlight.flightEpoch = hsf.recordEpoch; |
|
673 |
handshakeFlight.minMessageSeq = hsf.messageSeq; |
|
674 |
} |
|
30904 | 675 |
|
41820 | 676 |
if (hsf.handshakeType == HandshakeMessage.ht_finished) { |
677 |
handshakeFlight.maxMessageSeq = hsf.messageSeq; |
|
678 |
handshakeFlight.maxRecordEpoch = hsf.recordEpoch; |
|
679 |
handshakeFlight.maxRecordSeq = hsf.recordSeq; |
|
680 |
} else { |
|
681 |
if (handshakeFlight.maxMessageSeq < hsf.messageSeq) { |
|
682 |
handshakeFlight.maxMessageSeq = hsf.messageSeq; |
|
683 |
} |
|
30904 | 684 |
|
41820 | 685 |
int n = (hsf.recordEpoch - handshakeFlight.maxRecordEpoch); |
686 |
if (n > 0) { |
|
687 |
handshakeFlight.maxRecordEpoch = hsf.recordEpoch; |
|
688 |
handshakeFlight.maxRecordSeq = hsf.recordSeq; |
|
689 |
} else if (n == 0) { |
|
690 |
// the same epoch |
|
691 |
if (handshakeFlight.maxRecordSeq < hsf.recordSeq) { |
|
692 |
handshakeFlight.maxRecordSeq = hsf.recordSeq; |
|
30904 | 693 |
} |
41820 | 694 |
} // Otherwise, it is unlikely to happen. |
30904 | 695 |
} |
696 |
||
697 |
boolean fragmented = false; |
|
698 |
if ((hsf.fragmentOffset) != 0 || |
|
699 |
(hsf.fragmentLength != hsf.messageLength)) { |
|
700 |
||
701 |
fragmented = true; |
|
702 |
} |
|
703 |
||
41820 | 704 |
List<HoleDescriptor> holes = |
705 |
handshakeFlight.holesMap.get(hsf.handshakeType); |
|
30904 | 706 |
if (holes == null) { |
707 |
if (!fragmented) { |
|
708 |
holes = Collections.emptyList(); |
|
709 |
} else { |
|
710 |
holes = new LinkedList<HoleDescriptor>(); |
|
711 |
holes.add(new HoleDescriptor(0, hsf.messageLength)); |
|
712 |
} |
|
41820 | 713 |
handshakeFlight.holesMap.put(hsf.handshakeType, holes); |
30904 | 714 |
} else if (holes.isEmpty()) { |
715 |
// Have got the full handshake message. This record may be |
|
716 |
// a handshake message retransmission. Discard this record. |
|
717 |
// |
|
718 |
// It's OK to discard retransmission as the handshake hash |
|
719 |
// is computed as if each handshake message had been sent |
|
720 |
// as a single fragment. |
|
41820 | 721 |
if (debug != null && Debug.isOn("verbose")) { |
722 |
Debug.log("Have got the full message, discard it."); |
|
30904 | 723 |
} |
724 |
||
41820 | 725 |
return; |
30904 | 726 |
} |
727 |
||
728 |
if (fragmented) { |
|
729 |
int fragmentLimit = hsf.fragmentOffset + hsf.fragmentLength; |
|
730 |
for (int i = 0; i < holes.size(); i++) { |
|
731 |
||
732 |
HoleDescriptor hole = holes.get(i); |
|
733 |
if ((hole.limit <= hsf.fragmentOffset) || |
|
734 |
(hole.offset >= fragmentLimit)) { |
|
735 |
// Also discard overlapping handshake retransmissions. |
|
736 |
continue; |
|
737 |
} |
|
738 |
||
739 |
// The ranges SHOULD NOT overlap. |
|
740 |
if (((hole.offset > hsf.fragmentOffset) && |
|
741 |
(hole.offset < fragmentLimit)) || |
|
742 |
((hole.limit > hsf.fragmentOffset) && |
|
743 |
(hole.limit < fragmentLimit))) { |
|
744 |
||
745 |
if (debug != null && Debug.isOn("ssl")) { |
|
41820 | 746 |
Debug.log("Discard invalid record: " + |
30904 | 747 |
"handshake fragment ranges are overlapping"); |
748 |
} |
|
749 |
||
750 |
// invalid, discard it [section 4.1.2.7, RFC 6347] |
|
751 |
return; |
|
752 |
} |
|
753 |
||
754 |
// This record interacts with this hole, fill the hole. |
|
755 |
holes.remove(i); |
|
756 |
// i--; |
|
757 |
||
758 |
if (hsf.fragmentOffset > hole.offset) { |
|
759 |
holes.add(new HoleDescriptor( |
|
760 |
hole.offset, hsf.fragmentOffset)); |
|
761 |
// i++; |
|
762 |
} |
|
763 |
||
764 |
if (fragmentLimit < hole.limit) { |
|
765 |
holes.add(new HoleDescriptor( |
|
766 |
fragmentLimit, hole.limit)); |
|
767 |
// i++; |
|
768 |
} |
|
769 |
||
770 |
// As no ranges overlap, no interact with other holes. |
|
771 |
break; |
|
772 |
} |
|
773 |
} |
|
774 |
||
41820 | 775 |
// buffer this fragment |
776 |
if (hsf.handshakeType == HandshakeMessage.ht_finished) { |
|
777 |
// Need no status update. |
|
778 |
bufferedFragments.add(hsf); |
|
779 |
} else { |
|
780 |
bufferFragment(hsf); |
|
30904 | 781 |
} |
782 |
} |
|
783 |
||
41820 | 784 |
// Queue up a ChangeCipherSpec message |
785 |
void queueUpChangeCipherSpec(RecordFragment rf) { |
|
786 |
if (!isDesirable(rf)) { |
|
787 |
// Not a dedired record, discard it. |
|
30904 | 788 |
return; |
789 |
} |
|
790 |
||
41820 | 791 |
// Clean up the retransmission messages if necessary. |
792 |
cleanUpRetransmit(rf); |
|
30904 | 793 |
|
41820 | 794 |
// Is it the first message of this flight? |
795 |
// |
|
796 |
// Note: the first message of the final flight is ChangeCipherSpec. |
|
797 |
if (expectCCSFlight) { |
|
798 |
handshakeFlight.handshakeType = HandshakeFlight.HF_UNKNOWN; |
|
799 |
handshakeFlight.flightEpoch = rf.recordEpoch; |
|
800 |
} |
|
801 |
||
802 |
// The epoch should be the same as the first message of the flight. |
|
803 |
if (handshakeFlight.maxRecordSeq < rf.recordSeq) { |
|
804 |
handshakeFlight.maxRecordSeq = rf.recordSeq; |
|
30904 | 805 |
} |
806 |
||
41820 | 807 |
// buffer this fragment |
808 |
bufferFragment(rf); |
|
809 |
} |
|
810 |
||
811 |
// Queue up a ciphertext message. |
|
812 |
// |
|
813 |
// Note: not yet be able to decrypt the message. |
|
814 |
void queueUpFragment(RecordFragment rf) { |
|
815 |
if (!isDesirable(rf)) { |
|
816 |
// Not a dedired record, discard it. |
|
817 |
return; |
|
818 |
} |
|
819 |
||
820 |
// Clean up the retransmission messages if necessary. |
|
821 |
cleanUpRetransmit(rf); |
|
822 |
||
823 |
// buffer this fragment |
|
824 |
bufferFragment(rf); |
|
825 |
} |
|
826 |
||
827 |
private void bufferFragment(RecordFragment rf) { |
|
30904 | 828 |
// append this fragment |
829 |
bufferedFragments.add(rf); |
|
830 |
||
831 |
if (flightIsReady) { |
|
832 |
flightIsReady = false; |
|
833 |
} |
|
41820 | 834 |
|
835 |
if (!needToCheckFlight) { |
|
836 |
needToCheckFlight = true; |
|
837 |
} |
|
838 |
} |
|
839 |
||
840 |
private void cleanUpRetransmit(RecordFragment rf) { |
|
841 |
// Does the next flight start? |
|
842 |
boolean isNewFlight = false; |
|
843 |
if (precedingFlight != null) { |
|
844 |
if (precedingFlight.flightEpoch < rf.recordEpoch) { |
|
845 |
isNewFlight = true; |
|
846 |
} else { |
|
847 |
if (rf instanceof HandshakeFragment) { |
|
848 |
HandshakeFragment hsf = (HandshakeFragment)rf; |
|
849 |
if (precedingFlight.maxMessageSeq < hsf.messageSeq) { |
|
850 |
isNewFlight = true; |
|
851 |
} |
|
852 |
} else if (rf.contentType != Record.ct_change_cipher_spec) { |
|
853 |
// ciphertext |
|
854 |
if (precedingFlight.maxRecordEpoch < rf.recordEpoch) { |
|
855 |
isNewFlight = true; |
|
856 |
} |
|
857 |
} |
|
858 |
} |
|
859 |
} |
|
860 |
||
861 |
if (!isNewFlight) { |
|
862 |
// Need no cleanup. |
|
863 |
return; |
|
864 |
} |
|
865 |
||
866 |
// clean up the buffer |
|
867 |
for (Iterator<RecordFragment> it = bufferedFragments.iterator(); |
|
868 |
it.hasNext();) { |
|
869 |
||
870 |
RecordFragment frag = it.next(); |
|
871 |
boolean isOld = false; |
|
872 |
if (frag.recordEpoch < precedingFlight.maxRecordEpoch) { |
|
873 |
isOld = true; |
|
874 |
} else if (frag.recordEpoch == precedingFlight.maxRecordEpoch) { |
|
875 |
if (frag.recordSeq <= precedingFlight.maxRecordSeq) { |
|
876 |
isOld = true; |
|
877 |
} |
|
878 |
} |
|
879 |
||
880 |
if (!isOld && (frag instanceof HandshakeFragment)) { |
|
881 |
HandshakeFragment hsf = (HandshakeFragment)frag; |
|
882 |
isOld = (hsf.messageSeq <= precedingFlight.maxMessageSeq); |
|
883 |
} |
|
884 |
||
885 |
if (isOld) { |
|
886 |
it.remove(); |
|
887 |
} else { |
|
888 |
// Safe to break as items in the buffer are ordered. |
|
889 |
break; |
|
890 |
} |
|
891 |
} |
|
892 |
||
893 |
// discard retransmissions of the previous flight if any. |
|
894 |
precedingFlight = null; |
|
30904 | 895 |
} |
896 |
||
41820 | 897 |
// Is a desired record? |
898 |
// |
|
899 |
// Check for retransmission and lost records. |
|
900 |
private boolean isDesirable(RecordFragment rf) { |
|
901 |
// |
|
902 |
// Discard records old than the previous epoch. |
|
903 |
// |
|
904 |
int previousEpoch = nextRecordEpoch - 1; |
|
905 |
if (rf.recordEpoch < previousEpoch) { |
|
906 |
// Too old to use, discard this record. |
|
907 |
if (debug != null && Debug.isOn("verbose")) { |
|
908 |
Debug.log("Too old epoch to use this record, discard it."); |
|
909 |
} |
|
910 |
||
911 |
return false; |
|
912 |
} |
|
913 |
||
914 |
// |
|
915 |
// Allow retransmission of last flight of the previous epoch |
|
916 |
// |
|
917 |
// For example, the last server delivered flight for session |
|
918 |
// resuming abbreviated handshaking consist three messages: |
|
919 |
// ServerHello |
|
920 |
// [ChangeCipherSpec] |
|
921 |
// Finished |
|
922 |
// |
|
923 |
// The epoch number is incremented and the sequence number is reset |
|
924 |
// if the ChangeCipherSpec is sent. |
|
925 |
if (rf.recordEpoch == previousEpoch) { |
|
926 |
boolean isDesired = true; |
|
927 |
if (precedingFlight == null) { |
|
928 |
isDesired = false; |
|
929 |
} else { |
|
930 |
if (rf instanceof HandshakeFragment) { |
|
931 |
HandshakeFragment hsf = (HandshakeFragment)rf; |
|
932 |
if (precedingFlight.minMessageSeq > hsf.messageSeq) { |
|
933 |
isDesired = false; |
|
934 |
} |
|
935 |
} else if (rf.contentType == Record.ct_change_cipher_spec) { |
|
936 |
// ChangeCipherSpec |
|
937 |
if (precedingFlight.flightEpoch != rf.recordEpoch) { |
|
938 |
isDesired = false; |
|
939 |
} |
|
940 |
} else { // ciphertext |
|
941 |
if ((rf.recordEpoch < precedingFlight.maxRecordEpoch) || |
|
942 |
(rf.recordEpoch == precedingFlight.maxRecordEpoch && |
|
943 |
rf.recordSeq <= precedingFlight.maxRecordSeq)) { |
|
944 |
isDesired = false; |
|
945 |
} |
|
946 |
} |
|
947 |
} |
|
948 |
||
949 |
if (!isDesired) { |
|
950 |
// Too old to use, discard this retransmitted record |
|
951 |
if (debug != null && Debug.isOn("verbose")) { |
|
952 |
Debug.log("Too old retransmission to use, discard it."); |
|
953 |
} |
|
954 |
||
955 |
return false; |
|
956 |
} |
|
957 |
} else if ((rf.recordEpoch == nextRecordEpoch) && |
|
958 |
(nextRecordSeq > rf.recordSeq)) { |
|
959 |
||
960 |
// Previously disordered record for the current epoch. |
|
961 |
// |
|
962 |
// Should has been retransmitted. Discard this record. |
|
963 |
if (debug != null && Debug.isOn("verbose")) { |
|
964 |
Debug.log("Lagging behind record (sequence), discard it."); |
|
965 |
} |
|
966 |
||
967 |
return false; |
|
968 |
} |
|
969 |
||
970 |
return true; |
|
971 |
} |
|
972 |
||
973 |
private boolean isEmpty() { |
|
30904 | 974 |
return (bufferedFragments.isEmpty() || |
975 |
(!flightIsReady && !needToCheckFlight) || |
|
976 |
(needToCheckFlight && !flightIsReady())); |
|
977 |
} |
|
978 |
||
979 |
Plaintext acquirePlaintext() { |
|
980 |
if (bufferedFragments.isEmpty()) { |
|
41820 | 981 |
if (debug != null && Debug.isOn("verbose")) { |
982 |
Debug.log("No received handshake messages"); |
|
30904 | 983 |
} |
984 |
return null; |
|
985 |
} |
|
986 |
||
987 |
if (!flightIsReady && needToCheckFlight) { |
|
988 |
// check the fligth status |
|
989 |
flightIsReady = flightIsReady(); |
|
990 |
||
41820 | 991 |
// Reset if this flight is ready. |
30904 | 992 |
if (flightIsReady) { |
41820 | 993 |
// Retransmitted handshake messages are not needed for |
994 |
// further handshaking processing. |
|
995 |
if (handshakeFlight.isRetransmitOf(precedingFlight)) { |
|
996 |
// cleanup |
|
997 |
bufferedFragments.clear(); |
|
998 |
||
999 |
// Reset the next handshake flight. |
|
1000 |
resetHandshakeFlight(precedingFlight); |
|
1001 |
||
1002 |
if (debug != null && Debug.isOn("verbose")) { |
|
1003 |
Debug.log("Received a retransmission flight."); |
|
1004 |
} |
|
1005 |
||
1006 |
return Plaintext.PLAINTEXT_NULL; |
|
1007 |
} |
|
30904 | 1008 |
} |
1009 |
||
1010 |
needToCheckFlight = false; |
|
1011 |
} |
|
1012 |
||
1013 |
if (!flightIsReady) { |
|
41820 | 1014 |
if (debug != null && Debug.isOn("verbose")) { |
1015 |
Debug.log("The handshake flight is not ready to use: " + |
|
1016 |
handshakeFlight.handshakeType); |
|
1017 |
} |
|
30904 | 1018 |
return null; |
1019 |
} |
|
1020 |
||
1021 |
RecordFragment rFrag = bufferedFragments.first(); |
|
41820 | 1022 |
Plaintext plaintext; |
30904 | 1023 |
if (!rFrag.isCiphertext) { |
1024 |
// handshake message, or ChangeCipherSpec message |
|
41820 | 1025 |
plaintext = acquireHandshakeMessage(); |
1026 |
||
1027 |
// Reset the handshake flight. |
|
1028 |
if (bufferedFragments.isEmpty()) { |
|
1029 |
// Need not to backup the holes map. Clear up it at first. |
|
1030 |
handshakeFlight.holesMap.clear(); // cleanup holes map |
|
1031 |
||
1032 |
// Update the preceding flight. |
|
1033 |
precedingFlight = (HandshakeFlight)handshakeFlight.clone(); |
|
1034 |
||
1035 |
// Reset the next handshake flight. |
|
1036 |
resetHandshakeFlight(precedingFlight); |
|
1037 |
||
1038 |
if (expectCCSFlight && |
|
1039 |
(precedingFlight.flightEpoch == |
|
1040 |
HandshakeFlight.HF_UNKNOWN)) { |
|
1041 |
expectCCSFlight = false; |
|
1042 |
} |
|
1043 |
} |
|
30904 | 1044 |
} else { |
1045 |
// a Finished message or other ciphertexts |
|
41820 | 1046 |
plaintext = acquireCachedMessage(); |
30904 | 1047 |
} |
41820 | 1048 |
|
1049 |
return plaintext; |
|
1050 |
} |
|
1051 |
||
1052 |
// |
|
1053 |
// Reset the handshake flight from a previous one. |
|
1054 |
// |
|
1055 |
private void resetHandshakeFlight(HandshakeFlight prev) { |
|
1056 |
// Reset the next handshake flight. |
|
1057 |
handshakeFlight.handshakeType = HandshakeFlight.HF_UNKNOWN; |
|
1058 |
handshakeFlight.flightEpoch = prev.maxRecordEpoch; |
|
1059 |
if (prev.flightEpoch != prev.maxRecordEpoch) { |
|
1060 |
// a new epoch starts |
|
1061 |
handshakeFlight.minMessageSeq = 0; |
|
1062 |
} else { |
|
1063 |
// stay at the same epoch |
|
1064 |
// |
|
1065 |
// The minimal message sequence number will get updated if |
|
1066 |
// a flight retransmission happens. |
|
1067 |
handshakeFlight.minMessageSeq = prev.maxMessageSeq + 1; |
|
1068 |
} |
|
1069 |
||
1070 |
// cleanup the maximum sequence number and epoch number. |
|
1071 |
// |
|
1072 |
// Note: actually, we need to do nothing because the reassembler |
|
1073 |
// of handshake messages will reset them properly even for |
|
1074 |
// retransmissions. |
|
1075 |
// |
|
1076 |
handshakeFlight.maxMessageSeq = 0; |
|
1077 |
handshakeFlight.maxRecordEpoch = handshakeFlight.flightEpoch; |
|
1078 |
||
1079 |
// Record sequence number cannot wrap even for retransmissions. |
|
1080 |
handshakeFlight.maxRecordSeq = prev.maxRecordSeq + 1; |
|
1081 |
||
1082 |
// cleanup holes map |
|
1083 |
handshakeFlight.holesMap.clear(); |
|
1084 |
||
1085 |
// Ready to accept new input record. |
|
1086 |
flightIsReady = false; |
|
1087 |
needToCheckFlight = false; |
|
30904 | 1088 |
} |
1089 |
||
1090 |
private Plaintext acquireCachedMessage() { |
|
1091 |
||
1092 |
RecordFragment rFrag = bufferedFragments.first(); |
|
1093 |
if (readEpoch != rFrag.recordEpoch) { |
|
1094 |
if (readEpoch > rFrag.recordEpoch) { |
|
1095 |
// discard old records |
|
41820 | 1096 |
if (debug != null && Debug.isOn("verbose")) { |
1097 |
Debug.log("Discard old buffered ciphertext fragments."); |
|
1098 |
} |
|
30904 | 1099 |
bufferedFragments.remove(rFrag); // popup the fragment |
1100 |
} |
|
1101 |
||
1102 |
// reset the flight |
|
1103 |
if (flightIsReady) { |
|
1104 |
flightIsReady = false; |
|
1105 |
} |
|
41820 | 1106 |
|
1107 |
if (debug != null && Debug.isOn("verbose")) { |
|
1108 |
Debug.log("Not yet ready to decrypt the cached fragments."); |
|
1109 |
} |
|
30904 | 1110 |
return null; |
1111 |
} |
|
1112 |
||
1113 |
bufferedFragments.remove(rFrag); // popup the fragment |
|
1114 |
||
1115 |
ByteBuffer fragment = ByteBuffer.wrap(rFrag.fragment); |
|
1116 |
ByteBuffer plaintextFragment = null; |
|
1117 |
try { |
|
1118 |
plaintextFragment = decrypt(readAuthenticator, readCipher, |
|
1119 |
rFrag.contentType, fragment, rFrag.recordEnS); |
|
1120 |
} catch (BadPaddingException bpe) { |
|
41820 | 1121 |
if (debug != null && Debug.isOn("verbose")) { |
1122 |
Debug.log("Discard invalid record: " + bpe); |
|
30904 | 1123 |
} |
1124 |
||
1125 |
// invalid, discard this record [section 4.1.2.7, RFC 6347] |
|
1126 |
return null; |
|
1127 |
} |
|
1128 |
||
1129 |
// The ciphtext handshake message can only be Finished (the |
|
1130 |
// end of this flight), ClinetHello or HelloRequest (the |
|
1131 |
// beginning of the next flight) message. Need not to check |
|
1132 |
// any ChangeCipherSpec message. |
|
1133 |
if (rFrag.contentType == Record.ct_handshake) { |
|
1134 |
while (plaintextFragment.remaining() > 0) { |
|
1135 |
HandshakeFragment hsFrag = parseHandshakeMessage( |
|
1136 |
rFrag.contentType, |
|
1137 |
rFrag.majorVersion, rFrag.minorVersion, |
|
1138 |
rFrag.recordEnS, rFrag.recordEpoch, rFrag.recordSeq, |
|
1139 |
plaintextFragment); |
|
1140 |
||
1141 |
if (hsFrag == null) { |
|
1142 |
// invalid, discard this record |
|
41820 | 1143 |
if (debug != null && Debug.isOn("verbose")) { |
1144 |
Debug.printHex( |
|
1145 |
"Invalid handshake fragment, discard it", |
|
1146 |
plaintextFragment); |
|
1147 |
} |
|
30904 | 1148 |
return null; |
1149 |
} |
|
1150 |
||
41820 | 1151 |
queueUpHandshake(hsFrag); |
1152 |
// The flight ready status (flightIsReady) should have |
|
1153 |
// been checked and updated for the Finished handshake |
|
1154 |
// message before the decryption. Please don't update |
|
1155 |
// flightIsReady for Finished messages. |
|
1156 |
if (hsFrag.handshakeType != HandshakeMessage.ht_finished) { |
|
1157 |
flightIsReady = false; |
|
1158 |
needToCheckFlight = true; |
|
30904 | 1159 |
} |
1160 |
} |
|
1161 |
||
41820 | 1162 |
return acquirePlaintext(); |
30904 | 1163 |
} else { |
1164 |
return new Plaintext(rFrag.contentType, |
|
1165 |
rFrag.majorVersion, rFrag.minorVersion, |
|
41820 | 1166 |
rFrag.recordEpoch, |
1167 |
Authenticator.toLong(rFrag.recordEnS), |
|
30904 | 1168 |
plaintextFragment); |
1169 |
} |
|
1170 |
} |
|
1171 |
||
1172 |
private Plaintext acquireHandshakeMessage() { |
|
1173 |
||
1174 |
RecordFragment rFrag = bufferedFragments.first(); |
|
1175 |
if (rFrag.contentType == Record.ct_change_cipher_spec) { |
|
1176 |
this.nextRecordEpoch = rFrag.recordEpoch + 1; |
|
41820 | 1177 |
|
1178 |
// For retransmissions, the next record sequence number is a |
|
1179 |
// positive value. Don't worry about it as the acquiring of |
|
1180 |
// the immediately followed Finished handshake message will |
|
1181 |
// reset the next record sequence number correctly. |
|
30904 | 1182 |
this.nextRecordSeq = 0; |
1183 |
||
41820 | 1184 |
// Popup the fragment. |
1185 |
bufferedFragments.remove(rFrag); |
|
30904 | 1186 |
|
1187 |
// Reload if this message has been reserved for handshake hash. |
|
1188 |
handshakeHash.reload(); |
|
1189 |
||
1190 |
return new Plaintext(rFrag.contentType, |
|
1191 |
rFrag.majorVersion, rFrag.minorVersion, |
|
41820 | 1192 |
rFrag.recordEpoch, |
1193 |
Authenticator.toLong(rFrag.recordEnS), |
|
30904 | 1194 |
ByteBuffer.wrap(rFrag.fragment)); |
1195 |
} else { // rFrag.contentType == Record.ct_handshake |
|
1196 |
HandshakeFragment hsFrag = (HandshakeFragment)rFrag; |
|
1197 |
if ((hsFrag.messageLength == hsFrag.fragmentLength) && |
|
1198 |
(hsFrag.fragmentOffset == 0)) { // no fragmentation |
|
1199 |
||
1200 |
bufferedFragments.remove(rFrag); // popup the fragment |
|
1201 |
||
1202 |
// this.nextRecordEpoch = hsFrag.recordEpoch; |
|
1203 |
this.nextRecordSeq = hsFrag.recordSeq + 1; |
|
1204 |
||
1205 |
// Note: may try to avoid byte array copy in the future. |
|
1206 |
byte[] recordFrag = new byte[hsFrag.messageLength + 4]; |
|
1207 |
Plaintext plaintext = new Plaintext(hsFrag.contentType, |
|
1208 |
hsFrag.majorVersion, hsFrag.minorVersion, |
|
41820 | 1209 |
hsFrag.recordEpoch, |
1210 |
Authenticator.toLong(hsFrag.recordEnS), |
|
30904 | 1211 |
ByteBuffer.wrap(recordFrag)); |
1212 |
||
1213 |
// fill the handshake fragment of the record |
|
1214 |
recordFrag[0] = hsFrag.handshakeType; |
|
1215 |
recordFrag[1] = |
|
1216 |
(byte)((hsFrag.messageLength >>> 16) & 0xFF); |
|
1217 |
recordFrag[2] = |
|
1218 |
(byte)((hsFrag.messageLength >>> 8) & 0xFF); |
|
1219 |
recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF); |
|
1220 |
||
1221 |
System.arraycopy(hsFrag.fragment, 0, |
|
1222 |
recordFrag, 4, hsFrag.fragmentLength); |
|
1223 |
||
1224 |
// handshake hashing |
|
1225 |
handshakeHashing(hsFrag, plaintext); |
|
1226 |
||
1227 |
return plaintext; |
|
1228 |
} else { // fragmented handshake message |
|
1229 |
// the first record |
|
1230 |
// |
|
1231 |
// Note: may try to avoid byte array copy in the future. |
|
1232 |
byte[] recordFrag = new byte[hsFrag.messageLength + 4]; |
|
1233 |
Plaintext plaintext = new Plaintext(hsFrag.contentType, |
|
1234 |
hsFrag.majorVersion, hsFrag.minorVersion, |
|
41820 | 1235 |
hsFrag.recordEpoch, |
1236 |
Authenticator.toLong(hsFrag.recordEnS), |
|
30904 | 1237 |
ByteBuffer.wrap(recordFrag)); |
1238 |
||
1239 |
// fill the handshake fragment of the record |
|
1240 |
recordFrag[0] = hsFrag.handshakeType; |
|
1241 |
recordFrag[1] = |
|
1242 |
(byte)((hsFrag.messageLength >>> 16) & 0xFF); |
|
1243 |
recordFrag[2] = |
|
1244 |
(byte)((hsFrag.messageLength >>> 8) & 0xFF); |
|
1245 |
recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF); |
|
1246 |
||
1247 |
int msgSeq = hsFrag.messageSeq; |
|
1248 |
long maxRecodeSN = hsFrag.recordSeq; |
|
1249 |
HandshakeFragment hmFrag = hsFrag; |
|
1250 |
do { |
|
1251 |
System.arraycopy(hmFrag.fragment, 0, |
|
1252 |
recordFrag, hmFrag.fragmentOffset + 4, |
|
1253 |
hmFrag.fragmentLength); |
|
1254 |
// popup the fragment |
|
1255 |
bufferedFragments.remove(rFrag); |
|
1256 |
||
1257 |
if (maxRecodeSN < hmFrag.recordSeq) { |
|
1258 |
maxRecodeSN = hmFrag.recordSeq; |
|
1259 |
} |
|
1260 |
||
1261 |
// Note: may buffer retransmitted fragments in order to |
|
1262 |
// speed up the reassembly in the future. |
|
1263 |
||
1264 |
// read the next buffered record |
|
1265 |
if (!bufferedFragments.isEmpty()) { |
|
1266 |
rFrag = bufferedFragments.first(); |
|
1267 |
if (rFrag.contentType != Record.ct_handshake) { |
|
1268 |
break; |
|
1269 |
} else { |
|
1270 |
hmFrag = (HandshakeFragment)rFrag; |
|
1271 |
} |
|
1272 |
} |
|
1273 |
} while (!bufferedFragments.isEmpty() && |
|
1274 |
(msgSeq == hmFrag.messageSeq)); |
|
1275 |
||
1276 |
// handshake hashing |
|
1277 |
handshakeHashing(hsFrag, plaintext); |
|
1278 |
||
1279 |
this.nextRecordSeq = maxRecodeSN + 1; |
|
1280 |
||
1281 |
return plaintext; |
|
1282 |
} |
|
1283 |
} |
|
1284 |
} |
|
1285 |
||
1286 |
boolean flightIsReady() { |
|
1287 |
||
41820 | 1288 |
byte flightType = handshakeFlight.handshakeType; |
1289 |
if (flightType == HandshakeFlight.HF_UNKNOWN) { |
|
1290 |
// |
|
1291 |
// the ChangeCipherSpec/Finished flight |
|
1292 |
// |
|
1293 |
if (expectCCSFlight) { |
|
1294 |
// Have the ChangeCipherSpec/Finished flight been received? |
|
1295 |
boolean isReady = hasFinishedMessage(bufferedFragments); |
|
1296 |
if (debug != null && Debug.isOn("verbose")) { |
|
1297 |
Debug.log( |
|
1298 |
"Has the final flight been received? " + isReady); |
|
1299 |
} |
|
30904 | 1300 |
|
41820 | 1301 |
return isReady; |
1302 |
} |
|
1303 |
||
1304 |
if (debug != null && Debug.isOn("verbose")) { |
|
1305 |
Debug.log("No flight is received yet."); |
|
1306 |
} |
|
1307 |
||
30904 | 1308 |
return false; |
1309 |
} |
|
1310 |
||
1311 |
if ((flightType == HandshakeMessage.ht_client_hello) || |
|
1312 |
(flightType == HandshakeMessage.ht_hello_request) || |
|
1313 |
(flightType == HandshakeMessage.ht_hello_verify_request)) { |
|
1314 |
||
1315 |
// single handshake message flight |
|
41820 | 1316 |
boolean isReady = hasCompleted(flightType); |
1317 |
if (debug != null && Debug.isOn("verbose")) { |
|
1318 |
Debug.log("Is the handshake message completed? " + isReady); |
|
1319 |
} |
|
1320 |
||
1321 |
return isReady; |
|
30904 | 1322 |
} |
1323 |
||
1324 |
// |
|
1325 |
// the ServerHello flight |
|
1326 |
// |
|
1327 |
if (flightType == HandshakeMessage.ht_server_hello) { |
|
1328 |
// Firstly, check the first flight handshake message. |
|
41820 | 1329 |
if (!hasCompleted(flightType)) { |
1330 |
if (debug != null && Debug.isOn("verbose")) { |
|
1331 |
Debug.log( |
|
1332 |
"The ServerHello message is not completed yet."); |
|
1333 |
} |
|
1334 |
||
30904 | 1335 |
return false; |
1336 |
} |
|
1337 |
||
1338 |
// |
|
1339 |
// an abbreviated handshake |
|
1340 |
// |
|
41820 | 1341 |
if (hasFinishedMessage(bufferedFragments)) { |
1342 |
if (debug != null && Debug.isOn("verbose")) { |
|
1343 |
Debug.log("It's an abbreviated handshake."); |
|
1344 |
} |
|
1345 |
||
1346 |
return true; |
|
30904 | 1347 |
} |
1348 |
||
1349 |
// |
|
1350 |
// a full handshake |
|
1351 |
// |
|
41820 | 1352 |
List<HoleDescriptor> holes = handshakeFlight.holesMap.get( |
1353 |
HandshakeMessage.ht_server_hello_done); |
|
1354 |
if ((holes == null) || !holes.isEmpty()) { |
|
30904 | 1355 |
// Not yet got the final message of the flight. |
41820 | 1356 |
if (debug != null && Debug.isOn("verbose")) { |
1357 |
Debug.log("Not yet got the ServerHelloDone message"); |
|
1358 |
} |
|
1359 |
||
30904 | 1360 |
return false; |
1361 |
} |
|
1362 |
||
1363 |
// Have all handshake message been received? |
|
41820 | 1364 |
boolean isReady = hasCompleted(bufferedFragments, |
1365 |
handshakeFlight.minMessageSeq, |
|
1366 |
handshakeFlight.maxMessageSeq); |
|
1367 |
if (debug != null && Debug.isOn("verbose")) { |
|
1368 |
Debug.log("Is the ServerHello flight (message " + |
|
1369 |
handshakeFlight.minMessageSeq + "-" + |
|
1370 |
handshakeFlight.maxMessageSeq + |
|
1371 |
") completed? " + isReady); |
|
1372 |
} |
|
1373 |
||
1374 |
return isReady; |
|
30904 | 1375 |
} |
1376 |
||
1377 |
// |
|
1378 |
// the ClientKeyExchange flight |
|
1379 |
// |
|
1380 |
// Note: need to consider more messages in this flight if |
|
1381 |
// ht_supplemental_data and ht_certificate_url are |
|
1382 |
// suppported in the future. |
|
1383 |
// |
|
1384 |
if ((flightType == HandshakeMessage.ht_certificate) || |
|
1385 |
(flightType == HandshakeMessage.ht_client_key_exchange)) { |
|
1386 |
||
1387 |
// Firstly, check the first flight handshake message. |
|
41820 | 1388 |
if (!hasCompleted(flightType)) { |
1389 |
if (debug != null && Debug.isOn("verbose")) { |
|
1390 |
Debug.log( |
|
1391 |
"The ClientKeyExchange or client Certificate " + |
|
1392 |
"message is not completed yet."); |
|
1393 |
} |
|
30904 | 1394 |
|
1395 |
return false; |
|
1396 |
} |
|
1397 |
||
41820 | 1398 |
// Is client CertificateVerify a mandatory message? |
1399 |
if (flightType == HandshakeMessage.ht_certificate) { |
|
1400 |
if (needClientVerify(bufferedFragments) && |
|
1401 |
!hasCompleted(ht_certificate_verify)) { |
|
1402 |
||
1403 |
if (debug != null && Debug.isOn("verbose")) { |
|
1404 |
Debug.log( |
|
1405 |
"Not yet have the CertificateVerify message"); |
|
1406 |
} |
|
1407 |
||
1408 |
return false; |
|
1409 |
} |
|
30904 | 1410 |
} |
1411 |
||
41820 | 1412 |
if (!hasFinishedMessage(bufferedFragments)) { |
1413 |
// not yet have the ChangeCipherSpec/Finished messages |
|
1414 |
if (debug != null && Debug.isOn("verbose")) { |
|
1415 |
Debug.log( |
|
1416 |
"Not yet have the ChangeCipherSpec and " + |
|
1417 |
"Finished messages"); |
|
1418 |
} |
|
1419 |
||
30904 | 1420 |
return false; |
1421 |
} |
|
1422 |
||
1423 |
// Have all handshake message been received? |
|
41820 | 1424 |
boolean isReady = hasCompleted(bufferedFragments, |
1425 |
handshakeFlight.minMessageSeq, |
|
1426 |
handshakeFlight.maxMessageSeq); |
|
1427 |
if (debug != null && Debug.isOn("verbose")) { |
|
1428 |
Debug.log("Is the ClientKeyExchange flight (message " + |
|
1429 |
handshakeFlight.minMessageSeq + "-" + |
|
1430 |
handshakeFlight.maxMessageSeq + |
|
1431 |
") completed? " + isReady); |
|
1432 |
} |
|
1433 |
||
1434 |
return isReady; |
|
30904 | 1435 |
} |
1436 |
||
1437 |
// |
|
1438 |
// Otherwise, need to receive more handshake messages. |
|
1439 |
// |
|
41820 | 1440 |
if (debug != null && Debug.isOn("verbose")) { |
1441 |
Debug.log("Need to receive more handshake messages"); |
|
30904 | 1442 |
} |
1443 |
||
1444 |
return false; |
|
1445 |
} |
|
1446 |
||
1447 |
// Looking for the ChangeCipherSpec and Finished messages. |
|
1448 |
// |
|
1449 |
// As the cached Finished message should be a ciphertext, we don't |
|
1450 |
// exactly know a ciphertext is a Finished message or not. According |
|
1451 |
// to the spec of TLS/DTLS handshaking, a Finished message is always |
|
1452 |
// sent immediately after a ChangeCipherSpec message. The first |
|
1453 |
// ciphertext handshake message should be the expected Finished message. |
|
41820 | 1454 |
private boolean hasFinishedMessage(Set<RecordFragment> fragments) { |
30904 | 1455 |
|
1456 |
boolean hasCCS = false; |
|
1457 |
boolean hasFin = false; |
|
1458 |
for (RecordFragment fragment : fragments) { |
|
1459 |
if (fragment.contentType == Record.ct_change_cipher_spec) { |
|
1460 |
if (hasFin) { |
|
1461 |
return true; |
|
1462 |
} |
|
1463 |
hasCCS = true; |
|
1464 |
} else if (fragment.contentType == Record.ct_handshake) { |
|
1465 |
// Finished is the first expected message of a new epoch. |
|
1466 |
if (fragment.isCiphertext) { |
|
1467 |
if (hasCCS) { |
|
1468 |
return true; |
|
1469 |
} |
|
1470 |
hasFin = true; |
|
1471 |
} |
|
1472 |
} |
|
1473 |
} |
|
1474 |
||
1475 |
return hasFin && hasCCS; |
|
1476 |
} |
|
1477 |
||
41820 | 1478 |
// Is client CertificateVerify a mandatory message? |
1479 |
// |
|
1480 |
// In the current implementation, client CertificateVerify is a |
|
1481 |
// mandatory message if the client Certificate is not empty. |
|
1482 |
private boolean needClientVerify(Set<RecordFragment> fragments) { |
|
1483 |
||
1484 |
// The caller should have checked the completion of the first |
|
1485 |
// present handshake message. Need not to check it again. |
|
1486 |
for (RecordFragment rFrag : fragments) { |
|
1487 |
if ((rFrag.contentType != Record.ct_handshake) || |
|
1488 |
rFrag.isCiphertext) { |
|
1489 |
break; |
|
1490 |
} |
|
1491 |
||
1492 |
HandshakeFragment hsFrag = (HandshakeFragment)rFrag; |
|
1493 |
if (hsFrag.handshakeType != HandshakeMessage.ht_certificate) { |
|
1494 |
continue; |
|
1495 |
} |
|
1496 |
||
1497 |
return (rFrag.fragment != null) && |
|
1498 |
(rFrag.fragment.length > DTLSRecord.minCertPlaintextSize); |
|
1499 |
} |
|
1500 |
||
1501 |
return false; |
|
1502 |
} |
|
1503 |
||
1504 |
private boolean hasCompleted(byte handshakeType) { |
|
1505 |
List<HoleDescriptor> holes = |
|
1506 |
handshakeFlight.holesMap.get(handshakeType); |
|
30904 | 1507 |
if (holes == null) { |
1508 |
// not yet received this kind of handshake message |
|
1509 |
return false; |
|
1510 |
} |
|
1511 |
||
1512 |
return holes.isEmpty(); // no fragment hole for complete message |
|
1513 |
} |
|
1514 |
||
1515 |
private boolean hasCompleted( |
|
1516 |
Set<RecordFragment> fragments, |
|
1517 |
int presentMsgSeq, int endMsgSeq) { |
|
1518 |
||
1519 |
// The caller should have checked the completion of the first |
|
1520 |
// present handshake message. Need not to check it again. |
|
1521 |
for (RecordFragment rFrag : fragments) { |
|
1522 |
if ((rFrag.contentType != Record.ct_handshake) || |
|
1523 |
rFrag.isCiphertext) { |
|
1524 |
break; |
|
1525 |
} |
|
1526 |
||
1527 |
HandshakeFragment hsFrag = (HandshakeFragment)rFrag; |
|
1528 |
if (hsFrag.messageSeq == presentMsgSeq) { |
|
1529 |
continue; |
|
1530 |
} else if (hsFrag.messageSeq == (presentMsgSeq + 1)) { |
|
1531 |
// check the completion of the handshake message |
|
41820 | 1532 |
if (!hasCompleted(hsFrag.handshakeType)) { |
30904 | 1533 |
return false; |
1534 |
} |
|
1535 |
||
1536 |
presentMsgSeq = hsFrag.messageSeq; |
|
1537 |
} else { |
|
1538 |
// not yet got handshake message next to presentMsgSeq |
|
1539 |
break; |
|
1540 |
} |
|
1541 |
} |
|
1542 |
||
1543 |
return (presentMsgSeq >= endMsgSeq); |
|
1544 |
// false: if not yet got all messages of the flight. |
|
1545 |
} |
|
1546 |
||
1547 |
private void handshakeHashing( |
|
1548 |
HandshakeFragment hsFrag, Plaintext plaintext) { |
|
1549 |
||
1550 |
byte hsType = hsFrag.handshakeType; |
|
1551 |
if ((hsType == HandshakeMessage.ht_hello_request) || |
|
1552 |
(hsType == HandshakeMessage.ht_hello_verify_request)) { |
|
1553 |
||
1554 |
// omitted from handshake hash computation |
|
1555 |
return; |
|
1556 |
} |
|
1557 |
||
1558 |
if ((hsFrag.messageSeq == 0) && |
|
1559 |
(hsType == HandshakeMessage.ht_client_hello)) { |
|
1560 |
||
1561 |
// omit initial ClientHello message |
|
1562 |
// |
|
1563 |
// 4: handshake header |
|
1564 |
// 2: ClientHello.client_version |
|
1565 |
// 32: ClientHello.random |
|
1566 |
int sidLen = plaintext.fragment.get(38); |
|
1567 |
||
1568 |
if (sidLen == 0) { // empty session_id, initial handshake |
|
1569 |
return; |
|
1570 |
} |
|
1571 |
} |
|
1572 |
||
1573 |
// calculate the DTLS header |
|
1574 |
byte[] temporary = new byte[12]; // 12: handshake header size |
|
1575 |
||
1576 |
// Handshake.msg_type |
|
1577 |
temporary[0] = hsFrag.handshakeType; |
|
1578 |
||
1579 |
// Handshake.length |
|
1580 |
temporary[1] = (byte)((hsFrag.messageLength >> 16) & 0xFF); |
|
1581 |
temporary[2] = (byte)((hsFrag.messageLength >> 8) & 0xFF); |
|
1582 |
temporary[3] = (byte)(hsFrag.messageLength & 0xFF); |
|
1583 |
||
1584 |
// Handshake.message_seq |
|
1585 |
temporary[4] = (byte)((hsFrag.messageSeq >> 8) & 0xFF); |
|
1586 |
temporary[5] = (byte)(hsFrag.messageSeq & 0xFF); |
|
1587 |
||
1588 |
// Handshake.fragment_offset |
|
1589 |
temporary[6] = 0; |
|
1590 |
temporary[7] = 0; |
|
1591 |
temporary[8] = 0; |
|
1592 |
||
1593 |
// Handshake.fragment_length |
|
1594 |
temporary[9] = temporary[1]; |
|
1595 |
temporary[10] = temporary[2]; |
|
1596 |
temporary[11] = temporary[3]; |
|
1597 |
||
1598 |
plaintext.fragment.position(4); // ignore the TLS header |
|
1599 |
if ((hsType != HandshakeMessage.ht_finished) && |
|
1600 |
(hsType != HandshakeMessage.ht_certificate_verify)) { |
|
1601 |
||
1602 |
if (handshakeHash == null) { |
|
1603 |
// used for cache only |
|
1604 |
handshakeHash = new HandshakeHash(false); |
|
1605 |
} |
|
1606 |
handshakeHash.update(temporary, 0, 12); |
|
1607 |
handshakeHash.update(plaintext.fragment); |
|
1608 |
} else { |
|
1609 |
// Reserve until this handshake message has been processed. |
|
1610 |
if (handshakeHash == null) { |
|
1611 |
// used for cache only |
|
1612 |
handshakeHash = new HandshakeHash(false); |
|
1613 |
} |
|
1614 |
handshakeHash.reserve(temporary, 0, 12); |
|
1615 |
handshakeHash.reserve(plaintext.fragment); |
|
1616 |
} |
|
1617 |
plaintext.fragment.position(0); // restore the position |
|
1618 |
} |
|
1619 |
} |
|
1620 |
} |
|
1621 |