< prev index next >

src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java

Print this page


   1 /*
   2  * Copyright (c) 1996, 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
  23  * questions.
  24  */
  25 
  26 package sun.security.ssl;
  27 
  28 import java.io.*;
  29 import java.nio.*;
  30 import java.util.*;
  31 
  32 import javax.crypto.BadPaddingException;
  33 
  34 import javax.net.ssl.*;
  35 
  36 import sun.security.util.HexDumpEncoder;
  37 import static sun.security.ssl.Ciphertext.RecordType;
  38 
  39 /**
  40  * DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
  41  */
  42 final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
  43 
  44     private DTLSFragmenter fragmenter = null;
  45 
  46     int                 writeEpoch;
  47 
  48     int                 prevWriteEpoch;
  49     Authenticator       prevWriteAuthenticator;
  50     CipherBox           prevWriteCipher;
  51 
  52     private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
  53 
  54     DTLSOutputRecord() {
  55         this.writeAuthenticator = new MAC(true);
  56 
  57         this.writeEpoch = 0;
  58         this.prevWriteEpoch = 0;
  59         this.prevWriteCipher = CipherBox.NULL;
  60         this.prevWriteAuthenticator = new MAC(true);
  61 
  62         this.packetSize = DTLSRecord.maxRecordSize;
  63         this.protocolVersion = ProtocolVersion.DEFAULT_DTLS;
  64     }
  65 
  66     @Override
  67     void changeWriteCiphers(Authenticator writeAuthenticator,
  68             CipherBox writeCipher) throws IOException {


  69 









  70         encodeChangeCipherSpec();

  71 
  72         prevWriteCipher.dispose();
  73 
  74         this.prevWriteAuthenticator = this.writeAuthenticator;
  75         this.prevWriteCipher = this.writeCipher;
  76         this.prevWriteEpoch = this.writeEpoch;
  77 
  78         this.writeAuthenticator = writeAuthenticator;
  79         this.writeCipher = writeCipher;
  80         this.writeEpoch++;
  81 
  82         this.isFirstAppOutputRecord = true;
  83 
  84         // set the epoch number
  85         this.writeAuthenticator.setEpochNumber(this.writeEpoch);
  86     }
  87 
  88     @Override
  89     void encodeAlert(byte level, byte description) throws IOException {
  90         RecordMemo memo = new RecordMemo();
  91 
  92         memo.contentType = Record.ct_alert;
  93         memo.majorVersion = protocolVersion.major;
  94         memo.minorVersion = protocolVersion.minor;
  95         memo.encodeEpoch = writeEpoch;
  96         memo.encodeCipher = writeCipher;
  97         memo.encodeAuthenticator = writeAuthenticator;
  98 
  99         memo.fragment = new byte[2];
 100         memo.fragment[0] = level;
 101         memo.fragment[1] = description;
 102 
 103         alertMemos.add(memo);
 104     }
 105 
 106     @Override
 107     void encodeChangeCipherSpec() throws IOException {
 108         if (fragmenter == null) {
 109            fragmenter = new DTLSFragmenter();
 110         }
 111         fragmenter.queueUpChangeCipherSpec();
 112     }
 113 
 114     @Override
 115     void encodeHandshake(byte[] source,
 116             int offset, int length) throws IOException {
 117 
 118         if (firstMessage) {
 119             firstMessage = false;
 120         }
 121 
 122         if (fragmenter == null) {
 123            fragmenter = new DTLSFragmenter();
 124         }
 125 
 126         fragmenter.queueUpHandshake(source, offset, length);
 127     }
 128 
 129     @Override
 130     Ciphertext encode(ByteBuffer[] sources, int offset, int length,






 131             ByteBuffer destination) throws IOException {
 132 
 133         if (writeAuthenticator.seqNumOverflow()) {
 134             if (debug != null && Debug.isOn("ssl")) {
 135                 System.out.println(Thread.currentThread().getName() +
 136                     ", sequence number extremely close to overflow " +
 137                     "(2^64-1 packets). Closing connection.");
 138             }
 139 
 140             throw new SSLHandshakeException("sequence number overflow");
 141         }
 142 
 143         // not apply to handshake message
 144         int macLen = 0;
 145         if (writeAuthenticator instanceof MAC) {
 146             macLen = ((MAC)writeAuthenticator).MAClen();
















 147         }
 148 

 149         int fragLen;
 150         if (packetSize > 0) {
 151             fragLen = Math.min(maxRecordSize, packetSize);
 152             fragLen = writeCipher.calculateFragmentSize(
 153                     fragLen, macLen, headerSize);
 154 
 155             fragLen = Math.min(fragLen, Record.maxDataSize);
 156         } else {
 157             fragLen = Record.maxDataSize;
 158         }
 159 
 160         if (fragmentSize > 0) {
 161             fragLen = Math.min(fragLen, fragmentSize);
 162         }
 163 
 164         int dstPos = destination.position();
 165         int dstLim = destination.limit();
 166         int dstContent = dstPos + headerSize +
 167                                 writeCipher.getExplicitNonceSize();
 168         destination.position(dstContent);
 169 
 170         int remains = Math.min(fragLen, destination.remaining());
 171         fragLen = 0;
 172         int srcsLen = offset + length;
 173         for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
 174             int amount = Math.min(sources[i].remaining(), remains);
 175             int srcLimit = sources[i].limit();
 176             sources[i].limit(sources[i].position() + amount);
 177             destination.put(sources[i]);
 178             sources[i].limit(srcLimit);         // restore the limit
 179             remains -= amount;
 180             fragLen += amount;
 181         }
 182 
 183         destination.limit(destination.position());
 184         destination.position(dstContent);
 185 
 186         if ((debug != null) && Debug.isOn("record")) {
 187             System.out.println(Thread.currentThread().getName() +
 188                     ", WRITE: " + protocolVersion + " " +
 189                     Record.contentName(Record.ct_application_data) +
 190                     ", length = " + destination.remaining());
 191         }
 192 
 193         // Encrypt the fragment and wrap up a record.
 194         long recordSN = encrypt(writeAuthenticator, writeCipher,
 195                 Record.ct_application_data, destination,
 196                 dstPos, dstLim, headerSize,
 197                 protocolVersion, true);
 198 
 199         if ((debug != null) && Debug.isOn("packet")) {
 200             ByteBuffer temporary = destination.duplicate();
 201             temporary.limit(temporary.position());
 202             temporary.position(dstPos);
 203             Debug.printHex(
 204                     "[Raw write]: length = " + temporary.remaining(),
 205                     temporary);
 206         }
 207 
 208         // remain the limit unchanged
 209         destination.limit(dstLim);
 210 
 211         return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);

 212     }
 213 
 214     @Override
 215     Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
 216         if (alertMemos != null && !alertMemos.isEmpty()) {
 217             RecordMemo memo = alertMemos.pop();
 218 
 219             int macLen = 0;
 220             if (memo.encodeAuthenticator instanceof MAC) {
 221                 macLen = ((MAC)memo.encodeAuthenticator).MAClen();
 222             }
 223 
 224             int dstPos = destination.position();
 225             int dstLim = destination.limit();
 226             int dstContent = dstPos + headerSize +
 227                                 writeCipher.getExplicitNonceSize();
 228             destination.position(dstContent);
 229 
 230             destination.put(memo.fragment);
 231 
 232             destination.limit(destination.position());
 233             destination.position(dstContent);
 234 
 235             if ((debug != null) && Debug.isOn("record")) {
 236                 System.out.println(Thread.currentThread().getName() +
 237                         ", WRITE: " + protocolVersion + " " +
 238                         Record.contentName(Record.ct_alert) +
 239                         ", length = " + destination.remaining());
 240             }
 241 
 242             // Encrypt the fragment and wrap up a record.
 243             long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
 244                     Record.ct_alert, destination, dstPos, dstLim, headerSize,

 245                     ProtocolVersion.valueOf(memo.majorVersion,
 246                             memo.minorVersion), true);
 247 
 248             if ((debug != null) && Debug.isOn("packet")) {
 249                 ByteBuffer temporary = destination.duplicate();
 250                 temporary.limit(temporary.position());
 251                 temporary.position(dstPos);
 252                 Debug.printHex(
 253                         "[Raw write]: length = " + temporary.remaining(),
 254                         temporary);
 255             }
 256 
 257             // remain the limit unchanged
 258             destination.limit(dstLim);
 259 
 260             return new Ciphertext(RecordType.RECORD_ALERT, recordSN);

 261         }
 262 
 263         if (fragmenter != null) {
 264             return fragmenter.acquireCiphertext(destination);
 265         }
 266 
 267         return null;
 268     }
 269 
 270     @Override
 271     boolean isEmpty() {
 272         return ((fragmenter == null) || fragmenter.isEmpty()) &&
 273                ((alertMemos == null) || alertMemos.isEmpty());
 274     }
 275 
 276     @Override
 277     void initHandshaker() {
 278         // clean up
 279         fragmenter = null;
 280     }
 281 
 282     @Override
 283     void launchRetransmission() {
 284         // Note: Please don't retransmit if there are handshake messages
 285         // or alerts waiting in the queue.
 286         if (((alertMemos == null) || alertMemos.isEmpty()) &&
 287                 (fragmenter != null) && fragmenter.isRetransmittable()) {
 288             fragmenter.setRetransmission();
 289         }
 290     }
 291 
 292     // buffered record fragment
 293     private static class RecordMemo {
 294         byte            contentType;
 295         byte            majorVersion;
 296         byte            minorVersion;
 297         int             encodeEpoch;
 298         CipherBox       encodeCipher;
 299         Authenticator   encodeAuthenticator;
 300 
 301         byte[]          fragment;
 302     }
 303 
 304     private static class HandshakeMemo extends RecordMemo {
 305         byte            handshakeType;
 306         int             messageSequence;
 307         int             acquireOffset;
 308     }
 309 
 310     private final class DTLSFragmenter {
 311         private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();

 312         private int acquireIndex = 0;
 313         private int messageSequence = 0;
 314         private boolean flightIsReady = false;
 315 
 316         // Per section 4.1.1, RFC 6347:
 317         //
 318         // If repeated retransmissions do not result in a response, and the
 319         // PMTU is unknown, subsequent retransmissions SHOULD back off to a
 320         // smaller record size, fragmenting the handshake message as
 321         // appropriate.
 322         //
 323         // In this implementation, two times of retransmits would be attempted
 324         // before backing off.  The back off is supported only if the packet
 325         // size is bigger than 256 bytes.
 326         private int retransmits = 2;            // attemps of retransmits
 327 
 328         void queueUpChangeCipherSpec() {
 329 
 330             // Cleanup if a new flight starts.
 331             if (flightIsReady) {
 332                 handshakeMemos.clear();
 333                 acquireIndex = 0;
 334                 flightIsReady = false;
 335             }
 336 
 337             RecordMemo memo = new RecordMemo();
 338 
 339             memo.contentType = Record.ct_change_cipher_spec;
 340             memo.majorVersion = protocolVersion.major;
 341             memo.minorVersion = protocolVersion.minor;
 342             memo.encodeEpoch = writeEpoch;
 343             memo.encodeCipher = writeCipher;
 344             memo.encodeAuthenticator = writeAuthenticator;
 345 
 346             memo.fragment = new byte[1];
 347             memo.fragment[0] = 1;
 348 
 349             handshakeMemos.add(memo);
 350         }
 351 
 352         void queueUpHandshake(byte[] buf,
 353                 int offset, int length) throws IOException {
 354 
 355             // Cleanup if a new flight starts.
 356             if (flightIsReady) {
 357                 handshakeMemos.clear();
 358                 acquireIndex = 0;
 359                 flightIsReady = false;
 360             }
 361 
 362             HandshakeMemo memo = new HandshakeMemo();
 363 
 364             memo.contentType = Record.ct_handshake;
 365             memo.majorVersion = protocolVersion.major;
 366             memo.minorVersion = protocolVersion.minor;
 367             memo.encodeEpoch = writeEpoch;
 368             memo.encodeCipher = writeCipher;
 369             memo.encodeAuthenticator = writeAuthenticator;
 370 
 371             memo.handshakeType = buf[offset];
 372             memo.messageSequence = messageSequence++;
 373             memo.acquireOffset = 0;
 374             memo.fragment = new byte[length - 4];       // 4: header size
 375                                                         //    1: HandshakeType
 376                                                         //    3: message length
 377             System.arraycopy(buf, offset + 4, memo.fragment, 0, length - 4);
 378 
 379             handshakeHashing(memo, memo.fragment);
 380             handshakeMemos.add(memo);
 381 
 382             if ((memo.handshakeType == HandshakeMessage.ht_client_hello) ||
 383                 (memo.handshakeType == HandshakeMessage.ht_hello_request) ||
 384                 (memo.handshakeType ==
 385                         HandshakeMessage.ht_hello_verify_request) ||
 386                 (memo.handshakeType == HandshakeMessage.ht_server_hello_done) ||
 387                 (memo.handshakeType == HandshakeMessage.ht_finished)) {
 388 
 389                 flightIsReady = true;
 390             }
 391         }
 392 
 393         Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
 394             if (isEmpty()) {
 395                 if (isRetransmittable()) {
 396                     setRetransmission();    // configure for retransmission
 397                 } else {
 398                     return null;
 399                 }
 400             }
 401 
 402             RecordMemo memo = handshakeMemos.get(acquireIndex);
 403             HandshakeMemo hsMemo = null;
 404             if (memo.contentType == Record.ct_handshake) {
 405                 hsMemo = (HandshakeMemo)memo;
 406             }
 407 
 408             int macLen = 0;
 409             if (memo.encodeAuthenticator instanceof MAC) {
 410                 macLen = ((MAC)memo.encodeAuthenticator).MAClen();
 411             }
 412 
 413             // ChangeCipherSpec message is pretty small.  Don't worry about
 414             // the fragmentation of ChangeCipherSpec record.
 415             int fragLen;
 416             if (packetSize > 0) {
 417                 fragLen = Math.min(maxRecordSize, packetSize);
 418                 fragLen = memo.encodeCipher.calculateFragmentSize(
 419                         fragLen, macLen, 25);   // 25: header size
 420                                                 //   13: DTLS record
 421                                                 //   12: DTLS handshake message
 422                 fragLen = Math.min(fragLen, Record.maxDataSize);
 423             } else {
 424                 fragLen = Record.maxDataSize;
 425             }
 426 
 427             if (fragmentSize > 0) {
 428                 fragLen = Math.min(fragLen, fragmentSize);
 429             }
 430 
 431             int dstPos = dstBuf.position();
 432             int dstLim = dstBuf.limit();
 433             int dstContent = dstPos + headerSize +
 434                                     memo.encodeCipher.getExplicitNonceSize();
 435             dstBuf.position(dstContent);
 436 
 437             if (hsMemo != null) {
 438                 fragLen = Math.min(fragLen,
 439                         (hsMemo.fragment.length - hsMemo.acquireOffset));


 442                 dstBuf.put((byte)((hsMemo.fragment.length >> 16) & 0xFF));
 443                 dstBuf.put((byte)((hsMemo.fragment.length >> 8) & 0xFF));
 444                 dstBuf.put((byte)(hsMemo.fragment.length & 0xFF));
 445                 dstBuf.put((byte)((hsMemo.messageSequence >> 8) & 0xFF));
 446                 dstBuf.put((byte)(hsMemo.messageSequence & 0xFF));
 447                 dstBuf.put((byte)((hsMemo.acquireOffset >> 16) & 0xFF));
 448                 dstBuf.put((byte)((hsMemo.acquireOffset >> 8) & 0xFF));
 449                 dstBuf.put((byte)(hsMemo.acquireOffset & 0xFF));
 450                 dstBuf.put((byte)((fragLen >> 16) & 0xFF));
 451                 dstBuf.put((byte)((fragLen >> 8) & 0xFF));
 452                 dstBuf.put((byte)(fragLen & 0xFF));
 453                 dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, fragLen);
 454             } else {
 455                 fragLen = Math.min(fragLen, memo.fragment.length);
 456                 dstBuf.put(memo.fragment, 0, fragLen);
 457             }
 458 
 459             dstBuf.limit(dstBuf.position());
 460             dstBuf.position(dstContent);
 461 
 462             if ((debug != null) && Debug.isOn("record")) {
 463                 System.out.println(Thread.currentThread().getName() +
 464                         ", WRITE: " + protocolVersion + " " +
 465                         Record.contentName(memo.contentType) +
 466                         ", length = " + dstBuf.remaining());
 467             }
 468 
 469             // Encrypt the fragment and wrap up a record.
 470             long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
 471                     memo.contentType, dstBuf,
 472                     dstPos, dstLim, headerSize,
 473                     ProtocolVersion.valueOf(memo.majorVersion,
 474                             memo.minorVersion), true);
 475 
 476             if ((debug != null) && Debug.isOn("packet")) {
 477                 ByteBuffer temporary = dstBuf.duplicate();
 478                 temporary.limit(temporary.position());
 479                 temporary.position(dstPos);
 480                 Debug.printHex(
 481                         "[Raw write]: length = " + temporary.remaining(),
 482                         temporary);
 483             }
 484 
 485             // remain the limit unchanged
 486             dstBuf.limit(dstLim);
 487 
 488             // Reset the fragmentation offset.
 489             if (hsMemo != null) {
 490                 hsMemo.acquireOffset += fragLen;
 491                 if (hsMemo.acquireOffset == hsMemo.fragment.length) {
 492                     acquireIndex++;
 493                 }
 494 
 495                 return new Ciphertext(RecordType.valueOf(
 496                         hsMemo.contentType, hsMemo.handshakeType), recordSN);
 497             } else {
 498                 acquireIndex++;
 499                 return new Ciphertext(
 500                         RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
 501             }
 502         }
 503 
 504         private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
 505 
 506             byte hsType = hsFrag.handshakeType;
 507             if ((hsType == HandshakeMessage.ht_hello_request) ||
 508                 (hsType == HandshakeMessage.ht_hello_verify_request)) {
 509 
 510                 // omitted from handshake hash computation
 511                 return;
 512             }
 513 
 514             if ((hsFrag.messageSequence == 0) &&
 515                 (hsType == HandshakeMessage.ht_client_hello)) {
 516 
 517                 // omit initial ClientHello message
 518                 //
 519                 //  2: ClientHello.client_version
 520                 // 32: ClientHello.random
 521                 int sidLen = hsBody[34];
 522 
 523                 if (sidLen == 0) {      // empty session_id, initial handshake
 524                     return;
 525                 }
 526             }
 527 
 528             // calculate the DTLS header
 529             byte[] temporary = new byte[12];    // 12: handshake header size
 530 
 531             // Handshake.msg_type
 532             temporary[0] = hsFrag.handshakeType;
 533 
 534             // Handshake.length
 535             temporary[1] = (byte)((hsBody.length >> 16) & 0xFF);
 536             temporary[2] = (byte)((hsBody.length >> 8) & 0xFF);
 537             temporary[3] = (byte)(hsBody.length & 0xFF);
 538 
 539             // Handshake.message_seq
 540             temporary[4] = (byte)((hsFrag.messageSequence >> 8) & 0xFF);
 541             temporary[5] = (byte)(hsFrag.messageSequence & 0xFF);
 542 
 543             // Handshake.fragment_offset
 544             temporary[6] = 0;
 545             temporary[7] = 0;
 546             temporary[8] = 0;
 547 
 548             // Handshake.fragment_length
 549             temporary[9] = temporary[1];
 550             temporary[10] = temporary[2];
 551             temporary[11] = temporary[3];
 552 
 553             if ((hsType != HandshakeMessage.ht_finished) &&
 554                 (hsType != HandshakeMessage.ht_certificate_verify)) {
 555 
 556                 handshakeHash.update(temporary, 0, 12);
 557                 handshakeHash.update(hsBody, 0, hsBody.length);
 558             } else {
 559                 // Reserve until this handshake message has been processed.
 560                 handshakeHash.reserve(temporary, 0, 12);
 561                 handshakeHash.reserve(hsBody, 0, hsBody.length);
 562             }
 563 
 564         }
 565 
 566         boolean isEmpty() {
 567             if (!flightIsReady || handshakeMemos.isEmpty() ||
 568                     acquireIndex >= handshakeMemos.size()) {
 569                 return true;
 570             }
 571 
 572             return false;
 573         }
 574 
 575         boolean isRetransmittable() {
 576             return (flightIsReady && !handshakeMemos.isEmpty() &&
 577                                 (acquireIndex >= handshakeMemos.size()));
 578         }
 579 
 580         private void setRetransmission() {
 581             acquireIndex = 0;
 582             for (RecordMemo memo : handshakeMemos) {
 583                 if (memo instanceof HandshakeMemo) {


   1 /*
   2  * Copyright (c) 1996, 2018, 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.net.ssl.*;
  32 import sun.security.ssl.SSLCipher.SSLWriteCipher;


  33 
  34 /**
  35  * DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
  36  */
  37 final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
  38 
  39     private DTLSFragmenter fragmenter = null;
  40 
  41     int                 writeEpoch;
  42 
  43     int                 prevWriteEpoch;
  44     Authenticator       prevWriteAuthenticator;
  45     SSLWriteCipher      prevWriteCipher;
  46 
  47     private final LinkedList<RecordMemo> alertMemos = new LinkedList<>();
  48 
  49     DTLSOutputRecord(HandshakeHash handshakeHash) {
  50         super(handshakeHash, SSLWriteCipher.nullDTlsWriteCipher());
  51 
  52         this.writeEpoch = 0;
  53         this.prevWriteEpoch = 0;
  54         this.prevWriteCipher = SSLWriteCipher.nullDTlsWriteCipher();

  55 
  56         this.packetSize = DTLSRecord.maxRecordSize;
  57         this.protocolVersion = ProtocolVersion.NONE;
  58     }
  59 
  60     @Override
  61     void initHandshaker() {
  62         // clean up
  63         fragmenter = null;
  64     }
  65 
  66     @Override
  67     void finishHandshake() {
  68 //        fragmenter = null;
  69     }
  70 
  71     @Override
  72     void changeWriteCiphers(SSLWriteCipher writeCipher,
  73             boolean useChangeCipherSpec) throws IOException {
  74         if (useChangeCipherSpec) {
  75             encodeChangeCipherSpec();
  76         }
  77 
  78         prevWriteCipher.dispose();
  79 

  80         this.prevWriteCipher = this.writeCipher;
  81         this.prevWriteEpoch = this.writeEpoch;
  82 

  83         this.writeCipher = writeCipher;
  84         this.writeEpoch++;
  85 
  86         this.isFirstAppOutputRecord = true;
  87 
  88         // set the epoch number
  89         this.writeCipher.authenticator.setEpochNumber(this.writeEpoch);
  90     }
  91 
  92     @Override
  93     void encodeAlert(byte level, byte description) throws IOException {
  94         RecordMemo memo = new RecordMemo();
  95 
  96         memo.contentType = ContentType.ALERT.id;
  97         memo.majorVersion = protocolVersion.major;
  98         memo.minorVersion = protocolVersion.minor;
  99         memo.encodeEpoch = writeEpoch;
 100         memo.encodeCipher = writeCipher;

 101 
 102         memo.fragment = new byte[2];
 103         memo.fragment[0] = level;
 104         memo.fragment[1] = description;
 105 
 106         alertMemos.add(memo);
 107     }
 108 
 109     @Override
 110     void encodeChangeCipherSpec() throws IOException {
 111         if (fragmenter == null) {
 112            fragmenter = new DTLSFragmenter();
 113         }
 114         fragmenter.queueUpChangeCipherSpec();
 115     }
 116 
 117     @Override
 118     void encodeHandshake(byte[] source,
 119             int offset, int length) throws IOException {

 120         if (firstMessage) {
 121             firstMessage = false;
 122         }
 123 
 124         if (fragmenter == null) {
 125            fragmenter = new DTLSFragmenter();
 126         }
 127 
 128         fragmenter.queueUpHandshake(source, offset, length);
 129     }
 130 
 131     @Override
 132     Ciphertext encode(
 133         ByteBuffer[] srcs, int srcsOffset, int srcsLength,
 134         ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
 135         return encode(srcs, srcsOffset, srcsLength, dsts[0]);
 136     }
 137 
 138     private Ciphertext encode(ByteBuffer[] sources, int offset, int length,
 139             ByteBuffer destination) throws IOException {
 140 
 141         if (writeCipher.authenticator.seqNumOverflow()) {
 142             if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
 143                 SSLLogger.fine(
 144                     "sequence number extremely close to overflow " +
 145                     "(2^64-1 packets). Closing connection.");
 146             }
 147 
 148             throw new SSLHandshakeException("sequence number overflow");
 149         }
 150 
 151         // Don't process the incoming record until all of the buffered records
 152         // get handled.  May need retransmission if no sources specified.
 153         if (!isEmpty() || sources == null || sources.length == 0) {
 154             Ciphertext ct = acquireCiphertext(destination);
 155             if (ct != null) {
 156                 return ct;
 157             }
 158         }
 159 
 160         if (sources == null || sources.length == 0) {
 161             return null;
 162         }
 163 
 164         int srcsRemains = 0;
 165         for (int i = offset; i < offset + length; i++) {
 166             srcsRemains += sources[i].remaining();
 167         }
 168 
 169         if (srcsRemains == 0) {
 170             return null;
 171         }
 172 
 173         // not apply to handshake message
 174         int fragLen;
 175         if (packetSize > 0) {
 176             fragLen = Math.min(maxRecordSize, packetSize);
 177             fragLen = writeCipher.calculateFragmentSize(
 178                     fragLen, headerSize);
 179 
 180             fragLen = Math.min(fragLen, Record.maxDataSize);
 181         } else {
 182             fragLen = Record.maxDataSize;
 183         }
 184 
 185         if (fragmentSize > 0) {
 186             fragLen = Math.min(fragLen, fragmentSize);
 187         }
 188 
 189         int dstPos = destination.position();
 190         int dstLim = destination.limit();
 191         int dstContent = dstPos + headerSize +
 192                                 writeCipher.getExplicitNonceSize();
 193         destination.position(dstContent);
 194 
 195         int remains = Math.min(fragLen, destination.remaining());
 196         fragLen = 0;
 197         int srcsLen = offset + length;
 198         for (int i = offset; (i < srcsLen) && (remains > 0); i++) {
 199             int amount = Math.min(sources[i].remaining(), remains);
 200             int srcLimit = sources[i].limit();
 201             sources[i].limit(sources[i].position() + amount);
 202             destination.put(sources[i]);
 203             sources[i].limit(srcLimit);         // restore the limit
 204             remains -= amount;
 205             fragLen += amount;
 206         }
 207 
 208         destination.limit(destination.position());
 209         destination.position(dstContent);
 210 
 211         if (SSLLogger.isOn && SSLLogger.isOn("record")) {
 212             SSLLogger.fine(
 213                     "WRITE: " + protocolVersion + " " +
 214                     ContentType.APPLICATION_DATA.name +
 215                     ", length = " + destination.remaining());
 216         }
 217 
 218         // Encrypt the fragment and wrap up a record.
 219         long recordSN = encrypt(writeCipher,
 220                 ContentType.APPLICATION_DATA.id, destination,
 221                 dstPos, dstLim, headerSize,
 222                 protocolVersion);
 223 
 224         if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
 225             ByteBuffer temporary = destination.duplicate();
 226             temporary.limit(temporary.position());
 227             temporary.position(dstPos);
 228             SSLLogger.fine("Raw write", temporary);


 229         }
 230 
 231         // remain the limit unchanged
 232         destination.limit(dstLim);
 233 
 234         return new Ciphertext(ContentType.APPLICATION_DATA.id,
 235                 SSLHandshake.NOT_APPLICABLE.id, recordSN);
 236     }
 237 
 238     private Ciphertext acquireCiphertext(
 239             ByteBuffer destination) throws IOException {
 240         if (alertMemos != null && !alertMemos.isEmpty()) {
 241             RecordMemo memo = alertMemos.pop();
 242 





 243             int dstPos = destination.position();
 244             int dstLim = destination.limit();
 245             int dstContent = dstPos + headerSize +
 246                                 writeCipher.getExplicitNonceSize();
 247             destination.position(dstContent);
 248 
 249             destination.put(memo.fragment);
 250 
 251             destination.limit(destination.position());
 252             destination.position(dstContent);
 253 
 254             if (SSLLogger.isOn && SSLLogger.isOn("record")) {
 255                 SSLLogger.fine(
 256                         "WRITE: " + protocolVersion + " " +
 257                         ContentType.ALERT.name +
 258                         ", length = " + destination.remaining());
 259             }
 260 
 261             // Encrypt the fragment and wrap up a record.
 262             long recordSN = encrypt(memo.encodeCipher,
 263                     ContentType.ALERT.id,
 264                     destination, dstPos, dstLim, headerSize,
 265                     ProtocolVersion.valueOf(memo.majorVersion,
 266                             memo.minorVersion));
 267 
 268             if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
 269                 ByteBuffer temporary = destination.duplicate();
 270                 temporary.limit(temporary.position());
 271                 temporary.position(dstPos);
 272                 SSLLogger.fine("Raw write", temporary);


 273             }
 274 
 275             // remain the limit unchanged
 276             destination.limit(dstLim);
 277 
 278             return new Ciphertext(ContentType.ALERT.id,
 279                     SSLHandshake.NOT_APPLICABLE.id, recordSN);
 280         }
 281 
 282         if (fragmenter != null) {
 283             return fragmenter.acquireCiphertext(destination);
 284         }
 285 
 286         return null;
 287     }
 288 
 289     @Override
 290     boolean isEmpty() {
 291         return ((fragmenter == null) || fragmenter.isEmpty()) &&
 292                ((alertMemos == null) || alertMemos.isEmpty());
 293     }
 294 
 295     @Override






 296     void launchRetransmission() {
 297         // Note: Please don't retransmit if there are handshake messages
 298         // or alerts waiting in the queue.
 299         if (((alertMemos == null) || alertMemos.isEmpty()) &&
 300                 (fragmenter != null) && fragmenter.isRetransmittable()) {
 301             fragmenter.setRetransmission();
 302         }
 303     }
 304 
 305     // buffered record fragment
 306     private static class RecordMemo {
 307         byte            contentType;
 308         byte            majorVersion;
 309         byte            minorVersion;
 310         int             encodeEpoch;
 311         SSLWriteCipher  encodeCipher;

 312 
 313         byte[]          fragment;
 314     }
 315 
 316     private static class HandshakeMemo extends RecordMemo {
 317         byte            handshakeType;
 318         int             messageSequence;
 319         int             acquireOffset;
 320     }
 321 
 322     private final class DTLSFragmenter {
 323         private final LinkedList<RecordMemo> handshakeMemos =
 324                 new LinkedList<>();
 325         private int acquireIndex = 0;
 326         private int messageSequence = 0;
 327         private boolean flightIsReady = false;
 328 
 329         // Per section 4.1.1, RFC 6347:
 330         //
 331         // If repeated retransmissions do not result in a response, and the
 332         // PMTU is unknown, subsequent retransmissions SHOULD back off to a
 333         // smaller record size, fragmenting the handshake message as
 334         // appropriate.
 335         //
 336         // In this implementation, two times of retransmits would be attempted
 337         // before backing off.  The back off is supported only if the packet
 338         // size is bigger than 256 bytes.
 339         private int retransmits = 2;            // attemps of retransmits
 340 
 341         void queueUpChangeCipherSpec() {
 342 
 343             // Cleanup if a new flight starts.
 344             if (flightIsReady) {
 345                 handshakeMemos.clear();
 346                 acquireIndex = 0;
 347                 flightIsReady = false;
 348             }
 349 
 350             RecordMemo memo = new RecordMemo();
 351 
 352             memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
 353             memo.majorVersion = protocolVersion.major;
 354             memo.minorVersion = protocolVersion.minor;
 355             memo.encodeEpoch = writeEpoch;
 356             memo.encodeCipher = writeCipher;

 357 
 358             memo.fragment = new byte[1];
 359             memo.fragment[0] = 1;
 360 
 361             handshakeMemos.add(memo);
 362         }
 363 
 364         void queueUpHandshake(byte[] buf,
 365                 int offset, int length) throws IOException {
 366 
 367             // Cleanup if a new flight starts.
 368             if (flightIsReady) {
 369                 handshakeMemos.clear();
 370                 acquireIndex = 0;
 371                 flightIsReady = false;
 372             }
 373 
 374             HandshakeMemo memo = new HandshakeMemo();
 375 
 376             memo.contentType = ContentType.HANDSHAKE.id;
 377             memo.majorVersion = protocolVersion.major;
 378             memo.minorVersion = protocolVersion.minor;
 379             memo.encodeEpoch = writeEpoch;
 380             memo.encodeCipher = writeCipher;

 381 
 382             memo.handshakeType = buf[offset];
 383             memo.messageSequence = messageSequence++;
 384             memo.acquireOffset = 0;
 385             memo.fragment = new byte[length - 4];       // 4: header size
 386                                                         //    1: HandshakeType
 387                                                         //    3: message length
 388             System.arraycopy(buf, offset + 4, memo.fragment, 0, length - 4);
 389 
 390             handshakeHashing(memo, memo.fragment);
 391             handshakeMemos.add(memo);
 392 
 393             if ((memo.handshakeType == SSLHandshake.CLIENT_HELLO.id) ||
 394                 (memo.handshakeType == SSLHandshake.HELLO_REQUEST.id) ||
 395                 (memo.handshakeType ==
 396                         SSLHandshake.HELLO_VERIFY_REQUEST.id) ||
 397                 (memo.handshakeType == SSLHandshake.SERVER_HELLO_DONE.id) ||
 398                 (memo.handshakeType == SSLHandshake.FINISHED.id)) {
 399 
 400                 flightIsReady = true;
 401             }
 402         }
 403 
 404         Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
 405             if (isEmpty()) {
 406                 if (isRetransmittable()) {
 407                     setRetransmission();    // configure for retransmission
 408                 } else {
 409                     return null;
 410                 }
 411             }
 412 
 413             RecordMemo memo = handshakeMemos.get(acquireIndex);
 414             HandshakeMemo hsMemo = null;
 415             if (memo.contentType == ContentType.HANDSHAKE.id) {
 416                 hsMemo = (HandshakeMemo)memo;
 417             }
 418 





 419             // ChangeCipherSpec message is pretty small.  Don't worry about
 420             // the fragmentation of ChangeCipherSpec record.
 421             int fragLen;
 422             if (packetSize > 0) {
 423                 fragLen = Math.min(maxRecordSize, packetSize);
 424                 fragLen = memo.encodeCipher.calculateFragmentSize(
 425                         fragLen, 25);   // 25: header size
 426                                                 //   13: DTLS record
 427                                                 //   12: DTLS handshake message
 428                 fragLen = Math.min(fragLen, Record.maxDataSize);
 429             } else {
 430                 fragLen = Record.maxDataSize;
 431             }
 432 
 433             if (fragmentSize > 0) {
 434                 fragLen = Math.min(fragLen, fragmentSize);
 435             }
 436 
 437             int dstPos = dstBuf.position();
 438             int dstLim = dstBuf.limit();
 439             int dstContent = dstPos + headerSize +
 440                                     memo.encodeCipher.getExplicitNonceSize();
 441             dstBuf.position(dstContent);
 442 
 443             if (hsMemo != null) {
 444                 fragLen = Math.min(fragLen,
 445                         (hsMemo.fragment.length - hsMemo.acquireOffset));


 448                 dstBuf.put((byte)((hsMemo.fragment.length >> 16) & 0xFF));
 449                 dstBuf.put((byte)((hsMemo.fragment.length >> 8) & 0xFF));
 450                 dstBuf.put((byte)(hsMemo.fragment.length & 0xFF));
 451                 dstBuf.put((byte)((hsMemo.messageSequence >> 8) & 0xFF));
 452                 dstBuf.put((byte)(hsMemo.messageSequence & 0xFF));
 453                 dstBuf.put((byte)((hsMemo.acquireOffset >> 16) & 0xFF));
 454                 dstBuf.put((byte)((hsMemo.acquireOffset >> 8) & 0xFF));
 455                 dstBuf.put((byte)(hsMemo.acquireOffset & 0xFF));
 456                 dstBuf.put((byte)((fragLen >> 16) & 0xFF));
 457                 dstBuf.put((byte)((fragLen >> 8) & 0xFF));
 458                 dstBuf.put((byte)(fragLen & 0xFF));
 459                 dstBuf.put(hsMemo.fragment, hsMemo.acquireOffset, fragLen);
 460             } else {
 461                 fragLen = Math.min(fragLen, memo.fragment.length);
 462                 dstBuf.put(memo.fragment, 0, fragLen);
 463             }
 464 
 465             dstBuf.limit(dstBuf.position());
 466             dstBuf.position(dstContent);
 467 
 468             if (SSLLogger.isOn && SSLLogger.isOn("record")) {
 469                 SSLLogger.fine(
 470                         "WRITE: " + protocolVersion + " " +
 471                         ContentType.nameOf(memo.contentType) +
 472                         ", length = " + dstBuf.remaining());
 473             }
 474 
 475             // Encrypt the fragment and wrap up a record.
 476             long recordSN = encrypt(memo.encodeCipher,
 477                     memo.contentType, dstBuf,
 478                     dstPos, dstLim, headerSize,
 479                     ProtocolVersion.valueOf(memo.majorVersion,
 480                             memo.minorVersion));
 481 
 482             if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
 483                 ByteBuffer temporary = dstBuf.duplicate();
 484                 temporary.limit(temporary.position());
 485                 temporary.position(dstPos);
 486                 SSLLogger.fine(
 487                         "Raw write (" + temporary.remaining() + ")", temporary);

 488             }
 489 
 490             // remain the limit unchanged
 491             dstBuf.limit(dstLim);
 492 
 493             // Reset the fragmentation offset.
 494             if (hsMemo != null) {
 495                 hsMemo.acquireOffset += fragLen;
 496                 if (hsMemo.acquireOffset == hsMemo.fragment.length) {
 497                     acquireIndex++;
 498                 }
 499 
 500                 return new Ciphertext(hsMemo.contentType,
 501                         hsMemo.handshakeType, recordSN);
 502             } else {
 503                 acquireIndex++;
 504                 return new Ciphertext(ContentType.CHANGE_CIPHER_SPEC.id,
 505                         SSLHandshake.NOT_APPLICABLE.id, recordSN);
 506             }
 507         }
 508 
 509         private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
 510 
 511             byte hsType = hsFrag.handshakeType;
 512             if (!handshakeHash.isHashable(hsType)) {


 513                 // omitted from handshake hash computation
 514                 return;
 515             }
 516 














 517             // calculate the DTLS header
 518             byte[] temporary = new byte[12];    // 12: handshake header size
 519 
 520             // Handshake.msg_type
 521             temporary[0] = hsFrag.handshakeType;
 522 
 523             // Handshake.length
 524             temporary[1] = (byte)((hsBody.length >> 16) & 0xFF);
 525             temporary[2] = (byte)((hsBody.length >> 8) & 0xFF);
 526             temporary[3] = (byte)(hsBody.length & 0xFF);
 527 
 528             // Handshake.message_seq
 529             temporary[4] = (byte)((hsFrag.messageSequence >> 8) & 0xFF);
 530             temporary[5] = (byte)(hsFrag.messageSequence & 0xFF);
 531 
 532             // Handshake.fragment_offset
 533             temporary[6] = 0;
 534             temporary[7] = 0;
 535             temporary[8] = 0;
 536 
 537             // Handshake.fragment_length
 538             temporary[9] = temporary[1];
 539             temporary[10] = temporary[2];
 540             temporary[11] = temporary[3];
 541 
 542             handshakeHash.deliver(temporary, 0, 12);
 543             handshakeHash.deliver(hsBody, 0, hsBody.length);









 544         }
 545 
 546         boolean isEmpty() {
 547             if (!flightIsReady || handshakeMemos.isEmpty() ||
 548                     acquireIndex >= handshakeMemos.size()) {
 549                 return true;
 550             }
 551 
 552             return false;
 553         }
 554 
 555         boolean isRetransmittable() {
 556             return (flightIsReady && !handshakeMemos.isEmpty() &&
 557                                 (acquireIndex >= handshakeMemos.size()));
 558         }
 559 
 560         private void setRetransmission() {
 561             acquireIndex = 0;
 562             for (RecordMemo memo : handshakeMemos) {
 563                 if (memo instanceof HandshakeMemo) {


< prev index next >