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 }
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 records", 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 (int)(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 if (hs != null) {
588 return (this.handshakeType == hs.handshakeType) &&
589 (this.minMessageSeq == hs.minMessageSeq);
590 }
591
592 return false;
593 }
594
595 @Override
596 public Object clone() {
597 HandshakeFlight hf = new HandshakeFlight();
598
599 hf.handshakeType = this.handshakeType;
600 hf.flightEpoch = this.flightEpoch;
601 hf.minMessageSeq = this.minMessageSeq;
602
603 hf.maxMessageSeq = this.maxMessageSeq;
604 hf.maxRecordEpoch = this.maxRecordEpoch;
605 hf.maxRecordSeq = this.maxRecordSeq;
606
607 hf.holesMap = new HashMap<>(this.holesMap);
608
609 return hf;
610 }
611 }
612
613 final class DTLSReassembler {
614 // The handshake epoch.
615 final int handshakeEpoch;
616
617 // The buffered fragments.
618 TreeSet<RecordFragment> bufferedFragments = new TreeSet<>();
619
620 // The handshake flight in progress.
621 HandshakeFlight handshakeFlight = new HandshakeFlight();
622
623 // The preceding handshake flight.
624 HandshakeFlight precedingFlight = null;
625
626 // Epoch, sequence number and handshake message sequence of the
627 // next message acquisition of a flight.
628 int nextRecordEpoch; // next record epoch
629 long nextRecordSeq = 0; // next record sequence number
630
631 // Expect ChangeCipherSpec and Finished messages for the final flight.
632 boolean expectCCSFlight = false;
633
634 // Ready to process this flight if received all messages of the flight.
635 boolean flightIsReady = false;
636 boolean needToCheckFlight = false;
637
638 DTLSReassembler(int handshakeEpoch) {
639 this.handshakeEpoch = handshakeEpoch;
640 this.nextRecordEpoch = handshakeEpoch;
641
642 this.handshakeFlight.flightEpoch = handshakeEpoch;
643 }
644
645 void expectingFinishFlight() {
646 expectCCSFlight = true;
647 }
648
649 // Queue up a handshake message.
650 void queueUpHandshake(HandshakeFragment hsf) {
651 if (!isDesirable(hsf)) {
652 // No a dedired record, discard it.
653 return;
654 }
655
656 // Clean up the retransmission messages if necessary.
657 cleanUpRetransmit(hsf);
658
659 // Is it the first message of next flight?
660 //
661 // Note: the Finished message is handled in the final CCS flight.
662 boolean isMinimalFlightMessage = false;
663 if (handshakeFlight.minMessageSeq == hsf.messageSeq) {
664 isMinimalFlightMessage = true;
665 } else if ((precedingFlight != null) &&
666 (precedingFlight.minMessageSeq == hsf.messageSeq)) {
667 isMinimalFlightMessage = true;
668 }
669
670 if (isMinimalFlightMessage && (hsf.fragmentOffset == 0) &&
671 (hsf.handshakeType != HandshakeMessage.ht_finished)) {
672
673 // reset the handshake flight
674 handshakeFlight.handshakeType = hsf.handshakeType;
675 handshakeFlight.flightEpoch = hsf.recordEpoch;
676 handshakeFlight.minMessageSeq = hsf.messageSeq;
677 }
678
679 if (hsf.handshakeType == HandshakeMessage.ht_finished) {
680 handshakeFlight.maxMessageSeq = hsf.messageSeq;
681 handshakeFlight.maxRecordEpoch = hsf.recordEpoch;
682 handshakeFlight.maxRecordSeq = hsf.recordSeq;
683 } else {
684 if (handshakeFlight.maxMessageSeq < hsf.messageSeq) {
685 handshakeFlight.maxMessageSeq = hsf.messageSeq;
686 }
687
688 int n = (hsf.recordEpoch - handshakeFlight.maxRecordEpoch);
689 if (n > 0) {
690 handshakeFlight.maxRecordEpoch = hsf.recordEpoch;
691 handshakeFlight.maxRecordSeq = hsf.recordSeq;
692 } else if (n == 0) {
693 // the same epoch
694 if (handshakeFlight.maxRecordSeq < hsf.recordSeq) {
695 handshakeFlight.maxRecordSeq = hsf.recordSeq;
696 }
697 } // Otherwise, it is unlikely to happen.
698 }
699
700 boolean fragmented = false;
701 if ((hsf.fragmentOffset) != 0 ||
702 (hsf.fragmentLength != hsf.messageLength)) {
703
704 fragmented = true;
705 }
706
707 List<HoleDescriptor> holes =
708 handshakeFlight.holesMap.get(hsf.handshakeType);
709 if (holes == null) {
710 if (!fragmented) {
711 holes = Collections.emptyList();
712 } else {
713 holes = new LinkedList<HoleDescriptor>();
714 holes.add(new HoleDescriptor(0, hsf.messageLength));
715 }
716 handshakeFlight.holesMap.put(hsf.handshakeType, holes);
717 } else if (holes.isEmpty()) {
718 // Have got the full handshake message. This record may be
719 // a handshake message retransmission. Discard this record.
720 //
721 // It's OK to discard retransmission as the handshake hash
722 // is computed as if each handshake message had been sent
723 // as a single fragment.
724 if (debug != null && Debug.isOn("verbose")) {
725 Debug.log("Have got the full message, discard it.");
726 }
727
728 return;
729 }
730
731 if (fragmented) {
732 int fragmentLimit = hsf.fragmentOffset + hsf.fragmentLength;
733 for (int i = 0; i < holes.size(); i++) {
734
735 HoleDescriptor hole = holes.get(i);
736 if ((hole.limit <= hsf.fragmentOffset) ||
737 (hole.offset >= fragmentLimit)) {
738 // Also discard overlapping handshake retransmissions.
739 continue;
740 }
741
742 // The ranges SHOULD NOT overlap.
743 if (((hole.offset > hsf.fragmentOffset) &&
744 (hole.offset < fragmentLimit)) ||
745 ((hole.limit > hsf.fragmentOffset) &&
746 (hole.limit < fragmentLimit))) {
747
748 if (debug != null && Debug.isOn("ssl")) {
749 Debug.log("Discard invalid record: " +
750 "handshake fragment ranges are overlapping");
751 }
752
753 // invalid, discard it [section 4.1.2.7, RFC 6347]
754 return;
755 }
756
757 // This record interacts with this hole, fill the hole.
758 holes.remove(i);
759 // i--;
760
761 if (hsf.fragmentOffset > hole.offset) {
762 holes.add(new HoleDescriptor(
763 hole.offset, hsf.fragmentOffset));
764 // i++;
765 }
766
767 if (fragmentLimit < hole.limit) {
768 holes.add(new HoleDescriptor(
769 fragmentLimit, hole.limit));
770 // i++;
771 }
772
773 // As no ranges overlap, no interact with other holes.
774 break;
775 }
776 }
777
778 // buffer this fragment
779 if (hsf.handshakeType == HandshakeMessage.ht_finished) {
780 // Need no status update.
781 bufferedFragments.add(hsf);
782 } else {
783 bufferFragment(hsf);
784 }
785 }
786
787 // Queue up a ChangeCipherSpec message
788 void queueUpChangeCipherSpec(RecordFragment rf) {
789 if (!isDesirable(rf)) {
790 // No a dedired record, discard it.
791 return;
792 }
793
794 // Clean up the retransmission messages if necessary.
795 cleanUpRetransmit(rf);
796
797 // Is it the first message of this flight?
798 //
799 // Note: the first message of the final flight is ChangeCipherSpec.
800 if (expectCCSFlight) {
801 handshakeFlight.handshakeType = HandshakeFlight.HF_UNKNOWN;
802 handshakeFlight.flightEpoch = rf.recordEpoch;
803 }
804
805 // The epoch should be the same as the first message of the flight.
806 if (handshakeFlight.maxRecordSeq < rf.recordSeq) {
807 handshakeFlight.maxRecordSeq = rf.recordSeq;
808 }
809
810 // buffer this fragment
811 bufferFragment(rf);
812 }
813
814 // Queue up a ciphertext message.
815 //
816 // Note: not yet be able to decrypt the message.
817 void queueUpFragment(RecordFragment rf) {
818 if (!isDesirable(rf)) {
819 // No a dedired record, discard it.
820 return;
821 }
822
823 // Clean up the retransmission messages if necessary.
824 cleanUpRetransmit(rf);
825
826 // buffer this fragment
827 bufferFragment(rf);
828 }
829
830 private void bufferFragment(RecordFragment rf) {
831 // append this fragment
832 bufferedFragments.add(rf);
833
834 if (flightIsReady) {
835 flightIsReady = false;
836 }
837
838 if (!needToCheckFlight) {
839 needToCheckFlight = true;
840 }
841 }
842
843 private void cleanUpRetransmit(RecordFragment rf) {
844 // Does the next flight start?
845 boolean isNewFlight = false;
846 if (precedingFlight != null) {
847 if (precedingFlight.flightEpoch < rf.recordEpoch) {
848 isNewFlight = true;
849 } else {
850 if (rf instanceof HandshakeFragment) {
851 HandshakeFragment hsf = (HandshakeFragment)rf;
852 if (precedingFlight.maxMessageSeq < hsf.messageSeq) {
853 isNewFlight = true;
854 }
855 } else if (rf.contentType != Record.ct_change_cipher_spec) {
856 // ciphertext
857 if (precedingFlight.maxRecordEpoch < rf.recordEpoch) {
858 isNewFlight = true;
859 }
860 }
861 }
862 }
863
864 if (!isNewFlight) {
865 // Need no cleanup.
866 return;
867 }
868
869 // clean up the buffer
870 for (Iterator<RecordFragment> it = bufferedFragments.iterator();
871 it.hasNext();) {
872
873 RecordFragment frag = it.next();
874 boolean isOld = false;
875 if (frag.recordEpoch < precedingFlight.maxRecordEpoch) {
876 isOld = true;
877 } else if (frag.recordEpoch == precedingFlight.maxRecordEpoch) {
878 if (frag.recordSeq <= precedingFlight.maxRecordSeq) {
879 isOld = true;
880 }
881 }
882
883 if (!isOld && (frag instanceof HandshakeFragment)) {
884 HandshakeFragment hsf = (HandshakeFragment)frag;
885 isOld = (hsf.messageSeq <= precedingFlight.maxMessageSeq);
886 }
887
888 if (isOld) {
889 it.remove();
890 } else {
891 // Safe to break as items in the buffer are ordered.
892 break;
893 }
894 }
895
896 // discard retransmissions of the previous flight if any.
897 precedingFlight = null;
898 }
899
900 // Is a desired record?
901 //
902 // Check for retransmission and lost records.
903 private boolean isDesirable(RecordFragment rf) {
904 //
905 // Discard records old than the previous epoch.
906 //
907 int previousEpoch = nextRecordEpoch - 1;
908 if (rf.recordEpoch < previousEpoch) {
909 // Too old to use, discard this record.
910 if (debug != null && Debug.isOn("verbose")) {
911 Debug.log("Too old epoch to use this record, discard it.");
912 }
913
914 return false;
915 }
916
917 //
918 // Allow retransmission of last flight of the previous epoch
919 //
920 // For example, the last server delivered flight for session
921 // resuming abbreviated handshaking consist three messages:
922 // ServerHello
923 // [ChangeCipherSpec]
924 // Finished
925 //
926 // The epoch number is incremented and the sequence number is reset
927 // if the ChangeCipherSpec is sent.
928 if (rf.recordEpoch == previousEpoch) {
929 boolean isDesired = true;
930 if (precedingFlight == null) {
931 isDesired = false;
932 } else {
933 if (rf instanceof HandshakeFragment) {
934 HandshakeFragment hsf = (HandshakeFragment)rf;
935 if (precedingFlight.minMessageSeq > hsf.messageSeq) {
936 isDesired = false;
937 }
938 } else if (rf.contentType == Record.ct_change_cipher_spec) {
939 // ChangeCipherSpec
940 if (precedingFlight.flightEpoch != rf.recordEpoch) {
941 isDesired = false;
942 }
943 } else { // ciphertext
944 if ((rf.recordEpoch < precedingFlight.maxRecordEpoch) ||
945 (rf.recordEpoch == precedingFlight.maxRecordEpoch &&
946 rf.recordSeq <= precedingFlight.maxRecordSeq)) {
947 isDesired = false;
948 }
949 }
950 }
951
952 if (!isDesired) {
953 // Too old to use, discard this retransmitted record
954 if (debug != null && Debug.isOn("verbose")) {
955 Debug.log("Too old retransmission to use, discard it.");
956 }
957
958 return false;
959 }
960 } else if ((rf.recordEpoch == nextRecordEpoch) &&
961 (nextRecordSeq > rf.recordSeq)) {
962
963 // Previously disordered record for the current epoch.
964 //
965 // Should has been retransmitted. Discard this record.
966 if (debug != null && Debug.isOn("verbose")) {
967 Debug.log("Lagging behind record (sequence), discard it.");
968 }
969
970 return false;
971 }
972
973 return true;
974 }
975
976 private boolean isEmpty() {
977 return (bufferedFragments.isEmpty() ||
978 (!flightIsReady && !needToCheckFlight) ||
979 (needToCheckFlight && !flightIsReady()));
980 }
981
982 Plaintext acquirePlaintext() {
983 if (bufferedFragments.isEmpty()) {
984 if (debug != null && Debug.isOn("verbose")) {
985 Debug.log("No received handshake messages");
986 }
987 return null;
988 }
989
990 if (!flightIsReady && needToCheckFlight) {
991 // check the fligth status
992 flightIsReady = flightIsReady();
993
994 // Reset if this flight is ready.
995 if (flightIsReady) {
996 // Retransmitted handshake messages are not needed for
997 // further handshaking processing.
998 if (handshakeFlight.isRetransmitOf(precedingFlight)) {
999 // cleanup
1000 bufferedFragments.clear();
1001
1002 // Reset the next handshake flight.
1003 resetHandshakeFlight(precedingFlight);
1004
1005 if (debug != null && Debug.isOn("verbose")) {
1006 Debug.log("Received a retransmission flight.");
1007 }
1008
1009 return Plaintext.PLAINTEXT_NULL;
1010 }
1011 }
1012
1013 needToCheckFlight = false;
1014 }
1015
1016 if (!flightIsReady) {
1017 if (debug != null && Debug.isOn("verbose")) {
1018 Debug.log("The handshake flight is not ready to use: " +
1019 handshakeFlight.handshakeType);
1020 }
1021 return null;
1022 }
1023
1024 RecordFragment rFrag = bufferedFragments.first();
1025 Plaintext plaintext;
1026 if (!rFrag.isCiphertext) {
1027 // handshake message, or ChangeCipherSpec message
1028 plaintext = acquireHandshakeMessage();
1029
1030 // Reset the handshake flight.
1031 if (bufferedFragments.isEmpty()) {
1032 // Nedd not to backup the holes map. Clear up it at first.
1033 handshakeFlight.holesMap.clear(); // cleanup holes map
1034
1035 // Update the preceding flight.
1036 precedingFlight = (HandshakeFlight)handshakeFlight.clone();
1037
1038 // Reset the next handshake flight.
1039 resetHandshakeFlight(precedingFlight);
1040
1041 if (expectCCSFlight &&
1042 (precedingFlight.flightEpoch ==
1043 HandshakeFlight.HF_UNKNOWN)) {
1044 expectCCSFlight = false;
1045 }
1046 }
1047 } else {
1048 // a Finished message or other ciphertexts
1049 plaintext = acquireCachedMessage();
1050 }
1051
1052 return plaintext;
1053 }
1054
1055 //
1056 // Reset the handshake flight from a previous one.
1057 //
1058 private void resetHandshakeFlight(HandshakeFlight prev) {
1059 // Reset the next handshake flight.
1060 handshakeFlight.handshakeType = HandshakeFlight.HF_UNKNOWN;
1061 handshakeFlight.flightEpoch = prev.maxRecordEpoch;
1062 if (prev.flightEpoch != prev.maxRecordEpoch) {
1063 // a new epoch starts
1064 handshakeFlight.minMessageSeq = 0;
1065 } else {
1066 // stay at the same epoch
1067 //
1068 // The minimal message sequence number will get updated if
1069 // a flight retransmission happens.
1070 handshakeFlight.minMessageSeq = prev.maxMessageSeq + 1;
1071 }
1072
1073 // cleanup the maximum sequence number and epoch number.
1074 //
1075 // Note: actually, we need to do nothing because the reassembler
1076 // of handshake messages will reset them properly even for
1077 // retransmissions.
1078 //
1079 handshakeFlight.maxMessageSeq = 0;
1080 handshakeFlight.maxRecordEpoch = handshakeFlight.flightEpoch;
1081
1082 // Record sequence number cannot wrap even for retransmissions.
1083 handshakeFlight.maxRecordSeq = prev.maxRecordSeq + 1;
1084
1085 // cleanup holes map
1086 handshakeFlight.holesMap.clear();
1087
1088 // Ready to accept new input record.
1089 flightIsReady = false;
1090 needToCheckFlight = false;
1091 }
1092
1093 private Plaintext acquireCachedMessage() {
1094
1095 RecordFragment rFrag = bufferedFragments.first();
1096 if (readEpoch != rFrag.recordEpoch) {
1097 if (readEpoch > rFrag.recordEpoch) {
1098 // discard old records
1099 if (debug != null && Debug.isOn("verbose")) {
1100 Debug.log("Discard old buffered ciphertext fragments.");
1101 }
1102 bufferedFragments.remove(rFrag); // popup the fragment
1103 }
1104
1105 // reset the flight
1106 if (flightIsReady) {
1107 flightIsReady = false;
1108 }
1109
1110 if (debug != null && Debug.isOn("verbose")) {
1111 Debug.log("Not yet ready to decrypt the cached fragments.");
1112 }
1113 return null;
1114 }
1115
1116 bufferedFragments.remove(rFrag); // popup the fragment
1117
1118 ByteBuffer fragment = ByteBuffer.wrap(rFrag.fragment);
1119 ByteBuffer plaintextFragment = null;
1120 try {
1121 plaintextFragment = decrypt(readAuthenticator, readCipher,
1122 rFrag.contentType, fragment, rFrag.recordEnS);
1123 } catch (BadPaddingException bpe) {
1124 if (debug != null && Debug.isOn("verbose")) {
1125 Debug.log("Discard invalid record: " + bpe);
1126 }
1127
1128 // invalid, discard this record [section 4.1.2.7, RFC 6347]
1129 return null;
1130 }
1131
1132 // The ciphtext handshake message can only be Finished (the
1133 // end of this flight), ClinetHello or HelloRequest (the
1134 // beginning of the next flight) message. Need not to check
1135 // any ChangeCipherSpec message.
1136 if (rFrag.contentType == Record.ct_handshake) {
1137 while (plaintextFragment.remaining() > 0) {
1138 HandshakeFragment hsFrag = parseHandshakeMessage(
1139 rFrag.contentType,
1140 rFrag.majorVersion, rFrag.minorVersion,
1141 rFrag.recordEnS, rFrag.recordEpoch, rFrag.recordSeq,
1142 plaintextFragment);
1143
1144 if (hsFrag == null) {
1145 // invalid, discard this record
1146 if (debug != null && Debug.isOn("verbose")) {
1147 Debug.printHex(
1148 "Invalid handshake fragment, discard it",
1149 plaintextFragment);
1150 }
1151 return null;
1152 }
1153
1154 queueUpHandshake(hsFrag);
1155 // The flight ready status (flightIsReady) should have
1156 // been checked and updated for the Finished handshake
1157 // message before the decryption. Please don't update
1158 // flightIsReady for Finished messages.
1159 if (hsFrag.handshakeType != HandshakeMessage.ht_finished) {
1160 flightIsReady = false;
1161 needToCheckFlight = true;
1162 }
1163 }
1164
1165 return acquirePlaintext();
1166 } else {
1167 return new Plaintext(rFrag.contentType,
1168 rFrag.majorVersion, rFrag.minorVersion,
1169 rFrag.recordEpoch,
1170 Authenticator.toLong(rFrag.recordEnS),
1171 plaintextFragment);
1172 }
1173 }
1174
1175 private Plaintext acquireHandshakeMessage() {
1176
1177 RecordFragment rFrag = bufferedFragments.first();
1178 if (rFrag.contentType == Record.ct_change_cipher_spec) {
1179 this.nextRecordEpoch = rFrag.recordEpoch + 1;
1180
1181 // For retransmissions, the next record sequence number is a
1182 // positive value. Don't worry about it as the acquiring of
1183 // the immediately followed Finished handshake message will
1184 // reset the next record sequence number correctly.
1185 this.nextRecordSeq = 0;
1186
1187 // Popup the fragment.
1188 bufferedFragments.remove(rFrag);
1189
1190 // Reload if this message has been reserved for handshake hash.
1191 handshakeHash.reload();
1192
1193 return new Plaintext(rFrag.contentType,
1194 rFrag.majorVersion, rFrag.minorVersion,
1195 rFrag.recordEpoch,
1196 Authenticator.toLong(rFrag.recordEnS),
1197 ByteBuffer.wrap(rFrag.fragment));
1198 } else { // rFrag.contentType == Record.ct_handshake
1199 HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
1200 if ((hsFrag.messageLength == hsFrag.fragmentLength) &&
1201 (hsFrag.fragmentOffset == 0)) { // no fragmentation
1202
1203 bufferedFragments.remove(rFrag); // popup the fragment
1204
1205 // this.nextRecordEpoch = hsFrag.recordEpoch;
1206 this.nextRecordSeq = hsFrag.recordSeq + 1;
1207
1208 // Note: may try to avoid byte array copy in the future.
1209 byte[] recordFrag = new byte[hsFrag.messageLength + 4];
1210 Plaintext plaintext = new Plaintext(hsFrag.contentType,
1211 hsFrag.majorVersion, hsFrag.minorVersion,
1212 hsFrag.recordEpoch,
1213 Authenticator.toLong(hsFrag.recordEnS),
1214 ByteBuffer.wrap(recordFrag));
1215
1216 // fill the handshake fragment of the record
1217 recordFrag[0] = hsFrag.handshakeType;
1218 recordFrag[1] =
1219 (byte)((hsFrag.messageLength >>> 16) & 0xFF);
1220 recordFrag[2] =
1221 (byte)((hsFrag.messageLength >>> 8) & 0xFF);
1222 recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF);
1223
1224 System.arraycopy(hsFrag.fragment, 0,
1225 recordFrag, 4, hsFrag.fragmentLength);
1226
1227 // handshake hashing
1228 handshakeHashing(hsFrag, plaintext);
1229
1230 return plaintext;
1231 } else { // fragmented handshake message
1232 // the first record
1233 //
1234 // Note: may try to avoid byte array copy in the future.
1235 byte[] recordFrag = new byte[hsFrag.messageLength + 4];
1236 Plaintext plaintext = new Plaintext(hsFrag.contentType,
1237 hsFrag.majorVersion, hsFrag.minorVersion,
1238 hsFrag.recordEpoch,
1239 Authenticator.toLong(hsFrag.recordEnS),
1240 ByteBuffer.wrap(recordFrag));
1241
1242 // fill the handshake fragment of the record
1243 recordFrag[0] = hsFrag.handshakeType;
1244 recordFrag[1] =
1245 (byte)((hsFrag.messageLength >>> 16) & 0xFF);
1246 recordFrag[2] =
1247 (byte)((hsFrag.messageLength >>> 8) & 0xFF);
1248 recordFrag[3] = (byte)(hsFrag.messageLength & 0xFF);
1249
1250 int msgSeq = hsFrag.messageSeq;
1251 long maxRecodeSN = hsFrag.recordSeq;
1252 HandshakeFragment hmFrag = hsFrag;
1253 do {
1254 System.arraycopy(hmFrag.fragment, 0,
1255 recordFrag, hmFrag.fragmentOffset + 4,
1256 hmFrag.fragmentLength);
1257 // popup the fragment
1258 bufferedFragments.remove(rFrag);
1259
1263
1264 // Note: may buffer retransmitted fragments in order to
1265 // speed up the reassembly in the future.
1266
1267 // read the next buffered record
1268 if (!bufferedFragments.isEmpty()) {
1269 rFrag = bufferedFragments.first();
1270 if (rFrag.contentType != Record.ct_handshake) {
1271 break;
1272 } else {
1273 hmFrag = (HandshakeFragment)rFrag;
1274 }
1275 }
1276 } while (!bufferedFragments.isEmpty() &&
1277 (msgSeq == hmFrag.messageSeq));
1278
1279 // handshake hashing
1280 handshakeHashing(hsFrag, plaintext);
1281
1282 this.nextRecordSeq = maxRecodeSN + 1;
1283
1284 return plaintext;
1285 }
1286 }
1287 }
1288
1289 boolean flightIsReady() {
1290
1291 byte flightType = handshakeFlight.handshakeType;
1292 if (flightType == HandshakeFlight.HF_UNKNOWN) {
1293 //
1294 // the ChangeCipherSpec/Finished flight
1295 //
1296 if (expectCCSFlight) {
1297 // Have the ChangeCipherSpec/Finished flight been received?
1298 //
1299 // return hasFinisedMessage(bufferedFragments);
1300 boolean isReady = hasFinisedMessage(bufferedFragments);
1301 if (debug != null && Debug.isOn("verbose")) {
1302 Debug.log(
1303 "Has the final flight been received? " + isReady);
1304 }
1305
1306 return isReady;
1307 }
1308
1309 if (debug != null && Debug.isOn("verbose")) {
1310 Debug.log("No flight is received yet.");
1311 }
1312
1313 return false;
1314 }
1315
1316 if ((flightType == HandshakeMessage.ht_client_hello) ||
1317 (flightType == HandshakeMessage.ht_hello_request) ||
1318 (flightType == HandshakeMessage.ht_hello_verify_request)) {
1319
1320 // single handshake message flight
1321 boolean isReady = hasCompleted(flightType);
1322 if (debug != null && Debug.isOn("verbose")) {
1323 Debug.log("Is the handshake message completed? " + isReady);
1324 }
1325
1326 return isReady;
1327 }
1328
1329 //
1330 // the ServerHello flight
1331 //
1332 if (flightType == HandshakeMessage.ht_server_hello) {
1333 // Firstly, check the first flight handshake message.
1334 if (!hasCompleted(flightType)) {
1335 if (debug != null && Debug.isOn("verbose")) {
1336 Debug.log(
1337 "The ServerHello message is not completed yet.");
1338 }
1339
1340 return false;
1341 }
1342
1343 //
1344 // an abbreviated handshake
1345 //
1346 if (hasFinisedMessage(bufferedFragments)) {
1347 if (debug != null && Debug.isOn("verbose")) {
1348 Debug.log("It's an abbreviated handshake.");
1349 }
1350
1351 return true;
1352 }
1353
1354 //
1355 // a full handshake
1356 //
1357 List<HoleDescriptor> holes = handshakeFlight.holesMap.get(
1358 HandshakeMessage.ht_server_hello_done);
1359 if ((holes == null) || !holes.isEmpty()) {
1360 // Not yet got the final message of the flight.
1361 if (debug != null && Debug.isOn("verbose")) {
1362 Debug.log("Not yet got the ServerHelloDone message");
1363 }
1364
1365 return false;
1366 }
1367
1368 // Have all handshake message been received?
1369 boolean isReady = hasCompleted(bufferedFragments,
1370 handshakeFlight.minMessageSeq,
1371 handshakeFlight.maxMessageSeq);
1372 if (debug != null && Debug.isOn("verbose")) {
1373 Debug.log("Is the ServerHello flight (message " +
1374 handshakeFlight.minMessageSeq + "-" +
1375 handshakeFlight.maxMessageSeq +
1376 ") completed? " + isReady);
1377 }
1378
1379 return isReady;
1380 }
1381
1382 //
1383 // the ClientKeyExchange flight
1384 //
1385 // Note: need to consider more messages in this flight if
1386 // ht_supplemental_data and ht_certificate_url are
1387 // suppported in the future.
1388 //
1389 if ((flightType == HandshakeMessage.ht_certificate) ||
1390 (flightType == HandshakeMessage.ht_client_key_exchange)) {
1391
1392 // Firstly, check the first flight handshake message.
1393 if (!hasCompleted(flightType)) {
1394 if (debug != null && Debug.isOn("verbose")) {
1395 Debug.log(
1396 "The ClientKeyExchange or client Certificate " +
1397 "message is not completed yet.");
1398 }
1399
1400 return false;
1401 }
1402
1403 if (!hasFinisedMessage(bufferedFragments)) {
1404 // not yet have the ChangeCipherSpec/Finished messages
1405 if (debug != null && Debug.isOn("verbose")) {
1406 Debug.log(
1407 "Not yet have the ChangeCipherSpec and " +
1408 "Finished messages");
1409 }
1410
1411 return false;
1412 }
1413
1414 // Have all handshake message been received?
1415 boolean isReady = hasCompleted(bufferedFragments,
1416 handshakeFlight.minMessageSeq,
1417 handshakeFlight.maxMessageSeq);
1418 if (debug != null && Debug.isOn("verbose")) {
1419 Debug.log("Is the ClientKeyExchange flight (message " +
1420 handshakeFlight.minMessageSeq + "-" +
1421 handshakeFlight.maxMessageSeq +
1422 ") completed? " + isReady);
1423 }
1424
1425 return isReady;
1426 }
1427
1428 //
1429 // Otherwise, need to receive more handshake messages.
1430 //
1431 if (debug != null && Debug.isOn("verbose")) {
1432 Debug.log("Need to receive more handshake messages");
1433 }
1434
1435 return false;
1436 }
1437
1438 private boolean isSessionResuming(
1439 byte[] fragment, byte[] prevSid) throws SSLException {
1440
1441 // As the first fragment of ServerHello should be big enough
1442 // to hold the session_id field, need not to worry about the
1443 // fragmentation here.
1444 if ((fragment == null) || (fragment.length < 38)) {
1445 // 38: the minimal ServerHello body length
1446 throw new SSLException(
1447 "Invalid ServerHello message: no sufficient data");
1448 }
1449
1450 int sidLen = fragment[34]; // 34: the length field
1451 if (sidLen > 32) { // opaque SessionID<0, 32>
1452 throw new SSLException(
1453 "Invalid ServerHello message: invalid session id");
1454 }
1455
1456 if (fragment.length < 38 + sidLen) {
1457 throw new SSLException(
1458 "Invalid ServerHello message: no sufficient data");
1459 }
1460
1461 if (sidLen != 0 && (prevSid.length == sidLen)) {
1462 // may be a session-resuming handshake
1463 for (int i = 0; i < sidLen; i++) {
1464 if (prevSid[i] != fragment[35 + i]) {
1465 // 35: the session identifier
1466 return false;
1467 }
1468 }
1469
1470 return true;
1471 }
1498 for (RecordFragment fragment : fragments) {
1499 if (fragment.contentType == Record.ct_change_cipher_spec) {
1500 if (hasFin) {
1501 return true;
1502 }
1503 hasCCS = true;
1504 } else if (fragment.contentType == Record.ct_handshake) {
1505 // Finished is the first expected message of a new epoch.
1506 if (fragment.isCiphertext) {
1507 if (hasCCS) {
1508 return true;
1509 }
1510 hasFin = true;
1511 }
1512 }
1513 }
1514
1515 return hasFin && hasCCS;
1516 }
1517
1518 private boolean hasCompleted(byte handshakeType) {
1519 List<HoleDescriptor> holes =
1520 handshakeFlight.holesMap.get(handshakeType);
1521 if (holes == null) {
1522 // not yet received this kind of handshake message
1523 return false;
1524 }
1525
1526 return holes.isEmpty(); // no fragment hole for complete message
1527 }
1528
1529 private boolean hasCompleted(
1530 Set<RecordFragment> fragments,
1531 int presentMsgSeq, int endMsgSeq) {
1532
1533 // The caller should have checked the completion of the first
1534 // present handshake message. Need not to check it again.
1535 for (RecordFragment rFrag : fragments) {
1536 if ((rFrag.contentType != Record.ct_handshake) ||
1537 rFrag.isCiphertext) {
1538 break;
1539 }
1540
1541 HandshakeFragment hsFrag = (HandshakeFragment)rFrag;
1542 if (hsFrag.messageSeq == presentMsgSeq) {
1543 continue;
1544 } else if (hsFrag.messageSeq == (presentMsgSeq + 1)) {
1545 // check the completion of the handshake message
1546 if (!hasCompleted(hsFrag.handshakeType)) {
1547 return false;
1548 }
1549
1550 presentMsgSeq = hsFrag.messageSeq;
1551 } else {
1552 // not yet got handshake message next to presentMsgSeq
1553 break;
1554 }
1555 }
1556
1557 return (presentMsgSeq >= endMsgSeq);
1558 // false: if not yet got all messages of the flight.
1559 }
1560
1561 private void handshakeHashing(
1562 HandshakeFragment hsFrag, Plaintext plaintext) {
1563
1564 byte hsType = hsFrag.handshakeType;
1565 if ((hsType == HandshakeMessage.ht_hello_request) ||
1566 (hsType == HandshakeMessage.ht_hello_verify_request)) {
|