--- a/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java Fri May 11 14:55:56 2018 -0700
+++ b/src/java.base/share/classes/sun/security/ssl/SSLEngineOutputRecord.java Fri May 11 15:53:12 2018 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2015, 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,14 +25,13 @@
package sun.security.ssl;
-import java.io.*;
-import java.nio.*;
-import java.util.*;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.util.LinkedList;
+import javax.net.ssl.SSLHandshakeException;
-import javax.net.ssl.SSLException;
-import javax.net.ssl.SSLHandshakeException;
-import sun.security.util.HexDumpEncoder;
-import static sun.security.ssl.Ciphertext.RecordType;
+import sun.security.ssl.SSLCipher.SSLWriteCipher;
+import sun.security.ssl.KeyUpdate.KeyUpdateMessage;
/**
* {@code OutputRecord} implementation for {@code SSLEngine}.
@@ -40,23 +39,22 @@
final class SSLEngineOutputRecord extends OutputRecord implements SSLRecord {
private HandshakeFragment fragmenter = null;
- private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
private boolean isTalkingToV2 = false; // SSLv2Hello
private ByteBuffer v2ClientHello = null; // SSLv2Hello
private boolean isCloseWaiting = false;
- SSLEngineOutputRecord() {
- this.writeAuthenticator = MAC.TLS_NULL;
+ SSLEngineOutputRecord(HandshakeHash handshakeHash) {
+ super(handshakeHash, SSLWriteCipher.nullTlsWriteCipher());
this.packetSize = SSLRecord.maxRecordSize;
- this.protocolVersion = ProtocolVersion.DEFAULT_TLS;
+ this.protocolVersion = ProtocolVersion.NONE;
}
@Override
public synchronized void close() throws IOException {
if (!isClosed) {
- if (alertMemos != null && !alertMemos.isEmpty()) {
+ if (fragmenter != null && fragmenter.hasAlert()) {
isCloseWaiting = true;
} else {
super.close();
@@ -66,19 +64,11 @@
@Override
void encodeAlert(byte level, byte description) throws IOException {
- RecordMemo memo = new RecordMemo();
+ if (fragmenter == null) {
+ fragmenter = new HandshakeFragment();
+ }
- memo.contentType = Record.ct_alert;
- memo.majorVersion = protocolVersion.major;
- memo.minorVersion = protocolVersion.minor;
- memo.encodeCipher = writeCipher;
- memo.encodeAuthenticator = writeAuthenticator;
-
- memo.fragment = new byte[2];
- memo.fragment[0] = level;
- memo.fragment[1] = description;
-
- alertMemos.add(memo);
+ fragmenter.queueUpAlert(level, description);
}
@Override
@@ -93,7 +83,7 @@
firstMessage = false;
if ((helloVersion == ProtocolVersion.SSL20Hello) &&
- (source[offset] == HandshakeMessage.ht_client_hello) &&
+ (source[offset] == SSLHandshake.CLIENT_HELLO.id) &&
// 5: recode header size
(source[offset + 4 + 2 + 32] == 0)) {
// V3 session ID is empty
@@ -106,7 +96,7 @@
source, (offset + 4), (length - 4));
v2ClientHello.position(2); // exclude the header
- handshakeHash.update(v2ClientHello);
+ handshakeHash.deliver(v2ClientHello);
v2ClientHello.position(0);
return;
@@ -114,8 +104,8 @@
}
byte handshakeType = source[offset];
- if (handshakeType != HandshakeMessage.ht_hello_request) {
- handshakeHash.update(source, offset, length);
+ if (handshakeHash.isHashable(handshakeType)) {
+ handshakeHash.deliver(source, offset, length);
}
fragmenter.queueUpFragment(source, offset, length);
@@ -135,22 +125,43 @@
}
@Override
- Ciphertext encode(ByteBuffer[] sources, int offset, int length,
+ Ciphertext encode(
+ ByteBuffer[] srcs, int srcsOffset, int srcsLength,
+ ByteBuffer[] dsts, int dstsOffset, int dstsLength) throws IOException {
+ return encode(srcs, srcsOffset, srcsLength, dsts[0]);
+ }
+
+ private Ciphertext encode(ByteBuffer[] sources, int offset, int length,
ByteBuffer destination) throws IOException {
- if (writeAuthenticator.seqNumOverflow()) {
- if (debug != null && Debug.isOn("ssl")) {
- System.out.println(Thread.currentThread().getName() +
- ", sequence number extremely close to overflow " +
+ if (writeCipher.authenticator.seqNumOverflow()) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+ SSLLogger.fine(
+ "sequence number extremely close to overflow " +
"(2^64-1 packets). Closing connection.");
}
throw new SSLHandshakeException("sequence number overflow");
}
- int macLen = 0;
- if (writeAuthenticator instanceof MAC) {
- macLen = ((MAC)writeAuthenticator).MAClen();
+ // Don't process the incoming record until all of the
+ // buffered records get handled.
+ Ciphertext ct = acquireCiphertext(destination);
+ if (ct != null) {
+ return ct;
+ }
+
+ if (sources == null || sources.length == 0) {
+ return null;
+ }
+
+ int srcsRemains = 0;
+ for (int i = offset; i < offset + length; i++) {
+ srcsRemains += sources[i].remaining();
+ }
+
+ if (srcsRemains == 0) {
+ return null;
}
int dstLim = destination.limit();
@@ -169,8 +180,8 @@
needMorePayload = false;
if (packetLeftSize > 0) {
- fragLen = writeCipher.calculateFragmentSize(
- packetLeftSize, macLen, headerSize);
+ fragLen =writeCipher.calculateFragmentSize(
+ packetLeftSize, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
@@ -208,26 +219,24 @@
destination.limit(destination.position());
destination.position(dstContent);
- if ((debug != null) && Debug.isOn("record")) {
- System.out.println(Thread.currentThread().getName() +
- ", WRITE: " + protocolVersion + " " +
- Record.contentName(Record.ct_application_data) +
+ if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+ SSLLogger.fine(
+ "WRITE: " + protocolVersion + " " +
+ ContentType.APPLICATION_DATA.name +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
- recordSN = encrypt(writeAuthenticator, writeCipher,
- Record.ct_application_data, destination,
+ recordSN = encrypt(writeCipher,
+ ContentType.APPLICATION_DATA.id, destination,
dstPos, dstLim, headerSize,
- protocolVersion, false);
+ protocolVersion);
- if ((debug != null) && Debug.isOn("packet")) {
+ if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
- Debug.printHex(
- "[Raw write]: length = " + temporary.remaining(),
- temporary);
+ SSLLogger.fine("Raw write", temporary);
}
packetLeftSize -= destination.position() - dstPos;
@@ -238,103 +247,60 @@
if (isFirstAppOutputRecord) {
isFirstAppOutputRecord = false;
}
+
+ if (writeCipher.atKeyLimit()) {
+ if (SSLLogger.isOn && SSLLogger.isOn("ssl")) {
+ SSLLogger.fine("KeyUpdate: triggered");
+ }
+
+ PostHandshakeContext p = new PostHandshakeContext(tc);
+ KeyUpdate.handshakeProducer.produce(p,
+ new KeyUpdateMessage(p, KeyUpdateMessage.REQUSTED));
+ }
}
- return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
+ return new Ciphertext(ContentType.APPLICATION_DATA.id,
+ SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
- @Override
- Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
+ private Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
if (isTalkingToV2) { // SSLv2Hello
// We don't support SSLv2. Send an SSLv2 error message
// so that the connection can be closed gracefully.
//
// Please don't change the limit of the destination buffer.
destination.put(SSLRecord.v2NoCipher);
- if (debug != null && Debug.isOn("packet")) {
- Debug.printHex(
- "[Raw write]: length = " + SSLRecord.v2NoCipher.length,
- SSLRecord.v2NoCipher);
+ if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
+ SSLLogger.fine("Raw write", SSLRecord.v2NoCipher);
}
isTalkingToV2 = false;
- return new Ciphertext(RecordType.RECORD_ALERT, -1L);
+ return new Ciphertext(ContentType.ALERT.id,
+ SSLHandshake.NOT_APPLICABLE.id, -1L);
}
if (v2ClientHello != null) {
// deliver the SSLv2 format ClientHello message
//
// Please don't change the limit of the destination buffer.
- if (debug != null) {
- if (Debug.isOn("record")) {
- System.out.println(Thread.currentThread().getName() +
+ if (SSLLogger.isOn) {
+ if (SSLLogger.isOn("record")) {
+ SSLLogger.fine(Thread.currentThread().getName() +
", WRITE: SSLv2 ClientHello message" +
", length = " + v2ClientHello.remaining());
}
- if (Debug.isOn("packet")) {
- Debug.printHex(
- "[Raw write]: length = " + v2ClientHello.remaining(),
- v2ClientHello);
+ if (SSLLogger.isOn("packet")) {
+ SSLLogger.fine("Raw write", v2ClientHello);
}
}
destination.put(v2ClientHello);
v2ClientHello = null;
- return new Ciphertext(RecordType.RECORD_CLIENT_HELLO, -1L);
- }
-
- if (alertMemos != null && !alertMemos.isEmpty()) {
- RecordMemo memo = alertMemos.pop();
-
- int macLen = 0;
- if (memo.encodeAuthenticator instanceof MAC) {
- macLen = ((MAC)memo.encodeAuthenticator).MAClen();
- }
-
- int dstPos = destination.position();
- int dstLim = destination.limit();
- int dstContent = dstPos + headerSize +
- writeCipher.getExplicitNonceSize();
- destination.position(dstContent);
-
- destination.put(memo.fragment);
-
- destination.limit(destination.position());
- destination.position(dstContent);
-
- if ((debug != null) && Debug.isOn("record")) {
- System.out.println(Thread.currentThread().getName() +
- ", WRITE: " + protocolVersion + " " +
- Record.contentName(Record.ct_alert) +
- ", length = " + destination.remaining());
- }
-
- // Encrypt the fragment and wrap up a record.
- long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
- Record.ct_alert, destination, dstPos, dstLim, headerSize,
- ProtocolVersion.valueOf(memo.majorVersion,
- memo.minorVersion), false);
-
- if ((debug != null) && Debug.isOn("packet")) {
- ByteBuffer temporary = destination.duplicate();
- temporary.limit(temporary.position());
- temporary.position(dstPos);
- Debug.printHex(
- "[Raw write]: length = " + temporary.remaining(),
- temporary);
- }
-
- // remain the limit unchanged
- destination.limit(dstLim);
-
- if (isCloseWaiting && (memo.contentType == Record.ct_alert)) {
- isCloseWaiting = true;
- close();
- }
- return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
+ return new Ciphertext(ContentType.HANDSHAKE.id,
+ SSLHandshake.CLIENT_HELLO.id, -1L);
}
if (fragmenter != null) {
@@ -347,8 +313,7 @@
@Override
boolean isEmpty() {
return (!isTalkingToV2) && (v2ClientHello == null) &&
- ((fragmenter == null) || fragmenter.isEmpty()) &&
- ((alertMemos == null) || alertMemos.isEmpty());
+ ((fragmenter == null) || fragmenter.isEmpty());
}
// buffered record fragment
@@ -356,8 +321,7 @@
byte contentType;
byte majorVersion;
byte minorVersion;
- CipherBox encodeCipher;
- Authenticator encodeAuthenticator;
+ SSLWriteCipher encodeCipher;
byte[] fragment;
}
@@ -372,14 +336,12 @@
void queueUpFragment(byte[] source,
int offset, int length) throws IOException {
-
HandshakeMemo memo = new HandshakeMemo();
- memo.contentType = Record.ct_handshake;
+ memo.contentType = ContentType.HANDSHAKE.id;
memo.majorVersion = protocolVersion.major; // kick start version?
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
- memo.encodeAuthenticator = writeAuthenticator;
memo.handshakeType = source[offset];
memo.acquireOffset = 0;
@@ -394,11 +356,10 @@
void queueUpChangeCipherSpec() {
RecordMemo memo = new RecordMemo();
- memo.contentType = Record.ct_change_cipher_spec;
+ memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeCipher = writeCipher;
- memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[1];
memo.fragment[0] = 1;
@@ -406,6 +367,21 @@
handshakeMemos.add(memo);
}
+ void queueUpAlert(byte level, byte description) {
+ RecordMemo memo = new RecordMemo();
+
+ memo.contentType = ContentType.ALERT.id;
+ memo.majorVersion = protocolVersion.major;
+ memo.minorVersion = protocolVersion.minor;
+ memo.encodeCipher = writeCipher;
+
+ memo.fragment = new byte[2];
+ memo.fragment[0] = level;
+ memo.fragment[1] = description;
+
+ handshakeMemos.add(memo);
+ }
+
Ciphertext acquireCiphertext(ByteBuffer dstBuf) throws IOException {
if (isEmpty()) {
return null;
@@ -413,22 +389,17 @@
RecordMemo memo = handshakeMemos.getFirst();
HandshakeMemo hsMemo = null;
- if (memo.contentType == Record.ct_handshake) {
+ if (memo.contentType == ContentType.HANDSHAKE.id) {
hsMemo = (HandshakeMemo)memo;
}
- int macLen = 0;
- if (memo.encodeAuthenticator instanceof MAC) {
- macLen = ((MAC)memo.encodeAuthenticator).MAClen();
- }
-
// ChangeCipherSpec message is pretty small. Don't worry about
// the fragmentation of ChangeCipherSpec record.
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = memo.encodeCipher.calculateFragmentSize(
- fragLen, macLen, headerSize);
+ fragLen, headerSize);
} else {
fragLen = Record.maxDataSize;
}
@@ -474,11 +445,12 @@
!handshakeMemos.isEmpty()) {
// look for the next buffered record fragment
- RecordMemo reMemo = handshakeMemos.getFirst();
- if (reMemo.contentType == Record.ct_handshake) {
- hsMemo = (HandshakeMemo)reMemo;
+ RecordMemo rm = handshakeMemos.getFirst();
+ if (rm.contentType == ContentType.HANDSHAKE.id &&
+ rm.encodeCipher == hsMemo.encodeCipher) {
+ hsMemo = (HandshakeMemo)rm;
} else {
- // not handshake message, break the loop
+ // not of the flight, break the loop
break;
}
}
@@ -498,27 +470,26 @@
dstBuf.limit(dstBuf.position());
dstBuf.position(dstContent);
- if ((debug != null) && Debug.isOn("record")) {
- System.out.println(Thread.currentThread().getName() +
- ", WRITE: " + protocolVersion + " " +
- Record.contentName(memo.contentType) +
+ if (SSLLogger.isOn && SSLLogger.isOn("record")) {
+ SSLLogger.fine(
+ "WRITE: " + protocolVersion + " " +
+ ContentType.nameOf(memo.contentType) +
", length = " + dstBuf.remaining());
}
// Encrypt the fragment and wrap up a record.
- long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
+ long recordSN = encrypt(
+ memo.encodeCipher,
memo.contentType, dstBuf,
dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
- memo.minorVersion), false);
+ memo.minorVersion));
- if ((debug != null) && Debug.isOn("packet")) {
+ if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = dstBuf.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
- Debug.printHex(
- "[Raw write]: length = " + temporary.remaining(),
- temporary);
+ SSLLogger.fine("Raw write", temporary);
}
// remain the limit unchanged
@@ -526,17 +497,32 @@
// Reset the fragmentation offset.
if (hsMemo != null) {
- return new Ciphertext(RecordType.valueOf(
- hsMemo.contentType, hsMemo.handshakeType), recordSN);
+ return new Ciphertext(hsMemo.contentType,
+ hsMemo.handshakeType, recordSN);
} else {
- return new Ciphertext(
- RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
+ if (isCloseWaiting &&
+ memo.contentType == ContentType.ALERT.id) {
+ close();
+ }
+
+ return new Ciphertext(memo.contentType,
+ SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
}
boolean isEmpty() {
return handshakeMemos.isEmpty();
}
+
+ boolean hasAlert() {
+ for (RecordMemo memo : handshakeMemos) {
+ if (memo.contentType == ContentType.ALERT.id) {
+ return true;
+ }
+ }
+
+ return false;
+ }
}
/*