Package ch.ethz.inf.vs.scandium.dtls

Source Code of ch.ethz.inf.vs.scandium.dtls.ClientHello

/*******************************************************************************
* Copyright (c) 2014, Institute for Pervasive Computing, ETH Zurich.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
*    notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
*    notice, this list of conditions and the following disclaimer in the
*    documentation and/or other materials provided with the distribution.
* 3. Neither the name of the Institute nor the names of its contributors
*    may be used to endorse or promote products derived from this software
*    without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* This file is part of the Scandium (Sc) Security for Californium.
******************************************************************************/
package ch.ethz.inf.vs.scandium.dtls;

import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.logging.Logger;

import ch.ethz.inf.vs.scandium.dtls.CertificateTypeExtension.CertificateType;
import ch.ethz.inf.vs.scandium.dtls.SupportedPointFormatsExtension.ECPointFormat;
import ch.ethz.inf.vs.scandium.util.DatagramReader;
import ch.ethz.inf.vs.scandium.util.DatagramWriter;
import ch.ethz.inf.vs.scandium.util.ScProperties;

/**
* When a client first connects to a server, it is required to send the
* ClientHello as its first message. The client can also send a ClientHello in
* response to a {@link HelloRequest} or on its own initiative in order to
* renegotiate the security parameters in an existing connection. See <a
* href="http://tools.ietf.org/html/rfc5246#section-7.4.1.2">RFC 5246</a>.
*
* @author Stefan Jucker
*/
public class ClientHello extends HandshakeMessage {

  // Logging ///////////////////////////////////////////////////////////

  protected static final Logger LOGGER = Logger.getLogger(ClientHello.class.getCanonicalName());

  // DTLS-specific constants ///////////////////////////////////////////

  private static final int VERSION_BITS = 8; // for major and minor each

  private static final int RANDOM_BYTES = 32;

  private static final int SESSION_ID_LENGTH_BITS = 8;

  private static final int COOKIE_LENGTH = 8;

  private static final int CIPHER_SUITS_LENGTH_BITS = 16;

  private static final int COMPRESSION_METHODS_LENGTH_BITS = 8;

  // Members ///////////////////////////////////////////////////////////

  /**
   * The version of the DTLS protocol by which the client wishes to
   * communicate during this session.
   */
  private ProtocolVersion clientVersion = new ProtocolVersion();

  /** A client-generated random structure. */
  private Random random;

  /** The ID of a session the client wishes to use for this connection. */
  private SessionId sessionId;

  /** The cookie used to prevent flooding attacks (potentially empty). */
  private Cookie cookie;

  /**
   * This is a list of the cryptographic options supported by the client, with
   * the client's first preference first.
   */
  private List<CipherSuite> cipherSuites;

  /**
   * This is a list of the compression methods supported by the client, sorted
   * by client preference.
   */
  private List<CompressionMethod> compressionMethods;

  /**
   * Clients MAY request extended functionality from servers by sending data
   * in the extensions field.
   */
  private HelloExtensions extensions = null;

  // Constructors ///////////////////////////////////////////////////////////

