--- a/src/java.base/share/classes/sun/security/ssl/InputRecord.java Fri May 11 14:55:56 2018 -0700
+++ b/src/java.base/share/classes/sun/security/ssl/InputRecord.java Fri May 11 15:53:12 2018 -0700
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 1996, 2014, 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
@@ -27,14 +27,8 @@
import java.io.*;
import java.nio.*;
-import java.util.*;
-
import javax.crypto.BadPaddingException;
-
-import javax.net.ssl.*;
-
-import sun.security.util.HexDumpEncoder;
-
+import sun.security.ssl.SSLCipher.SSLReadCipher;
/**
* {@code InputRecord} takes care of the management of SSL/TLS/DTLS input
@@ -42,15 +36,10 @@
*
* @author David Brownell
*/
-class InputRecord implements Record, Closeable {
-
- /* Class and subclass dynamic debugging support */
- static final Debug debug = Debug.getInstance("ssl");
+abstract class InputRecord implements Record, Closeable {
+ SSLReadCipher readCipher;
- Authenticator readAuthenticator;
- CipherBox readCipher;
-
- HandshakeHash handshakeHash;
+ final HandshakeHash handshakeHash;
boolean isClosed;
// The ClientHello version to accept. If set to ProtocolVersion.SSL20Hello
@@ -61,10 +50,11 @@
// fragment size
int fragmentSize;
- InputRecord() {
- this.readCipher = CipherBox.NULL;
- this.readAuthenticator = null; // Please override this assignment.
- this.helloVersion = ProtocolVersion.DEFAULT_HELLO;
+ InputRecord(HandshakeHash handshakeHash, SSLReadCipher readCipher) {
+ this.readCipher = readCipher;
+ this.helloVersion = ProtocolVersion.TLS10;
+ this.handshakeHash = handshakeHash;
+ this.isClosed = false;
this.fragmentSize = Record.maxDataSize;
}
@@ -76,37 +66,9 @@
return helloVersion;
}
- /*
- * Set instance for the computation of handshake hashes.
- *
- * 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 ... especially in the
- * case of hashing the initial V2 message!
- */
- void setHandshakeHash(HandshakeHash handshakeHash) {
- if (handshakeHash != null) {
- byte[] reserved = null;
- if (this.handshakeHash != null) {
- reserved = this.handshakeHash.getAllHandshakeMessages();
- }
- if ((reserved != null) && (reserved.length != 0)) {
- handshakeHash.update(reserved, 0, reserved.length);
-
- if (debug != null && Debug.isOn("data")) {
- Debug.printHex(
- "[reserved] handshake hash: len = " + reserved.length,
- reserved);
- }
- }
- }
-
- this.handshakeHash = handshakeHash;
- }
-
boolean seqNumIsHuge() {
- return (readAuthenticator != null) &&
- readAuthenticator.seqNumIsHuge();
+ return (readCipher.authenticator != null) &&
+ readCipher.authenticator.seqNumIsHuge();
}
boolean isEmpty() {
@@ -118,6 +80,11 @@
// blank
}
+ // apply to DTLS SSLEngine
+ void finishHandshake() {
+ // blank
+ }
+
/**
* Prevent any more data from being read into this record,
* and flag the record as holding no data.
@@ -130,9 +97,12 @@
}
}
+ synchronized boolean isClosed() {
+ return isClosed;
+ }
+
// apply to SSLSocket and SSLEngine
- void changeReadCiphers(
- Authenticator readAuthenticator, CipherBox readCipher) {
+ void changeReadCiphers(SSLReadCipher readCipher) {
/*
* Dispose of any intermediate state in the underlying cipher.
@@ -144,7 +114,6 @@
*/
readCipher.dispose();
- this.readAuthenticator = readAuthenticator;
this.readCipher = readCipher;
}
@@ -160,24 +129,22 @@
* @return -1 if there are not enough bytes to tell (small header),
*/
// apply to SSLEngine only
- int bytesInCompletePacket(ByteBuffer buf) throws SSLException {
+ int bytesInCompletePacket(
+ ByteBuffer[] srcs, int srcsOffset, int srcsLength) throws IOException {
+
+ throw new UnsupportedOperationException("Not supported yet.");
+ }
+
+ // apply to SSLSocket only
+ int bytesInCompletePacket() throws IOException {
throw new UnsupportedOperationException();
}
// apply to SSLSocket only
- int bytesInCompletePacket(InputStream is) throws IOException {
+ void setReceiverStream(InputStream inputStream) {
throw new UnsupportedOperationException();
}
- /**
- * Return true if the specified record protocol version is out of the
- * range of the possible supported versions.
- */
- void checkRecordVersion(ProtocolVersion version,
- boolean allowSSL20Hello) throws SSLException {
- // blank
- }
-
// apply to DTLS SSLEngine only
Plaintext acquirePlaintext()
throws IOException, BadPaddingException {
@@ -186,17 +153,8 @@
// read, decrypt and decompress the network record.
//
- // apply to SSLEngine only
- Plaintext decode(ByteBuffer netData)
- throws IOException, BadPaddingException {
- throw new UnsupportedOperationException();
- }
-
- // apply to SSLSocket only
- Plaintext decode(InputStream is, ByteBuffer destination)
- throws IOException, BadPaddingException {
- throw new UnsupportedOperationException();
- }
+ abstract Plaintext[] decode(ByteBuffer[] srcs, int srcsOffset,
+ int srcsLength) throws IOException, BadPaddingException;
// apply to SSLSocket only
void setDeliverStream(OutputStream outputStream) {
@@ -216,9 +174,7 @@
// Not apply to DTLS
static ByteBuffer convertToClientHello(ByteBuffer packet) {
-
int srcPos = packet.position();
- int srcLim = packet.limit();
byte firstByte = packet.get();
byte secondByte = packet.get();
@@ -244,7 +200,7 @@
// 1: length byte of ClientHello.session_id
// 2: length bytes of ClientHello.cipher_suites
// 2: empty ClientHello.compression_methods
- int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3 );
+ int requiredSize = 48 + sessionIdLen + ((cipherSpecLen * 2 ) / 3);
byte[] converted = new byte[requiredSize];
/*
@@ -252,7 +208,7 @@
* that's now buffered up. (Lengths are fixed up later).
*/
// Note: need not to set the header actually.
- converted[0] = ct_handshake;
+ converted[0] = ContentType.HANDSHAKE.id;
converted[1] = majorVersion;
converted[2] = minorVersion;
// header [3..4] for handshake message length
@@ -372,237 +328,61 @@
return ByteBuffer.wrap(converted, 5, pointer - 5); // 5: header size
}
- static ByteBuffer decrypt(Authenticator authenticator, CipherBox box,
- byte contentType, ByteBuffer bb) throws BadPaddingException {
-
- return decrypt(authenticator, box, contentType, bb, null);
- }
-
- static ByteBuffer decrypt(Authenticator authenticator,
- CipherBox box, byte contentType, ByteBuffer bb,
- byte[] sequence) throws BadPaddingException {
-
- BadPaddingException reservedBPE = null;
- int tagLen =
- (authenticator instanceof MAC) ? ((MAC)authenticator).MAClen() : 0;
- int cipheredLength = bb.remaining();
- int srcPos = bb.position();
- if (!box.isNullCipher()) {
- try {
- // apply explicit nonce for AEAD/CBC cipher suites if needed
- int nonceSize = box.applyExplicitNonce(
- authenticator, contentType, bb, sequence);
-
- // decrypt the content
- if (box.isAEADMode()) {
- // DON'T decrypt the nonce_explicit for AEAD mode
- bb.position(srcPos + nonceSize);
- } // The explicit IV for CBC mode can be decrypted.
-
- // Note that the CipherBox.decrypt() does not change
- // the capacity of the buffer.
- box.decrypt(bb, tagLen);
- // We don't actually remove the nonce.
- bb.position(srcPos + nonceSize);
- } catch (BadPaddingException bpe) {
- // RFC 2246 states that decryption_failed should be used
- // for this purpose. However, that allows certain attacks,
- // so we just send bad record MAC. We also need to make
- // sure to always check the MAC to avoid a timing attack
- // for the same issue. See paper by Vaudenay et al and the
- // update in RFC 4346/5246.
- //
- // Failover to message authentication code checking.
- reservedBPE = bpe;
- }
- }
+ // Extract an SSL/(D)TLS record from the specified source buffers.
+ static ByteBuffer extract(
+ ByteBuffer[] buffers, int offset, int length, int headerSize) {
- // Requires message authentication code for null, stream and block
- // cipher suites.
- if ((authenticator instanceof MAC) && (tagLen != 0)) {
- MAC signer = (MAC)authenticator;
- int contentLen = bb.remaining() - tagLen;
-
- // Note that although it is not necessary, we run the same MAC
- // computation and comparison on the payload for both stream
- // cipher and CBC block cipher.
- if (contentLen < 0) {
- // negative data length, something is wrong
- if (reservedBPE == null) {
- reservedBPE = new BadPaddingException("bad record");
+ boolean hasFullHeader = false;
+ int contentLen = -1;
+ for (int i = offset, j = 0;
+ i < (offset + length) && j < headerSize; i++) {
+ int remains = buffers[i].remaining();
+ int pos = buffers[i].position();
+ for (int k = 0; k < remains && j < headerSize; j++, k++) {
+ byte b = buffers[i].get(pos + k);
+ if (j == (headerSize - 2)) {
+ contentLen = ((b & 0xFF) << 8);
+ } else if (j == (headerSize -1)) {
+ contentLen |= (b & 0xFF);
+ hasFullHeader = true;
+ break;
}
-
- // set offset of the dummy MAC
- contentLen = cipheredLength - tagLen;
- bb.limit(srcPos + cipheredLength);
- }
-
- // Run MAC computation and comparison on the payload.
- //
- // MAC data would be stripped off during the check.
- if (checkMacTags(contentType, bb, signer, sequence, false)) {
- if (reservedBPE == null) {
- reservedBPE = new BadPaddingException("bad record MAC");
- }
- }
-
- // Run MAC computation and comparison on the remainder.
- //
- // It is only necessary for CBC block cipher. It is used to get a
- // constant time of MAC computation and comparison on each record.
- if (box.isCBCMode()) {
- int remainingLen = calculateRemainingLen(
- signer, cipheredLength, contentLen);
-
- // NOTE: remainingLen may be bigger (less than 1 block of the
- // hash algorithm of the MAC) than the cipheredLength.
- //
- // Is it possible to use a static buffer, rather than allocate
- // it dynamically?
- remainingLen += signer.MAClen();
- ByteBuffer temporary = ByteBuffer.allocate(remainingLen);
-
- // Won't need to worry about the result on the remainder. And
- // then we won't need to worry about what's actual data to
- // check MAC tag on. We start the check from the header of the
- // buffer so that we don't need to construct a new byte buffer.
- checkMacTags(contentType, temporary, signer, sequence, true);
}
}
- // Is it a failover?
- if (reservedBPE != null) {
- throw reservedBPE;
- }
-
- return bb.slice();
- }
-
- /*
- * Run MAC computation and comparison
- *
- */
- private static boolean checkMacTags(byte contentType, ByteBuffer bb,
- MAC signer, byte[] sequence, boolean isSimulated) {
-
- int tagLen = signer.MAClen();
- int position = bb.position();
- int lim = bb.limit();
- int macOffset = lim - tagLen;
-
- bb.limit(macOffset);
- byte[] hash = signer.compute(contentType, bb, sequence, isSimulated);
- if (hash == null || tagLen != hash.length) {
- // Something is wrong with MAC implementation.
- throw new RuntimeException("Internal MAC error");
+ if (!hasFullHeader) {
+ throw new BufferUnderflowException();
}
- bb.position(macOffset);
- bb.limit(lim);
- try {
- int[] results = compareMacTags(bb, hash);
- return (results[0] != 0);
- } finally {
- // reset to the data
- bb.position(position);
- bb.limit(macOffset);
- }
- }
-
- /*
- * A constant-time comparison of the MAC tags.
- *
- * Please DON'T change the content of the ByteBuffer parameter!
- */
- private static int[] compareMacTags(ByteBuffer bb, byte[] tag) {
-
- // An array of hits is used to prevent Hotspot optimization for
- // the purpose of a constant-time check.
- int[] results = {0, 0}; // {missed #, matched #}
-
- // The caller ensures there are enough bytes available in the buffer.
- // So we won't need to check the remaining of the buffer.
- for (int i = 0; i < tag.length; i++) {
- if (bb.get() != tag[i]) {
- results[0]++; // mismatched bytes
- } else {
- results[1]++; // matched bytes
+ int packetLen = headerSize + contentLen;
+ int remains = 0;
+ for (int i = offset; i < offset + length; i++) {
+ remains += buffers[i].remaining();
+ if (remains >= packetLen) {
+ break;
}
}
- return results;
- }
-
- /*
- * Run MAC computation and comparison
- *
- * Please DON'T change the content of the byte buffer parameter!
- */
- private static boolean checkMacTags(byte contentType, byte[] buffer,
- int offset, int contentLen, MAC signer, boolean isSimulated) {
-
- int tagLen = signer.MAClen();
- byte[] hash = signer.compute(
- contentType, buffer, offset, contentLen, isSimulated);
- if (hash == null || tagLen != hash.length) {
- // Something is wrong with MAC implementation.
- throw new RuntimeException("Internal MAC error");
+ if (remains < packetLen) {
+ throw new BufferUnderflowException();
}
- int[] results = compareMacTags(buffer, offset + contentLen, hash);
- return (results[0] != 0);
- }
-
- /*
- * A constant-time comparison of the MAC tags.
- *
- * Please DON'T change the content of the byte buffer parameter!
- */
- private static int[] compareMacTags(
- byte[] buffer, int offset, byte[] tag) {
+ byte[] packet = new byte[packetLen];
+ int packetOffset = 0;
+ int packetSpaces = packetLen;
+ for (int i = offset; i < offset + length; i++) {
+ if (buffers[i].hasRemaining()) {
+ int len = Math.min(packetSpaces, buffers[i].remaining());
+ buffers[i].get(packet, packetOffset, len);
+ packetOffset += len;
+ packetSpaces -= len;
+ }
- // An array of hits is used to prevent Hotspot optimization for
- // the purpose of a constant-time check.
- int[] results = {0, 0}; // {missed #, matched #}
-
- // The caller ensures there are enough bytes available in the buffer.
- // So we won't need to check the length of the buffer.
- for (int i = 0; i < tag.length; i++) {
- if (buffer[offset + i] != tag[i]) {
- results[0]++; // mismatched bytes
- } else {
- results[1]++; // matched bytes
+ if (packetSpaces <= 0) {
+ break;
}
}
- return results;
- }
-
- /*
- * Calculate the length of a dummy buffer to run MAC computation
- * and comparison on the remainder.
- *
- * The caller MUST ensure that the fullLen is not less than usedLen.
- */
- private static int calculateRemainingLen(
- MAC signer, int fullLen, int usedLen) {
-
- int blockLen = signer.hashBlockLen();
- int minimalPaddingLen = signer.minimalPaddingLen();
-
- // (blockLen - minimalPaddingLen) is the maximum message size of
- // the last block of hash function operation. See FIPS 180-4, or
- // MD5 specification.
- fullLen += 13 - (blockLen - minimalPaddingLen);
- usedLen += 13 - (blockLen - minimalPaddingLen);
-
- // Note: fullLen is always not less than usedLen, and blockLen
- // is always bigger than minimalPaddingLen, so we don't worry
- // about negative values. 0x01 is added to the result to ensure
- // that the return value is positive. The extra one byte does
- // not impact the overall MAC compression function evaluations.
- return 0x01 + (int)(Math.ceil(fullLen/(1.0d * blockLen)) -
- Math.ceil(usedLen/(1.0d * blockLen))) * blockLen;
+ return ByteBuffer.wrap(packet);
}
}
-