Java Code Examples for javax.net.ssl.SSLEngineResult#HandshakeStatus

The following examples show how to use javax.net.ssl.SSLEngineResult#HandshakeStatus . You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example 1
Source File: SSLEngineResultTest.java    From j2objc with Apache License 2.0 6 votes vote down vote up
/**
 * Test for <code>getHandshakeStatus()</code> method
 */
public void test_getHandshakeStatus() {
    int[] pos = { 0, 1, 1000, Integer.MAX_VALUE, (Integer.MAX_VALUE - 1) };
    SSLEngineResult.Status [] enS =
        SSLEngineResult.Status.values();
    SSLEngineResult.HandshakeStatus [] enHS =
        SSLEngineResult.HandshakeStatus.values();
    for (int i = 0; i < enS.length; i++) {
        for (int j = 0; j < enHS.length; j++) {
            for (int n = 0; n < pos.length; n++) {
                for (int l = 0; l < pos.length; ++l) {
                    SSLEngineResult res = new SSLEngineResult(enS[i],
                            enHS[j], pos[n], pos[l]);
                    assertEquals("Incorrect HandshakeStatus", enHS[j],
                            res.getHandshakeStatus());
                }
            }
        }
    }
}
 
Example 2
Source File: SSLEngineResultTest.java    From j2objc with Apache License 2.0 6 votes vote down vote up
/**
 * Test for <code>getStatus()</code> method
 */
public void test_getStatus() {
    int[] pos = { 0, 1, 1000, Integer.MAX_VALUE, (Integer.MAX_VALUE - 1) };
    SSLEngineResult.Status [] enS =
        SSLEngineResult.Status.values();
    SSLEngineResult.HandshakeStatus [] enHS =
        SSLEngineResult.HandshakeStatus.values();
    for (int i = 0; i < enS.length; i++) {
        for (int j = 0; j < enHS.length; j++) {
            for (int n = 0; n < pos.length; n++) {
                for (int l = 0; l < pos.length; ++l) {
                    SSLEngineResult res = new SSLEngineResult(enS[i],
                            enHS[j], pos[n], pos[l]);
                    assertEquals("Incorrect Status", enS[i],
                            res.getStatus());
                }
            }
        }
    }
}
 
Example 3
Source File: SecureNioChannel.java    From Tomcat8-Source-Read with MIT License 5 votes vote down vote up
/**
 * Executes all the tasks needed on the same thread.
 * @return the status
 */
protected SSLEngineResult.HandshakeStatus tasks() {
    Runnable r = null;
    while ((r = sslEngine.getDelegatedTask()) != null) {
        r.run();
    }
    return sslEngine.getHandshakeStatus();
}
 
Example 4
Source File: SecureNio2Channel.java    From Tomcat8-Source-Read with MIT License 5 votes vote down vote up
/**
 * Executes all the tasks needed on the same thread.
 * @return the status
 */
protected SSLEngineResult.HandshakeStatus tasks() {
    Runnable r = null;
    while ( (r = sslEngine.getDelegatedTask()) != null) {
        r.run();
    }
    return sslEngine.getHandshakeStatus();
}
 
Example 5
Source File: TlsSocket.java    From swim with Apache License 2.0 5 votes vote down vote up
void didConnect() throws SSLException {
  do {
    final int oldStatus = this.status;
    if ((oldStatus & (CONNECTING | CONNECTED | HANDSHAKING)) != (CONNECTED | HANDSHAKING)) {
      final int newStatus = oldStatus & ~CONNECTING | (CONNECTED | HANDSHAKING);
      if (STATUS.compareAndSet(this, oldStatus, newStatus)) {
        if ((oldStatus & CONNECTED) != (newStatus & CONNECTED)) {
          this.socket.didConnect();
        }
        if ((oldStatus & HANDSHAKING) != (newStatus & HANDSHAKING)) {
          this.socket.willSecure();
        }
        this.sslEngine.beginHandshake();
        final SSLEngineResult.HandshakeStatus handshakeStatus = this.sslEngine.getHandshakeStatus();
        switch (handshakeStatus) {
          case NEED_UNWRAP:
            this.context.flowControl(FlowModifier.ENABLE_READ);
            break;
          case NEED_WRAP:
            this.context.flowControl(FlowModifier.ENABLE_READ_WRITE);
            break;
          default:
            throw new AssertionError(handshakeStatus); // unreachable
        }
        break;
      }
    } else {
      break;
    }
  } while (true);
}
 
