--- old/src/java.base/share/classes/sun/security/ssl/InputRecord.java 2018-05-11 15:05:18.189867900 -0700 +++ new/src/java.base/share/classes/sun/security/ssl/InputRecord.java 2018-05-11 15:05:17.658901600 -0700 @@ -1,5 +1,5 @@ /* - * Copyright (c) 1996, 2014, 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 @@ -27,14 +27,8 @@ import java.io.*; import java.nio.*; -import java.util.*; - import javax.crypto.BadPaddingException; - -import javax.net.ssl.*; - -import sun.security.util.HexDumpEncoder; - +import sun.security.ssl.SSLCipher.SSLReadCipher; /** * {@code InputRecord} takes care of the management of SSL/TLS/DTLS input @@ -42,15 +36,10 @@ * * @author David Brownell */ -class InputRecord implements Record, Closeable { - - /* Class and subclass dynamic debugging support */ - static final Debug debug = Debug.getInstance("ssl"); - - Authenticator readAuthenticator; - CipherBox readCipher; +abstract class InputRecord implements Record, Closeable { + SSLReadCipher readCipher; - HandshakeHash handshakeHash; + final HandshakeHash handshakeHash; boolean isClosed; // The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello @@ -61,10 +50,11 @@ // fragment size int fragmentSize; - InputRecord() { - this.readCipher = CipherBox.NULL; - this.readAuthenticator = null; // Please override this assignment. - this.helloVersion = ProtocolVersion.DEFAULT_HELLO; + InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) { + this.readCipher = readCipher; + this.helloVersion = ProtocolVersion.TLS10; + this.handshakeHash = handshakeHash; + this.isClosed = false; this.fragmentSize = Record.maxDataSize; } @@ -76,37 +66,9 @@ return helloVersion; } - /* - * Set instance for the computation of handshake hashes. - * - * 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 ... especially in the - * case of hashing the initial V2 message! - */ - void setHandshakeHash(HandshakeHash handshakeHash) { - if (handshakeHash != null) { - byte[] reserved = null; - if (this.handshakeHash != null) { - reserved = this.handshakeHash.getAllHandshakeMessages(); - } - if ((reserved != null) && (reserved.length != 0)) { - handshakeHash.update(reserved, 0, reserved.length); - - if (debug != null && Debug.isOn("data")) { - Debug.printHex( - "[reserved] handshake hash: len = " + reserved.length, - reserved); - } - } - } - - this.handshakeHash = handshakeHash; - } - boolean seqNumIsHuge() { - return (readAuthenticator != null) && - readAuthenticator.seqNumIsHuge(); + return (readCipher.authenticator != null) && + readCipher.authenticator.seqNumIsHuge(); } boolean isEmpty() { @@ -118,6 +80,11 @@ // blank } + // apply to DTLS SSLEngine + void finishHandshake() { + // blank + } + /** * Prevent any more data from being read into this record, * and flag the record as holding no data. @@ -130,9 +97,12 @@ } } + synchronized boolean isClosed() { + return isClosed; + } + // apply to SSLSocket and SSLEngine - void changeReadCiphers( - Authenticator readAuthenticator, CipherBox readCipher) { + void changeReadCiphers(SSLReadCipher readCipher) { /* * Dispose of any intermediate state in the underlying cipher. @@ -144,7 +114,6 @@ */ readCipher.dispose(); - this.readAuthenticator = readAuthenticator; this.readCipher = readCipher; } @@ -160,22 +129,20 @@ * @return -1 if there are not enough bytes to tell (small header), */ // apply to SSLEngine only - int bytesInCompletePacket(ByteBuffer buf) throws SSLException { - throw new UnsupportedOperationException(); + int bytesInCompletePacket( + ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException { + + throw new UnsupportedOperationException("Not supported yet."); } // apply to SSLSocket only - int bytesInCompletePacket(InputStream is) throws IOException { + int bytesInCompletePacket() throws IOException { throw new UnsupportedOperationException(); } - /** - * Return true if the specified record protocol version is out of the - * range of the possible supported versions. - */ - void checkRecordVersion(ProtocolVersion version, - boolean allowSSL20Hello) throws SSLException { - // blank + // apply to SSLSocket only + void setReceiverStream(InputStream inputStream) { + throw new UnsupportedOperationException(); } // apply to DTLS SSLEngine only @@ -186,17 +153,8 @@ // read, decrypt and decompress the network record. // - // apply to SSLEngine only - Plaintext decode(ByteBuffer netData) - throws IOException, BadPaddingException { - throw new UnsupportedOperationException(); - } - - // apply to SSLSocket only - Plaintext decode(InputStream is, ByteBuffer destination) - throws IOException, BadPaddingException { - throw new UnsupportedOperationException(); - } + abstract Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset, + int srcsLength) throws IOException, BadPaddingException; // apply to SSLSocket only void setDeliverStream(OutputStream outputStream) { @@ -216,9 +174,7 @@ // Not apply to DTLS static ByteBuffer convertToClientHello(ByteBuffer packet) { - int srcPos = packet.position(); - int srcLim = packet.limit(); byte firstByte = packet.get(); byte secondByte = packet.get(); @@ -244,7 +200,7 @@ // 1: length byte of ClientHello.session_id // 2: length bytes of ClientHello.cipher_suites // 2: empty ClientHello.compression_methods - int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3 ); + int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3); byte[] converted = new byte[requiredSize]; /* @@ -252,7 +208,7 @@ * that's now buffered up. (Lengths are fixed up later). */ // Note: need not to set the header actually. - converted[0] = ct_handshake; + converted[0] = ContentType.HANDSHAKE.id; converted[1] = majorVersion; converted[2] = minorVersion; // header [3..4] for handshake message length @@ -372,237 +328,61 @@ return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size } - static ByteBuffer decrypt(Authenticator authenticator, CipherBox box, - byte contentType, ByteBuffer bb) throws BadPaddingException { - - return decrypt(authenticator, box, contentType, bb, null); - } - - static ByteBuffer decrypt(Authenticator authenticator, - CipherBox box, byte contentType, ByteBuffer bb, - byte[] sequence) throws BadPaddingException { - - BadPaddingException reservedBPE = null; - int tagLen = - (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0; - int cipheredLength = bb.remaining(); - int srcPos = bb.position(); - if (!box.isNullCipher()) { - try { - // apply explicit nonce for AEAD/CBC cipher suites if needed - int nonceSize = box.applyExplicitNonce( - authenticator, contentType, bb, sequence); - - // decrypt the content - if (box.isAEADMode()) { - // DON'T decrypt the nonce_explicit for AEAD mode - bb.position(srcPos + nonceSize); - } // The explicit IV for CBC mode can be decrypted. - - // Note that the CipherBox.decrypt() does not change - // the capacity of the buffer. - box.decrypt(bb, tagLen); - // We don't actually remove the nonce. - bb.position(srcPos + nonceSize); - } catch (BadPaddingException bpe) { - // RFC 2246 states that decryption_failed should be used - // for this purpose. However, that allows certain attacks, - // so we just send bad record MAC. We also need to make - // sure to always check the MAC to avoid a timing attack - // for the same issue. See paper by Vaudenay et al and the - // update in RFC 4346/5246. - // - // Failover to message authentication code checking. - reservedBPE = bpe; - } - } - - // Requires message authentication code for null, stream and block - // cipher suites. - if ((authenticator instanceof MAC) && (tagLen != 0)) { - MAC signer = (MAC)authenticator; - int contentLen = bb.remaining() - tagLen; - - // Note that although it is not necessary, we run the same MAC - // computation and comparison on the payload for both stream - // cipher and CBC block cipher. - if (contentLen < 0) { - // negative data length, something is wrong - if (reservedBPE == null) { - reservedBPE = new BadPaddingException("bad record"); + // Extract an SSL/(D)TLS record from the specified source buffers. + static ByteBuffer extract( + ByteBuffer[] buffers, int offset, int length, int headerSize) { + + boolean hasFullHeader = false; + int contentLen = -1; + for (int i = offset, j = 0; + i < (offset + length) && j < headerSize; i++) { + int remains = buffers[i].remaining(); + int pos = buffers[i].position(); + for (int k = 0; k < remains && j < headerSize; j++, k++) { + byte b = buffers[i].get(pos + k); + if (j == (headerSize - 2)) { + contentLen = ((b & 0xFF) << 8); + } else if (j == (headerSize -1)) { + contentLen |= (b & 0xFF); + hasFullHeader = true; + break; } - - // set offset of the dummy MAC - contentLen = cipheredLength - tagLen; - bb.limit(srcPos + cipheredLength); } - - // Run MAC computation and comparison on the payload. - // - // MAC data would be stripped off during the check. - if (checkMacTags(contentType, bb, signer, sequence, false)) { - if (reservedBPE == null) { - reservedBPE = new BadPaddingException("bad record MAC"); - } - } - - // Run MAC computation and comparison on the remainder. - // - // It is only necessary for CBC block cipher. It is used to get a - // constant time of MAC computation and comparison on each record. - if (box.isCBCMode()) { - int remainingLen = calculateRemainingLen( - signer, cipheredLength, contentLen); - - // NOTE: remainingLen may be bigger (less than 1 block of the - // hash algorithm of the MAC) than the cipheredLength. - // - // Is it possible to use a static buffer, rather than allocate - // it dynamically? - remainingLen += signer.MAClen(); - ByteBuffer temporary = ByteBuffer.allocate(remainingLen); - - // Won't need to worry about the result on the remainder. And - // then we won't need to worry about what's actual data to - // check MAC tag on. We start the check from the header of the - // buffer so that we don't need to construct a new byte buffer. - checkMacTags(contentType, temporary, signer, sequence, true); - } - } - - // Is it a failover? - if (reservedBPE != null) { - throw reservedBPE; } - return bb.slice(); - } - - /* - * Run MAC computation and comparison - * - */ - private static boolean checkMacTags(byte contentType, ByteBuffer bb, - MAC signer, byte[] sequence, boolean isSimulated) { - - int tagLen = signer.MAClen(); - int position = bb.position(); - int lim = bb.limit(); - int macOffset = lim - tagLen; - - bb.limit(macOffset); - byte[] hash = signer.compute(contentType, bb, sequence, isSimulated); - if (hash == null || tagLen != hash.length) { - // Something is wrong with MAC implementation. - throw new RuntimeException("Internal MAC error"); + if (!hasFullHeader) { + throw new BufferUnderflowException(); } - bb.position(macOffset); - bb.limit(lim); - try { - int[] results = compareMacTags(bb, hash); - return (results[0] != 0); - } finally { - // reset to the data - bb.position(position); - bb.limit(macOffset); - } - } - - /* - * A constant-time comparison of the MAC tags. - * - * Please DON'T change the content of the ByteBuffer parameter! - */ - private static int[] compareMacTags(ByteBuffer bb, byte[] tag) { - - // An array of hits is used to prevent Hotspot optimization for - // the purpose of a constant-time check. - int[] results = {0, 0}; // {missed #, matched #} - - // The caller ensures there are enough bytes available in the buffer. - // So we won't need to check the remaining of the buffer. - for (int i = 0; i < tag.length; i++) { - if (bb.get() != tag[i]) { - results[0]++; // mismatched bytes - } else { - results[1]++; // matched bytes + int packetLen = headerSize + contentLen; + int remains = 0; + for (int i = offset; i < offset + length; i++) { + remains += buffers[i].remaining(); + if (remains >= packetLen) { + break; } } - return results; - } - - /* - * Run MAC computation and comparison - * - * Please DON'T change the content of the byte buffer parameter! - */ - private static boolean checkMacTags(byte contentType, byte[] buffer, - int offset, int contentLen, MAC signer, boolean isSimulated) { - - int tagLen = signer.MAClen(); - byte[] hash = signer.compute( - contentType, buffer, offset, contentLen, isSimulated); - if (hash == null || tagLen != hash.length) { - // Something is wrong with MAC implementation. - throw new RuntimeException("Internal MAC error"); + if (remains < packetLen) { + throw new BufferUnderflowException(); } - int[] results = compareMacTags(buffer, offset + contentLen, hash); - return (results[0] != 0); - } - - /* - * A constant-time comparison of the MAC tags. - * - * Please DON'T change the content of the byte buffer parameter! - */ - private static int[] compareMacTags( - byte[] buffer, int offset, byte[] tag) { + byte[] packet = new byte[packetLen]; + int packetOffset = 0; + int packetSpaces = packetLen; + for (int i = offset; i < offset + length; i++) { + if (buffers[i].hasRemaining()) { + int len = Math.min(packetSpaces, buffers[i].remaining()); + buffers[i].get(packet, packetOffset, len); + packetOffset += len; + packetSpaces -= len; + } - // An array of hits is used to prevent Hotspot optimization for - // the purpose of a constant-time check. - int[] results = {0, 0}; // {missed #, matched #} - - // The caller ensures there are enough bytes available in the buffer. - // So we won't need to check the length of the buffer. - for (int i = 0; i < tag.length; i++) { - if (buffer[offset + i] != tag[i]) { - results[0]++; // mismatched bytes - } else { - results[1]++; // matched bytes + if (packetSpaces <= 0) { + break; } } - return results; - } - - /* - * Calculate the length of a dummy buffer to run MAC computation - * and comparison on the remainder. - * - * The caller MUST ensure that the fullLen is not less than usedLen. - */ - private static int calculateRemainingLen( - MAC signer, int fullLen, int usedLen) { - - int blockLen = signer.hashBlockLen(); - int minimalPaddingLen = signer.minimalPaddingLen(); - - // (blockLen - minimalPaddingLen) is the maximum message size of - // the last block of hash function operation. See FIPS 180-4, or - // MD5 specification. - fullLen += 13 - (blockLen - minimalPaddingLen); - usedLen += 13 - (blockLen - minimalPaddingLen); - - // Note: fullLen is always not less than usedLen, and blockLen - // is always bigger than minimalPaddingLen, so we don't worry - // about negative values. 0x01 is added to the result to ensure - // that the return value is positive. The extra one byte does - // not impact the overall MAC compression function evaluations. - return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) - - Math.ceil(usedLen/(1.0d * blockLen))) * blockLen; + return ByteBuffer.wrap(packet); } } -