1 /* 2 * Copyright (c) 2016, The OpenThread Authors. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 3. Neither the name of the copyright holder nor the 13 * names of its contributors may be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 17 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 20 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 26 * POSSIBILITY OF SUCH DAMAGE. 27 */ 28 29 #ifndef COAP_SECURE_HPP_ 30 #define COAP_SECURE_HPP_ 31 32 #include "openthread-core-config.h" 33 34 #if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE 35 36 #include "coap/coap.hpp" 37 #include "common/callback.hpp" 38 #include "meshcop/meshcop.hpp" 39 #include "meshcop/secure_transport.hpp" 40 41 #include <openthread/coap_secure.h> 42 43 /** 44 * @file 45 * This file includes definitions for the secure CoAP agent. 46 */ 47 48 namespace ot { 49 50 namespace Coap { 51 52 class CoapSecure : public CoapBase 53 { 54 public: 55 /** 56 * Function pointer which is called reporting a connection event (when connection established or disconnected) 57 * 58 */ 59 typedef otHandleCoapSecureClientConnect ConnectEventCallback; 60 61 /** 62 * Callback to notify when the agent is automatically stopped due to reaching the maximum number of connection 63 * attempts. 64 * 65 */ 66 typedef otCoapSecureAutoStopCallback AutoStopCallback; 67 68 /** 69 * Initializes the object. 70 * 71 * @param[in] aInstance A reference to the OpenThread instance. 72 * @param[in] aLayerTwoSecurity Specifies whether to use layer two security or not. 73 * 74 */ 75 explicit CoapSecure(Instance &aInstance, bool aLayerTwoSecurity = false); 76 77 /** 78 * Starts the secure CoAP agent. 79 * 80 * @param[in] aPort The local UDP port to bind to. 81 * 82 * @retval kErrorNone Successfully started the CoAP agent. 83 * @retval kErrorAlready Already started. 84 * 85 */ 86 Error Start(uint16_t aPort); 87 88 /** 89 * Starts the secure CoAP agent and sets the maximum number of allowed connection attempts before stopping the 90 * agent automatically. 91 * 92 * @param[in] aPort The local UDP port to bind to. 93 * @param[in] aMaxAttempts Maximum number of allowed connection request attempts. Zero indicates no limit. 94 * @param[in] aCallback Callback to notify if max number of attempts has reached and agent is stopped. 95 * @param[in] aContext A pointer to arbitrary context to use with `AutoStopCallback`. 96 * 97 * @retval kErrorNone Successfully started the CoAP agent. 98 * @retval kErrorAlready Already started. 99 * 100 */ 101 Error Start(uint16_t aPort, uint16_t aMaxAttempts, AutoStopCallback aCallback, void *aContext); 102 103 /** 104 * Starts the secure CoAP agent, but do not use socket to transmit/receive messages. 105 * 106 * @param[in] aCallback A pointer to a function for sending messages. 107 * @param[in] aContext A pointer to arbitrary context information. 108 * 109 * @retval kErrorNone Successfully started the CoAP agent. 110 * @retval kErrorAlready Already started. 111 * 112 */ 113 Error Start(MeshCoP::SecureTransport::TransportCallback aCallback, void *aContext); 114 115 /** 116 * Sets connected callback of this secure CoAP agent. 117 * 118 * @param[in] aCallback A pointer to a function to get called when connection state changes. 119 * @param[in] aContext A pointer to arbitrary context information. 120 * 121 */ SetConnectEventCallback(ConnectEventCallback aCallback,void * aContext)122 void SetConnectEventCallback(ConnectEventCallback aCallback, void *aContext) 123 { 124 mConnectEventCallback.Set(aCallback, aContext); 125 } 126 127 /** 128 * Stops the secure CoAP agent. 129 * 130 */ 131 void Stop(void); 132 133 /** 134 * Initializes DTLS session with a peer. 135 * 136 * @param[in] aSockAddr A reference to the remote socket address, 137 * @param[in] aCallback A pointer to a function that will be called once DTLS connection is 138 * established. 139 * 140 * @retval kErrorNone Successfully started DTLS connection. 141 * 142 */ 143 Error Connect(const Ip6::SockAddr &aSockAddr, ConnectEventCallback aCallback, void *aContext); 144 145 /** 146 * Indicates whether or not the DTLS session is active. 147 * 148 * @retval TRUE If DTLS session is active. 149 * @retval FALSE If DTLS session is not active. 150 * 151 */ IsConnectionActive(void) const152 bool IsConnectionActive(void) const { return mDtls.IsConnectionActive(); } 153 154 /** 155 * Indicates whether or not the DTLS session is connected. 156 * 157 * @retval TRUE The DTLS session is connected. 158 * @retval FALSE The DTLS session is not connected. 159 * 160 */ IsConnected(void) const161 bool IsConnected(void) const { return mDtls.IsConnected(); } 162 163 /** 164 * Indicates whether or not the DTLS session is closed. 165 * 166 * @retval TRUE The DTLS session is closed 167 * @retval FALSE The DTLS session is not closed. 168 * 169 */ IsClosed(void) const170 bool IsClosed(void) const { return mDtls.IsClosed(); } 171 172 /** 173 * Stops the DTLS connection. 174 * 175 */ Disconnect(void)176 void Disconnect(void) { mDtls.Disconnect(); } 177 178 /** 179 * Returns a reference to the DTLS object. 180 * 181 * @returns A reference to the DTLS object. 182 * 183 */ GetDtls(void)184 MeshCoP::SecureTransport &GetDtls(void) { return mDtls; } 185 186 /** 187 * Gets the UDP port of this agent. 188 * 189 * @returns UDP port number. 190 * 191 */ GetUdpPort(void) const192 uint16_t GetUdpPort(void) const { return mDtls.GetUdpPort(); } 193 194 /** 195 * Sets the PSK. 196 * 197 * @param[in] aPsk A pointer to the PSK. 198 * @param[in] aPskLength The PSK length. 199 * 200 * @retval kErrorNone Successfully set the PSK. 201 * @retval kErrorInvalidArgs The PSK is invalid. 202 * 203 */ SetPsk(const uint8_t * aPsk,uint8_t aPskLength)204 Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength) { return mDtls.SetPsk(aPsk, aPskLength); } 205 206 /** 207 * Sets the PSK. 208 * 209 * @param[in] aPskd A Joiner PSKd. 210 * 211 */ 212 void SetPsk(const MeshCoP::JoinerPskd &aPskd); 213 214 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 215 216 #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED 217 /** 218 * Sets the Pre-Shared Key (PSK) for DTLS sessions identified by a PSK. 219 * 220 * DTLS mode "TLS with AES 128 CCM 8" for Application CoAPS. 221 * 222 * @param[in] aPsk A pointer to the PSK. 223 * @param[in] aPskLength The PSK char length. 224 * @param[in] aPskIdentity The Identity Name for the PSK. 225 * @param[in] aPskIdLength The PSK Identity Length. 226 * 227 */ SetPreSharedKey(const uint8_t * aPsk,uint16_t aPskLength,const uint8_t * aPskIdentity,uint16_t aPskIdLength)228 void SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength) 229 { 230 mDtls.SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength); 231 } 232 #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED 233 234 #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED 235 /** 236 * Sets a X509 certificate with corresponding private key for DTLS session. 237 * 238 * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS. 239 * 240 * @param[in] aX509Cert A pointer to the PEM formatted X509 PEM certificate. 241 * @param[in] aX509Length The length of certificate. 242 * @param[in] aPrivateKey A pointer to the PEM formatted private key. 243 * @param[in] aPrivateKeyLength The length of the private key. 244 * 245 */ SetCertificate(const uint8_t * aX509Cert,uint32_t aX509Length,const uint8_t * aPrivateKey,uint32_t aPrivateKeyLength)246 void SetCertificate(const uint8_t *aX509Cert, 247 uint32_t aX509Length, 248 const uint8_t *aPrivateKey, 249 uint32_t aPrivateKeyLength) 250 { 251 mDtls.SetCertificate(aX509Cert, aX509Length, aPrivateKey, aPrivateKeyLength); 252 } 253 254 /** 255 * Sets the trusted top level CAs. It is needed for validate the certificate of the peer. 256 * 257 * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS. 258 * 259 * @param[in] aX509CaCertificateChain A pointer to the PEM formatted X509 CA chain. 260 * @param[in] aX509CaCertChainLength The length of chain. 261 * 262 */ SetCaCertificateChain(const uint8_t * aX509CaCertificateChain,uint32_t aX509CaCertChainLength)263 void SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength) 264 { 265 mDtls.SetCaCertificateChain(aX509CaCertificateChain, aX509CaCertChainLength); 266 } 267 #endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED 268 269 #if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) 270 /** 271 * Returns the peer x509 certificate base64 encoded. 272 * 273 * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS. 274 * 275 * @param[out] aPeerCert A pointer to the base64 encoded certificate buffer. 276 * @param[out] aCertLength The length of the base64 encoded peer certificate. 277 * @param[in] aCertBufferSize The buffer size of aPeerCert. 278 * 279 * @retval kErrorNone Successfully get the peer certificate. 280 * @retval kErrorNoBufs Can't allocate memory for certificate. 281 * 282 */ GetPeerCertificateBase64(unsigned char * aPeerCert,size_t * aCertLength,size_t aCertBufferSize)283 Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize) 284 { 285 return mDtls.GetPeerCertificateBase64(aPeerCert, aCertLength, aCertBufferSize); 286 } 287 #endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE) 288 289 /** 290 * Sets the authentication mode for the CoAP secure connection. It disables or enables the verification 291 * of peer certificate. 292 * 293 * @param[in] aVerifyPeerCertificate true, if the peer certificate should be verified 294 * 295 */ SetSslAuthMode(bool aVerifyPeerCertificate)296 void SetSslAuthMode(bool aVerifyPeerCertificate) { mDtls.SetSslAuthMode(aVerifyPeerCertificate); } 297 298 #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE 299 300 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE 301 /** 302 * Sends a CoAP message over secure DTLS connection. 303 * 304 * If a response for a request is expected, respective function and context information should be provided. 305 * If no response is expected, these arguments should be NULL pointers. 306 * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. 307 * 308 * @param[in] aMessage A reference to the message to send. 309 * @param[in] aHandler A function pointer that shall be called on response reception or time-out. 310 * @param[in] aContext A pointer to arbitrary context information. 311 * @param[in] aTransmitHook A pointer to a hook function for outgoing block-wise transfer. 312 * @param[in] aReceiveHook A pointer to a hook function for incoming block-wise transfer. 313 * 314 * @retval kErrorNone Successfully sent CoAP message. 315 * @retval kErrorNoBufs Failed to allocate retransmission data. 316 * @retval kErrorInvalidState DTLS connection was not initialized. 317 * 318 */ 319 Error SendMessage(Message &aMessage, 320 ResponseHandler aHandler = nullptr, 321 void *aContext = nullptr, 322 otCoapBlockwiseTransmitHook aTransmitHook = nullptr, 323 otCoapBlockwiseReceiveHook aReceiveHook = nullptr); 324 325 /** 326 * Sends a CoAP message over secure DTLS connection. 327 * 328 * If a response for a request is expected, respective function and context information should be provided. 329 * If no response is expected, these arguments should be NULL pointers. 330 * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. 331 * 332 * @param[in] aMessage A reference to the message to send. 333 * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. 334 * @param[in] aHandler A function pointer that shall be called on response reception or time-out. 335 * @param[in] aContext A pointer to arbitrary context information. 336 * @param[in] aTransmitHook A pointer to a hook function for outgoing block-wise transfer. 337 * @param[in] aReceiveHook A pointer to a hook function for incoming block-wise transfer. 338 * 339 * @retval kErrorNone Successfully sent CoAP message. 340 * @retval kErrorNoBufs Failed to allocate retransmission data. 341 * @retval kErrorInvalidState DTLS connection was not initialized. 342 * 343 */ 344 Error SendMessage(Message &aMessage, 345 const Ip6::MessageInfo &aMessageInfo, 346 ResponseHandler aHandler = nullptr, 347 void *aContext = nullptr, 348 otCoapBlockwiseTransmitHook aTransmitHook = nullptr, 349 otCoapBlockwiseReceiveHook aReceiveHook = nullptr); 350 #else // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE 351 /** 352 * Sends a CoAP message over secure DTLS connection. 353 * 354 * If a response for a request is expected, respective function and context information should be provided. 355 * If no response is expected, these arguments should be nullptr pointers. 356 * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. 357 * 358 * @param[in] aMessage A reference to the message to send. 359 * @param[in] aHandler A function pointer that shall be called on response reception or time-out. 360 * @param[in] aContext A pointer to arbitrary context information. 361 * 362 * @retval kErrorNone Successfully sent CoAP message. 363 * @retval kErrorNoBufs Failed to allocate retransmission data. 364 * @retval kErrorInvalidState DTLS connection was not initialized. 365 * 366 */ 367 Error SendMessage(Message &aMessage, ResponseHandler aHandler = nullptr, void *aContext = nullptr); 368 369 /** 370 * Sends a CoAP message over secure DTLS connection. 371 * 372 * If a response for a request is expected, respective function and context information should be provided. 373 * If no response is expected, these arguments should be nullptr pointers. 374 * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message. 375 * 376 * @param[in] aMessage A reference to the message to send. 377 * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. 378 * @param[in] aHandler A function pointer that shall be called on response reception or time-out. 379 * @param[in] aContext A pointer to arbitrary context information. 380 * 381 * @retval kErrorNone Successfully sent CoAP message. 382 * @retval kErrorNoBufs Failed to allocate retransmission data. 383 * @retval kErrorInvalidState DTLS connection was not initialized. 384 * 385 */ 386 Error SendMessage(Message &aMessage, 387 const Ip6::MessageInfo &aMessageInfo, 388 ResponseHandler aHandler = nullptr, 389 void *aContext = nullptr); 390 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE 391 392 /** 393 * Is used to pass UDP messages to the secure CoAP server. 394 * 395 * @param[in] aMessage A reference to the received message. 396 * @param[in] aMessageInfo A reference to the message info associated with @p aMessage. 397 * 398 */ HandleUdpReceive(ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)399 void HandleUdpReceive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) 400 { 401 return mDtls.HandleReceive(aMessage, aMessageInfo); 402 } 403 404 /** 405 * Returns the DTLS session's peer address. 406 * 407 * @return DTLS session's message info. 408 * 409 */ GetMessageInfo(void) const410 const Ip6::MessageInfo &GetMessageInfo(void) const { return mDtls.GetMessageInfo(); } 411 412 private: 413 Error Open(uint16_t aMaxAttempts, AutoStopCallback aCallback, void *aContext); 414 Send(CoapBase & aCoapBase,ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)415 static Error Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo) 416 { 417 return static_cast<CoapSecure &>(aCoapBase).Send(aMessage, aMessageInfo); 418 } 419 Error Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo); 420 421 static void HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext); 422 void HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent); 423 424 static void HandleDtlsAutoClose(void *aContext); 425 void HandleDtlsAutoClose(void); 426 427 static void HandleDtlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength); 428 void HandleDtlsReceive(uint8_t *aBuf, uint16_t aLength); 429 430 static void HandleTransmit(Tasklet &aTasklet); 431 void HandleTransmit(void); 432 433 MeshCoP::SecureTransport mDtls; 434 Callback<ConnectEventCallback> mConnectEventCallback; 435 Callback<AutoStopCallback> mAutoStopCallback; 436 ot::MessageQueue mTransmitQueue; 437 TaskletContext mTransmitTask; 438 }; 439 440 } // namespace Coap 441 } // namespace ot 442 443 #endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE 444 445 #endif // COAP_SECURE_HPP_ 446