Example 6
Source File: TLSWrapper.java    From Openfire with Apache License 2.0 5 votes vote down vote up
private SSLEngineResult.HandshakeStatus doTasks() {

        Runnable runnable;

        /*
           * We could run this in a separate thread, but do in the current for now.
           */
        while ((runnable = tlsEngine.getDelegatedTask()) != null) {
            runnable.run();
        }
        return tlsEngine.getHandshakeStatus();
    }
 
Example 7
Source File: OpenSSLEngine.java    From wildfly-openssl with Apache License 2.0 5 votes vote down vote up
@Override
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
    if (accepted == 0 || destroyed != 0) {
        return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
    }
    initSsl();

    // Check if we are in the initial handshake phase
    if (!handshakeFinished) {
        // There is pending data in the network BIO -- call wrap
        if (SSL.getInstance().pendingWrittenBytesInBIO(networkBIO) != 0) {
            return SSLEngineResult.HandshakeStatus.NEED_WRAP;
        }

        // No pending data to be sent to the peer
        // Check to see if we have finished handshaking
        if (SSL.getInstance().isInInit(ssl) == 0) {
            handshakeFinished();
            return SSLEngineResult.HandshakeStatus.FINISHED;
        }

        // No pending data and still handshaking
        // Must be waiting on the peer to send more data
        return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
    }

    // Check if we are in the shutdown phase
    if (engineClosed) {
        // Waiting to send the close_notify message
        if (SSL.getInstance().pendingWrittenBytesInBIO(networkBIO) != 0) {
            return SSLEngineResult.HandshakeStatus.NEED_WRAP;
        }

        // Must be waiting to receive the close_notify message
        return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
    }

    return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
}
 
Example 8
Source File: SslReadWriteSelectorHandler.java    From simplewebserver with Apache License 2.0 5 votes vote down vote up
/**
 * Do all the outstanding handshake tasks in the current Thread.
 */
private SSLEngineResult.HandshakeStatus doTasks() {
    Runnable runnable;

    /*
     * We could run this in a separate thread, but
     * do in the current for now.
     */
    while ((runnable = sslEngine.getDelegatedTask()) != null) {
        runnable.run();
    }
    return sslEngine.getHandshakeStatus();
}
 
Example 9
Source File: SslHandler.java    From neoscada with Eclipse Public License 1.0 5 votes vote down vote up
/**
 * Do all the outstanding handshake tasks in the current Thread.
 */
private SSLEngineResult.HandshakeStatus doTasks() {
    /*
     * We could run this in a separate thread, but I don't see the need for
     * this when used from SSLFilter. Use thread filters in MINA instead?
     */
    Runnable runnable;
    while ((runnable = sslEngine.getDelegatedTask()) != null) {
        // TODO : we may have to use a thread pool here to improve the
        // performances
        runnable.run();
    }
    return sslEngine.getHandshakeStatus();
}
 
Example 10
Source File: SecureNioChannel.java    From Tomcat7.0.67 with Apache License 2.0 5 votes vote down vote up
/**
 * Executes all the tasks needed on the same thread.
 * @return HandshakeStatus
 */
protected SSLEngineResult.HandshakeStatus tasks() {
    Runnable r = null;
    while ( (r = sslEngine.getDelegatedTask()) != null) {
        r.run();
    }
    return sslEngine.getHandshakeStatus();
}
 
Example 11
Source File: OpenSslEngine.java    From netty4.0.27Learn with Apache License 2.0 5 votes vote down vote up
@Override
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
    if (accepted == 0 || destroyed != 0) {
        return NOT_HANDSHAKING;
    }

    // Check if we are in the initial handshake phase
    if (!handshakeFinished) {
        // There is pending data in the network BIO -- call wrap
        if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
            return NEED_WRAP;
        }

        // No pending data to be sent to the peer
        // Check to see if we have finished handshaking
        if (SSL.isInInit(ssl) == 0) {
            handshakeFinished = true;
            return FINISHED;
        }

        // No pending data and still handshaking
        // Must be waiting on the peer to send more data
        return NEED_UNWRAP;
    }

    // Check if we are in the shutdown phase
    if (engineClosed) {
        // Waiting to send the close_notify message
        if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
            return NEED_WRAP;
        }

        // Must be waiting to receive the close_notify message
        return NEED_UNWRAP;
    }

    return NOT_HANDSHAKING;
}
 
