jdk/src/java.base/share/classes/sun/security/ssl/EngineInputRecord.java
changeset 33927 567b299aa689
parent 33926 3a19edba4808
parent 31008 5b500c93ce48
child 33928 d2c9fee88260
equal deleted inserted replaced
33926:3a19edba4808 33927:567b299aa689
     1 /*
       
     2  * Copyright (c) 2003, 2014, 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 
       
    27 package sun.security.ssl;
       
    28 
       
    29 import java.io.*;
       
    30 import java.nio.*;
       
    31 import javax.net.ssl.*;
       
    32 import javax.crypto.BadPaddingException;
       
    33 import sun.misc.HexDumpEncoder;
       
    34 
       
    35 
       
    36 /**
       
    37  * Wrapper class around InputRecord.
       
    38  *
       
    39  * Application data is kept external to the InputRecord,
       
    40  * but handshake data (alert/change_cipher_spec/handshake) will
       
    41  * be kept internally in the ByteArrayInputStream.
       
    42  *
       
    43  * @author Brad Wetmore
       
    44  */
       
    45 final class EngineInputRecord extends InputRecord {
       
    46 
       
    47     private SSLEngineImpl engine;
       
    48 
       
    49     /*
       
    50      * A dummy ByteBuffer we'll pass back even when the data
       
    51      * is stored internally.  It'll never actually be used.
       
    52      */
       
    53     static private ByteBuffer tmpBB = ByteBuffer.allocate(0);
       
    54 
       
    55     /*
       
    56      * Flag to tell whether the last read/parsed data resides
       
    57      * internal in the ByteArrayInputStream, or in the external
       
    58      * buffers.
       
    59      */
       
    60     private boolean internalData;
       
    61 
       
    62     EngineInputRecord(SSLEngineImpl engine) {
       
    63         super();
       
    64         this.engine = engine;
       
    65     }
       
    66 
       
    67     @Override
       
    68     byte contentType() {
       
    69         if (internalData) {
       
    70             return super.contentType();
       
    71         } else {
       
    72             return ct_application_data;
       
    73         }
       
    74     }
       
    75 
       
    76     /*
       
    77      * Check if there is enough inbound data in the ByteBuffer
       
    78      * to make a inbound packet.  Look for both SSLv2 and SSLv3.
       
    79      *
       
    80      * @return -1 if there are not enough bytes to tell (small header),
       
    81      */
       
    82     int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
       
    83 
       
    84         /*
       
    85          * SSLv2 length field is in bytes 0/1
       
    86          * SSLv3/TLS length field is in bytes 3/4
       
    87          */
       
    88         if (buf.remaining() < 5) {
       
    89             return -1;
       
    90         }
       
    91 
       
    92         int pos = buf.position();
       
    93         byte byteZero = buf.get(pos);
       
    94 
       
    95         int len = 0;
       
    96 
       
    97         /*
       
    98          * If we have already verified previous packets, we can
       
    99          * ignore the verifications steps, and jump right to the
       
   100          * determination.  Otherwise, try one last hueristic to
       
   101          * see if it's SSL/TLS.
       
   102          */
       
   103         if (formatVerified ||
       
   104                 (byteZero == ct_handshake) ||
       
   105                 (byteZero == ct_alert)) {
       
   106             /*
       
   107              * Last sanity check that it's not a wild record
       
   108              */
       
   109             ProtocolVersion recordVersion =
       
   110                 ProtocolVersion.valueOf(buf.get(pos + 1), buf.get(pos + 2));
       
   111 
       
   112             // check the record version
       
   113             checkRecordVersion(recordVersion, false);
       
   114 
       
   115             /*
       
   116              * Reasonably sure this is a V3, disable further checks.
       
   117              * We can't do the same in the v2 check below, because
       
   118              * read still needs to parse/handle the v2 clientHello.
       
   119              */
       
   120             formatVerified = true;
       
   121 
       
   122             /*
       
   123              * One of the SSLv3/TLS message types.
       
   124              */
       
   125             len = ((buf.get(pos + 3) & 0xff) << 8) +
       
   126                 (buf.get(pos + 4) & 0xff) + headerSize;
       
   127 
       
   128         } else {
       
   129             /*
       
   130              * Must be SSLv2 or something unknown.
       
   131              * Check if it's short (2 bytes) or
       
   132              * long (3) header.
       
   133              *
       
   134              * Internals can warn about unsupported SSLv2
       
   135              */
       
   136             boolean isShort = ((byteZero & 0x80) != 0);
       
   137 
       
   138             if (isShort &&
       
   139                     ((buf.get(pos + 2) == 1) || buf.get(pos + 2) == 4)) {
       
   140 
       
   141                 ProtocolVersion recordVersion =
       
   142                     ProtocolVersion.valueOf(buf.get(pos + 3), buf.get(pos + 4));
       
   143 
       
   144                 // check the record version
       
   145                 checkRecordVersion(recordVersion, true);
       
   146 
       
   147                 /*
       
   148                  * Client or Server Hello
       
   149                  */
       
   150                 int mask = (isShort ? 0x7f : 0x3f);
       
   151                 len = ((byteZero & mask) << 8) + (buf.get(pos + 1) & 0xff) +
       
   152                     (isShort ? 2 : 3);
       
   153 
       
   154             } else {
       
   155                 // Gobblygook!
       
   156                 throw new SSLException(
       
   157                     "Unrecognized SSL message, plaintext connection?");
       
   158             }
       
   159         }
       
   160 
       
   161         return len;
       
   162     }
       
   163 
       
   164     /*
       
   165      * Pass the data down if it's internally cached, otherwise
       
   166      * do it here.
       
   167      *
       
   168      * If internal data, data is decrypted internally.
       
   169      *
       
   170      * If external data(app), return a new ByteBuffer with data to
       
   171      * process.
       
   172      */
       
   173     ByteBuffer decrypt(Authenticator authenticator,
       
   174             CipherBox box, ByteBuffer bb) throws BadPaddingException {
       
   175 
       
   176         if (internalData) {
       
   177             decrypt(authenticator, box);   // MAC is checked during decryption
       
   178             return tmpBB;
       
   179         }
       
   180 
       
   181         BadPaddingException reservedBPE = null;
       
   182         int tagLen =
       
   183             (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
       
   184         int cipheredLength = bb.remaining();
       
   185 
       
   186         if (!box.isNullCipher()) {
       
   187             try {
       
   188                 // apply explicit nonce for AEAD/CBC cipher suites if needed
       
   189                 int nonceSize =
       
   190                     box.applyExplicitNonce(authenticator, contentType(), bb);
       
   191 
       
   192                 // decrypt the content
       
   193                 if (box.isAEADMode()) {
       
   194                     // DON'T encrypt the nonce_explicit for AEAD mode
       
   195                     bb.position(bb.position() + nonceSize);
       
   196                 }   // The explicit IV for CBC mode can be decrypted.
       
   197 
       
   198                 // Note that the CipherBox.decrypt() does not change
       
   199                 // the capacity of the buffer.
       
   200                 box.decrypt(bb, tagLen);
       
   201                 bb.position(nonceSize); // We don't actually remove the nonce.
       
   202             } catch (BadPaddingException bpe) {
       
   203                 // RFC 2246 states that decryption_failed should be used
       
   204                 // for this purpose. However, that allows certain attacks,
       
   205                 // so we just send bad record MAC. We also need to make
       
   206                 // sure to always check the MAC to avoid a timing attack
       
   207                 // for the same issue. See paper by Vaudenay et al and the
       
   208                 // update in RFC 4346/5246.
       
   209                 //
       
   210                 // Failover to message authentication code checking.
       
   211                 reservedBPE = bpe;
       
   212             }
       
   213         }
       
   214 
       
   215         // Requires message authentication code for null, stream and block
       
   216         // cipher suites.
       
   217         if ((authenticator instanceof MAC) && (tagLen != 0)) {
       
   218             MAC signer = (MAC)authenticator;
       
   219             int macOffset = bb.limit() - tagLen;
       
   220 
       
   221             // Note that although it is not necessary, we run the same MAC
       
   222             // computation and comparison on the payload for both stream
       
   223             // cipher and CBC block cipher.
       
   224             if (bb.remaining() < tagLen) {
       
   225                 // negative data length, something is wrong
       
   226                 if (reservedBPE == null) {
       
   227                     reservedBPE = new BadPaddingException("bad record");
       
   228                 }
       
   229 
       
   230                 // set offset of the dummy MAC
       
   231                 macOffset = cipheredLength - tagLen;
       
   232                 bb.limit(cipheredLength);
       
   233             }
       
   234 
       
   235             // Run MAC computation and comparison on the payload.
       
   236             if (checkMacTags(contentType(), bb, signer, false)) {
       
   237                 if (reservedBPE == null) {
       
   238                     reservedBPE = new BadPaddingException("bad record MAC");
       
   239                 }
       
   240             }
       
   241 
       
   242             // Run MAC computation and comparison on the remainder.
       
   243             //
       
   244             // It is only necessary for CBC block cipher.  It is used to get a
       
   245             // constant time of MAC computation and comparison on each record.
       
   246             if (box.isCBCMode()) {
       
   247                 int remainingLen = calculateRemainingLen(
       
   248                                         signer, cipheredLength, macOffset);
       
   249 
       
   250                 // NOTE: here we use the InputRecord.buf because I did not find
       
   251                 // an effective way to work on ByteBuffer when its capacity is
       
   252                 // less than remainingLen.
       
   253 
       
   254                 // NOTE: remainingLen may be bigger (less than 1 block of the
       
   255                 // hash algorithm of the MAC) than the cipheredLength. However,
       
   256                 // We won't need to worry about it because we always use a
       
   257                 // maximum buffer for every record.  We need a change here if
       
   258                 // we use small buffer size in the future.
       
   259                 if (remainingLen > buf.length) {
       
   260                     // unlikely to happen, just a placehold
       
   261                     throw new RuntimeException(
       
   262                         "Internal buffer capacity error");
       
   263                 }
       
   264 
       
   265                 // Won't need to worry about the result on the remainder. And
       
   266                 // then we won't need to worry about what's actual data to
       
   267                 // check MAC tag on.  We start the check from the header of the
       
   268                 // buffer so that we don't need to construct a new byte buffer.
       
   269                 checkMacTags(contentType(), buf, 0, remainingLen, signer, true);
       
   270             }
       
   271 
       
   272             bb.limit(macOffset);
       
   273         }
       
   274 
       
   275         // Is it a failover?
       
   276         if (reservedBPE != null) {
       
   277             throw reservedBPE;
       
   278         }
       
   279 
       
   280         return bb.slice();
       
   281     }
       
   282 
       
   283     /*
       
   284      * Run MAC computation and comparison
       
   285      *
       
   286      * Please DON'T change the content of the ByteBuffer parameter!
       
   287      */
       
   288     private static boolean checkMacTags(byte contentType, ByteBuffer bb,
       
   289             MAC signer, boolean isSimulated) {
       
   290 
       
   291         int position = bb.position();
       
   292         int tagLen = signer.MAClen();
       
   293         int lim = bb.limit();
       
   294         int macData = lim - tagLen;
       
   295 
       
   296         bb.limit(macData);
       
   297         byte[] hash = signer.compute(contentType, bb, isSimulated);
       
   298         if (hash == null || tagLen != hash.length) {
       
   299             // Something is wrong with MAC implementation.
       
   300             throw new RuntimeException("Internal MAC error");
       
   301         }
       
   302 
       
   303         bb.position(macData);
       
   304         bb.limit(lim);
       
   305         try {
       
   306             int[] results = compareMacTags(bb, hash);
       
   307             return (results[0] != 0);
       
   308         } finally {
       
   309             // reset to the data
       
   310             bb.position(position);
       
   311             bb.limit(macData);
       
   312         }
       
   313     }
       
   314 
       
   315     /*
       
   316      * A constant-time comparison of the MAC tags.
       
   317      *
       
   318      * Please DON'T change the content of the ByteBuffer parameter!
       
   319      */
       
   320     private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
       
   321 
       
   322         // An array of hits is used to prevent Hotspot optimization for
       
   323         // the purpose of a constant-time check.
       
   324         int[] results = {0, 0};     // {missed #, matched #}
       
   325 
       
   326         // The caller ensures there are enough bytes available in the buffer.
       
   327         // So we won't need to check the remaining of the buffer.
       
   328         for (int i = 0; i < tag.length; i++) {
       
   329             if (bb.get() != tag[i]) {
       
   330                 results[0]++;       // mismatched bytes
       
   331             } else {
       
   332                 results[1]++;       // matched bytes
       
   333             }
       
   334         }
       
   335 
       
   336         return results;
       
   337     }
       
   338 
       
   339     /*
       
   340      * Override the actual write below.  We do things this way to be
       
   341      * consistent with InputRecord.  InputRecord may try to write out
       
   342      * data to the peer, and *then* throw an Exception.  This forces
       
   343      * data to be generated/output before the exception is ever
       
   344      * generated.
       
   345      */
       
   346     @Override
       
   347     void writeBuffer(OutputStream s, byte [] buf, int off, int len)
       
   348             throws IOException {
       
   349         /*
       
   350          * Copy data out of buffer, it's ready to go.
       
   351          */
       
   352         ByteBuffer netBB = ByteBuffer.allocate(len).put(buf, 0, len).flip();
       
   353         engine.writer.putOutboundDataSync(netBB);
       
   354     }
       
   355 
       
   356     /*
       
   357      * Delineate or read a complete packet from src.
       
   358      *
       
   359      * If internal data (hs, alert, ccs), the data is read and
       
   360      * stored internally.
       
   361      *
       
   362      * If external data (app), return a new ByteBuffer which points
       
   363      * to the data to process.
       
   364      */
       
   365     ByteBuffer read(ByteBuffer srcBB) throws IOException {
       
   366         /*
       
   367          * Could have a src == null/dst == null check here,
       
   368          * but that was already checked by SSLEngine.unwrap before
       
   369          * ever attempting to read.
       
   370          */
       
   371 
       
   372         /*
       
   373          * If we have anything besides application data,
       
   374          * or if we haven't even done the initial v2 verification,
       
   375          * we send this down to be processed by the underlying
       
   376          * internal cache.
       
   377          */
       
   378         if (!formatVerified ||
       
   379                 (srcBB.get(srcBB.position()) != ct_application_data)) {
       
   380             internalData = true;
       
   381             read(new ByteBufferInputStream(srcBB), (OutputStream) null);
       
   382             return tmpBB;
       
   383         }
       
   384 
       
   385         internalData = false;
       
   386 
       
   387         int srcPos = srcBB.position();
       
   388         int srcLim = srcBB.limit();
       
   389 
       
   390         ProtocolVersion recordVersion = ProtocolVersion.valueOf(
       
   391                 srcBB.get(srcPos + 1), srcBB.get(srcPos + 2));
       
   392 
       
   393         // check the record version
       
   394         checkRecordVersion(recordVersion, false);
       
   395 
       
   396         /*
       
   397          * It's really application data.  How much to consume?
       
   398          * Jump over the header.
       
   399          */
       
   400         int len = bytesInCompletePacket(srcBB);
       
   401         assert(len > 0);
       
   402 
       
   403         if (debug != null && Debug.isOn("packet")) {
       
   404             try {
       
   405                 HexDumpEncoder hd = new HexDumpEncoder();
       
   406                 ByteBuffer bb = srcBB.duplicate();  // Use copy of BB
       
   407                 bb.limit(srcPos + len);
       
   408 
       
   409                 System.out.println("[Raw read (bb)]: length = " + len);
       
   410                 hd.encodeBuffer(bb, System.out);
       
   411             } catch (IOException e) { }
       
   412         }
       
   413 
       
   414         // Demarcate past header to end of packet.
       
   415         srcBB.position(srcPos + headerSize);
       
   416         srcBB.limit(srcPos + len);
       
   417 
       
   418         // Protect remainder of buffer, create slice to actually
       
   419         // operate on.
       
   420         ByteBuffer bb = srcBB.slice();
       
   421 
       
   422         srcBB.position(srcBB.limit());
       
   423         srcBB.limit(srcLim);
       
   424 
       
   425         return bb;
       
   426     }
       
   427 }