@Override
  public DTLSFlight getStartHandshakeMessage() {
    ClientHello message = new ClientHello(maxProtocolVersion, new SecureRandom());

    // store client random for later calculations
    clientRandom = message.getRandom();

    // the mandatory to implement ciphersuites, the preferred one should be first in the list
    if (ScProperties.std
        .getStr("PREFERRED_CIPHER_SUITE")
        .equals(CipherSuite.TLS_PSK_WITH_AES_128_CCM_8.toString())) {
      message.addCipherSuite(CipherSuite.TLS_PSK_WITH_AES_128_CCM_8);
      message.addCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
    } else {
      message.addCipherSuite(CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8);
      message.addCipherSuite(CipherSuite.TLS_PSK_WITH_AES_128_CCM_8);
    }

    message.addCompressionMethod(CompressionMethod.NULL);

    // set current state
    state = message.getMessageType().getCode();

    // store for later calculations
    clientHello = message;
    DTLSFlight flight = new DTLSFlight();
    flight.addMessage(wrapMessage(message));

    return flight;
  }
 /**
  * Cancels any pending re-transmission of an outbound flight that has been registered previously
  * using the {@link #setPendingFlight(DTLSFlight)} method.
  *
  * <p>This method is usually invoked once an flight has been acknowledged by the peer.
  */
 public void cancelPendingFlight() {
   if (pendingFlight != null) {
     pendingFlight.getRetransmitTask().cancel();
     pendingFlight.setRetransmitTask(null);
     pendingFlight = null;
   }
 }
  /**
   * A {@link HelloVerifyRequest} is sent by the server upon the arrival of the client's {@link
   * ClientHello}. It is sent by the server to prevent flooding of a client. The client answers with
   * the same {@link ClientHello} as before with the additional cookie.
   *
   * @param message the server's {@link HelloVerifyRequest}.
   * @return {@link ClientHello} with server's {@link Cookie} set.
   */
  private DTLSFlight receivedHelloVerifyRequest(HelloVerifyRequest message) {

    clientHello.setCookie(message.getCookie());
    // update the length (cookie added)
    clientHello.setFragmentLength(clientHello.getMessageLength());

    DTLSFlight flight = new DTLSFlight();
    flight.addMessage(wrapMessage(clientHello));

    return flight;
  }
  /**
   * Called when the client received the server's finished message. If the data can be verified,
   * encrypted application data can be sent.
   *
   * @param message the {@link Finished} message.
   * @return the list
   * @throws HandshakeException
   */
  private DTLSFlight receivedServerFinished(Finished message) throws HandshakeException {
    DTLSFlight flight = new DTLSFlight();

    message.verifyData(getMasterSecret(), false, handshakeHash);

    state = HandshakeType.FINISHED.getCode();
    session.setActive(true);

    // received server's Finished message, now able to send encrypted
    // message
    ApplicationMessage applicationMessage = new ApplicationMessage(this.message.getBytes());

    flight.addMessage(wrapMessage(applicationMessage));
    // application data is not retransmitted
    flight.setRetransmissionNeeded(false);

    return flight;
  }
 private ClientHello getClientHello(DTLSFlight flight)
     throws GeneralSecurityException, HandshakeException {
   Record record = flight.getMessages().get(0);
   return (ClientHello) record.getFragment();
 }
  /**
   * The ServerHelloDone message is sent by the server to indicate the end of the ServerHello and
   * associated messages. The client prepares all necessary messages (depending on server's previous
   * flight) and returns the next flight.
   *
   * @return the client's next flight to be sent.
   * @throws HandshakeException
   */
  private DTLSFlight receivedServerHelloDone(ServerHelloDone message) throws HandshakeException {
    DTLSFlight flight = new DTLSFlight();
    if (serverHelloDone != null && (serverHelloDone.getMessageSeq() == message.getMessageSeq())) {
      // discard duplicate message
      return flight;
    }
    serverHelloDone = message;

    /*
     * All possible handshake messages sent in this flight. Used to compute
     * handshake hash.
     */
    CertificateMessage clientCertificate = null;
    ClientKeyExchange clientKeyExchange = null;
    CertificateVerify certificateVerify = null;

    /*
     * First, if required by server, send Certificate.
     */
    if (certificateRequest != null) {
      // TODO load the client's certificate according to the allowed
      // parameters in the CertificateRequest
      clientCertificate = new CertificateMessage(certificates, session.sendRawPublicKey());

      flight.addMessage(wrapMessage(clientCertificate));
    }

    /*
     * Second, send ClientKeyExchange as specified by the key exchange
     * algorithm.
     */
    byte[] premasterSecret;
    switch (keyExchange) {
      case EC_DIFFIE_HELLMAN:
        clientKeyExchange = new ECDHClientKeyExchange(ecdhe.getPublicKey());
        premasterSecret = ecdhe.getSecret(ephemeralServerPublicKey).getEncoded();

        generateKeys(premasterSecret);

        break;

      case PSK:
        String identity = ScProperties.std.getProperty("PSK_IDENTITY");
        clientKeyExchange = new PSKClientKeyExchange(identity);
        byte[] psk = sharedKeys.get(identity);

        if (psk == null) {
          AlertMessage alert =
              new AlertMessage(AlertLevel.FATAL, AlertDescription.HANDSHAKE_FAILURE);
          throw new HandshakeException(
              "No preshared secret found for identity: " + identity, alert);
        }

        LOGGER.info("Using PSK identity: " + identity);

        premasterSecret = generatePremasterSecretFromPSK(psk);
        generateKeys(premasterSecret);

        break;

      case NULL:
        clientKeyExchange = new NULLClientKeyExchange();

        // We assume, that the premaster secret is empty
        generateKeys(new byte[] {});
        break;

      default:
        AlertMessage alert = new AlertMessage(AlertLevel.FATAL, AlertDescription.HANDSHAKE_FAILURE);
        throw new HandshakeException("Unknown key exchange algorithm: " + keyExchange, alert);
    }
    flight.addMessage(wrapMessage(clientKeyExchange));

    /*
     * Third, send CertificateVerify message if necessary.
     */
    if (certificateRequest != null) {
      // prepare handshake messages
      handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, clientHello.toByteArray());
      handshakeMessages = ByteArrayUtils.concatenate(handshakeMessages, serverHello.toByteArray());
      handshakeMessages =
          ByteArrayUtils.concatenate(handshakeMessages, serverCertificate.toByteArray());
      handshakeMessages =
          ByteArrayUtils.concatenate(handshakeMessages, serverKeyExchange.toByteArray());
      handshakeMessages =
          ByteArrayUtils.concatenate(handshakeMessages, certificateRequest.toByteArray());
      handshakeMessages =
          ByteArrayUtils.concatenate(handshakeMessages, serverHelloDone.toByteArray());
      handshakeMessages =
          ByteArrayUtils.concatenate(handshakeMessages, clientCertificate.toByteArray());
      handshakeMessages =
          ByteArrayUtils.concatenate(handshakeMessages, clientKeyExchange.toByteArray());

      // TODO make sure, that signature is supported
      SignatureAndHashAlgorithm signatureAndHashAlgorithm =
          certificateRequest.getSupportedSignatureAlgorithms().get(0);
      certificateVerify =
          new CertificateVerify(signatureAndHashAlgorithm, privateKey, handshakeMessages);

      flight.addMessage(wrapMessage(certificateVerify));
    }

    /*
     * Fourth, send ChangeCipherSpec
     */
    ChangeCipherSpecMessage changeCipherSpecMessage = new ChangeCipherSpecMessage();
    flight.addMessage(wrapMessage(changeCipherSpecMessage));
    setCurrentWriteState();
    session.incrementWriteEpoch();

    /*
     * Fifth, send the finished message.
     */
    try {
      // create hash of handshake messages
      // can't do this on the fly, since there is no explicit ordering of
      // messages

      MessageDigest md = MessageDigest.getInstance("SHA-256");
      md.update(clientHello.toByteArray());
      md.update(serverHello.toByteArray());
      if (serverCertificate != null) {
        md.update(serverCertificate.toByteArray());
      }
      if (serverKeyExchange != null) {
        md.update(serverKeyExchange.toByteArray());
      }
      if (certificateRequest != null) {
        md.update(certificateRequest.toByteArray());
      }
      md.update(serverHelloDone.toByteArray());

      if (clientCertificate != null) {
        md.update(clientCertificate.toByteArray());
      }
      md.update(clientKeyExchange.toByteArray());

      if (certificateVerify != null) {
        md.update(certificateVerify.toByteArray());
      }

      MessageDigest mdWithClientFinished = null;
      try {
        mdWithClientFinished = (MessageDigest) md.clone();
      } catch (CloneNotSupportedException e) {
        LOGGER.severe("Clone not supported.");
        e.printStackTrace();
      }

      handshakeHash = md.digest();
      Finished finished = new Finished(getMasterSecret(), isClient, handshakeHash);
      flight.addMessage(wrapMessage(finished));

      // compute handshake hash with client's finished message also
      // included, used for server's finished message
      mdWithClientFinished.update(finished.toByteArray());
      handshakeHash = mdWithClientFinished.digest();

    } catch (NoSuchAlgorithmException e) {
      LOGGER.severe("No such Message Digest Algorithm available.");
      e.printStackTrace();
    }

    return flight;
  }