  /**
   *
   * @param version
   * @param secureRandom
   */
  public ClientHello(ProtocolVersion version, SecureRandom secureRandom) {
    this.clientVersion = version;
    this.random = new Random(secureRandom);
    this.sessionId = new SessionId(new byte[] {});
    this.cookie = new Cookie();
    this.extensions = new HelloExtensions();
   
    // the supported elliptic curves
    List<Integer> curves = Arrays.asList(
        ECDHServerKeyExchange.NAMED_CURVE_INDEX.get("secp256r1"),
        ECDHServerKeyExchange.NAMED_CURVE_INDEX.get("secp384r1"),
        ECDHServerKeyExchange.NAMED_CURVE_INDEX.get("secp521r1"));
    HelloExtension supportedCurvesExtension = new SupportedEllipticCurvesExtension(curves);
    this.extensions.addExtension(supportedCurvesExtension);
   
    // the supported point formats
    List<ECPointFormat> formats = Arrays.asList(ECPointFormat.UNCOMPRESSED);
    HelloExtension supportedPointFormatsExtension = new SupportedPointFormatsExtension(formats);
    this.extensions.addExtension(supportedPointFormatsExtension);
   
    // the certificate types the client is able to provide to the server
    CertificateTypeExtension clientCertificateType = new ClientCertificateTypeExtension(true);
    if (ScProperties.std.getBool("USE_RAW_PUBLIC_KEY")) {
      clientCertificateType.addCertificateType(CertificateType.RAW_PUBLIC_KEY);
    } else {
      // the client supports rawPublicKeys but prefers X.509 certificates
     
      // http://tools.ietf.org/html/draft-ietf-tls-oob-pubkey-07#section-3:
      // this extension MUST be omitted if the client only supports X.509 certificates
      clientCertificateType.addCertificateType(CertificateType.X_509);
      clientCertificateType.addCertificateType(CertificateType.RAW_PUBLIC_KEY);
    }
   
    // the type of certificates the client is able to process when provided by the server
    CertificateTypeExtension serverCertificateType = new ServerCertificateTypeExtension(true);
    if (ScProperties.std.getBool("USE_RAW_PUBLIC_KEY")) {
      serverCertificateType.addCertificateType(CertificateType.RAW_PUBLIC_KEY);
      serverCertificateType.addCertificateType(CertificateType.X_509);
    } else {
      // the client supports rawPublicKeys but prefers X.509 certificates
     
      // http://tools.ietf.org/html/draft-ietf-tls-oob-pubkey-07#section-3:
      // this extension MUST be omitted if the client only supports X.509 certificates
      serverCertificateType.addCertificateType(CertificateType.X_509);
      serverCertificateType.addCertificateType(CertificateType.RAW_PUBLIC_KEY);
    }
   
    this.extensions.addExtension(clientCertificateType);
    this.extensions.addExtension(serverCertificateType);
  }

  /**
   * Constructor used when resuming a session; session ID must be known.
   *
   * @param version
   *            the version
   * @param secureRandom
   *            the secure random
   * @param session
   *            the session
   */
  public ClientHello(ProtocolVersion version, SecureRandom secureRandom, DTLSSession session) {
    this.clientVersion = version;
    this.random = new Random(secureRandom);
    this.sessionId = session.getSessionIdentifier();
    this.cookie = new Cookie();
    addCipherSuite(session.getWriteState().getCipherSuite());
    addCompressionMethod(session.getReadState().getCompressionMethod());
  }

  /**
   * Constructor used when reconstructing from byteArray.
   *
   * @param clientVersion
   *            the requested version.
   * @param random
   *            the client the client's random.
   * @param sessionId
   *            the session id (potentially empty).
   * @param cookie
   *            the cookie (potentially empty).
   * @param cipherSuites
   *            the available cipher suites.
   * @param compressionMethods
   *            the available compression methods.
   * @param extensions
   *            the extensions (potentially empty).
   */
  public ClientHello(ProtocolVersion clientVersion, Random random, SessionId sessionId, Cookie cookie, List<CipherSuite> cipherSuites, List<CompressionMethod> compressionMethods, HelloExtensions extensions) {
    this.clientVersion = clientVersion;
    this.random = random;
    this.sessionId = sessionId;
    this.cookie = cookie;
    this.cipherSuites = cipherSuites;
    this.compressionMethods = compressionMethods;
    this.extensions = extensions;
  }

  // Serialization //////////////////////////////////////////////////