Example 12
Source File: SSLCodec.java    From NetBare with MIT License 5 votes vote down vote up
void handshake(SSLEngine engine, ByteBuffer input, CodecCallback callback)
        throws IOException {
    if (!mHandshakeStarted) {
        engine.beginHandshake();
        mHandshakeStarted = true;
    }
    SSLEngineResult.HandshakeStatus status = engine.getHandshakeStatus();
    while (!mHandshakeFinished) {
        if (mEngineClosed) {
            throw new IOException("Handshake failed: Engine is closed.");
        }
        if (status == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
            // Should never happen
            throw new IOException("Handshake failed: Invalid handshake status: " + status);
        } else if (status == SSLEngineResult.HandshakeStatus.FINISHED) {
            mHandshakeFinished = true;
            NetBareLog.i("SSL handshake finished!");
            if (input.hasRemaining()) {
                decode(engine, input, callback);
            }
        } else if (status == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
            status = handshakeWrap(engine, callback).getHandshakeStatus();
        } else if (status == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
            // Wait next encrypted buffer.
            if (!input.hasRemaining()) {
                break;
            }
            status = handshakeUnwrap(engine, input, callback).getHandshakeStatus();
        } else if (status == SSLEngineResult.HandshakeStatus.NEED_TASK) {
            runDelegatedTasks(engine);
        }
    }
}
 
Example 13
Source File: Worker.java    From t-io with Apache License 2.0 4 votes vote down vote up
SSLEngineResult.HandshakeStatus getHandshakeStatus() {
	return _engine.getHandshakeStatus();
}
 
Example 14
Source File: DiameterFirewall.java    From SigFW with GNU Affero General Public License v3.0 4 votes vote down vote up
/**
 * DTLS produce handshake packets
 */
