--- old/src/java.base/share/classes/sun/security/ssl/OutputRecord.java 2018-05-11 15:05:23.787091800 -0700 +++ new/src/java.base/share/classes/sun/security/ssl/OutputRecord.java 2018-05-11 15:05:23.274235800 -0700 @@ -1,5 +1,5 @@ /* - * 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 @@ -25,30 +25,27 @@ 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 @@ -75,16 +72,18 @@ * 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. } @@ -100,15 +99,6 @@ } /* - * 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. */ @@ -117,8 +107,8 @@ } boolean seqNumIsHuge() { - return (writeAuthenticator != null) && - writeAuthenticator.seqNumIsHuge(); + return (writeCipher.authenticator != null) && + writeCipher.authenticator.seqNumIsHuge(); } // SSLEngine and SSLSocket @@ -132,8 +122,10 @@ 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(); } @@ -143,7 +135,8 @@ } // 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(); } @@ -152,15 +145,11 @@ throw new UnsupportedOperationException(); } - // apply to SSLEngine only - Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException { - throw new UnsupportedOperationException(); - } - - void changeWriteCiphers(Authenticator writeAuthenticator, - CipherBox writeCipher) throws IOException { - - encodeChangeCipherSpec(); + void changeWriteCiphers(SSLWriteCipher writeCipher, + boolean useChangeCipherSpec) throws IOException { + if (useChangeCipherSpec) { + encodeChangeCipherSpec(); + } /* * Dispose of any intermediate state in the underlying cipher. @@ -172,7 +161,6 @@ */ writeCipher.dispose(); - this.writeAuthenticator = writeAuthenticator; this.writeCipher = writeCipher; this.isFirstAppOutputRecord = true; } @@ -195,6 +183,11 @@ } // apply to DTLS SSLEngine + void finishHandshake() { + // blank + } + + // apply to DTLS SSLEngine void launchRetransmission() { // blank } @@ -207,6 +200,10 @@ } } + synchronized boolean isClosed() { + return isClosed; + } + // // shared helpers // @@ -216,103 +213,133 @@ // 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); + } + } else { + if (protocolVersion.useTLS13PlusSpec()) { + return t13Encrypt(encCipher, + contentType, destination, headerOffset, + dstLim, headerSize, protocolVersion); + } else { + return t10Encrypt(encCipher, + contentType, destination, headerOffset, + dstLim, headerSize, protocolVersion); + } } + } - // The sequence number may be shared for different purpose. - boolean sharedSequenceNumber = false; + static long d13Encrypt( + SSLWriteCipher encCipher, byte contentType, ByteBuffer destination, + int headerOffset, int dstLim, int headerSize, + ProtocolVersion protocolVersion) { + throw new UnsupportedOperationException("Not supported yet."); + } - // "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; - } - } - } + 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); + + // 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]); + destination.put(headerOffset + 7, sequenceNumber[4]); + destination.put(headerOffset + 8, sequenceNumber[5]); + destination.put(headerOffset + 9, sequenceNumber[6]); + destination.put(headerOffset + 10, sequenceNumber[7]); + + // fragment length + destination.put(headerOffset + 11, (byte)(fragLen >> 8)); + destination.put(headerOffset + 12, (byte)fragLen); + + // 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()) { - 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 { + // 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); - 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]); - destination.put(headerOffset + 7, sequenceNumber[4]); - destination.put(headerOffset + 8, sequenceNumber[5]); - destination.put(headerOffset + 9, sequenceNumber[6]); - destination.put(headerOffset + 10, sequenceNumber[7]); - - // 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(); - } - } + + // 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()); @@ -324,55 +351,78 @@ // // Uses the internal expandable buf variable and the current // protocolVersion variable. - void encrypt(Authenticator authenticator, - CipherBox encCipher, byte contentType, int headerSize) { + long encrypt( + SSLWriteCipher encCipher, byte contentType, int headerSize) { + if (protocolVersion.useTLS13PlusSpec()) { + return t13Encrypt(encCipher, contentType, headerSize); + } else { + return t10Encrypt(encCipher, contentType, headerSize); + } + } - int position = headerSize + writeCipher.getExplicitNonceSize(); + private static final class T13PaddingHolder { + private static final byte[] zeros = new byte[16]; + } - // "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); + long t13Encrypt( + SSLWriteCipher encCipher, byte contentType, int headerSize) { + if (!encCipher.isNullCipher()) { + // inner plaintext + write(contentType); + write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length); + } - write(hash, 0, hash.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; } + // use the right TLSCiphertext.opaque_type and legacy_record_version + ProtocolVersion pv = protocolVersion; 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); - } + pv = ProtocolVersion.TLS12; + contentType = ContentType.APPLICATION_DATA.id; + } else { + pv = ProtocolVersion.TLS12; + } - 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]; - 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; - // Encrypt may pad, so again the count may be changed. - count = position + - encCipher.encrypt(buf, position, (count - position)); + 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; @@ -381,11 +431,12 @@ 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 @@ -449,7 +500,7 @@ 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