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_DTLS_ENABLE
35 
36 #include "coap/coap.hpp"
37 #include "meshcop/dtls.hpp"
38 #include "meshcop/meshcop.hpp"
39 
40 #include <openthread/coap_secure.h>
41 
42 /**
43  * @file
44  *   This file includes definitions for the secure CoAP agent.
45  */
46 
47 namespace ot {
48 
49 namespace Coap {
50 
51 class CoapSecure : public CoapBase
52 {
53 public:
54     /**
55      * This function pointer is called once DTLS connection is established.
56      *
57      * @param[in]  aConnected  TRUE if a connection was established, FALSE otherwise.
58      * @param[in]  aContext    A pointer to arbitrary context information.
59      *
60      */
61     typedef void (*ConnectedCallback)(bool aConnected, void *aContext);
62 
63     /**
64      * This constructor initializes the object.
65      *
66      * @param[in]  aInstance           A reference to the OpenThread instance.
67      * @param[in]  aLayerTwoSecurity   Specifies whether to use layer two security or not.
68      *
69      */
70     explicit CoapSecure(Instance &aInstance, bool aLayerTwoSecurity = false);
71 
72     /**
73      * This method starts the secure CoAP agent.
74      *
75      * @param[in]  aPort      The local UDP port to bind to.
76      *
77      * @retval kErrorNone        Successfully started the CoAP agent.
78      * @retval kErrorAlready     Already started.
79      *
80      */
81     Error Start(uint16_t aPort);
82 
83     /**
84      * This method starts the secure CoAP agent, but do not use socket to transmit/receive messages.
85      *
86      * @param[in]  aCallback  A pointer to a function for sending messages.
87      * @param[in]  aContext   A pointer to arbitrary context information.
88      *
89      * @retval kErrorNone        Successfully started the CoAP agent.
90      * @retval kErrorAlready     Already started.
91      *
92      */
93     Error Start(MeshCoP::Dtls::TransportCallback aCallback, void *aContext);
94 
95     /**
96      * This method sets connected callback of this secure CoAP agent.
97      *
98      * @param[in]  aCallback  A pointer to a function to get called when connection state changes.
99      * @param[in]  aContext   A pointer to arbitrary context information.
100      *
101      */
SetConnectedCallback(ConnectedCallback aCallback,void * aContext)102     void SetConnectedCallback(ConnectedCallback aCallback, void *aContext)
103     {
104         mConnectedCallback = aCallback;
105         mConnectedContext  = aContext;
106     }
107 
108     /**
109      * This method stops the secure CoAP agent.
110      *
111      */
112     void Stop(void);
113 
114     /**
115      * This method initializes DTLS session with a peer.
116      *
117      * @param[in]  aSockAddr               A reference to the remote socket address,
118      * @param[in]  aCallback               A pointer to a function that will be called once DTLS connection is
119      * established.
120      *
121      * @retval kErrorNone  Successfully started DTLS connection.
122      *
123      */
124     Error Connect(const Ip6::SockAddr &aSockAddr, ConnectedCallback aCallback, void *aContext);
125 
126     /**
127      * This method indicates whether or not the DTLS session is active.
128      *
129      * @retval TRUE  If DTLS session is active.
130      * @retval FALSE If DTLS session is not active.
131      *
132      */
IsConnectionActive(void) const133     bool IsConnectionActive(void) const { return mDtls.IsConnectionActive(); }
134 
135     /**
136      * This method indicates whether or not the DTLS session is connected.
137      *
138      * @retval TRUE   The DTLS session is connected.
139      * @retval FALSE  The DTLS session is not connected.
140      *
141      */
IsConnected(void) const142     bool IsConnected(void) const { return mDtls.IsConnected(); }
143 
144     /**
145      * This method stops the DTLS connection.
146      *
147      */
Disconnect(void)148     void Disconnect(void) { mDtls.Disconnect(); }
149 
150     /**
151      * This method returns a reference to the DTLS object.
152      *
153      * @returns  A reference to the DTLS object.
154      *
155      */
GetDtls(void)156     MeshCoP::Dtls &GetDtls(void) { return mDtls; }
157 
158     /**
159      * This method gets the UDP port of this agent.
160      *
161      * @returns  UDP port number.
162      *
163      */
GetUdpPort(void) const164     uint16_t GetUdpPort(void) const { return mDtls.GetUdpPort(); }
165 
166     /**
167      * This method sets the PSK.
168      *
169      * @param[in]  aPsk        A pointer to the PSK.
170      * @param[in]  aPskLength  The PSK length.
171      *
172      * @retval kErrorNone         Successfully set the PSK.
173      * @retval kErrorInvalidArgs  The PSK is invalid.
174      *
175      */
SetPsk(const uint8_t * aPsk,uint8_t aPskLength)176     Error SetPsk(const uint8_t *aPsk, uint8_t aPskLength) { return mDtls.SetPsk(aPsk, aPskLength); }
177 
178     /**
179      * This method sets the PSK.
180      *
181      * @param[in]  aPskd  A Joiner PSKd.
182      *
183      */
184     void SetPsk(const MeshCoP::JoinerPskd &aPskd);
185 
186 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
187 
188 #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
189     /**
190      * This method sets the Pre-Shared Key (PSK) for DTLS sessions identified by a PSK.
191      *
192      * DTLS mode "TLS with AES 128 CCM 8" for Application CoAPS.
193      *
194      * @param[in]  aPsk          A pointer to the PSK.
195      * @param[in]  aPskLength    The PSK char length.
196      * @param[in]  aPskIdentity  The Identity Name for the PSK.
197      * @param[in]  aPskIdLength  The PSK Identity Length.
198      *
199      */
SetPreSharedKey(const uint8_t * aPsk,uint16_t aPskLength,const uint8_t * aPskIdentity,uint16_t aPskIdLength)200     void SetPreSharedKey(const uint8_t *aPsk, uint16_t aPskLength, const uint8_t *aPskIdentity, uint16_t aPskIdLength)
201     {
202         mDtls.SetPreSharedKey(aPsk, aPskLength, aPskIdentity, aPskIdLength);
203     }
204 #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
205 
206 #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
207     /**
208      * This method sets a X509 certificate with corresponding private key for DTLS session.
209      *
210      * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
211      *
212      * @param[in]  aX509Certificate   A pointer to the PEM formatted X509 PEM certificate.
213      * @param[in]  aX509CertLength    The length of certificate.
214      * @param[in]  aPrivateKey        A pointer to the PEM formatted private key.
215      * @param[in]  aPrivateKeyLength  The length of the private key.
216      *
217      */
SetCertificate(const uint8_t * aX509Cert,uint32_t aX509Length,const uint8_t * aPrivateKey,uint32_t aPrivateKeyLength)218     void SetCertificate(const uint8_t *aX509Cert,
219                         uint32_t       aX509Length,
220                         const uint8_t *aPrivateKey,
221                         uint32_t       aPrivateKeyLength)
222     {
223         mDtls.SetCertificate(aX509Cert, aX509Length, aPrivateKey, aPrivateKeyLength);
224     }
225 
226     /**
227      * This method sets the trusted top level CAs. It is needed for validate the certificate of the peer.
228      *
229      * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
230      *
231      * @param[in]  aX509CaCertificateChain  A pointer to the PEM formatted X509 CA chain.
232      * @param[in]  aX509CaCertChainLength   The length of chain.
233      *
234      */
SetCaCertificateChain(const uint8_t * aX509CaCertificateChain,uint32_t aX509CaCertChainLength)235     void SetCaCertificateChain(const uint8_t *aX509CaCertificateChain, uint32_t aX509CaCertChainLength)
236     {
237         mDtls.SetCaCertificateChain(aX509CaCertificateChain, aX509CaCertChainLength);
238     }
239 #endif // MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
240 
241 #if defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
242     /**
243      * This method returns the peer x509 certificate base64 encoded.
244      *
245      * DTLS mode "ECDHE ECDSA with AES 128 CCM 8" for Application CoAPS.
246      *
247      * @param[out]  aPeerCert        A pointer to the base64 encoded certificate buffer.
248      * @param[out]  aCertLength      The length of the base64 encoded peer certificate.
249      * @param[in]   aCertBufferSize  The buffer size of aPeerCert.
250      *
251      * @retval kErrorNone    Successfully get the peer certificate.
252      * @retval kErrorNoBufs  Can't allocate memory for certificate.
253      *
254      */
GetPeerCertificateBase64(unsigned char * aPeerCert,size_t * aCertLength,size_t aCertBufferSize)255     Error GetPeerCertificateBase64(unsigned char *aPeerCert, size_t *aCertLength, size_t aCertBufferSize)
256     {
257         return mDtls.GetPeerCertificateBase64(aPeerCert, aCertLength, aCertBufferSize);
258     }
259 #endif // defined(MBEDTLS_BASE64_C) && defined(MBEDTLS_SSL_KEEP_PEER_CERTIFICATE)
260 
261     /**
262      * This method sets the connected callback to indicate, when a Client connect to the CoAP Secure server.
263      *
264      * @param[in]  aCallback     A pointer to a function that will be called once DTLS connection is established.
265      * @param[in]  aContext      A pointer to arbitrary context information.
266      *
267      */
SetClientConnectedCallback(ConnectedCallback aCallback,void * aContext)268     void SetClientConnectedCallback(ConnectedCallback aCallback, void *aContext)
269     {
270         mConnectedCallback = aCallback;
271         mConnectedContext  = aContext;
272     }
273 
274     /**
275      * This method sets the authentication mode for the CoAP secure connection. It disables or enables the verification
276      * of peer certificate.
277      *
278      * @param[in]  aVerifyPeerCertificate  true, if the peer certificate should be verified
279      *
280      */
SetSslAuthMode(bool aVerifyPeerCertificate)281     void SetSslAuthMode(bool aVerifyPeerCertificate) { mDtls.SetSslAuthMode(aVerifyPeerCertificate); }
282 
283 #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
284 
285 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
286     /**
287      * This method sends a CoAP message over secure DTLS connection.
288      *
289      * If a response for a request is expected, respective function and context information should be provided.
290      * If no response is expected, these arguments should be NULL pointers.
291      * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.
292      *
293      * @param[in]  aMessage      A reference to the message to send.
294      * @param[in]  aHandler      A function pointer that shall be called on response reception or time-out.
295      * @param[in]  aContext      A pointer to arbitrary context information.
296      * @param[in]  aTransmitHook A pointer to a hook function for outgoing block-wise transfer.
297      * @param[in]  aReceiveHook  A pointer to a hook function for incoming block-wise transfer.
298      *
299      * @retval kErrorNone          Successfully sent CoAP message.
300      * @retval kErrorNoBufs        Failed to allocate retransmission data.
301      * @retval kErrorInvalidState  DTLS connection was not initialized.
302      *
303      */
304     Error SendMessage(Message &                   aMessage,
305                       ResponseHandler             aHandler      = nullptr,
306                       void *                      aContext      = nullptr,
307                       otCoapBlockwiseTransmitHook aTransmitHook = nullptr,
308                       otCoapBlockwiseReceiveHook  aReceiveHook  = nullptr);
309 
310     /**
311      * This method sends a CoAP message over secure DTLS connection.
312      *
313      * If a response for a request is expected, respective function and context information should be provided.
314      * If no response is expected, these arguments should be NULL pointers.
315      * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.
316      *
317      * @param[in]  aMessage      A reference to the message to send.
318      * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
319      * @param[in]  aHandler      A function pointer that shall be called on response reception or time-out.
320      * @param[in]  aContext      A pointer to arbitrary context information.
321      * @param[in]  aTransmitHook A pointer to a hook function for outgoing block-wise transfer.
322      * @param[in]  aReceiveHook  A pointer to a hook function for incoming block-wise transfer.
323      *
324      * @retval kErrorNone          Successfully sent CoAP message.
325      * @retval kErrorNoBufs        Failed to allocate retransmission data.
326      * @retval kErrorInvalidState  DTLS connection was not initialized.
327      *
328      */
329     Error SendMessage(Message &                   aMessage,
330                       const Ip6::MessageInfo &    aMessageInfo,
331                       ResponseHandler             aHandler      = nullptr,
332                       void *                      aContext      = nullptr,
333                       otCoapBlockwiseTransmitHook aTransmitHook = nullptr,
334                       otCoapBlockwiseReceiveHook  aReceiveHook  = nullptr);
335 #else  // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
336     /**
337      * This method sends a CoAP message over secure DTLS connection.
338      *
339      * If a response for a request is expected, respective function and context information should be provided.
340      * If no response is expected, these arguments should be nullptr pointers.
341      * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.
342      *
343      * @param[in]  aMessage      A reference to the message to send.
344      * @param[in]  aHandler      A function pointer that shall be called on response reception or time-out.
345      * @param[in]  aContext      A pointer to arbitrary context information.
346      *
347      * @retval kErrorNone          Successfully sent CoAP message.
348      * @retval kErrorNoBufs        Failed to allocate retransmission data.
349      * @retval kErrorInvalidState  DTLS connection was not initialized.
350      *
351      */
352     Error SendMessage(Message &aMessage, ResponseHandler aHandler = nullptr, void *aContext = nullptr);
353 
354     /**
355      * This method sends a CoAP message over secure DTLS connection.
356      *
357      * If a response for a request is expected, respective function and context information should be provided.
358      * If no response is expected, these arguments should be nullptr pointers.
359      * If Message Id was not set in the header (equal to 0), this function will assign unique Message Id to the message.
360      *
361      * @param[in]  aMessage      A reference to the message to send.
362      * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
363      * @param[in]  aHandler      A function pointer that shall be called on response reception or time-out.
364      * @param[in]  aContext      A pointer to arbitrary context information.
365      *
366      * @retval kErrorNone          Successfully sent CoAP message.
367      * @retval kErrorNoBufs        Failed to allocate retransmission data.
368      * @retval kErrorInvalidState  DTLS connection was not initialized.
369      *
370      */
371     Error SendMessage(Message &               aMessage,
372                       const Ip6::MessageInfo &aMessageInfo,
373                       ResponseHandler         aHandler = nullptr,
374                       void *                  aContext = nullptr);
375 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
376 
377     /**
378      * This method is used to pass UDP messages to the secure CoAP server.
379      *
380      * @param[in]  aMessage      A reference to the received message.
381      * @param[in]  aMessageInfo  A reference to the message info associated with @p aMessage.
382      *
383      */
HandleUdpReceive(ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)384     void HandleUdpReceive(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
385     {
386         return mDtls.HandleUdpReceive(aMessage, aMessageInfo);
387     }
388 
389     /**
390      * This method returns the DTLS session's peer address.
391      *
392      * @return DTLS session's message info.
393      *
394      */
GetMessageInfo(void) const395     const Ip6::MessageInfo &GetMessageInfo(void) const { return mDtls.GetMessageInfo(); }
396 
397 private:
Send(CoapBase & aCoapBase,ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)398     static Error Send(CoapBase &aCoapBase, ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
399     {
400         return static_cast<CoapSecure &>(aCoapBase).Send(aMessage, aMessageInfo);
401     }
402     Error Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo);
403 
404     static void HandleDtlsConnected(void *aContext, bool aConnected);
405     void        HandleDtlsConnected(bool aConnected);
406 
407     static void HandleDtlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength);
408     void        HandleDtlsReceive(uint8_t *aBuf, uint16_t aLength);
409 
410     static void HandleTransmit(Tasklet &aTasklet);
411     void        HandleTransmit(void);
412 
413     MeshCoP::Dtls     mDtls;
414     ConnectedCallback mConnectedCallback;
415     void *            mConnectedContext;
416     ot::MessageQueue  mTransmitQueue;
417     TaskletContext    mTransmitTask;
418 };
419 
420 } // namespace Coap
421 } // namespace ot
422 
423 #endif // OPENTHREAD_CONFIG_DTLS_ENABLE
424 
425 #endif // COAP_SECURE_HPP_
426