boolean dtls_produceHandshakePackets(SSLEngine engine, /*SocketAddress socketAddr,*/ String peer_realm,
        String side, List<DatagramOverDiameterPacket> packets) throws Exception {

    long _t = System.currentTimeMillis();
    long _end = _t + DTLS_MAX_HANDSHAKE_DURATION*1000;

    boolean endLoops = false;
    int loops = DTLS_MAX_HANDSHAKE_LOOPS / 2;
    while (!endLoops && System.currentTimeMillis() < _end/*&&
            (dtls_serverException == null) && (dtls_clientException == null)*/) {

        if (--loops < 0) {
            throw new RuntimeException(
                    "Too much loops to produce handshake packets");
        }

        ByteBuffer oNet = ByteBuffer.allocate(DTLS_BUFFER_SIZE);
        ByteBuffer oApp = ByteBuffer.allocate(0);
        SSLEngineResult r = engine.wrap(oApp, oNet);
        oNet.flip();

        SSLEngineResult.Status rs = r.getStatus();
        SSLEngineResult.HandshakeStatus hs = r.getHandshakeStatus();
        logger.debug("DTLS " + side + ": " + "----produce handshake packet(" +
                loops + ", " + rs + ", " + hs + ")----");
        if (rs == SSLEngineResult.Status.BUFFER_OVERFLOW) {
            // the client maximum fragment size config does not work?
            throw new Exception("Buffer overflow: " +
                        "incorrect server maximum fragment size");
        } else if (rs == SSLEngineResult.Status.BUFFER_UNDERFLOW) {
            logger.debug("DTLS " + side + ": " +
                    "Produce handshake packets: BUFFER_UNDERFLOW occured");
            logger.debug("DTLS " + side + ": " +
                    "Produce handshake packets: Handshake status: " + hs);
            // bad packet, or the client maximum fragment size
            // config does not work?
            if (hs != SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {
                throw new Exception("Buffer underflow: " +
                        "incorrect server maximum fragment size");
            } // otherwise, ignore this packet
        } else if (rs == SSLEngineResult.Status.CLOSED) {
            throw new Exception("SSLEngine has closed");
        } else if (rs == SSLEngineResult.Status.OK) {
            // OK
        } else {
            throw new Exception("Can't reach here, result is " + rs);
        }

        // SSLEngineResult.Status.OK:
        if (oNet.hasRemaining()) {
            byte[] ba = new byte[oNet.remaining()];
            oNet.get(ba);
            DatagramOverDiameterPacket packet = createHandshakePacket(ba, peer_realm);
            packets.add(packet);
        }

        if (hs == SSLEngineResult.HandshakeStatus.FINISHED) {
            logger.debug("DTLS " + side + ": " + "Produce handshake packets: "
                        + "Handshake status is FINISHED, finish the loop");
            return true;
        }

        boolean endInnerLoop = false;
        SSLEngineResult.HandshakeStatus nhs = hs;
        while (!endInnerLoop) {
            if (nhs == SSLEngineResult.HandshakeStatus.NEED_TASK) {
                dtls_runDelegatedTasks(engine);
            } else if (nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP ||
                nhs == SSLEngineResult.HandshakeStatus.NEED_UNWRAP_AGAIN ||
                nhs == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING) {

                endInnerLoop = true;
                endLoops = true;
            } else if (nhs == SSLEngineResult.HandshakeStatus.NEED_WRAP) {
                endInnerLoop = true;
            } else if (nhs == SSLEngineResult.HandshakeStatus.FINISHED) {
                throw new Exception(
                        "Unexpected status, SSLEngine.getHandshakeStatus() "
                                + "shouldn't return FINISHED");
            } else {
                throw new Exception("Can't reach here, handshake status is "
                        + nhs);
            }
            nhs = engine.getHandshakeStatus();
        }
    }

    return false;
}
 
Example 15
Source File: PeerChannel.java    From nifi with Apache License 2.0 4 votes vote down vote up
public void performHandshake() throws IOException {
    if (sslEngine == null) {
        return;
    }

    sslEngine.beginHandshake();

    final ByteBuffer emptyMessage = ByteBuffer.allocate(0);
    ByteBuffer unwrapBuffer = ByteBuffer.allocate(0);

    while (true) {
        final SSLEngineResult.HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();

        switch (handshakeStatus) {
            case FINISHED:
            case NOT_HANDSHAKING:
                streamBuffer.clear();
                destinationBuffer.clear();
                logger.debug("Completed SSL Handshake with Peer {}", peerDescription);
                return;

            case NEED_TASK:
                logger.debug("SSL Handshake with Peer {} Needs Task", peerDescription);

                Runnable runnable;
                while ((runnable = sslEngine.getDelegatedTask()) != null) {
                    runnable.run();
                }
                break;

            case NEED_WRAP:
                logger.trace("SSL Handshake with Peer {} Needs Wrap", peerDescription);

                encrypt(emptyMessage);
                final int bytesWritten = write(destinationBuffer);
                logger.debug("Wrote {} bytes for NEED_WRAP portion of Handshake", bytesWritten);
                break;

            case NEED_UNWRAP:
                logger.trace("SSL Handshake with Peer {} Needs Unwrap", peerDescription);

                while (sslEngine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NEED_UNWRAP) {
                    final boolean decrypted = decrypt(unwrapBuffer);
                    if (decrypted) {
                        logger.trace("Decryption was successful for NEED_UNWRAP portion of Handshake");
                        break;
                    }

                    if (unwrapBuffer.capacity() - unwrapBuffer.position() < 1) {
                        logger.trace("Enlarging size of Buffer for NEED_UNWRAP portion of Handshake");

                        // destinationBuffer is not large enough. Need to increase the size.
                        final ByteBuffer tempBuffer = ByteBuffer.allocate(unwrapBuffer.capacity() + sslEngine.getSession().getApplicationBufferSize());
                        tempBuffer.put(unwrapBuffer);
                        unwrapBuffer = tempBuffer;
                        unwrapBuffer.flip();
                        continue;
                    }

                    logger.trace("Need to read more bytes for NEED_UNWRAP portion of Handshake");

                    // Need to read more data.
                    unwrapBuffer.compact();
                    final int bytesRead = socketChannel.read(unwrapBuffer);
                    unwrapBuffer.flip();
                    logger.debug("Read {} bytes for NEED_UNWRAP portion of Handshake", bytesRead);
                }

                break;
        }
    }
}
 
Example 16
Source File: ReferenceCountedOpenSslEngine.java    From netty-4.1.22 with Apache License 2.0 4 votes vote down vote up
private SSLEngineResult.HandshakeStatus getHandshakeStatus(int pending) {
    // Check if we are in the initial handshake phase or shutdown phase
    return needPendingStatus() ? pendingStatus(pending) : NOT_HANDSHAKING;
}
 
Example 17
Source File: ReferenceCountedOpenSslEngine.java    From netty-4.1.22 with Apache License 2.0 4 votes vote down vote up
@Override
public final synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
    // Check if we are in the initial handshake phase or shutdown phase
    return needPendingStatus() ? pendingStatus(SSL.bioLengthNonApplication(networkBIO)) : NOT_HANDSHAKING;
}
 
