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
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
35 import sun.security.util.HexDumpEncoder;
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();
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
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;
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
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 }
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)) {
|
1 /*
2 * Copyright (c) 2015, 2016, 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
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
35 import sun.security.util.HexDumpEncoder;
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();
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) {
113 return reassembler.acquirePlaintext();
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
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
149 int contentLen = ((packet.get() & 0xFF) << 8) |
150 (packet.get() & 0xFF); // pos: 11, 12
151
152 if (debug != null && Debug.isOn("record")) {
153 Debug.log("READ: " +
154 ProtocolVersion.valueOf(majorVersion, minorVersion) +
155 " " + Record.contentName(contentType) + ", length = " +
156 contentLen);
157 }
158
159 int recLim = srcPos + DTLSRecord.headerSize + contentLen;
160
161 if (this.prevReadEpoch > recordEpoch) {
162 // Reset the position of the packet buffer.
163 packet.position(recLim);
164 if (debug != null && Debug.isOn("record")) {
165 Debug.printHex("READ: discard this old record", recordEnS);
166 }
167 return null;
168 }
169
170 // Buffer next epoch message if necessary.
171 if (this.readEpoch < recordEpoch) {
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
179 packet.position(recLim);
180
181 if (debug != null && Debug.isOn("verbose")) {
182 Debug.log("Premature record (epoch), discard it.");
183 }
184
185 return null;
186 }
187
188 // Not ready to decrypt this record, may be an encrypted Finished
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
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
248 reassembler = null;
249 }
250
251 return new Plaintext(contentType, majorVersion, minorVersion,
252 recordEpoch, Authenticator.toLong(recordEnS),
253 plaintextFragment);
254 }
255
256 if (contentType == Record.ct_change_cipher_spec) {
257 if (reassembler == null) {
258 if (this.readEpoch != recordEpoch) {
259 // handshake has not started, should be an
260 // old handshake message, discard it.
261
262 if (debug != null && Debug.isOn("verbose")) {
263 Debug.log(
264 "Lagging behind ChangeCipherSpec, discard it.");
265 }
266
267 return null;
268 }
269
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);
284
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 }
293
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
304 return null;
305 }
306
307 reassembler = new DTLSReassembler(recordEpoch);
308 }
309
310 reassembler.queueUpHandshake(hsFrag);
311 }
312 }
313
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;
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
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")) {
381 Debug.log("Discard invalid record: " +
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")) {
407 Debug.log("Discard invalid record: " +
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;
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) {
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 }
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) {
530 // keep the insertion order of handshake messages
531 return this.messageSeq - other.messageSeq;
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;
538 }
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);
545 }
546
547 return super.compareTo(o);
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
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
610 final class DTLSReassembler {
611 // The handshake epoch.
612 final int handshakeEpoch;
613
614 // The buffered fragments.
615 TreeSet<RecordFragment> bufferedFragments = new TreeSet<>();
616
617 // The handshake flight in progress.
618 HandshakeFlight handshakeFlight = new HandshakeFlight();
619
620 // The preceding handshake flight.
621 HandshakeFlight precedingFlight = null;
622
623 // Epoch, sequence number and handshake message sequence of the
624 // next message acquisition of a flight.
625 int nextRecordEpoch; // next record epoch
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
635 DTLSReassembler(int handshakeEpoch) {
636 this.handshakeEpoch = handshakeEpoch;
637 this.nextRecordEpoch = handshakeEpoch;
638
639 this.handshakeFlight.flightEpoch = handshakeEpoch;
640 }
641
642 void expectingFinishFlight() {
643 expectCCSFlight = true;
644 }
645
646 // Queue up a handshake message.
647 void queueUpHandshake(HandshakeFragment hsf) {
648 if (!isDesirable(hsf)) {
649 // Not a dedired record, discard it.
650 return;
651 }
652
653 // Clean up the retransmission messages if necessary.
654 cleanUpRetransmit(hsf);
655
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)) {
669
670 // reset the handshake flight
671 handshakeFlight.handshakeType = hsf.handshakeType;
672 handshakeFlight.flightEpoch = hsf.recordEpoch;
673 handshakeFlight.minMessageSeq = hsf.messageSeq;
674 }
675
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 }
684
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;
693 }
694 } // Otherwise, it is unlikely to happen.
695 }
696
697 boolean fragmented = false;
698 if ((hsf.fragmentOffset) != 0 ||
699 (hsf.fragmentLength != hsf.messageLength)) {
700
701 fragmented = true;
702 }
703
704 List<HoleDescriptor> holes =
705 handshakeFlight.holesMap.get(hsf.handshakeType);
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 }
713 handshakeFlight.holesMap.put(hsf.handshakeType, holes);
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.
721 if (debug != null && Debug.isOn("verbose")) {
722 Debug.log("Have got the full message, discard it.");
723 }
724
725 return;
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")) {
746 Debug.log("Discard invalid record: " +
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
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);
781 }
782 }
783
784 // Queue up a ChangeCipherSpec message
785 void queueUpChangeCipherSpec(RecordFragment rf) {
786 if (!isDesirable(rf)) {
787 // Not a dedired record, discard it.
788 return;
789 }
790
791 // Clean up the retransmission messages if necessary.
792 cleanUpRetransmit(rf);
793
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;
805 }
806
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) {
828 // append this fragment
829 bufferedFragments.add(rf);
830
831 if (flightIsReady) {
832 flightIsReady = false;
833 }
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;
895 }
896
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() {
974 return (bufferedFragments.isEmpty() ||
975 (!flightIsReady && !needToCheckFlight) ||
976 (needToCheckFlight && !flightIsReady()));
977 }
978
979 Plaintext acquirePlaintext() {
980 if (bufferedFragments.isEmpty()) {
981 if (debug != null && Debug.isOn("verbose")) {
982 Debug.log("No received handshake messages");
983 }
984 return null;
985 }
986
987 if (!flightIsReady && needToCheckFlight) {
988 // check the fligth status
989 flightIsReady = flightIsReady();
990
991 // Reset if this flight is ready.
992 if (flightIsReady) {
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 }
1008 }
1009
1010 needToCheckFlight = false;
1011 }
1012
1013 if (!flightIsReady) {
1014 if (debug != null && Debug.isOn("verbose")) {
1015 Debug.log("The handshake flight is not ready to use: " +
1016 handshakeFlight.handshakeType);
1017 }
1018 return null;
1019 }
1020
1021 RecordFragment rFrag = bufferedFragments.first();
1022 Plaintext plaintext;
1023 if (!rFrag.isCiphertext) {
1024 // handshake message, or ChangeCipherSpec message
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 }
1044 } else {
1045 // a Finished message or other ciphertexts
1046 plaintext = acquireCachedMessage();
1047 }
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;
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
1096 if (debug != null && Debug.isOn("verbose")) {
1097 Debug.log("Discard old buffered ciphertext fragments.");
1098 }
1099 bufferedFragments.remove(rFrag); // popup the fragment
1100 }
1101
1102 // reset the flight
1103 if (flightIsReady) {
1104 flightIsReady = false;
1105 }
1106
1107 if (debug != null && Debug.isOn("verbose")) {
1108 Debug.log("Not yet ready to decrypt the cached fragments.");
1109 }
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) {
1121 if (debug != null && Debug.isOn("verbose")) {
1122 Debug.log("Discard invalid record: " + bpe);
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
1143 if (debug != null && Debug.isOn("verbose")) {
1144 Debug.printHex(
1145 "Invalid handshake fragment, discard it",
1146 plaintextFragment);
1147 }
1148 return null;
1149 }
1150
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;
1159 }
1160 }
1161
1162 return acquirePlaintext();
1163 } else {
1164 return new Plaintext(rFrag.contentType,
1165 rFrag.majorVersion, rFrag.minorVersion,
1166 rFrag.recordEpoch,
1167 Authenticator.toLong(rFrag.recordEnS),
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;
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.
1182 this.nextRecordSeq = 0;
1183
1184 // Popup the fragment.
1185 bufferedFragments.remove(rFrag);
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,
1192 rFrag.recordEpoch,
1193 Authenticator.toLong(rFrag.recordEnS),
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,
1209 hsFrag.recordEpoch,
1210 Authenticator.toLong(hsFrag.recordEnS),
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,
1235 hsFrag.recordEpoch,
1236 Authenticator.toLong(hsFrag.recordEnS),
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
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
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 }
1300
1301 return isReady;
1302 }
1303
1304 if (debug != null && Debug.isOn("verbose")) {
1305 Debug.log("No flight is received yet.");
1306 }
1307
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
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;
1322 }
1323
1324 //
1325 // the ServerHello flight
1326 //
1327 if (flightType == HandshakeMessage.ht_server_hello) {
1328 // Firstly, check the first flight handshake message.
1329 if (!hasCompleted(flightType)) {
1330 if (debug != null && Debug.isOn("verbose")) {
1331 Debug.log(
1332 "The ServerHello message is not completed yet.");
1333 }
1334
1335 return false;
1336 }
1337
1338 //
1339 // an abbreviated handshake
1340 //
1341 if (hasFinishedMessage(bufferedFragments)) {
1342 if (debug != null && Debug.isOn("verbose")) {
1343 Debug.log("It's an abbreviated handshake.");
1344 }
1345
1346 return true;
1347 }
1348
1349 //
1350 // a full handshake
1351 //
1352 List<HoleDescriptor> holes = handshakeFlight.holesMap.get(
1353 HandshakeMessage.ht_server_hello_done);
1354 if ((holes == null) || !holes.isEmpty()) {
1355 // Not yet got the final message of the flight.
1356 if (debug != null && Debug.isOn("verbose")) {
1357 Debug.log("Not yet got the ServerHelloDone message");
1358 }
1359
1360 return false;
1361 }
1362
1363 // Have all handshake message been received?
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;
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.
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 }
1394
1395 return false;
1396 }
1397
1398 if (!hasFinishedMessage(bufferedFragments)) {
1399 // not yet have the ChangeCipherSpec/Finished messages
1400 if (debug != null && Debug.isOn("verbose")) {
1401 Debug.log(
1402 "Not yet have the ChangeCipherSpec and " +
1403 "Finished messages");
1404 }
1405
1406 return false;
1407 }
1408
1409 // Have all handshake message been received?
1410 boolean isReady = hasCompleted(bufferedFragments,
1411 handshakeFlight.minMessageSeq,
1412 handshakeFlight.maxMessageSeq);
1413 if (debug != null && Debug.isOn("verbose")) {
1414 Debug.log("Is the ClientKeyExchange flight (message " +
1415 handshakeFlight.minMessageSeq + "-" +
1416 handshakeFlight.maxMessageSeq +
1417 ") completed? " + isReady);
1418 }
1419
1420 return isReady;
1421 }
1422
1423 //
1424 // Otherwise, need to receive more handshake messages.
1425 //
1426 if (debug != null && Debug.isOn("verbose")) {
1427 Debug.log("Need to receive more handshake messages");
1428 }
1429
1430 return false;
1431 }
1432
1433 private boolean isSessionResuming(
1434 byte[] fragment, byte[] prevSid) throws SSLException {
1435
1436 // As the first fragment of ServerHello should be big enough
1437 // to hold the session_id field, need not to worry about the
1438 // fragmentation here.
1439 if ((fragment == null) || (fragment.length < 38)) {
1440 // 38: the minimal ServerHello body length
1441 throw new SSLException(
1442 "Invalid ServerHello message: no sufficient data");
1443 }
1444
1445 // SessionId.MAX_LENGTH is 32.
1446 int sidLen = fragment[34]; // 34: the length field
1447 if (sidLen > 32) { // opaque SessionID<0, 32>
1448 throw new SSLException(
1449 "Invalid ServerHello message: invalid session id");
1450 }
1451
1452 if (fragment.length < 38 + sidLen) {
1453 throw new SSLException(
1454 "Invalid ServerHello message: no sufficient data");
1455 }
1456
1457 if (sidLen != 0 && (prevSid.length == sidLen)) {
1458 // may be a session-resuming handshake
1459 for (int i = 0; i < sidLen; i++) {
1460 if (prevSid[i] != fragment[35 + i]) {
1461 // 35: the session identifier
1462 return false;
1463 }
1464 }
1465
1466 return true;
1467 }
1469 return false;
1470 }
1471
1472 private byte[] getSessionID(byte[] fragment) {
1473 // The validity has been checked in the call to isSessionResuming().
1474 int sidLen = fragment[34]; // 34: the sessionID length field
1475
1476 byte[] temporary = new byte[sidLen];
1477 System.arraycopy(fragment, 35, temporary, 0, sidLen);
1478
1479 return temporary;
1480 }
1481
1482 // Looking for the ChangeCipherSpec and Finished messages.
1483 //
1484 // As the cached Finished message should be a ciphertext, we don't
1485 // exactly know a ciphertext is a Finished message or not. According
1486 // to the spec of TLS/DTLS handshaking, a Finished message is always
1487 // sent immediately after a ChangeCipherSpec message. The first
1488 // ciphertext handshake message should be the expected Finished message.
1489 private boolean hasFinishedMessage(
1490 Set<RecordFragment> fragments) {
1491
1492 boolean hasCCS = false;
1493 boolean hasFin = false;
1494 for (RecordFragment fragment : fragments) {
1495 if (fragment.contentType == Record.ct_change_cipher_spec) {
1496 if (hasFin) {
1497 return true;
1498 }
1499 hasCCS = true;
1500 } else if (fragment.contentType == Record.ct_handshake) {
1501 // Finished is the first expected message of a new epoch.
1502 if (fragment.isCiphertext) {
1503 if (hasCCS) {
1504 return true;
1505 }
1506 hasFin = true;
1507 }
1508 }
1509 }
1510
1511 return hasFin && hasCCS;
1512 }
1513
1514 private boolean hasCompleted(byte handshakeType) {
1515 List<HoleDescriptor> holes =
1516 handshakeFlight.holesMap.get(handshakeType);
1517 if (holes == null) {
1518 // not yet received this kind of handshake message
1519 return false;
1520 }
1521
1522 return holes.isEmpty(); // no fragment hole for complete message
1523 }
1524
1525 private boolean hasCompleted(
1526 Set<RecordFragment> fragments,
1527 int presentMsgSeq, int endMsgSeq) {
1528
1529 // The caller should have checked the completion of the first
1530 // present handshake message. Need not to check it again.
1531 for (RecordFragment rFrag : fragments) {
1532 if ((rFrag.contentType != Record.ct_handshake) ||
1533 rFrag.isCiphertext) {
1534 break;
1535 }
1536
1537 HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
1538 if (hsFrag.messageSeq == presentMsgSeq) {
1539 continue;
1540 } else if (hsFrag.messageSeq == (presentMsgSeq + 1)) {
1541 // check the completion of the handshake message
1542 if (!hasCompleted(hsFrag.handshakeType)) {
1543 return false;
1544 }
1545
1546 presentMsgSeq = hsFrag.messageSeq;
1547 } else {
1548 // not yet got handshake message next to presentMsgSeq
1549 break;
1550 }
1551 }
1552
1553 return (presentMsgSeq >= endMsgSeq);
1554 // false: if not yet got all messages of the flight.
1555 }
1556
1557 private void handshakeHashing(
1558 HandshakeFragment hsFrag, Plaintext plaintext) {
1559
1560 byte hsType = hsFrag.handshakeType;
1561 if ((hsType == HandshakeMessage.ht_hello_request) ||
1562 (hsType == HandshakeMessage.ht_hello_verify_request)) {
|