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 #include "coap_secure.hpp"
30 
31 #if OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
32 
33 #include "common/locator_getters.hpp"
34 #include "common/log.hpp"
35 #include "common/new.hpp"
36 #include "instance/instance.hpp"
37 #include "meshcop/secure_transport.hpp"
38 
39 #include "thread/thread_netif.hpp"
40 
41 /**
42  * @file
43  *   This file implements the secure CoAP agent.
44  */
45 
46 namespace ot {
47 namespace Coap {
48 
49 RegisterLogModule("CoapSecure");
50 
CoapSecure(Instance & aInstance,bool aLayerTwoSecurity)51 CoapSecure::CoapSecure(Instance &aInstance, bool aLayerTwoSecurity)
52     : CoapBase(aInstance, &CoapSecure::Send)
53     , mDtls(aInstance, aLayerTwoSecurity)
54     , mTransmitTask(aInstance, CoapSecure::HandleTransmit, this)
55 {
56 }
57 
Start(uint16_t aPort)58 Error CoapSecure::Start(uint16_t aPort) { return Start(aPort, /* aMaxAttempts */ 0, nullptr, nullptr); }
59 
Start(uint16_t aPort,uint16_t aMaxAttempts,AutoStopCallback aCallback,void * aContext)60 Error CoapSecure::Start(uint16_t aPort, uint16_t aMaxAttempts, AutoStopCallback aCallback, void *aContext)
61 {
62     Error error;
63 
64     SuccessOrExit(error = Open(aMaxAttempts, aCallback, aContext));
65     error = mDtls.Bind(aPort);
66 
67 exit:
68     return error;
69 }
70 
Start(MeshCoP::SecureTransport::TransportCallback aCallback,void * aContext)71 Error CoapSecure::Start(MeshCoP::SecureTransport::TransportCallback aCallback, void *aContext)
72 {
73     Error error;
74 
75     SuccessOrExit(error = Open(/* aMaxAttemps */ 0, nullptr, nullptr));
76     error = mDtls.Bind(aCallback, aContext);
77 
78 exit:
79     return error;
80 }
81 
Open(uint16_t aMaxAttempts,AutoStopCallback aCallback,void * aContext)82 Error CoapSecure::Open(uint16_t aMaxAttempts, AutoStopCallback aCallback, void *aContext)
83 {
84     Error error = kErrorAlready;
85 
86     SuccessOrExit(mDtls.SetMaxConnectionAttempts(aMaxAttempts, HandleDtlsAutoClose, this));
87     mAutoStopCallback.Set(aCallback, aContext);
88     mConnectEventCallback.Clear();
89     SuccessOrExit(mDtls.Open(HandleDtlsReceive, HandleDtlsConnectEvent, this));
90 
91     error = kErrorNone;
92 
93 exit:
94     return error;
95 }
96 
Stop(void)97 void CoapSecure::Stop(void)
98 {
99     mDtls.Close();
100 
101     mTransmitQueue.DequeueAndFreeAll();
102     ClearRequestsAndResponses();
103 }
104 
Connect(const Ip6::SockAddr & aSockAddr,ConnectEventCallback aCallback,void * aContext)105 Error CoapSecure::Connect(const Ip6::SockAddr &aSockAddr, ConnectEventCallback aCallback, void *aContext)
106 {
107     mConnectEventCallback.Set(aCallback, aContext);
108 
109     return mDtls.Connect(aSockAddr);
110 }
111 
SetPsk(const MeshCoP::JoinerPskd & aPskd)112 void CoapSecure::SetPsk(const MeshCoP::JoinerPskd &aPskd)
113 {
114     static_assert(static_cast<uint16_t>(MeshCoP::JoinerPskd::kMaxLength) <=
115                       static_cast<uint16_t>(MeshCoP::SecureTransport::kPskMaxLength),
116                   "The maximum length of DTLS PSK is smaller than joiner PSKd");
117 
118     SuccessOrAssert(mDtls.SetPsk(reinterpret_cast<const uint8_t *>(aPskd.GetAsCString()), aPskd.GetLength()));
119 }
120 
121 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
SendMessage(Message & aMessage,ResponseHandler aHandler,void * aContext,otCoapBlockwiseTransmitHook aTransmitHook,otCoapBlockwiseReceiveHook aReceiveHook)122 Error CoapSecure::SendMessage(Message                    &aMessage,
123                               ResponseHandler             aHandler,
124                               void                       *aContext,
125                               otCoapBlockwiseTransmitHook aTransmitHook,
126                               otCoapBlockwiseReceiveHook  aReceiveHook)
127 {
128     Error error = kErrorNone;
129 
130     VerifyOrExit(IsConnected(), error = kErrorInvalidState);
131 
132     error = CoapBase::SendMessage(aMessage, mDtls.GetMessageInfo(), TxParameters::GetDefault(), aHandler, aContext,
133                                   aTransmitHook, aReceiveHook);
134 
135 exit:
136     return error;
137 }
138 
SendMessage(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,ResponseHandler aHandler,void * aContext,otCoapBlockwiseTransmitHook aTransmitHook,otCoapBlockwiseReceiveHook aReceiveHook)139 Error CoapSecure::SendMessage(Message                    &aMessage,
140                               const Ip6::MessageInfo     &aMessageInfo,
141                               ResponseHandler             aHandler,
142                               void                       *aContext,
143                               otCoapBlockwiseTransmitHook aTransmitHook,
144                               otCoapBlockwiseReceiveHook  aReceiveHook)
145 {
146     return CoapBase::SendMessage(aMessage, aMessageInfo, TxParameters::GetDefault(), aHandler, aContext, aTransmitHook,
147                                  aReceiveHook);
148 }
149 #else  // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
SendMessage(Message & aMessage,ResponseHandler aHandler,void * aContext)150 Error CoapSecure::SendMessage(Message &aMessage, ResponseHandler aHandler, void *aContext)
151 {
152     Error error = kErrorNone;
153 
154     VerifyOrExit(IsConnected(), error = kErrorInvalidState);
155 
156     error = CoapBase::SendMessage(aMessage, mDtls.GetMessageInfo(), aHandler, aContext);
157 
158 exit:
159     return error;
160 }
161 
SendMessage(Message & aMessage,const Ip6::MessageInfo & aMessageInfo,ResponseHandler aHandler,void * aContext)162 Error CoapSecure::SendMessage(Message                &aMessage,
163                               const Ip6::MessageInfo &aMessageInfo,
164                               ResponseHandler         aHandler,
165                               void                   *aContext)
166 {
167     return CoapBase::SendMessage(aMessage, aMessageInfo, aHandler, aContext);
168 }
169 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
170 
Send(ot::Message & aMessage,const Ip6::MessageInfo & aMessageInfo)171 Error CoapSecure::Send(ot::Message &aMessage, const Ip6::MessageInfo &aMessageInfo)
172 {
173     OT_UNUSED_VARIABLE(aMessageInfo);
174 
175     mTransmitQueue.Enqueue(aMessage);
176     mTransmitTask.Post();
177 
178     return kErrorNone;
179 }
180 
HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent,void * aContext)181 void CoapSecure::HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent, void *aContext)
182 {
183     return static_cast<CoapSecure *>(aContext)->HandleDtlsConnectEvent(aEvent);
184 }
185 
HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent)186 void CoapSecure::HandleDtlsConnectEvent(MeshCoP::SecureTransport::ConnectEvent aEvent)
187 {
188     mConnectEventCallback.InvokeIfSet(aEvent);
189 }
190 
HandleDtlsAutoClose(void * aContext)191 void CoapSecure::HandleDtlsAutoClose(void *aContext)
192 {
193     return static_cast<CoapSecure *>(aContext)->HandleDtlsAutoClose();
194 }
195 
HandleDtlsAutoClose(void)196 void CoapSecure::HandleDtlsAutoClose(void)
197 {
198     Stop();
199     mAutoStopCallback.InvokeIfSet();
200 }
201 
HandleDtlsReceive(void * aContext,uint8_t * aBuf,uint16_t aLength)202 void CoapSecure::HandleDtlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength)
203 {
204     return static_cast<CoapSecure *>(aContext)->HandleDtlsReceive(aBuf, aLength);
205 }
206 
HandleDtlsReceive(uint8_t * aBuf,uint16_t aLength)207 void CoapSecure::HandleDtlsReceive(uint8_t *aBuf, uint16_t aLength)
208 {
209     ot::Message *message = nullptr;
210 
211     VerifyOrExit((message = Get<MessagePool>().Allocate(Message::kTypeIp6, Message::GetHelpDataReserved())) != nullptr);
212     SuccessOrExit(message->AppendBytes(aBuf, aLength));
213 
214     CoapBase::Receive(*message, mDtls.GetMessageInfo());
215 
216 exit:
217     FreeMessage(message);
218 }
219 
HandleTransmit(Tasklet & aTasklet)220 void CoapSecure::HandleTransmit(Tasklet &aTasklet)
221 {
222     static_cast<CoapSecure *>(static_cast<TaskletContext &>(aTasklet).GetContext())->HandleTransmit();
223 }
224 
HandleTransmit(void)225 void CoapSecure::HandleTransmit(void)
226 {
227     Error        error   = kErrorNone;
228     ot::Message *message = mTransmitQueue.GetHead();
229 
230     VerifyOrExit(message != nullptr);
231     mTransmitQueue.Dequeue(*message);
232 
233     if (mTransmitQueue.GetHead() != nullptr)
234     {
235         mTransmitTask.Post();
236     }
237 
238     SuccessOrExit(error = mDtls.Send(*message, message->GetLength()));
239 
240 exit:
241     if (error != kErrorNone)
242     {
243         LogNote("Transmit: %s", ErrorToString(error));
244         message->Free();
245     }
246     else
247     {
248         LogDebg("Transmit: %s", ErrorToString(error));
249     }
250 }
251 
252 } // namespace Coap
253 } // namespace ot
254 
255 #endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
256