Example 18
Source File: SSLContextValidatorEngine.java    From pulsar with Apache License 2.0 4 votes vote down vote up
void handshake(SSLContextValidatorEngine peerEngine) throws SSLException {
    SSLEngineResult.HandshakeStatus handshakeStatus = sslEngine.getHandshakeStatus();
    while (true) {
        switch (handshakeStatus) {
            case NEED_WRAP:
                handshakeResult = sslEngine.wrap(EMPTY_BUF, netBuffer);
                switch (handshakeResult.getStatus()) {
                    case OK: break;
                    case BUFFER_OVERFLOW:
                        netBuffer.compact();
                        netBuffer = ensureCapacity(netBuffer, sslEngine.getSession().getPacketBufferSize());
                        netBuffer.flip();
                        break;
                    case BUFFER_UNDERFLOW:
                    case CLOSED:
                    default:
                        throw new SSLException("Unexpected handshake status: " + handshakeResult.getStatus());
                }
                return;
            case NEED_UNWRAP:
                if (peerEngine.netBuffer.position() == 0) {
                    return;
                }
                peerEngine.netBuffer.flip(); // unwrap the data from peer
                handshakeResult = sslEngine.unwrap(peerEngine.netBuffer, appBuffer);
                peerEngine.netBuffer.compact();
                handshakeStatus = handshakeResult.getHandshakeStatus();
                switch (handshakeResult.getStatus()) {
                    case OK: break;
                    case BUFFER_OVERFLOW:
                        appBuffer = ensureCapacity(appBuffer, sslEngine.getSession().getApplicationBufferSize());
                        break;
                    case BUFFER_UNDERFLOW:
                        netBuffer = ensureCapacity(netBuffer, sslEngine.getSession().getPacketBufferSize());
                        break;
                    case CLOSED:
                    default:
                        throw new SSLException("Unexpected handshake status: " + handshakeResult.getStatus());
                }
                break;
            case NEED_TASK:
                sslEngine.getDelegatedTask().run();
                handshakeStatus = sslEngine.getHandshakeStatus();
                break;
            case FINISHED:
                return;
            case NOT_HANDSHAKING:
                if (handshakeResult.getHandshakeStatus() != SSLEngineResult.HandshakeStatus.FINISHED) {
                    throw new SSLException("Did not finish handshake");
                }
                return;
            default:
                throw new IllegalStateException("Unexpected handshake status " + handshakeStatus);
        }
    }
}
 
Example 19
Source File: XmppTcpTransportModule.java    From Smack with Apache License 2.0 4 votes vote down vote up
@Override
public OutputResult output(ByteBuffer outputData, boolean isFinalDataOfElement, boolean destinationAddressChanged,
        boolean moreDataAvailable) throws SSLException {
    if (outputData != null) {
        pendingOutputData.add(outputData);
        pendingOutputBytes += outputData.remaining();
        if (moreDataAvailable && pendingOutputBytes < MAX_PENDING_OUTPUT_BYTES) {
            return OutputResult.NO_OUTPUT;
        }
    }

    ByteBuffer[] outputDataArray = pendingOutputData.toArray(new ByteBuffer[pendingOutputData.size()]);

    myNetData.clear();

    while (true) {
        SSLEngineResult result;
        try {
            result = engine.wrap(outputDataArray, myNetData);
        } catch (SSLException e) {
            handleSslException(e);
            throw e;
        }

        debugLogSslEngineResult("wrap", result);

        SSLEngineResult.Status engineResultStatus = result.getStatus();

        pendingOutputBytes -= result.bytesConsumed();

        if (engineResultStatus == SSLEngineResult.Status.OK) {
            wrapInBytes += result.bytesConsumed();
            wrapOutBytes += result.bytesProduced();

            SSLEngineResult.HandshakeStatus handshakeStatus = handleHandshakeStatus(result);
            switch (handshakeStatus) {
                case NEED_UNWRAP:
                    // NEED_UNWRAP means that we need to receive something in order to continue the handshake. The
                    // standard channelSelectedCallback logic will take care of this, as there is eventually always
                    // a interest to read from the socket.
                    break;
                case NEED_WRAP:
                    // Same as need task: Cycle the reactor.
                case NEED_TASK:
                    // Note that we also set pendingOutputFilterData in the OutputResult in the NEED_TASK case, as
                    // we also want to retry the wrap() operation above in this case.
                    return new OutputResult(true, myNetData);
                default:
                    break;
            }
        }

        switch (engineResultStatus) {
        case OK:
            // No need to outputData.compact() here, since we do not reuse the buffer.
            // Clean up the pending output data.
            pruneBufferList(pendingOutputData);
            return new OutputResult(!pendingOutputData.isEmpty(), myNetData);
        case CLOSED:
            pendingOutputData.clear();
            return OutputResult.NO_OUTPUT;
        case BUFFER_OVERFLOW:
            LOGGER.warning("SSLEngine status BUFFER_OVERFLOW, this is hopefully uncommon");
            int outputDataRemaining = outputData != null ? outputData.remaining() : 0;
            int newCapacity = (int) (1.3 * outputDataRemaining);
            // If newCapacity would not increase myNetData, then double it.
            if (newCapacity <= myNetData.capacity()) {
                newCapacity = 2 * myNetData.capacity();
            }
            ByteBuffer newMyNetData = ByteBuffer.allocateDirect(newCapacity);
            myNetData.flip();
            newMyNetData.put(myNetData);
            myNetData = newMyNetData;
            continue;
        case BUFFER_UNDERFLOW:
            throw new IllegalStateException(
                    "Buffer underflow as result of SSLEngine.wrap() should never happen");
        }
    }
}
 
