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