--- a/src/java.base/share/classes/sun/security/ssl/OutputRecord.java Fri May 11 14:55:56 2018 -0700
+++ b/src/java.base/share/classes/sun/security/ssl/OutputRecord.java Fri May 11 15:53:12 2018 -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 {
+abstract class OutputRecord
+ extends ByteArrayOutputStream implements Record, Closeable {
+ SSLWriteCipher writeCipher;
+ // Needed for KeyUpdate
+ TransportContext tc;
- /* Class and subclass dynamic debugging support */
- static final Debug debug = Debug.getInstance("ssl");
-
- Authenticator writeAuthenticator;
- CipherBox writeCipher;
-
- 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,72 +213,47 @@
// 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();
- }
-
- // 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;
- }
+ 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);
}
}
+ }
- 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
+ static long d13Encrypt(
+ SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
+ int headerOffset, int dstLim, int headerSize,
+ ProtocolVersion protocolVersion) {
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
- // 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());
- }
+ 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;
@@ -289,30 +261,85 @@
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]);
+
+ // 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()) {
+ // 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);
+ }
- // fragment length
- destination.put(headerOffset + 11, (byte)(fragLen >> 8));
- destination.put(headerOffset + 12, (byte)fragLen);
+ // 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);
- // 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 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());
@@ -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())) {
+ pv = ProtocolVersion.TLS12;
+ contentType = ContentType.APPLICATION_DATA.id;
+ } else {
+ pv = ProtocolVersion.TLS12;
+ }
- byte[] nonce = encCipher.createExplicitNonce(
- authenticator, contentType, (count - position));
- int noncePos = position - nonce.length;
- System.arraycopy(nonce, 0, buf, noncePos, nonce.length);
- }
+ 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;
- 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
+ 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);
+ }
- // 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;
- }
+ long t10Encrypt(
+ SSLWriteCipher encCipher, byte contentType, int headerSize) {
+ byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
+ int position = headerSize + writeCipher.getExplicitNonceSize();
+ int contentLen = count - position;
- // Encrypt may pad, so again the count may be changed.
- count = position +
- encCipher.encrypt(buf, position, (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