Example 20
Source File: OpenSSLEngine.java    From Tomcat8-Source-Read with MIT License 4 votes vote down vote up
@Override
public synchronized SSLEngineResult.HandshakeStatus getHandshakeStatus() {
    if (accepted == Accepted.NOT || destroyed) {
        return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
    }

    // Check if we are in the initial handshake phase
    if (!handshakeFinished) {

        // There is pending data in the network BIO -- call wrap
        if (sendHandshakeError || SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
            if (sendHandshakeError) {
                // After a last wrap, consider it is going to be done
                sendHandshakeError = false;
                currentHandshake++;
            }
            return SSLEngineResult.HandshakeStatus.NEED_WRAP;
        }

        /*
         * Tomcat Native stores a count of the completed handshakes in the
         * SSL instance and increments it every time a handshake is
         * completed. Comparing the handshake count when the handshake
         * started to the current handshake count enables this code to
         * detect when the handshake has completed.
         *
         * Obtaining client certificates after the connection has been
         * established requires additional checks. We need to trigger
         * additional reads until the certificates have been read but we
         * don't know how many reads we will need as it depends on both
         * client and network behaviour.
         *
         * The additional reads are triggered by returning NEED_UNWRAP
         * rather than FINISHED. This allows the standard I/O code to be
         * used.
         *
         * For TLSv1.2 and below, the handshake completes before the
         * renegotiation. We therefore use SSL.renegotiatePending() to
         * check on the current status of the renegotiation and return
         * NEED_UNWRAP until it completes which means the client
         * certificates will have been read from the client.
         *
         * For TLSv1.3, Tomcat Native sets a flag when post handshake
         * authentication is started and updates it once the client
         * certificate has been received. We therefore use
         * SSL.getPostHandshakeAuthInProgress() to check the current status
         * and return NEED_UNWRAP until that methods indicates that PHA is
         * no longer in progress.
         */

        // No pending data to be sent to the peer
        // Check to see if we have finished handshaking
        int handshakeCount = SSL.getHandshakeCount(ssl);
        if (handshakeCount != currentHandshake && SSL.renegotiatePending(ssl) == 0 &&
                (SSL.getPostHandshakeAuthInProgress(ssl) == 0)) {
            if (alpn) {
                selectedProtocol = SSL.getAlpnSelected(ssl);
                if (selectedProtocol == null) {
                    selectedProtocol = SSL.getNextProtoNegotiated(ssl);
                }
            }
            session.lastAccessedTime = System.currentTimeMillis();
            version = SSL.getVersion(ssl);
            handshakeFinished = true;
            return SSLEngineResult.HandshakeStatus.FINISHED;
        }

        // No pending data
        // Still handshaking / renegotiation / post-handshake auth pending
        // Must be waiting on the peer to send more data
        return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
    }

    // Check if we are in the shutdown phase
    if (engineClosed) {
        // Waiting to send the close_notify message
        if (SSL.pendingWrittenBytesInBIO(networkBIO) != 0) {
            return SSLEngineResult.HandshakeStatus.NEED_WRAP;
        }

        // Must be waiting to receive the close_notify message
        return SSLEngineResult.HandshakeStatus.NEED_UNWRAP;
    }

    return SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING;
}