jdk/src/share/classes/sun/security/ssl/EngineOutputRecord.java
changeset 16913 a6f4d1626ad9
parent 16126 aad71cf676d7
--- a/jdk/src/share/classes/sun/security/ssl/EngineOutputRecord.java	Thu Apr 11 14:47:54 2013 -0700
+++ b/jdk/src/share/classes/sun/security/ssl/EngineOutputRecord.java	Thu Apr 11 18:57:14 2013 -0700
@@ -29,7 +29,6 @@
 import java.io.*;
 import java.nio.*;
 
-
 /**
  * A OutputRecord class extension which uses external ByteBuffers
  * or the internal ByteArrayOutputStream for data manipulations.
@@ -101,51 +100,6 @@
         return finishedMsg;
     }
 
-
-    /**
-     * Calculate the MAC value, storing the result either in
-     * the internal buffer, or at the end of the destination
-     * ByteBuffer.
-     * <P>
-     * We assume that the higher levels have assured us enough
-     * room, otherwise we'll indirectly throw a
-     * BufferOverFlowException runtime exception.
-     *
-     * position should equal limit, and points to the next
-     * free spot.
-     */
-    private void addMAC(MAC signer, ByteBuffer bb)
-            throws IOException {
-
-        if (signer.MAClen() != 0) {
-            byte[] hash = signer.compute(contentType(), bb, false);
-
-            /*
-             * position was advanced to limit in compute above.
-             *
-             * Mark next area as writable (above layers should have
-             * established that we have plenty of room), then write
-             * out the hash.
-             */
-            bb.limit(bb.limit() + hash.length);
-            bb.put(hash);
-        }
-    }
-
-    /*
-     * Encrypt a ByteBuffer.
-     *
-     * We assume that the higher levels have assured us enough
-     * room for the encryption (plus padding), otherwise we'll
-     * indirectly throw a BufferOverFlowException runtime exception.
-     *
-     * position and limit will be the same, and points to the
-     * next free spot.
-     */
-    void encrypt(CipherBox box, ByteBuffer bb) {
-        box.encrypt(bb);
-    }
-
     /*
      * Override the actual write below.  We do things this way to be
      * consistent with InputRecord.  InputRecord may try to write out
@@ -160,7 +114,8 @@
          * Copy data out of buffer, it's ready to go.
          */
         ByteBuffer netBB = (ByteBuffer)
-            ByteBuffer.allocate(len).put(buf, 0, len).flip();
+            ByteBuffer.allocate(len).put(buf, off, len).flip();
+
         writer.putOutboundData(netBB);
     }
 
@@ -168,17 +123,19 @@
      * Main method for writing non-application data.
      * We MAC/encrypt, then send down for processing.
      */
