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