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 mConnectedCallback.Clear();
89 SuccessOrExit(mDtls.Open(HandleDtlsReceive, HandleDtlsConnected, 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,ConnectedCallback aCallback,void * aContext)105 Error CoapSecure::Connect(const Ip6::SockAddr &aSockAddr, ConnectedCallback aCallback, void *aContext)
106 {
107 mConnectedCallback.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
HandleDtlsConnected(void * aContext,bool aConnected)181 void CoapSecure::HandleDtlsConnected(void *aContext, bool aConnected)
182 {
183 return static_cast<CoapSecure *>(aContext)->HandleDtlsConnected(aConnected);
184 }
185
HandleDtlsConnected(bool aConnected)186 void CoapSecure::HandleDtlsConnected(bool aConnected) { mConnectedCallback.InvokeIfSet(aConnected); }
187
HandleDtlsAutoClose(void * aContext)188 void CoapSecure::HandleDtlsAutoClose(void *aContext)
189 {
190 return static_cast<CoapSecure *>(aContext)->HandleDtlsAutoClose();
191 }
192
HandleDtlsAutoClose(void)193 void CoapSecure::HandleDtlsAutoClose(void)
194 {
195 Stop();
196 mAutoStopCallback.InvokeIfSet();
197 }
198
HandleDtlsReceive(void * aContext,uint8_t * aBuf,uint16_t aLength)199 void CoapSecure::HandleDtlsReceive(void *aContext, uint8_t *aBuf, uint16_t aLength)
200 {
201 return static_cast<CoapSecure *>(aContext)->HandleDtlsReceive(aBuf, aLength);
202 }
203
HandleDtlsReceive(uint8_t * aBuf,uint16_t aLength)204 void CoapSecure::HandleDtlsReceive(uint8_t *aBuf, uint16_t aLength)
205 {
206 ot::Message *message = nullptr;
207
208 VerifyOrExit((message = Get<MessagePool>().Allocate(Message::kTypeIp6, Message::GetHelpDataReserved())) != nullptr);
209 SuccessOrExit(message->AppendBytes(aBuf, aLength));
210
211 CoapBase::Receive(*message, mDtls.GetMessageInfo());
212
213 exit:
214 FreeMessage(message);
215 }
216
HandleTransmit(Tasklet & aTasklet)217 void CoapSecure::HandleTransmit(Tasklet &aTasklet)
218 {
219 static_cast<CoapSecure *>(static_cast<TaskletContext &>(aTasklet).GetContext())->HandleTransmit();
220 }
221
HandleTransmit(void)222 void CoapSecure::HandleTransmit(void)
223 {
224 Error error = kErrorNone;
225 ot::Message *message = mTransmitQueue.GetHead();
226
227 VerifyOrExit(message != nullptr);
228 mTransmitQueue.Dequeue(*message);
229
230 if (mTransmitQueue.GetHead() != nullptr)
231 {
232 mTransmitTask.Post();
233 }
234
235 SuccessOrExit(error = mDtls.Send(*message, message->GetLength()));
236
237 exit:
238 if (error != kErrorNone)
239 {
240 LogNote("Transmit: %s", ErrorToString(error));
241 message->Free();
242 }
243 else
244 {
245 LogDebg("Transmit: %s", ErrorToString(error));
246 }
247 }
248
249 } // namespace Coap
250 } // namespace ot
251
252 #endif // OPENTHREAD_CONFIG_SECURE_TRANSPORT_ENABLE
253