@Override
  public Optional<ByteBuf> handleMessage(final ByteBuf buf) throws HandshakeException {
    checkState(
        status.get() == Handshaker.HandshakeStatus.IN_PROGRESS,
        "illegal invocation of onMessage on handshake that is not in progress");

    // Take as many bytes as expected in the next message.
    int expectedLength = ECIESEncryptionEngine.ENCRYPTION_OVERHEAD;
    expectedLength +=
        initiator
            ? ResponderHandshakeMessageV1.MESSAGE_LENGTH
            : InitiatorHandshakeMessageV1.MESSAGE_LENGTH;

    if (buf.readableBytes() < expectedLength) {
      buf.markReaderIndex();
      final int size = buf.readUnsignedShort();
      if (size > buf.readableBytes() + 2) {
        buf.resetReaderIndex();
        return Optional.empty();
      }
      expectedLength = size;
      buf.resetReaderIndex();
    }

    buf.markReaderIndex();
    final ByteBuf bufferedBytes = buf.readSlice(expectedLength);
    final byte[] encryptedBytes = new byte[bufferedBytes.readableBytes()];
    bufferedBytes.getBytes(0, encryptedBytes);
    Bytes bytes = Bytes.wrap(encryptedBytes);

    Bytes encryptedMsg = bytes;
    try {
      // Decrypt the message with our private key.
      try {
        // Assume new format
        final int size = bufferedBytes.readUnsignedShort();
        if (buf.writerIndex() >= size) {
          bufferedBytes.readerIndex(0);
          final byte[] fullMessage = new byte[size + 2];
          bufferedBytes.readBytes(fullMessage, 0, expectedLength);
          buf.readBytes(fullMessage, expectedLength, size - expectedLength + 2);
          encryptedMsg = Bytes.wrap(fullMessage);
          bytes = EncryptedMessage.decryptMsgEIP8(encryptedMsg, nodeKey);
          version4 = true;
        } else {
          throw new HandshakeException("Failed to decrypt handshake message");
        }
      } catch (final Exception ex) {
        bytes = EncryptedMessage.decryptMsg(bytes, nodeKey);
        version4 = false;
      }
    } catch (final InvalidCipherTextException e) {
      status.set(Handshaker.HandshakeStatus.FAILED);
      throw new HandshakeException("Decrypting an incoming handshake message failed", e);
    } catch (final SecurityModuleException e) {
      status.set(Handshaker.HandshakeStatus.FAILED);
      throw new HandshakeException(
          "Unable to create ECDH Key agreement due to Crypto engine failure", e);
    }

    Optional<Bytes> nextMsg = Optional.empty();
    if (initiator) {
      // If we are the initiator, we have already sent our request and we're waiting for the
      // responder's ack;
      // when we receive it, we can build the handshake secret material and declare a SUCCESS.
      checkState(
          responderMsg == null,
          "unexpected message: responder message had " + "already been received");

      // Store the message, as we need it to generating our ingress and egress MACs.
      responderMsgEnc = encryptedMsg;
      if (version4) {
        responderMsg = ResponderHandshakeMessageV4.decode(bytes);
      } else {
        responderMsg = ResponderHandshakeMessageV1.decode(bytes);
      }

      // Extract the responder's nonce and ephemeral pubkey, which will be used to generate the
      // shared secrets.
      responderNonce = responderMsg.getNonce();
      partyEphPubKey = responderMsg.getEphPublicKey();

      LOG.trace(
          "Received responder's ECIES handshake message from node {}...: {}",
          partyPubKey.getEncodedBytes().slice(0, 16),
          responderMsg);

    } else {
      // If we are the responder, we are waiting for an initiator message; after we generate our
      // message and
      // we can build the handshake secret material and declare a SUCCESS.
      checkState(
          initiatorMsg == null,
          "unexpected message: initiator message " + "had already been received");

      // Store the message, as we need it to generating our ingress and egress MACs.
      initiatorMsgEnc = encryptedMsg;
      try {
        if (version4) {
          initiatorMsg = InitiatorHandshakeMessageV4.decode(bytes, nodeKey);
        } else {
          initiatorMsg = InitiatorHandshakeMessageV1.decode(bytes, nodeKey);
        }
      } catch (final SecurityModuleException e) {
        status.set(Handshaker.HandshakeStatus.FAILED);
        throw new HandshakeException(
            "Unable to create ECDH Key agreement due to Crypto engine failure", e);
      }

      LOG.trace(
          "[{}] Received initiator's ECIES handshake message: {}",
          nodeKey.getPublicKey().getEncodedBytes(),
          initiatorMsg);

      // Extract the initiator's data.
      initiatorNonce = initiatorMsg.getNonce();
      partyPubKey = initiatorMsg.getPubKey();
      partyEphPubKey = initiatorMsg.getEphPubKey();

      checkState(
          keccak256(partyEphPubKey.getEncodedBytes()).equals(initiatorMsg.getEphPubKeyHash()),
          "keccak hash of recovered ephemeral pubkey does not match announced hash");

      // Build the response message.
      if (version4) {
        responderMsg =
            ResponderHandshakeMessageV4.create(ephKeyPair.getPublicKey(), responderNonce);
      } else {
        responderMsg =
            ResponderHandshakeMessageV1.create(ephKeyPair.getPublicKey(), responderNonce, false);
      }

      LOG.trace(
          "Generated responder's ECIES handshake message against peer {}...: {}",
          partyPubKey.getEncodedBytes().slice(0, 16),
          responderMsg);

      try {
        if (version4) {
          responderMsgEnc = EncryptedMessage.encryptMsgEip8(responderMsg.encode(), partyPubKey);
        } else {
          responderMsgEnc = EncryptedMessage.encryptMsg(responderMsg.encode(), partyPubKey);
        }
      } catch (final InvalidCipherTextException e) {
        status.set(Handshaker.HandshakeStatus.FAILED);
        throw new HandshakeException("Encrypting the next handshake message failed", e);
      }
      nextMsg = Optional.of(responderMsgEnc);

      // Compute the secrets and declare this handshake as successful.
    }

    try {
      computeSecrets();
    } catch (final SecurityModuleException e) {
      status.set(Handshaker.HandshakeStatus.FAILED);
      throw new HandshakeException(
          "Unable to create ECDH Key agreement due to Crypto engine failure", e);
    }

    status.set(Handshaker.HandshakeStatus.SUCCESS);
    LOG.trace("Handshake status set to {}", status.get());
    return nextMsg.map(bv -> Unpooled.wrappedBuffer(bv.toArray()));
  }
An Elliptic Curve Integrated Encryption Scheme implementation, following the handshake ceremony
of Ethereum.

@see <a href="https://github.com/ethereum/devp2p/blob/master/rlpx.md#encrypted-handshake">RLPx
    encrypted handshake</a>
ethereum/p2p/src/main/java/org/hyperledger/besu/ethereum/p2p/rlpx/handshake/ecies/ECIESHandshaker.java