  @Override
  public byte[] fragmentToByteArray() {

    DatagramWriter writer = new DatagramWriter();

    writer.write(clientVersion.getMajor(), VERSION_BITS);
    writer.write(clientVersion.getMinor(), VERSION_BITS);

    writer.writeBytes(random.getRandomBytes());

    writer.write(sessionId.length(), SESSION_ID_LENGTH_BITS);
    writer.writeBytes(sessionId.getSessionId());

    writer.write(cookie.length(), COOKIE_LENGTH);
    writer.writeBytes(cookie.getCookie());

    writer.write(cipherSuites.size() * 2, CIPHER_SUITS_LENGTH_BITS);
    writer.writeBytes(CipherSuite.listToByteArray(cipherSuites));

    writer.write(compressionMethods.size(), COMPRESSION_METHODS_LENGTH_BITS);
    writer.writeBytes(CompressionMethod.listToByteArray(compressionMethods));

    if (extensions != null) {
      writer.writeBytes(extensions.toByteArray());
    }

    return writer.toByteArray();
  }

  public static HandshakeMessage fromByteArray(byte[] byteArray) throws HandshakeException {
    DatagramReader reader = new DatagramReader(byteArray);

    int major = reader.read(VERSION_BITS);
    int minor = reader.read(VERSION_BITS);
    ProtocolVersion clientVersion = new ProtocolVersion(major, minor);

    Random random = new Random(reader.readBytes(RANDOM_BYTES));

    int sessionIdLength = reader.read(SESSION_ID_LENGTH_BITS);
    SessionId sessionId = new SessionId(reader.readBytes(sessionIdLength));

    int cookieLength = reader.read(COOKIE_LENGTH);
    Cookie cookie = new Cookie(reader.readBytes(cookieLength));

    int cipherSuitesLength = reader.read(CIPHER_SUITS_LENGTH_BITS);
    List<CipherSuite> cipherSuites = CipherSuite.listFromByteArray(reader.readBytes(cipherSuitesLength), cipherSuitesLength / 2); // 2

    int compressionMethodsLength = reader.read(COMPRESSION_METHODS_LENGTH_BITS);
    List<CompressionMethod> compressionMethods = CompressionMethod.listFromByteArray(reader.readBytes(compressionMethodsLength), compressionMethodsLength);

    byte[] bytesLeft = reader.readBytesLeft();
    HelloExtensions extensions = null;
    if (bytesLeft.length > 0) {
      extensions = HelloExtensions.fromByteArray(bytesLeft);
    }

    return new ClientHello(clientVersion, random, sessionId, cookie, cipherSuites, compressionMethods, extensions);

  }

  // Methods ////////////////////////////////////////////////////////

  @Override
  public HandshakeType getMessageType() {
    return HandshakeType.CLIENT_HELLO;
  }

  @Override
  public int getMessageLength() {
    /*
     * if no extensions set, empty; otherwise 2 bytes for field length and
     * then the length of the extensions. See
     * http://tools.ietf.org/html/rfc5246#section-7.4.1.2
     */
    int extensionsLength = (extensions != null) ? (2 + extensions.getLength()) : 0;

    /*
     * fixed sizes: version (2) + random (32) + session ID length (1) +
     * cookie length (1) + cipher suites length (2) + compression methods
     * length (1) = 39
     */
    return 39 + sessionId.length() + cookie.length() + cipherSuites.size() * 2 + compressionMethods.size() + extensionsLength;
  }

  @Override
  public String toString() {
    StringBuilder sb = new StringBuilder();
    sb.append(super.toString());
    sb.append("\t\tVersion: " + clientVersion.getMajor() + ", " + clientVersion.getMinor() + "\n");
    sb.append("\t\tRandom: \n" + random.toString());
    sb.append("\t\tSession ID Length: " + sessionId.length() + "\n");
    if (sessionId.length() > 0) {
      sb.append("\t\tSession ID: " + sessionId.getSessionId() + "\n");
    }
    sb.append("\t\tCookie Length: " + cookie.length() + "\n");
    if (cookie.length() > 0) {
      sb.append("\t\tCookie: " + ByteArrayUtils.toHexString(cookie.getCookie()) + "\n");
    }
    sb.append("\t\tCipher Suites Length: " + cipherSuites.size() * 2 + "\n");
    sb.append("\t\tCipher Suites (" + cipherSuites.size() + " suites)\n");
    for (CipherSuite cipher : cipherSuites) {
      sb.append("\t\t\tCipher Suite: " + cipher.toString() + "\n");
    }
    sb.append("\t\tCompression Methods Length: " + compressionMethods.size() + "\n");
    sb.append("\t\tCompression Methods (" + compressionMethods.size() + " method)" + "\n");
    for (CompressionMethod method : compressionMethods) {
      sb.append("\t\t\tCompression Method: " + method.toString() + "\n");
    }
    if (extensions != null) {
      sb.append(extensions.toString());
    }

    return sb.toString();
  }

