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