package sun.security.ssl;
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;
abstract class OutputRecord
extends ByteArrayOutputStream implements Record, Closeable {
SSLWriteCipher writeCipher;
TransportContext tc;
final HandshakeHash handshakeHash;
boolean firstMessage;
ProtocolVersion protocolVersion;
ProtocolVersion helloVersion;
boolean isFirstAppOutputRecord = true;
int packetSize;
int fragmentSize;
volatile boolean isClosed;
private static final int[] V3toV2CipherMap1 =
{-1, -1, -1, 0x02, 0x01, -1, 0x04, 0x05, -1, 0x06, 0x07};
private static final int[] V3toV2CipherMap3 =
{-1, -1, -1, 0x80, 0x80, -1, 0x80, 0x80, -1, 0x40, 0xC0};
private static final byte[] HANDSHAKE_MESSAGE_KEY_UPDATE =
{SSLHandshake.KEY_UPDATE.id, 0x00, 0x00, 0x01, 0x00};
OutputRecord(HandshakeHash handshakeHash, SSLWriteCipher writeCipher) {
this.writeCipher = writeCipher;
this.firstMessage = true;
this.fragmentSize = Record.maxDataSize;
this.handshakeHash = handshakeHash;
}
synchronized void setVersion(ProtocolVersion protocolVersion) {
this.protocolVersion = protocolVersion;
}
synchronized void setHelloVersion(ProtocolVersion helloVersion) {
this.helloVersion = helloVersion;
}
boolean isEmpty() {
return false;
}
synchronized boolean seqNumIsHuge() {
return (writeCipher.authenticator != null) &&
writeCipher.authenticator.seqNumIsHuge();
}
abstract void encodeAlert(byte level, byte description) throws IOException;
abstract void encodeHandshake(byte[] buffer,
int offset, int length) throws IOException;
abstract void encodeChangeCipherSpec() throws IOException;
Ciphertext encode(
ByteBuffer[] srcs, int srcsOffset, int srcsLength,
ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
throw new UnsupportedOperationException();
}
void encodeV2NoCipher() throws IOException {
throw new UnsupportedOperationException();
}
void deliver(
byte[] source, int offset, int length) throws IOException {
throw new UnsupportedOperationException();
}
void setDeliverStream(OutputStream outputStream) {
throw new UnsupportedOperationException();
}
synchronized void changeWriteCiphers(SSLWriteCipher writeCipher,
boolean useChangeCipherSpec) throws IOException {
if (isClosed()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"change_cipher_spec message");
}
return;
}
if (useChangeCipherSpec) {
encodeChangeCipherSpec();
}
writeCipher.dispose();
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
}
synchronized void changeWriteCiphers(SSLWriteCipher writeCipher,
byte keyUpdateRequest) throws IOException {
if (isClosed()) {
if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
SSLLogger.warning("outbound has closed, ignore outbound " +
"key_update handshake message");
}
return;
}
byte[] hm = HANDSHAKE_MESSAGE_KEY_UPDATE.clone();
hm[hm.length - 1] = keyUpdateRequest;
encodeHandshake(hm, 0, hm.length);
flush();
writeCipher.dispose();
this.writeCipher = writeCipher;
this.isFirstAppOutputRecord = true;
}
synchronized void changePacketSize(int packetSize) {
this.packetSize = packetSize;
}
synchronized void changeFragmentSize(int fragmentSize) {
this.fragmentSize = fragmentSize;
}
synchronized int getMaxPacketSize() {
return packetSize;
}
void initHandshaker() {
}
void finishHandshake() {
}
void launchRetransmission() {
}
@Override
public synchronized void close() throws IOException {
if (isClosed) {
return;
}
isClosed = true;
writeCipher.dispose();
}
boolean isClosed() {
return isClosed;
}
static long encrypt(
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion) {
boolean isDTLS = protocolVersion.isDTLS;
if (isDTLS) {
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);
}
}
}
private static long d13Encrypt(
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion) {
throw new UnsupportedOperationException("Not supported yet.");
}
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);
int fragLen = destination.limit() - headerOffset - headerSize;
destination.put(headerOffset, contentType);
destination.put(headerOffset + 1, protocolVersion.major);
destination.put(headerOffset + 2, protocolVersion.minor);
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]);
destination.put(headerOffset + 11, (byte)(fragLen >> 8));
destination.put(headerOffset + 12, (byte)fragLen);
destination.position(destination.limit());
return Authenticator.toLong(sequenceNumber);
}
private static long t13Encrypt(
SSLWriteCipher encCipher, byte contentType, ByteBuffer destination,
int headerOffset, int dstLim, int headerSize,
ProtocolVersion protocolVersion) {
if (!encCipher.isNullCipher()) {
int endOfPt = destination.limit();
destination.limit(endOfPt + 1);
destination.put(endOfPt, contentType);
}
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);
int fragLen = destination.limit() - headerOffset - headerSize;
destination.put(headerOffset, contentType);
destination.put(headerOffset + 1, pv.major);
destination.put(headerOffset + 2, pv.minor);
destination.put(headerOffset + 3, (byte)(fragLen >> 8));
destination.put(headerOffset + 4, (byte)fragLen);
destination.position(destination.limit());
return Authenticator.toLong(sequenceNumber);
}
private 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);
int fragLen = destination.limit() - headerOffset - headerSize;
destination.put(headerOffset, contentType);
destination.put(headerOffset + 1, protocolVersion.major);
destination.put(headerOffset + 2, protocolVersion.minor);
destination.put(headerOffset + 3, (byte)(fragLen >> 8));
destination.put(headerOffset + 4, (byte)fragLen);
destination.position(destination.limit());
return Authenticator.toLong(sequenceNumber);
}
long encrypt(
SSLWriteCipher encCipher, byte contentType, int headerSize) {
if (protocolVersion.useTLS13PlusSpec()) {
return t13Encrypt(encCipher, contentType, headerSize);
} else {
return t10Encrypt(encCipher, contentType, headerSize);
}
}
private static final class T13PaddingHolder {
private static final byte[] zeros = new byte[16];
}
private long t13Encrypt(
SSLWriteCipher encCipher, byte contentType, int headerSize) {
if (!encCipher.isNullCipher()) {
write(contentType);
write(T13PaddingHolder.zeros, 0, T13PaddingHolder.zeros.length);
}
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
int position = headerSize;
int contentLen = count - position;
int requiredPacketSize =
encCipher.calculatePacketSize(contentLen, headerSize);
if (requiredPacketSize > buf.length) {
byte[] newBuf = new byte[requiredPacketSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
ProtocolVersion pv = protocolVersion;
if (!encCipher.isNullCipher()) {
pv = ProtocolVersion.TLS12;
contentType = ContentType.APPLICATION_DATA.id;
} else {
pv = ProtocolVersion.TLS12;
}
ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
count = headerSize + encCipher.encrypt(contentType, destination);
int fragLen = count - headerSize;
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);
}
private long t10Encrypt(
SSLWriteCipher encCipher, byte contentType, int headerSize) {
byte[] sequenceNumber = encCipher.authenticator.sequenceNumber();
int position = headerSize + writeCipher.getExplicitNonceSize();
int contentLen = count - position;
int requiredPacketSize =
encCipher.calculatePacketSize(contentLen, headerSize);
if (requiredPacketSize > buf.length) {
byte[] newBuf = new byte[requiredPacketSize];
System.arraycopy(buf, 0, newBuf, 0, count);
buf = newBuf;
}
ByteBuffer destination = ByteBuffer.wrap(buf, position, contentLen);
count = headerSize + encCipher.encrypt(contentType, destination);
int fragLen = count - headerSize;
buf[0] = contentType;
buf[1] = protocolVersion.major;
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;
int v3SessIdLen = fragment[v3SessIdLenOffset];
int v3CSLenOffset = v3SessIdLenOffset + 1 + v3SessIdLen;
int v3CSLen = ((fragment[v3CSLenOffset] & 0xff) << 8) +
(fragment[v3CSLenOffset + 1] & 0xff);
int cipherSpecs = v3CSLen / 2;
int v2MaxMsgLen = 11 + (cipherSpecs * 6) + 3 + 32;
byte[] dstBytes = new byte[v2MaxMsgLen];
ByteBuffer dstBuf = ByteBuffer.wrap(dstBytes);
int v3CSOffset = v3CSLenOffset + 2;
int v2CSLen = 0;
dstBuf.position(11);
boolean containsRenegoInfoSCSV = false;
for (int i = 0; i < cipherSpecs; i++) {
byte byte1, byte2;
byte1 = fragment[v3CSOffset++];
byte2 = fragment[v3CSOffset++];
v2CSLen += V3toV2CipherSuite(dstBuf, byte1, byte2);
if (!containsRenegoInfoSCSV &&
byte1 == (byte)0x00 && byte2 == (byte)0xFF) {
containsRenegoInfoSCSV = true;
}
}
if (!containsRenegoInfoSCSV) {
v2CSLen += V3toV2CipherSuite(dstBuf, (byte)0x00, (byte)0xFF);
}
dstBuf.put(fragment, (offset + 2), 32);
int msgLen = dstBuf.position() - 2;
dstBuf.position(0);
dstBuf.put((byte)(0x80 | ((msgLen >>> 8) & 0xFF)));
dstBuf.put((byte)(msgLen & 0xFF));
dstBuf.put(SSLHandshake.CLIENT_HELLO.id);
dstBuf.put(fragment[offset]);
dstBuf.put(fragment[offset + 1]);
dstBuf.put((byte)(v2CSLen >>> 8));
dstBuf.put((byte)(v2CSLen & 0xFF));
dstBuf.put((byte)0x00);
dstBuf.put((byte)0x00);
dstBuf.put((byte)0x00);
dstBuf.put((byte)32);
dstBuf.position(0);
dstBuf.limit(msgLen + 2);
return dstBuf;
}
private static int V3toV2CipherSuite(ByteBuffer dstBuf,
byte byte1, byte byte2) {
dstBuf.put((byte)0);
dstBuf.put(byte1);
dstBuf.put(byte2);
if (((byte2 & 0xff) > 0xA) || (V3toV2CipherMap1[byte2] == -1)) {
return 3;
}
dstBuf.put((byte)V3toV2CipherMap1[byte2]);
dstBuf.put((byte)0);
dstBuf.put((byte)V3toV2CipherMap3[byte2]);
return 6;
}
}