  // Getters and Setters ////////////////////////////////////////////

  public ProtocolVersion getClientVersion() {
    return clientVersion;
  }

  public void setClientVersion(ProtocolVersion clientVersion) {
    this.clientVersion = clientVersion;
  }

  public Random getRandom() {
    return random;
  }

  public void setRandom(Random random) {
    this.random = random;
  }

  public SessionId getSessionId() {
    return sessionId;
  }

  public void setSessionId(SessionId sessionId) {
    this.sessionId = sessionId;
  }

  public Cookie getCookie() {
    return cookie;
  }

  public void setCookie(Cookie cookie) {
    this.cookie = cookie;
  }

  public List<CipherSuite> getCipherSuites() {
    return cipherSuites;
  }

  public void setCipherSuits(List<CipherSuite> cipherSuits) {
    this.cipherSuites = cipherSuits;
  }

  public void addCipherSuite(CipherSuite cipherSuite) {
    if (cipherSuites == null) {
      cipherSuites = new ArrayList<CipherSuite>();
    }
    cipherSuites.add(cipherSuite);
  }

  public List<CompressionMethod> getCompressionMethods() {
    return compressionMethods;
  }

  public void setCompressionMethods(List<CompressionMethod> compressionMethods) {
    this.compressionMethods = compressionMethods;
  }

  public void addCompressionMethod(CompressionMethod compressionMethod) {
    if (compressionMethods == null) {
      compressionMethods = new ArrayList<CompressionMethod>();
    }
    compressionMethods.add(compressionMethod);
  }
 
  /**
   * Gets the supported elliptic curves.
   *
   * @return the client's supported elliptic curves extension if available,
   *         otherwise <code>null</code>.
   */
  public SupportedEllipticCurvesExtension getSupportedEllipticCurvesExtension() {
    if (extensions != null) {
      List<HelloExtension> exts = extensions.getExtensions();
      for (HelloExtension helloExtension : exts) {
        if (helloExtension instanceof SupportedEllipticCurvesExtension) {
          return (SupportedEllipticCurvesExtension) helloExtension;
        }
      }
    }
    return null;
  }
 
  /**
   *
   * @return the client's certificate type extension if available,
   *         otherwise <code>null</code>.
   */
  public ClientCertificateTypeExtension getClientCertificateTypeExtension() {
    if (extensions != null) {
      List<HelloExtension> exts = extensions.getExtensions();
      for (HelloExtension helloExtension : exts) {
        if (helloExtension instanceof ClientCertificateTypeExtension) {
          return (ClientCertificateTypeExtension) helloExtension;
        }
      }
    }
    return null;
  }
 
  /**
   *
   * @return the client's certificate type extension if available,
   *         otherwise <code>null</code>.
   */
  public ServerCertificateTypeExtension getServerCertificateTypeExtension() {
    if (extensions != null) {
      List<HelloExtension> exts = extensions.getExtensions();
      for (HelloExtension helloExtension : exts) {
        if (helloExtension instanceof ServerCertificateTypeExtension) {
          return (ServerCertificateTypeExtension) helloExtension;
        }
      }
    }
    return null;
  }

}
TOP

Related Classes of ch.ethz.inf.vs.scandium.dtls.ClientHello

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.