text: /*
   *  Copyright (c) 2016, The OpenThread Authors.
   *  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 copyright holder 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 COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
   */
  
  /**
   * @file
   *   This file includes definitions for using mbedTLS.
   */
  
  #ifndef DTLS_HPP_
  #define DTLS_HPP_
  
  #include "openthread-core-config.h"
  
  #include <mbedtls/certs.h>
  #include <mbedtls/net_sockets.h>
  #include <mbedtls/ssl.h>
  #include <mbedtls/ssl_cookie.h>
  
  #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
  #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
  #include <mbedtls/base64.h>
  #include <mbedtls/x509.h>
  #include <mbedtls/x509_crl.h>
  #include <mbedtls/x509_crt.h>
  #include <mbedtls/x509_csr.h>
  #endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
  #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
  
  #include "common/locator.hpp"
  #include "common/message.hpp"
  #include "common/random.hpp"
  #include "common/timer.hpp"
  #include "crypto/sha256.hpp"
  #include "meshcop/meshcop_tlvs.hpp"
  #include "net/socket.hpp"
  #include "net/udp6.hpp"
  
  namespace ot {
  
  namespace MeshCoP {
  
  class Dtls : public InstanceLocator
  {
  public:
      enum
      {
          kPskMaxLength                = 32,
          kGuardTimeNewConnectionMilli = 2000,
  #if !OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
          kApplicationDataMaxLength = 512,
  #else
          kApplicationDataMaxLength = OPENTHREAD_CONFIG_DTLS_APPLICATION_DATA_MAX_LENGTH,
  #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
      };
  
      enum State
      {
          kStateClosed = 0,
          kStateOpen,
          kStateInitializing,
          kStateConnecting,
          kStateConnected,
          kStateCloseNotify,
      };
  
      /**
       * This constructor initializes the DTLS object.
       *
       * @param[in]  aNetif               A reference to the Thread network interface.
       * @param[in]  aLayerTwoSecurity    Specifies whether to use layer two security or not.
       *
       */
      explicit Dtls(Instance &aInstance, bool aLayerTwoSecurity);
  
      /**
       * This function pointer is called when a connection is established or torn down.
       *
       * @param[in]  aContext    A pointer to application-specific context.
       * @param[in]  aConnected  TRUE if a connection was established, FALSE otherwise.
       *
       */
      typedef void (*ConnectedHandler)(void *aContext, bool aConnected);
  
      /**
       * This function pointer is called when data is received from the DTLS session.
       *
       * @param[in]  aContext  A pointer to application-specific context.
       * @param[in]  aBuf      A pointer to the received data buffer.
       * @param[in]  aLength   Number of bytes in the received data buffer.
       *
       */
      typedef void (*ReceiveHandler)(void *aContext, uint8_t *aBuf, uint16_t aLength);
  
      /**
       * This function pointer is called when secure CoAP server want to send encrypted message.
       *
       * @param[in]  aContext      A pointer to arbitrary context information.
       * @param[in]  aMessage      A reference to the message to send.
       * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
       *
       */
      typedef otError (*TransportCallback)(void *aContext, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
  
      /**
       * This function pointer is called when data is ready to transmit for the DTLS session.
       *
       * @param[in]  aContext         A pointer to application-specific context.
       * @param[in]  aBuf             A pointer to the transmit data buffer.
       * @param[in]  aLength          Number of bytes in the transmit data buffer.
       * @param[in]  aMessageSubtype  A message sub type information for the sender.
       *
       */
      typedef otError (*SendHandler)(void *aContext, const uint8_t *aBuf, uint16_t aLength, uint8_t aMessageSubType);
  
      /**
       * This method opens the DTLS socket.
       *
       * @param[in]  aReceiveHandler      A pointer to a function that is called to receive DTLS payload.
       * @param[in]  aConnectedHandler    A pointer to a function that is called when connected or disconnected.
       * @param[in]  aContext             A pointer to arbitrary context information.
       *
       * @retval OT_ERROR_NONE     Successfully opened the socket.
       * @retval OT_ERROR_ALREADY  The DTLS is already open.
       *
       */
      otError Open(ReceiveHandler aReceiveHandler, ConnectedHandler aConnectedHandler, void *aContext);
  
      /**
       * This method binds this DTLS to a UDP port.
       *
       * @param[in]  aPort              The port to bind.
       *
       * @retval OT_ERROR_NONE           Successfully bound the DTLS socket.
       * @retval OT_ERROR_INVALID_STATE  The DTLS service is not in state kStateOpen.
       * @retval OT_ERROR_ALREADY        Already bound.
       *
       */
      otError Bind(uint16_t aPort);
  
      /**
       * This method binds this DTLS with a transport callback.
       *
       * @param[in]  aCallback  A pointer to a function for sending messages.
       * @param[in]  aContext   A pointer to arbitrary context information.
       *
       * @retval OT_ERROR_NONE           Successfully bound the DTLS socket.
       * @retval OT_ERROR_INVALID_STATE  The DTLS service is not in state kStateOpen.
       * @retval OT_ERROR_ALREADY        Already bound.
       *
       */
      otError Bind(TransportCallback aCallback, void *aContext);
  
      /**
       * This method establishes a DTLS session.
       *
       * For CoAP Secure API do first:
       * Set X509 Pk and Cert for use DTLS mode ECDHE ECDSA with AES 128 CCM 8 or
       * set PreShared Key for use DTLS mode PSK with AES 128 CCM 8.
       *
       * @param[in]  aSockAddr               A reference to the remote sockaddr.
       *
       * @retval OT_ERROR_NONE           Successfully started DTLS handshake.
       * @retval OT_ERROR_INVALID_STATE  The DTLS service is not in state kStateOpen.
       *
       */
      otError Connect(const Ip6::SockAddr &aSockAddr);
  
      /**
       * This method indicates whether or not the DTLS session is active.
       *
       * In other words, the state is kStateConnecting, kStateConnected, or kStateCloseNotify.
       *
       * @retval TRUE  If DTLS session is active.
       * @retval FALSE If DTLS session is not active.
       *
       */
      bool IsConnectionActive(void) const { return mState >= kStateConnecting; }
  
      /**
       * This method indicates whether or not the DTLS session is connected.
       *
       * In other words, the state is kStateConnected.
       *
       * @retval TRUE   The DTLS session is connected.
       * @retval FALSE  The DTLS session is not connected.
       *
       */
      bool IsConnected(void) const { return mState == kStateConnected; }
  
      /**
       * This method disconnects the DTLS session.
       *
       */
      void Disconnect(void);
  
      /**
       * This method closes the DTLS socket.
       *
       */
      void Close(void);
  
      /**
       * This method returns the DTLS connection state.
       *
       * @retval kStateClosed       The UDP socket closed.
       * @retval kStateOpen         The UDP socket is open.
       * @retval kStateConnecting   The DTLS service is establishing a connection.
       * @retval kStateConnected    The DTLS service has a connection established.
       * @retval kStateCloseNotify  The DTLS service is closing a connection.
       *
       */
      State GetState(void) const { return mState; }
  
      /**
       * This method sets the PSK.
       *
       * @param[in]  aPsk  A pointer to the PSK.
       *
       * @retval OT_ERROR_NONE          Successfully set the PSK.
       * @retval OT_ERROR_INVALID_ARGS  The PSK is invalid.
       *
       */
      otError SetPsk(const uint8_t *aPsk, uint8_t aPskLength);
  
  #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
  #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
      /**
       * This method sets the Pre-Shared Key (PSK) for DTLS sessions-
       * identified by a PSK.
       *
       * DTLS mode "PSK with AES 128 CCM 8" for Application CoAPS.
       *
       * @param[in]  aPsk          A pointer to the PSK.
       * @param[in]  aPskLength    The PSK char length.
       * @param[in]  aPskIdentity  The Identity Name for the PSK.
       * @param[in]  aPskIdLength  The PSK Identity Length.
       *
       * @retval OT_ERROR_NONE  Successfully set the PSK.
       *
       */
      void SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength);
  
  #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
  
  #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
      /**
       * This method sets a reference to the own x509 certificate with corresponding private key.
       *
       * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
       *
       * @param[in]  aX509Certificate   A pointer to the PEM formatted X509 certificate.
       * @param[in]  aX509CertLength    The length of certificate.
       * @param[in]  aPrivateKey        A pointer to the PEM formatted private key.
       * @param[in]  aPrivateKeyLength  The length of the private key.
       *
       */
      void SetCertificate(const uint8_t *aX509Certificate,
                          uint32_t       aX509CertLength,
                          const uint8_t *aPrivateKey,
                          uint32_t       aPrivateKeyLength);
  
      /**
       * This method sets the trusted top level CAs. It is needed for validate the
       * certificate of the peer.
       *
       * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
       *
       * @param[in]  aX509CaCertificateChain  A pointer to the PEM formatted X509 CA chain.
       * @param[in]  aX509CaCertChainLength   The length of chain.
       *
       */
      void SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength);
  #endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
  
  #ifdef MBEDTLS_BASE64_C
      /**
       * This method returns the peer x509 certificate base64 encoded.
       *
       * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
       *
       * @param[out]  aPeerCert        A pointer to the base64 encoded certificate buffer.
       * @param[out]  aCertLength      The length of the base64 encoded peer certificate.
       * @param[in]   aCertBufferSize  The buffer size of aPeerCert.
       *
       * @retval OT_ERROR_INVALID_STATE   Not connected yet.
       * @retval OT_ERROR_NONE            Successfully get the peer certificate.
       * @retval OT_ERROR_NO_BUFS         Can't allocate memory for certificate.
       *
       */
      otError GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize);
  #endif // MBEDTLS_BASE64_C
  
      /**
       * This method set the authentication mode for a dtls connection.
       *
       * Disable or enable the verification of peer certificate.
       * Must called before start.
       *
       * @param[in]  aVerifyPeerCertificate  true, if the peer certificate should verify.
       *
       */
      void SetSslAuthMode(bool aVerifyPeerCertificate);
  #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
  
  #ifdef MBEDTLS_SSL_SRV_C
      /**
       * This method sets the Client ID used for generating the Hello Cookie.
       *
       * @param[in]  aClientId  A pointer to the Client ID.
       * @param[in]  aLength    Number of bytes in the Client ID.
       *
       * @retval OT_ERROR_NONE  Successfully set the Client ID.
       *
       */
      otError SetClientId(const uint8_t *aClientId, uint8_t aLength);
  #endif
  
      /**
       * This method sends data within the DTLS session.
       *
       * @param[in]  aMessage  A message to send via DTLS.
       * @param[in]  aLength   Number of bytes in the data buffer.
       *
       * @retval OT_ERROR_NONE     Successfully sent the data via the DTLS session.
       * @retval OT_ERROR_NO_BUFS  A message is too long.
       *
       */
      otError Send(Message &aMessage, uint16_t aLength);
  
      /**
       * This method provides a received DTLS message to the DTLS object.
       *
       * @param[in]  aMessage  A reference to the message.
       *
       */
      void Receive(Message &aMessage);
  
      /**
       * This method sets the default message sub-type that will be used for all messages without defined
       * sub-type.
       *
       * @param[in]  aMessageSubType  The default message sub-type.
       *
       */
      void SetDefaultMessageSubType(uint8_t aMessageSubType) { mMessageDefaultSubType = aMessageSubType; }
  
      /**
       * This method returns the DTLS session's peer address.
       *
       * @return DTLS session's message info.
       *
       */
      const Ip6::MessageInfo &GetPeerAddress(void) const { return mPeerAddress; }
  
      void HandleUdpReceive(Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
  
  private:
      void    FreeMbedtls(void);
      otError Setup(bool aClient);
  
  #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
      /**
       * Set keys and/or certificates for dtls session dependent of used cipher suite.
       *
       * @retval mbedtls error, 0 if successfully.
       *
       */
      int SetApplicationCoapSecureKeys(void);
  #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
  
      static void HandleMbedtlsDebug(void *ctx, int level, const char *file, int line, const char *str);
  
      static int HandleMbedtlsGetTimer(void *aContext);
      int        HandleMbedtlsGetTimer(void);
  
      static void HandleMbedtlsSetTimer(void *aContext, uint32_t aIntermediate, uint32_t aFinish);
      void        HandleMbedtlsSetTimer(uint32_t aIntermediate, uint32_t aFinish);
  
      static int HandleMbedtlsReceive(void *aContext, unsigned char *aBuf, size_t aLength);
      int        HandleMbedtlsReceive(unsigned char *aBuf, size_t aLength);
  
      static int HandleMbedtlsTransmit(void *aContext, const unsigned char *aBuf, size_t aLength);
      int        HandleMbedtlsTransmit(const unsigned char *aBuf, size_t aLength);
  
      static int HandleMbedtlsExportKeys(void *               aContext,
                                         const unsigned char *aMasterSecret,
                                         const unsigned char *aKeyBlock,
                                         size_t               aMacLength,
                                         size_t               aKeyLength,
                                         size_t               aIvLength);
      int        HandleMbedtlsExportKeys(const unsigned char *aMasterSecret,
                                         const unsigned char *aKeyBlock,
                                         size_t               aMacLength,
                                         size_t               aKeyLength,
                                         size_t               aIvLength);
  
      static void HandleTimer(Timer &aTimer);
      void        HandleTimer(void);
  
      static void HandleUdpReceive(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo);
  
      void    HandleDtlsReceive(const uint8_t *aBuf, uint16_t aLength);
      otError HandleDtlsSend(const uint8_t *aBuf, uint16_t aLength, uint8_t aMessageSubType);
  
      static void HandleUdpTransmit(Tasklet &aTasklet);
      void        HandleUdpTransmit(void);
  
      void Process(void);
  
      State mState;
  
      int     mCipherSuites[2];
      uint8_t mPsk[kPskMaxLength];
      uint8_t mPskLength;
  
  #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
  #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
  
      const uint8_t *    mCaChainSrc;
      uint32_t           mCaChainLength;
      const uint8_t *    mOwnCertSrc;
      uint32_t           mOwnCertLength;
      const uint8_t *    mPrivateKeySrc;
      uint32_t           mPrivateKeyLength;
      mbedtls_x509_crt   mCaChain;
      mbedtls_x509_crt   mOwnCert;
      mbedtls_pk_context mPrivateKey;
  #endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
  
  #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
      const uint8_t *mPreSharedKey;
      const uint8_t *mPreSharedKeyIdentity;
      uint16_t       mPreSharedKeyLength;
      uint16_t       mPreSharedKeyIdLength;
  #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
  #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
  
      bool mVerifyPeerCertificate;
  
      mbedtls_ssl_context mSsl;
      mbedtls_ssl_config  mConf;
  
  #ifdef MBEDTLS_SSL_COOKIE_C
      mbedtls_ssl_cookie_ctx mCookieCtx;
  #endif
  
      TimerMilliContext mTimer;
  
      uint32_t mTimerIntermediate;
      bool     mTimerSet : 1;
  
      bool mLayerTwoSecurity : 1;
  
      Message *mReceiveMessage;
  
      ConnectedHandler mConnectedHandler;
      ReceiveHandler   mReceiveHandler;
      SendHandler      mSendHandler;
      void *           mContext;
  
      Ip6::MessageInfo mPeerAddress;
      Ip6::UdpSocket   mSocket;
  
      TransportCallback mTransportCallback;
      void *            mTransportContext;
  
      uint8_t mMessageSubType;
      uint8_t mMessageDefaultSubType;
  };
  
  } // namespace MeshCoP
  } // namespace ot
  
  #endif // DTLS_HPP_
  