< prev index next >

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

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 1996, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 1996, 2018, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -23,34 +23,31 @@
  * questions.
  */
 
 package sun.security.ssl;
 
-import java.io.*;
-import java.nio.*;
-import java.util.Arrays;
-
-import javax.net.ssl.SSLException;
-import sun.security.util.HexDumpEncoder;
-
+import java.io.ByteArrayOutputStream;
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.nio.ByteBuffer;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
 
 /**
- * {@code OutputRecord} takes care of the management of SSL/TLS/DTLS output
- * records, including buffering, encryption, handshake messages marshal, etc.
+ * {@code OutputRecord} takes care of the management of SSL/(D)TLS
+ * output records, including buffering, encryption, handshake
+ * messages marshal, etc.
  *
  * @author David Brownell
  */
-abstract class OutputRecord extends ByteArrayOutputStream
-            implements Record, Closeable {
-
-    /* Class and subclass dynamic debugging support */
-    static final Debug          debug = Debug.getInstance("ssl");
-
-    Authenticator               writeAuthenticator;
-    CipherBox                   writeCipher;
+abstract class OutputRecord
+        extends ByteArrayOutputStream implements Record, Closeable {
+    SSLWriteCipher              writeCipher;
+    // Needed for KeyUpdate
+    TransportContext            tc;
 
-    HandshakeHash               handshakeHash;
+    final HandshakeHash         handshakeHash;
     boolean                     firstMessage;
 
     // current protocol version, sent as record version
     ProtocolVersion             protocolVersion;
 

@@ -73,20 +70,22 @@
 
     /*
      * Mappings from V3 cipher suite encodings to their pure V2 equivalents.
      * This is taken from the SSL V3 specification, Appendix E.
      */
-    private static int[] V3toV2CipherMap1 =
+    private static final int[] V3toV2CipherMap1 =
         {-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
-    private static int[] V3toV2CipherMap3 =
+    private static final int[] V3toV2CipherMap3 =
         {-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
 
-    OutputRecord() {
-        this.writeCipher = CipherBox.NULL;
+    OutputRecord(HandshakeHash handshakeHash, SSLWriteCipher writeCipher) {
+        this.writeCipher = writeCipher;
         this.firstMessage = true;
         this.fragmentSize = Record.maxDataSize;
 
+        this.handshakeHash = handshakeHash;
+
         // Please set packetSize and protocolVersion in the implementation.
     }
 
     void setVersion(ProtocolVersion protocolVersion) {
         this.protocolVersion = protocolVersion;

@@ -98,29 +97,20 @@
     synchronized void setHelloVersion(ProtocolVersion helloVersion) {
         this.helloVersion = helloVersion;
     }
 
     /*
-     * For handshaking, we need to be able to hash every byte above the
-     * record marking layer.  This is where we're guaranteed to see those
-     * bytes, so this is where we can hash them.
-     */
-    void setHandshakeHash(HandshakeHash handshakeHash) {
-        this.handshakeHash = handshakeHash;
-    }
-
-    /*
      * Return true iff the record is empty -- to avoid doing the work
      * of sending empty records over the network.
      */
     boolean isEmpty() {
         return false;
     }
 
     boolean seqNumIsHuge() {
-        return (writeAuthenticator != null) &&
-                        writeAuthenticator.seqNumIsHuge();
+        return (writeCipher.authenticator != null) &&
+                        writeCipher.authenticator.seqNumIsHuge();
     }
 
     // SSLEngine and SSLSocket
     abstract void encodeAlert(byte level, byte description) throws IOException;
 

@@ -130,39 +120,38 @@
 
     // SSLEngine and SSLSocket
     abstract void encodeChangeCipherSpec() throws IOException;
 
     // apply to SSLEngine only
-    Ciphertext encode(ByteBuffer[] sources, int offset, int length,
-            ByteBuffer destination) throws IOException {
+    Ciphertext encode(
+        ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+        ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
+
         throw new UnsupportedOperationException();
     }
 
     // apply to SSLEngine only
     void encodeV2NoCipher() throws IOException {
         throw new UnsupportedOperationException();
     }
 
     // apply to SSLSocket only
-    void deliver(byte[] source, int offset, int length) throws IOException {
+    void deliver(
+            byte[] source, int offset, int length) throws IOException {
         throw new UnsupportedOperationException();
     }
 
     // apply to SSLSocket only
     void setDeliverStream(OutputStream outputStream) {
         throw new UnsupportedOperationException();
     }
 
-    // apply to SSLEngine only
-    Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
-        throw new UnsupportedOperationException();
-    }
-
-    void changeWriteCiphers(Authenticator writeAuthenticator,
-            CipherBox writeCipher) throws IOException {
-
+    void changeWriteCiphers(SSLWriteCipher writeCipher,
+            boolean useChangeCipherSpec) throws IOException {
+        if (useChangeCipherSpec) {
         encodeChangeCipherSpec();
+        }
 
         /*
          * Dispose of any intermediate state in the underlying cipher.
          * For PKCS11 ciphers, this will release any attached sessions,
          * and thus make finalization faster.

@@ -170,11 +159,10 @@
          * Since MAC's doFinal() is called for every SSL/TLS packet, it's
          * not necessary to do the same with MAC's.
          */
         writeCipher.dispose();
 
-        this.writeAuthenticator = writeAuthenticator;
         this.writeCipher = writeCipher;
         this.isFirstAppOutputRecord = true;
     }
 
     void changePacketSize(int packetSize) {

@@ -193,10 +181,15 @@
     void initHandshaker() {
         // blank
     }
 
     // apply to DTLS SSLEngine
+    void finishHandshake() {
+        // blank
+    }
+
+    // apply to DTLS SSLEngine
     void launchRetransmission() {
         // blank
     }
 
     @Override

@@ -205,97 +198,72 @@
             isClosed = true;
             writeCipher.dispose();
         }
     }
 
+    synchronized boolean isClosed() {
+        return isClosed;
+    }
+
     //
     // shared helpers
     //
 
     // Encrypt a fragment and wrap up a record.
     //
     // To be consistent with the spec of SSLEngine.wrap() methods, the
     // destination ByteBuffer's position is updated to reflect the amount
     // of data produced.  The limit remains the same.
-    static long encrypt(Authenticator authenticator,
-            CipherBox encCipher, byte contentType, ByteBuffer destination,
+    static long encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
             int headerOffset, int dstLim, int headerSize,
-            ProtocolVersion protocolVersion, boolean isDTLS) {
-
-        byte[] sequenceNumber = null;
-        int dstContent = destination.position();
-
-        // Acquire the current sequence number before using.
+            ProtocolVersion protocolVersion) {
+        boolean isDTLS = protocolVersion.isDTLS;
         if (isDTLS) {
-            sequenceNumber = authenticator.sequenceNumber();
+            if (protocolVersion.useTLS13PlusSpec()) {
+                return d13Encrypt(encCipher,
+                        contentType, destination, headerOffset,
+                        dstLim, headerSize, protocolVersion);
+            } else {
+                return d10Encrypt(encCipher,
+                        contentType, destination, headerOffset,
+                        dstLim, headerSize, protocolVersion);
         }
-
-        // The sequence number may be shared for different purpose.
-        boolean sharedSequenceNumber = false;
-
-        // "flip" but skip over header again, add MAC & encrypt
-        if (authenticator instanceof MAC) {
-            MAC signer = (MAC)authenticator;
-            if (signer.MAClen() != 0) {
-                byte[] hash = signer.compute(contentType, destination, false);
-
-                /*
-                 * position was advanced to limit in MAC compute above.
-                 *
-                 * Mark next area as writable (above layers should have
-                 * established that we have plenty of room), then write
-                 * out the hash.
-                 */
-                destination.limit(destination.limit() + hash.length);
-                destination.put(hash);
-
-                // reset the position and limit
-                destination.limit(destination.position());
-                destination.position(dstContent);
-
-                // The signer has used and increased the sequence number.
-                if (isDTLS) {
-                    sharedSequenceNumber = true;
+        } else {
+            if (protocolVersion.useTLS13PlusSpec()) {
+                return t13Encrypt(encCipher,
+                        contentType, destination, headerOffset,
+                        dstLim, headerSize, protocolVersion);
+            } else {
+                return t10Encrypt(encCipher,
+                        contentType, destination, headerOffset,
+                        dstLim, headerSize, protocolVersion);
                 }
             }
         }
 
-        if (!encCipher.isNullCipher()) {
-            if (protocolVersion.useTLS11PlusSpec() &&
-                    (encCipher.isCBCMode() || encCipher.isAEADMode())) {
-                byte[] nonce = encCipher.createExplicitNonce(
-                        authenticator, contentType, destination.remaining());
-                destination.position(headerOffset + headerSize);
-                destination.put(nonce);
-            }
-            if (!encCipher.isAEADMode()) {
-                // The explicit IV in TLS 1.1 and later can be encrypted.
-                destination.position(headerOffset + headerSize);
-            }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
-
-            // Encrypt may pad, so again the limit may be changed.
-            encCipher.encrypt(destination, dstLim);
-
-            // The cipher has used and increased the sequence number.
-            if (isDTLS && encCipher.isAEADMode()) {
-                sharedSequenceNumber = true;
-            }
-        } else {
-            destination.position(destination.limit());
+    static long d13Encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion) {
+        throw new UnsupportedOperationException("Not supported yet.");
         }
 
+    private static long d10Encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion) {
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        encCipher.encrypt(contentType, destination);
+
         // Finish out the record header.
         int fragLen = destination.limit() - headerOffset - headerSize;
 
         destination.put(headerOffset, contentType);         // content type
         destination.put(headerOffset + 1, protocolVersion.major);
         destination.put(headerOffset + 2, protocolVersion.minor);
-        if (!isDTLS) {
-            // fragment length
-            destination.put(headerOffset + 3, (byte)(fragLen >> 8));
-            destination.put(headerOffset + 4, (byte)fragLen);
-        } else {
+
             // epoch and sequence_number
             destination.put(headerOffset + 3, sequenceNumber[0]);
             destination.put(headerOffset + 4, sequenceNumber[1]);
             destination.put(headerOffset + 5, sequenceNumber[2]);
             destination.put(headerOffset + 6, sequenceNumber[3]);

@@ -306,88 +274,171 @@
 
             // fragment length
             destination.put(headerOffset + 11, (byte)(fragLen >> 8));
             destination.put(headerOffset + 12, (byte)fragLen);
 
-            // Increase the sequence number for next use if it is not shared.
-            if (!sharedSequenceNumber) {
-                authenticator.increaseSequenceNumber();
+        // Update destination position to reflect the amount of data produced.
+        destination.position(destination.limit());
+
+        return Authenticator.toLong(sequenceNumber);
+    }
+
+    static long t13Encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion) {
+        if (!encCipher.isNullCipher()) {
+            // inner plaintext, using zero length padding.
+            int pos = destination.position();
+            destination.position(destination.limit());
+            destination.limit(destination.limit() + 1);
+            destination.put(contentType);
+            destination.position(pos);
+        }
+
+        // use the right TLSCiphertext.opaque_type and legacy_record_version
+        ProtocolVersion pv = protocolVersion;
+        if (!encCipher.isNullCipher()) {
+            pv = ProtocolVersion.TLS12;
+            contentType = ContentType.APPLICATION_DATA.id;
+        } else if (protocolVersion.useTLS13PlusSpec()) {
+            pv = ProtocolVersion.TLS12;
             }
+
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        encCipher.encrypt(contentType, destination);
+
+        // Finish out the record header.
+        int fragLen = destination.limit() - headerOffset - headerSize;
+        destination.put(headerOffset, contentType);
+        destination.put(headerOffset + 1, pv.major);
+        destination.put(headerOffset + 2, pv.minor);
+
+        // fragment length
+        destination.put(headerOffset + 3, (byte)(fragLen >> 8));
+        destination.put(headerOffset + 4, (byte)fragLen);
+
+        // Update destination position to reflect the amount of data produced.
+        destination.position(destination.limit());
+
+        return Authenticator.toLong(sequenceNumber);
         }
 
+    static long t10Encrypt(
+            SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+            int headerOffset, int dstLim, int headerSize,
+            ProtocolVersion protocolVersion) {
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        encCipher.encrypt(contentType, destination);
+
+        // Finish out the record header.
+        int fragLen = destination.limit() - headerOffset - headerSize;
+
+        destination.put(headerOffset, contentType);         // content type
+        destination.put(headerOffset + 1, protocolVersion.major);
+        destination.put(headerOffset + 2, protocolVersion.minor);
+
+        // fragment length
+        destination.put(headerOffset + 3, (byte)(fragLen >> 8));
+        destination.put(headerOffset + 4, (byte)fragLen);
+
         // Update destination position to reflect the amount of data produced.
         destination.position(destination.limit());
 
         return Authenticator.toLong(sequenceNumber);
     }
 
     // Encrypt a fragment and wrap up a record.
     //
     // Uses the internal expandable buf variable and the current
     // protocolVersion variable.
-    void encrypt(Authenticator authenticator,
-            CipherBox encCipher, byte contentType, int headerSize) {
-
-        int position = headerSize + writeCipher.getExplicitNonceSize();
-
-        // "flip" but skip over header again, add MAC & encrypt
-        int macLen = 0;
-        if (authenticator instanceof MAC) {
-            MAC signer = (MAC)authenticator;
-            macLen = signer.MAClen();
-            if (macLen != 0) {
-                byte[] hash = signer.compute(contentType,
-                        buf, position, (count - position), false);
-
-                write(hash, 0, hash.length);
+    long encrypt(
+            SSLWriteCipher encCipher, byte contentType, int headerSize) {
+        if (protocolVersion.useTLS13PlusSpec()) {
+            return t13Encrypt(encCipher, contentType, headerSize);
+        } else {
+            return t10Encrypt(encCipher, contentType, headerSize);
             }
         }
 
+    private static final class T13PaddingHolder {
+        private static final byte[] zeros = new byte[16];
+    }
+
+    long t13Encrypt(
+            SSLWriteCipher encCipher, byte contentType, int headerSize) {
         if (!encCipher.isNullCipher()) {
-            // Requires explicit IV/nonce for CBC/AEAD cipher suites for
-            // TLS 1.1 or later.
-            if (protocolVersion.useTLS11PlusSpec() &&
-                    (encCipher.isCBCMode() || encCipher.isAEADMode())) {
-
-                byte[] nonce = encCipher.createExplicitNonce(
-                        authenticator, contentType, (count - position));
-                int noncePos = position - nonce.length;
-                System.arraycopy(nonce, 0, buf, noncePos, nonce.length);
-            }
-
-            if (!encCipher.isAEADMode()) {
-                // The explicit IV in TLS 1.1 and later can be encrypted.
-                position = headerSize;
-            }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
-
-            // increase buf capacity if necessary
-            int fragSize = count - position;
-            int packetSize =
-                    encCipher.calculatePacketSize(fragSize, macLen, headerSize);
-            if (packetSize > (buf.length - position)) {
-                byte[] newBuf = new byte[position + packetSize];
+            // inner plaintext
+            write(contentType);
+            write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length);
+        }
+
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        int position = headerSize;
+        int contentLen = count - position;
+
+        // ensure the capacity
+        int packetSize = encCipher.calculatePacketSize(contentLen, headerSize);
+        if (packetSize > buf.length) {
+            byte[] newBuf = new byte[packetSize];
                 System.arraycopy(buf, 0, newBuf, 0, count);
                 buf = newBuf;
             }
 
-            // Encrypt may pad, so again the count may be changed.
-            count = position +
-                    encCipher.encrypt(buf, position, (count - position));
+        // use the right TLSCiphertext.opaque_type and legacy_record_version
+        ProtocolVersion pv = protocolVersion;
+        if (!encCipher.isNullCipher()) {
+            pv = ProtocolVersion.TLS12;
+            contentType = ContentType.APPLICATION_DATA.id;
+        } else {
+            pv = ProtocolVersion.TLS12;
+        }
+
+        ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
+        count = headerSize + encCipher.encrypt(contentType, destination);
+
+        // Fill out the header, write it and the message.
+        int fragLen = count - headerSize;
+
+        buf[0] = contentType;
+        buf[1] = pv.major;
+        buf[2] = pv.minor;
+        buf[3] = (byte)((fragLen >> 8) & 0xFF);
+        buf[4] = (byte)(fragLen & 0xFF);
+
+        return Authenticator.toLong(sequenceNumber);
+    }
+
+    long t10Encrypt(
+            SSLWriteCipher encCipher, byte contentType, int headerSize) {
+        byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+        int position = headerSize + writeCipher.getExplicitNonceSize();
+        int contentLen = count - position;
+
+        // ensure the capacity
+        int packetSize = encCipher.calculatePacketSize(contentLen, headerSize);
+        if (packetSize > buf.length) {
+            byte[] newBuf = new byte[packetSize];
+            System.arraycopy(buf, 0, newBuf, 0, count);
+            buf = newBuf;
         }
+        ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
+        count = headerSize + encCipher.encrypt(contentType, destination);
 
         // Fill out the header, write it and the message.
         int fragLen = count - headerSize;
         buf[0] = contentType;
         buf[1] = protocolVersion.major;
         buf[2] = protocolVersion.minor;
         buf[3] = (byte)((fragLen >> 8) & 0xFF);
         buf[4] = (byte)(fragLen & 0xFF);
+
+        return Authenticator.toLong(sequenceNumber);
     }
 
     static ByteBuffer encodeV2ClientHello(
             byte[] fragment, int offset, int length) throws IOException {
-
         int v3SessIdLenOffset = offset + 34;      //  2: client_version
                                                   // 32: random
 
         int v3SessIdLen = fragment[v3SessIdLenOffset];
         int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;

@@ -447,11 +498,11 @@
          */
         int msgLen = dstBuf.position() - 2;   // Exclude the legth field itself
         dstBuf.position(0);
         dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF)));  // pos: 0
         dstBuf.put((byte)(msgLen & 0xFF));                   // pos: 1
-        dstBuf.put(HandshakeMessage.ht_client_hello);        // pos: 2
+        dstBuf.put(SSLHandshake.CLIENT_HELLO.id);            // pos: 2
         dstBuf.put(fragment[offset]);         // major version, pos: 3
         dstBuf.put(fragment[offset + 1]);     // minor version, pos: 4
         dstBuf.put((byte)(v2CSLen >>> 8));                   // pos: 5
         dstBuf.put((byte)(v2CSLen & 0xFF));                  // pos: 6
         dstBuf.put((byte)0x00);           // session_id_length, pos: 7
< prev index next >