-    void write(MAC writeMAC, CipherBox writeCipher) throws IOException {
+    void write(Authenticator authenticator, CipherBox writeCipher)
+            throws IOException {
+
         /*
          * Sanity check.
          */
         switch (contentType()) {
-        case ct_change_cipher_spec:
-        case ct_alert:
-        case ct_handshake:
-            break;
-        default:
-            throw new RuntimeException("unexpected byte buffers");
+            case ct_change_cipher_spec:
+            case ct_alert:
+            case ct_handshake:
+                break;
+            default:
+                throw new RuntimeException("unexpected byte buffers");
         }
 
         /*
@@ -193,10 +150,10 @@
          */
         if (!isEmpty()) {
             // compress();              // eventually
-            addMAC(writeMAC);
-            encrypt(writeCipher);
-            write((OutputStream)null, false,  // send down for processing
-                (ByteArrayOutputStream)null);
+            encrypt(authenticator, writeCipher);
+
+            // send down for processing
+            write((OutputStream)null, false, (ByteArrayOutputStream)null);
         }
         return;
     }
@@ -204,8 +161,8 @@
     /**
      * Main wrap/write driver.
      */
-    void write(EngineArgs ea, MAC writeMAC, CipherBox writeCipher)
-            throws IOException {
+    void write(EngineArgs ea, Authenticator authenticator,
+            CipherBox writeCipher) throws IOException {
         /*
          * sanity check to make sure someone didn't inadvertantly
          * send us an impossible combination we don't know how
@@ -217,7 +174,7 @@
          * Have we set the MAC's yet?  If not, we're not ready
          * to process application data yet.
          */
-        if (writeMAC == MAC.NULL) {
+        if (authenticator == MAC.NULL) {
             return;
         }
 
@@ -255,7 +212,7 @@
          */
         int length;
         if (engine.needToSplitPayload(writeCipher, protocolVersion)) {
-            write(ea, writeMAC, writeCipher, 0x01);
+            write(ea, authenticator, writeCipher, 0x01);
             ea.resetLim();      // reset application data buffer limit
             length = Math.min(ea.getAppRemaining(),
                         maxDataSizeMinusOneByteRecord);
@@ -265,14 +222,14 @@
 
         // Don't bother to really write empty records.
         if (length > 0) {
-            write(ea, writeMAC, writeCipher, length);
+            write(ea, authenticator, writeCipher, length);
         }
 
         return;
     }
 
-    void write(EngineArgs ea, MAC writeMAC, CipherBox writeCipher,
-            int length) throws IOException {
+    void write(EngineArgs ea, Authenticator authenticator,
+            CipherBox writeCipher, int length) throws IOException {
         /*
          * Copy out existing buffer values.
          */
@@ -286,39 +243,76 @@
          * Don't need to worry about SSLv2 rewrites, if we're here,
          * that's long since done.
          */
-        int dstData = dstPos + headerSize;
+        int dstData = dstPos + headerSize + writeCipher.getExplicitNonceSize();
         dstBB.position(dstData);
 
+        /*
+         * transfer application data into the network data buffer
+         */
         ea.gather(length);
+        dstBB.limit(dstBB.position());
+        dstBB.position(dstData);
 
         /*
          * "flip" but skip over header again, add MAC & encrypt
-         * addMAC will expand the limit to reflect the new
-         * data.
          */
-        dstBB.limit(dstBB.position());
-        dstBB.position(dstData);
-        addMAC(writeMAC, dstBB);
+        if (authenticator instanceof MAC) {
+            MAC signer = (MAC)authenticator;
+            if (signer.MAClen() != 0) {
+                byte[] hash = signer.compute(contentType(), dstBB, false);
+
+                /*
+                 * position was advanced to limit in compute above.
+                 *
+                 * Mark next area as writable (above layers should have
+                 * established that we have plenty of room), then write
+                 * out the hash.
+                 */
+                dstBB.limit(dstBB.limit() + hash.length);
+                dstBB.put(hash);
+
+                // reset the position and limit
+                dstBB.limit(dstBB.position());
+                dstBB.position(dstData);
+            }
+        }
 
-        /*
-         * Encrypt may pad, so again the limit may have changed.
-         */
-        dstBB.limit(dstBB.position());
-        dstBB.position(dstData);
-        encrypt(writeCipher, dstBB);
+        if (!writeCipher.isNullCipher()) {
+            /*
+             * Requires explicit IV/nonce for CBC/AEAD cipher suites for TLS 1.1
+             * or later.
+             */
+            if (protocolVersion.v >= ProtocolVersion.TLS11.v &&
+                    (writeCipher.isCBCMode() || writeCipher.isAEADMode())) {
+                byte[] nonce = writeCipher.createExplicitNonce(
+                        authenticator, contentType(), dstBB.remaining());
+                dstBB.position(dstPos + headerSize);
+                dstBB.put(nonce);
+                if (!writeCipher.isAEADMode()) {
+                    // The explicit IV in TLS 1.1 and later can be encrypted.
+                    dstBB.position(dstPos + headerSize);
+                }   // Otherwise, DON'T encrypt the nonce_explicit for AEAD mode
+            }
 
-        if (debug != null
-                && (Debug.isOn("record") || Debug.isOn("handshake"))) {
-            if ((debug != null && Debug.isOn("record"))
-                    || contentType() == ct_change_cipher_spec)
+            /*
+             * Encrypt may pad, so again the limit may have changed.
+             */
+            writeCipher.encrypt(dstBB, dstLim);
+
+            if ((debug != null) && (Debug.isOn("record") ||
+                    (Debug.isOn("handshake") &&
+                        (contentType() == ct_change_cipher_spec)))) {
                 System.out.println(Thread.currentThread().getName()
                     // v3.0/v3.1 ...
                     + ", WRITE: " + protocolVersion
                     + " " + InputRecord.contentName(contentType())
                     + ", length = " + length);
+            }
+        } else {
+            dstBB.position(dstBB.limit());
         }
 
-        int packetLength = dstBB.limit() - dstData;
+        int packetLength = dstBB.limit() - dstPos - headerSize;
 
         /*
          * Finish out the record header.
@@ -333,7 +327,5 @@
          * Position was already set by encrypt() above.
          */
         dstBB.limit(dstLim);
-
-        return;
     }
 }