1 /*
2 * Copyright (c) 2018, 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 /**
30 * @file
31 * This file implements a simple CLI for the CoAP Secure service.
32 */
33
34 #include "cli_coap_secure.hpp"
35
36 #if OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
37
38 #include <mbedtls/debug.h>
39 #include <openthread/random_noncrypto.h>
40
41 #include "cli/cli.hpp"
42
43 // header for place your x509 certificate and private key
44 #include "x509_cert_key.hpp"
45
46 namespace ot {
47 namespace Cli {
48
CoapSecure(otInstance * aInstance,OutputImplementer & aOutputImplementer)49 CoapSecure::CoapSecure(otInstance *aInstance, OutputImplementer &aOutputImplementer)
50 : Utils(aInstance, aOutputImplementer)
51 , mShutdownFlag(false)
52 , mUseCertificate(false)
53 , mPskLength(0)
54 , mPskIdLength(0)
55 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
56 , mBlockCount(1)
57 #endif
58 {
59 ClearAllBytes(mResource);
60 ClearAllBytes(mPsk);
61 ClearAllBytes(mPskId);
62 ClearAllBytes(mUriPath);
63 strncpy(mResourceContent, "0", sizeof(mResourceContent));
64 mResourceContent[sizeof(mResourceContent) - 1] = '\0';
65 }
66
PrintPayload(otMessage * aMessage)67 void CoapSecure::PrintPayload(otMessage *aMessage)
68 {
69 uint8_t buf[kMaxBufferSize];
70 uint16_t bytesToPrint;
71 uint16_t bytesPrinted = 0;
72 uint16_t length = otMessageGetLength(aMessage) - otMessageGetOffset(aMessage);
73
74 if (length > 0)
75 {
76 OutputFormat(" with payload: ");
77
78 while (length > 0)
79 {
80 bytesToPrint = Min(length, static_cast<uint16_t>(sizeof(buf)));
81 otMessageRead(aMessage, otMessageGetOffset(aMessage) + bytesPrinted, buf, bytesToPrint);
82
83 OutputBytes(buf, static_cast<uint8_t>(bytesToPrint));
84
85 length -= bytesToPrint;
86 bytesPrinted += bytesToPrint;
87 }
88 }
89
90 OutputNewLine();
91 }
92
93 /**
94 * @cli coaps resource (get,set)
95 * @code
96 * coaps resource test-resource
97 * Done
98 * @endcode
99 * @code
100 * coaps resource
101 * test-resource
102 * Done
103 * @endcode
104 * @cparam coaps resource [@ca{uri-path}]
105 * @par
106 * Gets or sets the URI path of the CoAPS server resource. @moreinfo{@coaps}.
107 * @sa otCoapSecureAddBlockWiseResource
108 */
Process(Arg aArgs[])109 template <> otError CoapSecure::Process<Cmd("resource")>(Arg aArgs[])
110 {
111 otError error = OT_ERROR_NONE;
112
113 if (!aArgs[0].IsEmpty())
114 {
115 VerifyOrExit(aArgs[0].GetLength() < kMaxUriLength, error = OT_ERROR_INVALID_ARGS);
116
117 mResource.mUriPath = mUriPath;
118 mResource.mContext = this;
119 mResource.mHandler = &CoapSecure::HandleRequest;
120
121 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
122 mResource.mReceiveHook = &CoapSecure::BlockwiseReceiveHook;
123 mResource.mTransmitHook = &CoapSecure::BlockwiseTransmitHook;
124
125 if (!aArgs[1].IsEmpty())
126 {
127 SuccessOrExit(error = aArgs[1].ParseAsUint32(mBlockCount));
128 }
129 #endif
130
131 strncpy(mUriPath, aArgs[0].GetCString(), sizeof(mUriPath) - 1);
132 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
133 otCoapSecureAddBlockWiseResource(GetInstancePtr(), &mResource);
134 #else
135 otCoapSecureAddResource(GetInstancePtr(), &mResource);
136 #endif
137 }
138 else
139 {
140 OutputLine("%s", mResource.mUriPath != nullptr ? mResource.mUriPath : "");
141 }
142
143 exit:
144 return error;
145 }
146
147 /**
148 * @cli coaps set
149 * @code
150 * coaps set Testing123
151 * Done
152 * @endcode
153 * @cparam coaps set @ca{new-content}
154 * @par
155 * Sets the content sent by the resource on the CoAPS server. @moreinfo{@coaps}.
156 */
Process(Arg aArgs[])157 template <> otError CoapSecure::Process<Cmd("set")>(Arg aArgs[])
158 {
159 otError error = OT_ERROR_NONE;
160
161 if (!aArgs[0].IsEmpty())
162 {
163 VerifyOrExit(aArgs[0].GetLength() < sizeof(mResourceContent), error = OT_ERROR_INVALID_ARGS);
164 strncpy(mResourceContent, aArgs[0].GetCString(), sizeof(mResourceContent));
165 mResourceContent[sizeof(mResourceContent) - 1] = '\0';
166 }
167 else
168 {
169 OutputLine("%s", mResourceContent);
170 }
171
172 exit:
173 return error;
174 }
175
176 /**
177 * @cli coaps start
178 * @code
179 * coaps start
180 * Done
181 * @endcode
182 * @code
183 * coaps start false
184 * Done
185 * @endcode
186 * @code
187 * coaps start 8
188 * Done
189 * @endcode
190 * @cparam coaps start [@ca{check-peer-cert} | @ca{max-conn-attempts}]
191 * The `check-peer-cert` parameter determines if the peer-certificate check is
192 * enabled (default) or disabled.
193 * The `max-conn-attempts` parameter sets the maximum number of allowed
194 * attempts, successful or failed, to connect to the CoAP Secure server.
195 * The default value of this parameter is `0`, which means that there is
196 * no limit to the number of attempts.
197 * The `check-peer-cert` and `max-conn-attempts` parameters work
198 * together in the following combinations, even though you can only specify
199 * one argument:
200 * * No argument specified: Defaults are used.
201 * * Setting `check-peer-cert` to `true`:
202 * Has the same effect as omitting the argument, which is that the
203 * `check-peer-cert` value is `true`, and the `max-conn-attempts` value is 0.
204 * * Setting `check-peer-cert` to `false`:
205 * `check-peer-cert` value is `false`, and the `max-conn-attempts` value is 0.
206 * * Specifying a number:
207 * `check-peer-cert` is `true`, and the `max-conn-attempts` value is the
208 * number specified in the argument.
209 * @par
210 * Starts the CoAP Secure service. @moreinfo{@coaps}.
211 * @sa otCoapSecureStart
212 * @sa otCoapSecureSetSslAuthMode
213 * @sa otCoapSecureSetClientConnectEventCallback
214 */
Process(Arg aArgs[])215 template <> otError CoapSecure::Process<Cmd("start")>(Arg aArgs[])
216 {
217 otError error = OT_ERROR_NONE;
218 bool verifyPeerCert = true;
219 uint16_t maxConnAttempts = 0;
220
221 if (!aArgs[0].IsEmpty())
222 {
223 if (aArgs[0] == "false")
224 {
225 verifyPeerCert = false;
226 }
227 else if (aArgs[0] == "true")
228 {
229 verifyPeerCert = true;
230 }
231 else
232 {
233 SuccessOrExit(error = aArgs[0].ParseAsUint16(maxConnAttempts));
234 }
235 }
236
237 otCoapSecureSetSslAuthMode(GetInstancePtr(), verifyPeerCert);
238 otCoapSecureSetClientConnectEventCallback(GetInstancePtr(), &CoapSecure::HandleConnectEvent, this);
239
240 #if CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER
241 otCoapSecureSetDefaultHandler(GetInstancePtr(), &CoapSecure::DefaultHandler, this);
242 #endif
243
244 error = otCoapSecureStartWithMaxConnAttempts(GetInstancePtr(), OT_DEFAULT_COAP_SECURE_PORT, maxConnAttempts,
245 nullptr, nullptr);
246
247 exit:
248 return error;
249 }
250
251 /**
252 * @cli coaps stop
253 * @code
254 * coaps stop
255 * Done
256 * @endcode
257 * @par
258 * Stops the CoAP Secure service. @moreinfo{@coaps}.
259 * @sa otCoapSecureStop
260 */
Process(Arg aArgs[])261 template <> otError CoapSecure::Process<Cmd("stop")>(Arg aArgs[])
262 {
263 OT_UNUSED_VARIABLE(aArgs);
264
265 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
266 otCoapRemoveBlockWiseResource(GetInstancePtr(), &mResource);
267 #else
268 otCoapRemoveResource(GetInstancePtr(), &mResource);
269 #endif
270
271 if (otCoapSecureIsConnectionActive(GetInstancePtr()))
272 {
273 otCoapSecureDisconnect(GetInstancePtr());
274 mShutdownFlag = true;
275 }
276 else
277 {
278 Stop();
279 }
280
281 return OT_ERROR_NONE;
282 }
283
284 /**
285 * @cli coaps isclosed
286 * @code
287 * coaps isclosed
288 * no
289 * Done
290 * @endcode
291 * @par
292 * Indicates if the CoAP Secure service is closed. @moreinfo{@coaps}.
293 * @sa otCoapSecureIsClosed
294 */
Process(Arg aArgs[])295 template <> otError CoapSecure::Process<Cmd("isclosed")>(Arg aArgs[])
296 {
297 return ProcessIsRequest(aArgs, otCoapSecureIsClosed);
298 }
299
300 /**
301 * @cli coaps isconnected
302 * @code
303 * coaps isconnected
304 * yes
305 * Done
306 * @endcode
307 * @par
308 * Indicates if the CoAP Secure service is connected. @moreinfo{@coaps}.
309 * @sa otCoapSecureIsConnected
310 */
Process(Arg aArgs[])311 template <> otError CoapSecure::Process<Cmd("isconnected")>(Arg aArgs[])
312 {
313 return ProcessIsRequest(aArgs, otCoapSecureIsConnected);
314 }
315
316 /**
317 * @cli coaps isconnactive
318 * @code
319 * coaps isconnactive
320 * yes
321 * Done
322 * @endcode
323 * @par
324 * Indicates if the CoAP Secure service connection is active
325 * (either already connected or in the process of establishing a connection).
326 * @moreinfo{@coaps}.
327 * @sa otCoapSecureIsConnectionActive
328 */
Process(Arg aArgs[])329 template <> otError CoapSecure::Process<Cmd("isconnactive")>(Arg aArgs[])
330 {
331 return ProcessIsRequest(aArgs, otCoapSecureIsConnectionActive);
332 }
333
ProcessIsRequest(Arg aArgs[],bool (* IsChecker)(otInstance *))334 otError CoapSecure::ProcessIsRequest(Arg aArgs[], bool (*IsChecker)(otInstance *))
335 {
336 otError error = OT_ERROR_NONE;
337
338 VerifyOrExit(aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
339 OutputLine("%s", IsChecker(GetInstancePtr()) ? "yes" : "no");
340
341 exit:
342 return error;
343 }
344
345 /**
346 * @cli coaps get
347 * @code
348 * coaps get test-resource
349 * Done
350 * @endcode
351 * @code
352 * coaps get test-resource block-1024
353 * Done
354 * @endcode
355 * @cparam coaps get @ca{uri-path} [@ca{type}]
356 * * `uri-path`: URI path of the resource.
357 * * `type`:
358 * * `con`: Confirmable
359 * * `non-con`: Non-confirmable (default)
360 * * `block-`: Use this option, followed by the block-wise value,
361 * if the response should be transferred block-wise.
362 * Valid values are: `block-16`, `block-32`, `block-64`, `block-128`,
363 * `block-256`, `block-512`, or `block-1024`.
364 * @par
365 * Gets information about the specified CoAPS resource on the CoAPS server.
366 * @moreinfo{@coaps}.
367 */
Process(Arg aArgs[])368 template <> otError CoapSecure::Process<Cmd("get")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_GET); }
369
370 /**
371 * @cli coaps post
372 * @code
373 * coaps post test-resource con hellothere
374 * Done
375 * @endcode
376 * @code
377 * coaps post test-resource block-1024 10
378 * Done
379 * @endcode
380 * @cparam @ca{uri-path} [@ca{type}] [@ca{payload}]
381 * * `uri-path`: URI path of the resource.
382 * * `type`:
383 * * `con`: Confirmable
384 * * `non-con`: Non-confirmable (default)
385 * * `block-`: Use this option, followed by the block-wise value,
386 * to send blocks with a randomly generated number of bytes for the payload.
387 * Valid values are: `block-16`, `block-32`, `block-64`, `block-128`,
388 * `block-256`, `block-512`, or `block-1024`.
389 * * `payload`: CoAPS payload request, which if used is either a string
390 * or an integer, depending on the `type`. If the `type` is `con` or `non-con`,
391 * the payload parameter is optional. If you leave out the payload
392 * parameter, an empty payload is sent. However, If you use the payload
393 * parameter, its value must be a string, such as `hellothere`. If the
394 * `type` is `block-`, the value of the payload parameter must be an
395 * integer that specifies the number of blocks to send. The `block-` type
396 * requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
397 * @par
398 * Creates the specified CoAPS resource. @moreinfo{@coaps}.
399 */
Process(Arg aArgs[])400 template <> otError CoapSecure::Process<Cmd("post")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_POST); }
401
402 /**
403 * @cli coaps put
404 * @code
405 * coaps put test-resource con hellothere
406 * Done
407 * @endcode
408 * @code
409 * coaps put test-resource block-1024 10
410 * Done
411 * @endcode
412 * @cparam @ca{uri-path} [@ca{type}] [@ca{payload}]
413 * * `uri-path`: URI path of the resource.
414 * * `type`:
415 * * `con`: Confirmable
416 * * `non-con`: Non-confirmable (default)
417 * * `block-`: Use this option, followed by the block-wise value,
418 * to send blocks with a randomly generated number of bytes for the payload.
419 * Valid values are: `block-16`, `block-32`, `block-64`, `block-128`,
420 * `block-256`, `block-512`, or `block-1024`.
421 * * `payload`: CoAPS payload request, which if used is either a string
422 * or an integer, depending on the `type`. If the `type` is `con` or `non-con`,
423 * the payload parameter is optional. If you leave out the payload
424 * parameter, an empty payload is sent. However, If you use the payload
425 * parameter, its value must be a string, such as `hellothere`. If the
426 * `type` is `block-`, the value of the payload parameter must be an
427 * integer that specifies the number of blocks to send. The `block-` type
428 * requires `OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE` to be set.
429 * @par
430 * Modifies the specified CoAPS resource. @moreinfo{@coaps}.
431 */
Process(Arg aArgs[])432 template <> otError CoapSecure::Process<Cmd("put")>(Arg aArgs[]) { return ProcessRequest(aArgs, OT_COAP_CODE_PUT); }
433
434 /**
435 * @cli coaps delete
436 * @code
437 * coaps delete test-resource con hellothere
438 * Done
439 * @endcode
440 * @cparam coaps delete @ca{uri-path} [@ca{type}] [@ca{payload}]
441 * * `uri-path`: URI path of the resource.
442 * * `type`:
443 * * `con`: Confirmable
444 * * `non-con`: Non-confirmable (default)
445 * * `payload`: CoAPS payload request.
446 * @par
447 * The CoAPS payload string to delete.
448 */
Process(Arg aArgs[])449 template <> otError CoapSecure::Process<Cmd("delete")>(Arg aArgs[])
450 {
451 return ProcessRequest(aArgs, OT_COAP_CODE_DELETE);
452 }
453
ProcessRequest(Arg aArgs[],otCoapCode aCoapCode)454 otError CoapSecure::ProcessRequest(Arg aArgs[], otCoapCode aCoapCode)
455 {
456 otError error = OT_ERROR_NONE;
457 otMessage *message = nullptr;
458 uint16_t payloadLength = 0;
459
460 // Default parameters
461 char coapUri[kMaxUriLength];
462 otCoapType coapType = OT_COAP_TYPE_NON_CONFIRMABLE;
463 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
464 bool coapBlock = false;
465 otCoapBlockSzx coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
466 BlockType coapBlockType = (aCoapCode == OT_COAP_CODE_GET) ? kBlockType2 : kBlockType1;
467 #endif
468
469 VerifyOrExit(!aArgs[0].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
470 VerifyOrExit(aArgs[0].GetLength() < sizeof(coapUri), error = OT_ERROR_INVALID_ARGS);
471 strcpy(coapUri, aArgs[0].GetCString());
472
473 if (!aArgs[1].IsEmpty())
474 {
475 if (aArgs[1] == "con")
476 {
477 coapType = OT_COAP_TYPE_CONFIRMABLE;
478 }
479 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
480 else if (aArgs[1] == "block-16")
481 {
482 coapType = OT_COAP_TYPE_CONFIRMABLE;
483 coapBlock = true;
484 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_16;
485 }
486 else if (aArgs[1] == "block-32")
487 {
488 coapType = OT_COAP_TYPE_CONFIRMABLE;
489 coapBlock = true;
490 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_32;
491 }
492 else if (aArgs[1] == "block-64")
493 {
494 coapType = OT_COAP_TYPE_CONFIRMABLE;
495 coapBlock = true;
496 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_64;
497 }
498 else if (aArgs[1] == "block-128")
499 {
500 coapType = OT_COAP_TYPE_CONFIRMABLE;
501 coapBlock = true;
502 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_128;
503 }
504 else if (aArgs[1] == "block-256")
505 {
506 coapType = OT_COAP_TYPE_CONFIRMABLE;
507 coapBlock = true;
508 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_256;
509 }
510 else if (aArgs[1] == "block-512")
511 {
512 coapType = OT_COAP_TYPE_CONFIRMABLE;
513 coapBlock = true;
514 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_512;
515 }
516 else if (aArgs[1] == "block-1024")
517 {
518 coapType = OT_COAP_TYPE_CONFIRMABLE;
519 coapBlock = true;
520 coapBlockSize = OT_COAP_OPTION_BLOCK_SZX_1024;
521 }
522 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
523 }
524
525 message = otCoapNewMessage(GetInstancePtr(), nullptr);
526 VerifyOrExit(message != nullptr, error = OT_ERROR_NO_BUFS);
527
528 otCoapMessageInit(message, coapType, aCoapCode);
529 otCoapMessageGenerateToken(message, OT_COAP_DEFAULT_TOKEN_LENGTH);
530 SuccessOrExit(error = otCoapMessageAppendUriPathOptions(message, coapUri));
531
532 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
533 if (coapBlock)
534 {
535 if (coapBlockType == kBlockType1)
536 {
537 SuccessOrExit(error = otCoapMessageAppendBlock1Option(message, 0, true, coapBlockSize));
538 }
539 else
540 {
541 SuccessOrExit(error = otCoapMessageAppendBlock2Option(message, 0, false, coapBlockSize));
542 }
543 }
544 #endif
545
546 if (!aArgs[2].IsEmpty())
547 {
548 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
549 if (coapBlock)
550 {
551 SuccessOrExit(error = aArgs[2].ParseAsUint32(mBlockCount));
552 }
553 else
554 {
555 #endif
556 payloadLength = aArgs[2].GetLength();
557
558 if (payloadLength > 0)
559 {
560 SuccessOrExit(error = otCoapMessageSetPayloadMarker(message));
561 }
562 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
563 }
564 #endif
565 }
566
567 if (payloadLength > 0)
568 {
569 SuccessOrExit(error = otMessageAppend(message, aArgs[2].GetCString(), payloadLength));
570 }
571
572 if ((coapType == OT_COAP_TYPE_CONFIRMABLE) || (aCoapCode == OT_COAP_CODE_GET))
573 {
574 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
575 if (coapBlock)
576 {
577 error =
578 otCoapSecureSendRequestBlockWise(GetInstancePtr(), message, &CoapSecure::HandleResponse, this,
579 &CoapSecure::BlockwiseTransmitHook, &CoapSecure::BlockwiseReceiveHook);
580 }
581 else
582 {
583 #endif
584 error = otCoapSecureSendRequest(GetInstancePtr(), message, &CoapSecure::HandleResponse, this);
585 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
586 }
587 #endif
588 }
589 else
590 {
591 error = otCoapSecureSendRequest(GetInstancePtr(), message, nullptr, nullptr);
592 }
593
594 exit:
595
596 if ((error != OT_ERROR_NONE) && (message != nullptr))
597 {
598 otMessageFree(message);
599 }
600
601 return error;
602 }
603
604 /**
605 * @cli coaps connect
606 * @code
607 * coaps connect fdde:ad00:beef:0:9903:14b:27e0:5744
608 * Done
609 * coaps connected
610 * @endcode
611 * @cparam coaps connect @ca{address}
612 * The `address` parameter is the IPv6 address of the peer.
613 * @par
614 * Initializes a Datagram Transport Layer Security (DTLS) session with a peer.
615 * @moreinfo{@coaps}.
616 * @sa otCoapSecureConnect
617 */
Process(Arg aArgs[])618 template <> otError CoapSecure::Process<Cmd("connect")>(Arg aArgs[])
619 {
620 otError error;
621 otSockAddr sockaddr;
622
623 ClearAllBytes(sockaddr);
624 SuccessOrExit(error = aArgs[0].ParseAsIp6Address(sockaddr.mAddress));
625 sockaddr.mPort = OT_DEFAULT_COAP_SECURE_PORT;
626
627 if (!aArgs[1].IsEmpty())
628 {
629 SuccessOrExit(error = aArgs[1].ParseAsUint16(sockaddr.mPort));
630 }
631
632 SuccessOrExit(error = otCoapSecureConnect(GetInstancePtr(), &sockaddr, &CoapSecure::HandleConnectEvent, this));
633
634 exit:
635 return error;
636 }
637
638 /**
639 * @cli coaps disconnect
640 * @code
641 * coaps disconnect
642 * coaps disconnected
643 * Done
644 * @endcode
645 * @par
646 * Stops the DTLS session.
647 * @sa otCoapSecureDisconnect
648 */
Process(Arg aArgs[])649 template <> otError CoapSecure::Process<Cmd("disconnect")>(Arg aArgs[])
650 {
651 OT_UNUSED_VARIABLE(aArgs);
652
653 otCoapSecureDisconnect(GetInstancePtr());
654
655 return OT_ERROR_NONE;
656 }
657
658 /**
659 * <!--- This tag is before the IF statement so that Doxygen imports the command. --->
660 * @cli coaps psk
661 * @code
662 * coaps psk 1234 key1
663 * Done
664 * @endcode
665 * @cparam coaps psk @ca{psk-value} @ca{psk-id}
666 * * `psk-value`: The pre-shared key
667 * * `psk-id`: The pre-shared key identifier.
668 * @par
669 * Sets the pre-shared key (PSK) and cipher suite DTLS_PSK_WITH_AES_128_CCM_8.
670 * @note This command requires the build-time feature
671 * `MBEDTLS_KEY_EXCHANGE_PSK_ENABLED` to be enabled.
672 * @sa #otCoapSecureSetPsk
673 */
674 #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
Process(Arg aArgs[])675 template <> otError CoapSecure::Process<Cmd("psk")>(Arg aArgs[])
676 {
677 otError error = OT_ERROR_NONE;
678 uint16_t length;
679
680 VerifyOrExit(!aArgs[1].IsEmpty(), error = OT_ERROR_INVALID_ARGS);
681
682 length = aArgs[0].GetLength();
683 VerifyOrExit(length <= sizeof(mPsk), error = OT_ERROR_INVALID_ARGS);
684 mPskLength = static_cast<uint8_t>(length);
685 memcpy(mPsk, aArgs[0].GetCString(), mPskLength);
686
687 length = aArgs[1].GetLength();
688 VerifyOrExit(length <= sizeof(mPskId), error = OT_ERROR_INVALID_ARGS);
689 mPskIdLength = static_cast<uint8_t>(length);
690 memcpy(mPskId, aArgs[1].GetCString(), mPskIdLength);
691
692 otCoapSecureSetPsk(GetInstancePtr(), mPsk, mPskLength, mPskId, mPskIdLength);
693 mUseCertificate = false;
694
695 exit:
696 return error;
697 }
698 #endif // MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
699
700 /**
701 * <!--- This tag is before the IF statement so that Doxygen imports the command. --->
702 * @cli coaps x509
703 * @code
704 * coaps x509
705 * Done
706 * @endcode
707 * @par
708 * Sets the X509 certificate of the local device with the corresponding private key for
709 * the DTLS session with `DTLS_ECDHE_ECDSA_WITH_AES_128_CCM_8`.
710 * @note This command requires `MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED=1`
711 * to be enabled.
712 * The X.509 certificate is stored in the location: `src/cli/x509_cert_key.hpp`.
713 * @sa otCoapSecureSetCertificate
714 * @sa otCoapSecureSetCaCertificateChain
715 */
716 #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
Process(Arg aArgs[])717 template <> otError CoapSecure::Process<Cmd("x509")>(Arg aArgs[])
718 {
719 OT_UNUSED_VARIABLE(aArgs);
720
721 otCoapSecureSetCertificate(GetInstancePtr(), reinterpret_cast<const uint8_t *>(OT_CLI_COAPS_X509_CERT),
722 sizeof(OT_CLI_COAPS_X509_CERT), reinterpret_cast<const uint8_t *>(OT_CLI_COAPS_PRIV_KEY),
723 sizeof(OT_CLI_COAPS_PRIV_KEY));
724
725 otCoapSecureSetCaCertificateChain(GetInstancePtr(),
726 reinterpret_cast<const uint8_t *>(OT_CLI_COAPS_TRUSTED_ROOT_CERTIFICATE),
727 sizeof(OT_CLI_COAPS_TRUSTED_ROOT_CERTIFICATE));
728 mUseCertificate = true;
729
730 return OT_ERROR_NONE;
731 }
732 #endif
733
Process(Arg aArgs[])734 otError CoapSecure::Process(Arg aArgs[])
735 {
736 #define CmdEntry(aCommandString) \
737 { \
738 aCommandString, &CoapSecure::Process<Cmd(aCommandString)> \
739 }
740
741 static constexpr Command kCommands[] = {
742 CmdEntry("connect"), CmdEntry("delete"), CmdEntry("disconnect"), CmdEntry("get"),
743 CmdEntry("isclosed"), CmdEntry("isconnactive"), CmdEntry("isconnected"), CmdEntry("post"),
744 #ifdef MBEDTLS_KEY_EXCHANGE_PSK_ENABLED
745 CmdEntry("psk"),
746 #endif
747 CmdEntry("put"), CmdEntry("resource"), CmdEntry("set"), CmdEntry("start"),
748 CmdEntry("stop"),
749 #ifdef MBEDTLS_KEY_EXCHANGE_ECDHE_ECDSA_ENABLED
750 CmdEntry("x509"),
751 #endif
752 };
753
754 #undef CmdEntry
755
756 static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
757
758 otError error = OT_ERROR_INVALID_COMMAND;
759 const Command *command;
760
761 if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
762 {
763 OutputCommandTable(kCommands);
764 ExitNow(error = aArgs[0].IsEmpty() ? OT_ERROR_INVALID_ARGS : OT_ERROR_NONE);
765 }
766
767 command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
768 VerifyOrExit(command != nullptr);
769
770 error = (this->*command->mHandler)(aArgs + 1);
771
772 exit:
773 return error;
774 }
775
Stop(void)776 void CoapSecure::Stop(void)
777 {
778 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
779 otCoapRemoveBlockWiseResource(GetInstancePtr(), &mResource);
780 #else
781 otCoapRemoveResource(GetInstancePtr(), &mResource);
782 #endif
783 otCoapSecureStop(GetInstancePtr());
784 }
785
HandleConnectEvent(otCoapSecureConnectEvent aEvent,void * aContext)786 void CoapSecure::HandleConnectEvent(otCoapSecureConnectEvent aEvent, void *aContext)
787 {
788 static_cast<CoapSecure *>(aContext)->HandleConnectEvent(aEvent);
789 }
790
HandleConnectEvent(otCoapSecureConnectEvent aEvent)791 void CoapSecure::HandleConnectEvent(otCoapSecureConnectEvent aEvent)
792 {
793 if (aEvent == OT_COAP_SECURE_CONNECTED)
794 {
795 OutputLine("coaps connected");
796 }
797 else
798 {
799 OutputLine("coaps disconnected");
800
801 if (mShutdownFlag)
802 {
803 Stop();
804 mShutdownFlag = false;
805 }
806 }
807 }
808
HandleRequest(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)809 void CoapSecure::HandleRequest(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
810 {
811 static_cast<CoapSecure *>(aContext)->HandleRequest(aMessage, aMessageInfo);
812 }
813
HandleRequest(otMessage * aMessage,const otMessageInfo * aMessageInfo)814 void CoapSecure::HandleRequest(otMessage *aMessage, const otMessageInfo *aMessageInfo)
815 {
816 otError error = OT_ERROR_NONE;
817 otMessage *responseMessage = nullptr;
818 otCoapCode responseCode = OT_COAP_CODE_EMPTY;
819 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
820 uint64_t blockValue = 0;
821 bool blockPresent = false;
822 otCoapOptionIterator iterator;
823 #endif
824
825 OutputFormat("coaps request from ");
826 OutputIp6Address(aMessageInfo->mPeerAddr);
827 OutputFormat(" ");
828
829 switch (otCoapMessageGetCode(aMessage))
830 {
831 case OT_COAP_CODE_GET:
832 OutputFormat("GET");
833 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
834 SuccessOrExit(error = otCoapOptionIteratorInit(&iterator, aMessage));
835 if (otCoapOptionIteratorGetFirstOptionMatching(&iterator, OT_COAP_OPTION_BLOCK2) != nullptr)
836 {
837 SuccessOrExit(error = otCoapOptionIteratorGetOptionUintValue(&iterator, &blockValue));
838 blockPresent = true;
839 }
840 #endif
841 break;
842
843 case OT_COAP_CODE_DELETE:
844 OutputFormat("DELETE");
845 break;
846
847 case OT_COAP_CODE_PUT:
848 OutputFormat("PUT");
849 break;
850
851 case OT_COAP_CODE_POST:
852 OutputFormat("POST");
853 break;
854
855 default:
856 OutputLine("Undefined");
857 return;
858 }
859
860 PrintPayload(aMessage);
861
862 if ((otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE) ||
863 (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET))
864 {
865 if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
866 {
867 responseCode = OT_COAP_CODE_CONTENT;
868 }
869 else
870 {
871 responseCode = OT_COAP_CODE_VALID;
872 }
873
874 responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
875 VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS);
876
877 SuccessOrExit(
878 error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_ACKNOWLEDGMENT, responseCode));
879
880 if (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET)
881 {
882 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
883 if (blockPresent)
884 {
885 SuccessOrExit(error = otCoapMessageAppendBlock2Option(responseMessage,
886 static_cast<uint32_t>(blockValue >> 4), true,
887 static_cast<otCoapBlockSzx>(blockValue & 0x7)));
888 }
889 #endif
890 SuccessOrExit(error = otCoapMessageSetPayloadMarker(responseMessage));
891 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
892 if (!blockPresent)
893 {
894 #endif
895 SuccessOrExit(error = otMessageAppend(responseMessage, &mResourceContent,
896 static_cast<uint16_t>(strlen(mResourceContent))));
897 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
898 }
899 #endif
900 }
901
902 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
903 if (blockPresent)
904 {
905 SuccessOrExit(error = otCoapSecureSendResponseBlockWise(GetInstancePtr(), responseMessage, aMessageInfo,
906 this, mResource.mTransmitHook));
907 }
908 else
909 {
910 #endif
911 SuccessOrExit(error = otCoapSecureSendResponse(GetInstancePtr(), responseMessage, aMessageInfo));
912 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
913 }
914 #endif
915 }
916
917 exit:
918
919 if (error != OT_ERROR_NONE)
920 {
921 if (responseMessage != nullptr)
922 {
923 OutputLine("coaps send response error %d: %s", error, otThreadErrorToString(error));
924 otMessageFree(responseMessage);
925 }
926 }
927 else if (responseCode >= OT_COAP_CODE_RESPONSE_MIN)
928 {
929 OutputLine("coaps response sent");
930 }
931 }
932
HandleResponse(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)933 void CoapSecure::HandleResponse(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
934 {
935 static_cast<CoapSecure *>(aContext)->HandleResponse(aMessage, aMessageInfo, aError);
936 }
937
HandleResponse(otMessage * aMessage,const otMessageInfo * aMessageInfo,otError aError)938 void CoapSecure::HandleResponse(otMessage *aMessage, const otMessageInfo *aMessageInfo, otError aError)
939 {
940 OT_UNUSED_VARIABLE(aMessageInfo);
941
942 if (aError != OT_ERROR_NONE)
943 {
944 OutputLine("coaps receive response error %d: %s", aError, otThreadErrorToString(aError));
945 }
946 else
947 {
948 OutputFormat("coaps response from ");
949 OutputIp6Address(aMessageInfo->mPeerAddr);
950
951 PrintPayload(aMessage);
952 }
953 }
954
955 #if CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER
DefaultHandler(void * aContext,otMessage * aMessage,const otMessageInfo * aMessageInfo)956 void CoapSecure::DefaultHandler(void *aContext, otMessage *aMessage, const otMessageInfo *aMessageInfo)
957 {
958 static_cast<CoapSecure *>(aContext)->DefaultHandler(aMessage, aMessageInfo);
959 }
960
DefaultHandler(otMessage * aMessage,const otMessageInfo * aMessageInfo)961 void CoapSecure::DefaultHandler(otMessage *aMessage, const otMessageInfo *aMessageInfo)
962 {
963 otError error = OT_ERROR_NONE;
964 otMessage *responseMessage = nullptr;
965
966 if ((otCoapMessageGetType(aMessage) == OT_COAP_TYPE_CONFIRMABLE) ||
967 (otCoapMessageGetCode(aMessage) == OT_COAP_CODE_GET))
968 {
969 responseMessage = otCoapNewMessage(GetInstancePtr(), nullptr);
970 VerifyOrExit(responseMessage != nullptr, error = OT_ERROR_NO_BUFS);
971
972 SuccessOrExit(error = otCoapMessageInitResponse(responseMessage, aMessage, OT_COAP_TYPE_NON_CONFIRMABLE,
973 OT_COAP_CODE_NOT_FOUND));
974
975 SuccessOrExit(error = otCoapSecureSendResponse(GetInstancePtr(), responseMessage, aMessageInfo));
976 }
977
978 exit:
979 if (error != OT_ERROR_NONE && responseMessage != nullptr)
980 {
981 otMessageFree(responseMessage);
982 }
983 }
984 #endif // CLI_COAP_SECURE_USE_COAP_DEFAULT_HANDLER
985
986 #if OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
BlockwiseReceiveHook(void * aContext,const uint8_t * aBlock,uint32_t aPosition,uint16_t aBlockLength,bool aMore,uint32_t aTotalLength)987 otError CoapSecure::BlockwiseReceiveHook(void *aContext,
988 const uint8_t *aBlock,
989 uint32_t aPosition,
990 uint16_t aBlockLength,
991 bool aMore,
992 uint32_t aTotalLength)
993 {
994 return static_cast<CoapSecure *>(aContext)->BlockwiseReceiveHook(aBlock, aPosition, aBlockLength, aMore,
995 aTotalLength);
996 }
997
BlockwiseReceiveHook(const uint8_t * aBlock,uint32_t aPosition,uint16_t aBlockLength,bool aMore,uint32_t aTotalLength)998 otError CoapSecure::BlockwiseReceiveHook(const uint8_t *aBlock,
999 uint32_t aPosition,
1000 uint16_t aBlockLength,
1001 bool aMore,
1002 uint32_t aTotalLength)
1003 {
1004 OT_UNUSED_VARIABLE(aMore);
1005 OT_UNUSED_VARIABLE(aTotalLength);
1006
1007 OutputLine("received block: Num %i Len %i", aPosition / aBlockLength, aBlockLength);
1008
1009 for (uint16_t i = 0; i < aBlockLength / 16; i++)
1010 {
1011 OutputBytesLine(&aBlock[i * 16], 16);
1012 }
1013
1014 return OT_ERROR_NONE;
1015 }
1016
BlockwiseTransmitHook(void * aContext,uint8_t * aBlock,uint32_t aPosition,uint16_t * aBlockLength,bool * aMore)1017 otError CoapSecure::BlockwiseTransmitHook(void *aContext,
1018 uint8_t *aBlock,
1019 uint32_t aPosition,
1020 uint16_t *aBlockLength,
1021 bool *aMore)
1022 {
1023 return static_cast<CoapSecure *>(aContext)->BlockwiseTransmitHook(aBlock, aPosition, aBlockLength, aMore);
1024 }
1025
BlockwiseTransmitHook(uint8_t * aBlock,uint32_t aPosition,uint16_t * aBlockLength,bool * aMore)1026 otError CoapSecure::BlockwiseTransmitHook(uint8_t *aBlock, uint32_t aPosition, uint16_t *aBlockLength, bool *aMore)
1027 {
1028 static uint32_t blockCount = 0;
1029 OT_UNUSED_VARIABLE(aPosition);
1030
1031 // Send a random payload
1032 otRandomNonCryptoFillBuffer(aBlock, *aBlockLength);
1033
1034 OutputLine("send block: Num %i Len %i", blockCount, *aBlockLength);
1035
1036 for (uint16_t i = 0; i < *aBlockLength / 16; i++)
1037 {
1038 OutputBytesLine(&aBlock[i * 16], 16);
1039 }
1040
1041 if (blockCount == mBlockCount - 1)
1042 {
1043 blockCount = 0;
1044 *aMore = false;
1045 }
1046 else
1047 {
1048 *aMore = true;
1049 blockCount++;
1050 }
1051
1052 return OT_ERROR_NONE;
1053 }
1054 #endif // OPENTHREAD_CONFIG_COAP_BLOCKWISE_TRANSFER_ENABLE
1055
1056 } // namespace Cli
1057 } // namespace ot
1058
1059 #endif // OPENTHREAD_CONFIG_COAP_SECURE_API_ENABLE
1060