< prev index next >
src/java.base/share/classes/sun/security/ssl/DTLSOutputRecord.java
Print this page
*** 1,7 ****
/*
! * Copyright (c) 1996, 2016, 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
--- 1,7 ----
/*
! * 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
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
*** 26,42 ****
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
-
- import javax.crypto.BadPaddingException;
-
import javax.net.ssl.*;
!
! import sun.security.util.HexDumpEncoder;
! import static sun.security.ssl.Ciphertext.RecordType;
/**
* DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
*/
final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
--- 26,37 ----
package sun.security.ssl;
import java.io.*;
import java.nio.*;
import java.util.*;
import javax.net.ssl.*;
! import sun.security.ssl.SSLCipher.SSLWriteCipher;
/**
* DTLS {@code OutputRecord} implementation for {@code SSLEngine}.
*/
final class DTLSOutputRecord extends OutputRecord implements DTLSRecord {
*** 45,102 ****
int writeEpoch;
int prevWriteEpoch;
Authenticator prevWriteAuthenticator;
! CipherBox prevWriteCipher;
! private LinkedList<RecordMemo> alertMemos = new LinkedList<>();
! DTLSOutputRecord() {
! this.writeAuthenticator = new MAC(true);
this.writeEpoch = 0;
this.prevWriteEpoch = 0;
! this.prevWriteCipher = CipherBox.NULL;
! this.prevWriteAuthenticator = new MAC(true);
this.packetSize = DTLSRecord.maxRecordSize;
! this.protocolVersion = ProtocolVersion.DEFAULT_DTLS;
}
@Override
! void changeWriteCiphers(Authenticator writeAuthenticator,
! CipherBox writeCipher) throws IOException {
encodeChangeCipherSpec();
prevWriteCipher.dispose();
- this.prevWriteAuthenticator = this.writeAuthenticator;
this.prevWriteCipher = this.writeCipher;
this.prevWriteEpoch = this.writeEpoch;
- this.writeAuthenticator = writeAuthenticator;
this.writeCipher = writeCipher;
this.writeEpoch++;
this.isFirstAppOutputRecord = true;
// set the epoch number
! this.writeAuthenticator.setEpochNumber(this.writeEpoch);
}
@Override
void encodeAlert(byte level, byte description) throws IOException {
RecordMemo memo = new RecordMemo();
! memo.contentType = Record.ct_alert;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
- memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[2];
memo.fragment[0] = level;
memo.fragment[1] = description;
--- 40,105 ----
int writeEpoch;
int prevWriteEpoch;
Authenticator prevWriteAuthenticator;
! SSLWriteCipher prevWriteCipher;
! private final LinkedList<RecordMemo> alertMemos = new LinkedList<>();
! DTLSOutputRecord(HandshakeHash handshakeHash) {
! super(handshakeHash, SSLWriteCipher.nullDTlsWriteCipher());
this.writeEpoch = 0;
this.prevWriteEpoch = 0;
! this.prevWriteCipher = SSLWriteCipher.nullDTlsWriteCipher();
this.packetSize = DTLSRecord.maxRecordSize;
! this.protocolVersion = ProtocolVersion.NONE;
}
@Override
! void initHandshaker() {
! // clean up
! fragmenter = null;
! }
+ @Override
+ void finishHandshake() {
+ // fragmenter = null;
+ }
+
+ @Override
+ void changeWriteCiphers(SSLWriteCipher writeCipher,
+ boolean useChangeCipherSpec) throws IOException {
+ if (useChangeCipherSpec) {
encodeChangeCipherSpec();
+ }
prevWriteCipher.dispose();
this.prevWriteCipher = this.writeCipher;
this.prevWriteEpoch = this.writeEpoch;
this.writeCipher = writeCipher;
this.writeEpoch++;
this.isFirstAppOutputRecord = true;
// set the epoch number
! this.writeCipher.authenticator.setEpochNumber(this.writeEpoch);
}
@Override
void encodeAlert(byte level, byte description) throws IOException {
RecordMemo memo = new RecordMemo();
! memo.contentType = ContentType.ALERT.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.fragment = new byte[2];
memo.fragment[0] = level;
memo.fragment[1] = description;
*** 112,122 ****
}
@Override
void encodeHandshake(byte[] source,
int offset, int length) throws IOException {
-
if (firstMessage) {
firstMessage = false;
}
if (fragmenter == null) {
--- 115,124 ----
*** 125,158 ****
fragmenter.queueUpHandshake(source, offset, length);
}
@Override
! 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 " +
"(2^64-1 packets). Closing connection.");
}
throw new SSLHandshakeException("sequence number overflow");
}
! // not apply to handshake message
! int macLen = 0;
! if (writeAuthenticator instanceof MAC) {
! macLen = ((MAC)writeAuthenticator).MAClen();
}
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = writeCipher.calculateFragmentSize(
! fragLen, macLen, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
}
--- 127,183 ----
fragmenter.queueUpHandshake(source, offset, length);
}
@Override
! 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 (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");
}
! // Don't process the incoming record until all of the buffered records
! // get handled. May need retransmission if no sources specified.
! if (!isEmpty() || sources == null || sources.length == 0) {
! 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;
}
+ // not apply to handshake message
int fragLen;
if (packetSize > 0) {
fragLen = Math.min(maxRecordSize, packetSize);
fragLen = writeCipher.calculateFragmentSize(
! fragLen, headerSize);
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
}
*** 181,228 ****
}
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) +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
! long recordSN = encrypt(writeAuthenticator, writeCipher,
! Record.ct_application_data, destination,
dstPos, dstLim, headerSize,
! protocolVersion, true);
! 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);
! return new Ciphertext(RecordType.RECORD_APPLICATION_DATA, recordSN);
}
! @Override
! Ciphertext acquireCiphertext(ByteBuffer destination) throws IOException {
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);
--- 206,247 ----
}
destination.limit(destination.position());
destination.position(dstContent);
! 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.
! long recordSN = encrypt(writeCipher,
! ContentType.APPLICATION_DATA.id, destination,
dstPos, dstLim, headerSize,
! protocolVersion);
! if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
! SSLLogger.fine("Raw write", temporary);
}
// remain the limit unchanged
destination.limit(dstLim);
! return new Ciphertext(ContentType.APPLICATION_DATA.id,
! SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
! private Ciphertext acquireCiphertext(
! ByteBuffer destination) throws IOException {
if (alertMemos != null && !alertMemos.isEmpty()) {
RecordMemo memo = alertMemos.pop();
int dstPos = destination.position();
int dstLim = destination.limit();
int dstContent = dstPos + headerSize +
writeCipher.getExplicitNonceSize();
destination.position(dstContent);
*** 230,265 ****
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), true);
! 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);
! return new Ciphertext(RecordType.RECORD_ALERT, recordSN);
}
if (fragmenter != null) {
return fragmenter.acquireCiphertext(destination);
}
--- 249,284 ----
destination.put(memo.fragment);
destination.limit(destination.position());
destination.position(dstContent);
! if (SSLLogger.isOn && SSLLogger.isOn("record")) {
! SSLLogger.fine(
! "WRITE: " + protocolVersion + " " +
! ContentType.ALERT.name +
", length = " + destination.remaining());
}
// Encrypt the fragment and wrap up a record.
! long recordSN = encrypt(memo.encodeCipher,
! ContentType.ALERT.id,
! destination, dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
! memo.minorVersion));
! if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = destination.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
! SSLLogger.fine("Raw write", temporary);
}
// remain the limit unchanged
destination.limit(dstLim);
! return new Ciphertext(ContentType.ALERT.id,
! SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
if (fragmenter != null) {
return fragmenter.acquireCiphertext(destination);
}
*** 272,287 ****
return ((fragmenter == null) || fragmenter.isEmpty()) &&
((alertMemos == null) || alertMemos.isEmpty());
}
@Override
- void initHandshaker() {
- // clean up
- fragmenter = null;
- }
-
- @Override
void launchRetransmission() {
// Note: Please don't retransmit if there are handshake messages
// or alerts waiting in the queue.
if (((alertMemos == null) || alertMemos.isEmpty()) &&
(fragmenter != null) && fragmenter.isRetransmittable()) {
--- 291,300 ----
*** 293,304 ****
private static class RecordMemo {
byte contentType;
byte majorVersion;
byte minorVersion;
int encodeEpoch;
! CipherBox encodeCipher;
! Authenticator encodeAuthenticator;
byte[] fragment;
}
private static class HandshakeMemo extends RecordMemo {
--- 306,316 ----
private static class RecordMemo {
byte contentType;
byte majorVersion;
byte minorVersion;
int encodeEpoch;
! SSLWriteCipher encodeCipher;
byte[] fragment;
}
private static class HandshakeMemo extends RecordMemo {
*** 306,316 ****
int messageSequence;
int acquireOffset;
}
private final class DTLSFragmenter {
! private LinkedList<RecordMemo> handshakeMemos = new LinkedList<>();
private int acquireIndex = 0;
private int messageSequence = 0;
private boolean flightIsReady = false;
// Per section 4.1.1, RFC 6347:
--- 318,329 ----
int messageSequence;
int acquireOffset;
}
private final class DTLSFragmenter {
! private final LinkedList<RecordMemo> handshakeMemos =
! new LinkedList<>();
private int acquireIndex = 0;
private int messageSequence = 0;
private boolean flightIsReady = false;
// Per section 4.1.1, RFC 6347:
*** 334,349 ****
flightIsReady = false;
}
RecordMemo memo = new RecordMemo();
! memo.contentType = Record.ct_change_cipher_spec;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
- memo.encodeAuthenticator = writeAuthenticator;
memo.fragment = new byte[1];
memo.fragment[0] = 1;
handshakeMemos.add(memo);
--- 347,361 ----
flightIsReady = false;
}
RecordMemo memo = new RecordMemo();
! memo.contentType = ContentType.CHANGE_CIPHER_SPEC.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.fragment = new byte[1];
memo.fragment[0] = 1;
handshakeMemos.add(memo);
*** 359,374 ****
flightIsReady = false;
}
HandshakeMemo memo = new HandshakeMemo();
! memo.contentType = Record.ct_handshake;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
- memo.encodeAuthenticator = writeAuthenticator;
memo.handshakeType = buf[offset];
memo.messageSequence = messageSequence++;
memo.acquireOffset = 0;
memo.fragment = new byte[length - 4]; // 4: header size
--- 371,385 ----
flightIsReady = false;
}
HandshakeMemo memo = new HandshakeMemo();
! memo.contentType = ContentType.HANDSHAKE.id;
memo.majorVersion = protocolVersion.major;
memo.minorVersion = protocolVersion.minor;
memo.encodeEpoch = writeEpoch;
memo.encodeCipher = writeCipher;
memo.handshakeType = buf[offset];
memo.messageSequence = messageSequence++;
memo.acquireOffset = 0;
memo.fragment = new byte[length - 4]; // 4: header size
*** 377,392 ****
System.arraycopy(buf, offset + 4, memo.fragment, 0, length - 4);
handshakeHashing(memo, memo.fragment);
handshakeMemos.add(memo);
! if ((memo.handshakeType == HandshakeMessage.ht_client_hello) ||
! (memo.handshakeType == HandshakeMessage.ht_hello_request) ||
(memo.handshakeType ==
! HandshakeMessage.ht_hello_verify_request) ||
! (memo.handshakeType == HandshakeMessage.ht_server_hello_done) ||
! (memo.handshakeType == HandshakeMessage.ht_finished)) {
flightIsReady = true;
}
}
--- 388,403 ----
System.arraycopy(buf, offset + 4, memo.fragment, 0, length - 4);
handshakeHashing(memo, memo.fragment);
handshakeMemos.add(memo);
! if ((memo.handshakeType == SSLHandshake.CLIENT_HELLO.id) ||
! (memo.handshakeType == SSLHandshake.HELLO_REQUEST.id) ||
(memo.handshakeType ==
! SSLHandshake.HELLO_VERIFY_REQUEST.id) ||
! (memo.handshakeType == SSLHandshake.SERVER_HELLO_DONE.id) ||
! (memo.handshakeType == SSLHandshake.FINISHED.id)) {
flightIsReady = true;
}
}
*** 399,424 ****
}
}
RecordMemo memo = handshakeMemos.get(acquireIndex);
HandshakeMemo hsMemo = null;
! if (memo.contentType == Record.ct_handshake) {
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, 25); // 25: header size
// 13: DTLS record
// 12: DTLS handshake message
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
--- 410,430 ----
}
}
RecordMemo memo = handshakeMemos.get(acquireIndex);
HandshakeMemo hsMemo = null;
! if (memo.contentType == ContentType.HANDSHAKE.id) {
hsMemo = (HandshakeMemo)memo;
}
// 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, 25); // 25: header size
// 13: DTLS record
// 12: DTLS handshake message
fragLen = Math.min(fragLen, Record.maxDataSize);
} else {
fragLen = Record.maxDataSize;
*** 457,487 ****
}
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) +
", length = " + dstBuf.remaining());
}
// Encrypt the fragment and wrap up a record.
! long recordSN = encrypt(memo.encodeAuthenticator, memo.encodeCipher,
memo.contentType, dstBuf,
dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
! memo.minorVersion), true);
! if ((debug != null) && Debug.isOn("packet")) {
ByteBuffer temporary = dstBuf.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
! Debug.printHex(
! "[Raw write]: length = " + temporary.remaining(),
! temporary);
}
// remain the limit unchanged
dstBuf.limit(dstLim);
--- 463,492 ----
}
dstBuf.limit(dstBuf.position());
dstBuf.position(dstContent);
! 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.encodeCipher,
memo.contentType, dstBuf,
dstPos, dstLim, headerSize,
ProtocolVersion.valueOf(memo.majorVersion,
! memo.minorVersion));
! if (SSLLogger.isOn && SSLLogger.isOn("packet")) {
ByteBuffer temporary = dstBuf.duplicate();
temporary.limit(temporary.position());
temporary.position(dstPos);
! SSLLogger.fine(
! "Raw write (" + temporary.remaining() + ")", temporary);
}
// remain the limit unchanged
dstBuf.limit(dstLim);
*** 490,532 ****
hsMemo.acquireOffset += fragLen;
if (hsMemo.acquireOffset == hsMemo.fragment.length) {
acquireIndex++;
}
! return new Ciphertext(RecordType.valueOf(
! hsMemo.contentType, hsMemo.handshakeType), recordSN);
} else {
acquireIndex++;
! return new Ciphertext(
! RecordType.RECORD_CHANGE_CIPHER_SPEC, recordSN);
}
}
private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
byte hsType = hsFrag.handshakeType;
! if ((hsType == HandshakeMessage.ht_hello_request) ||
! (hsType == HandshakeMessage.ht_hello_verify_request)) {
!
// omitted from handshake hash computation
return;
}
- if ((hsFrag.messageSequence == 0) &&
- (hsType == HandshakeMessage.ht_client_hello)) {
-
- // omit initial ClientHello message
- //
- // 2: ClientHello.client_version
- // 32: ClientHello.random
- int sidLen = hsBody[34];
-
- if (sidLen == 0) { // empty session_id, initial handshake
- return;
- }
- }
-
// calculate the DTLS header
byte[] temporary = new byte[12]; // 12: handshake header size
// Handshake.msg_type
temporary[0] = hsFrag.handshakeType;
--- 495,521 ----
hsMemo.acquireOffset += fragLen;
if (hsMemo.acquireOffset == hsMemo.fragment.length) {
acquireIndex++;
}
! return new Ciphertext(hsMemo.contentType,
! hsMemo.handshakeType, recordSN);
} else {
acquireIndex++;
! return new Ciphertext(ContentType.CHANGE_CIPHER_SPEC.id,
! SSLHandshake.NOT_APPLICABLE.id, recordSN);
}
}
private void handshakeHashing(HandshakeMemo hsFrag, byte[] hsBody) {
byte hsType = hsFrag.handshakeType;
! if (!handshakeHash.isHashable(hsType)) {
// omitted from handshake hash computation
return;
}
// calculate the DTLS header
byte[] temporary = new byte[12]; // 12: handshake header size
// Handshake.msg_type
temporary[0] = hsFrag.handshakeType;
*** 548,568 ****
// Handshake.fragment_length
temporary[9] = temporary[1];
temporary[10] = temporary[2];
temporary[11] = temporary[3];
! if ((hsType != HandshakeMessage.ht_finished) &&
! (hsType != HandshakeMessage.ht_certificate_verify)) {
!
! handshakeHash.update(temporary, 0, 12);
! handshakeHash.update(hsBody, 0, hsBody.length);
! } else {
! // Reserve until this handshake message has been processed.
! handshakeHash.reserve(temporary, 0, 12);
! handshakeHash.reserve(hsBody, 0, hsBody.length);
! }
!
}
boolean isEmpty() {
if (!flightIsReady || handshakeMemos.isEmpty() ||
acquireIndex >= handshakeMemos.size()) {
--- 537,548 ----
// Handshake.fragment_length
temporary[9] = temporary[1];
temporary[10] = temporary[2];
temporary[11] = temporary[3];
! handshakeHash.deliver(temporary, 0, 12);
! handshakeHash.deliver(hsBody, 0, hsBody.length);
}
boolean isEmpty() {
if (!flightIsReady || handshakeMemos.isEmpty() ||
acquireIndex >= handshakeMemos.size()) {
< prev index next >