1 /*
2  *  Copyright (c) 2023, 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 "openthread-core-config.h"
30 
31 #include "cli/cli_utils.hpp"
32 
33 #include "cli/cli_tcat.hpp"
34 #include "common/code_utils.hpp"
35 #include "common/error.hpp"
36 
37 #include <openthread/ble_secure.h>
38 
39 #include <mbedtls/oid.h>
40 #include <openthread/error.h>
41 #include <openthread/tcat.h>
42 #include <openthread/platform/ble.h>
43 
44 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
45 
46 // DeviceCert1 default identity for TCAT certification testing.
47 // WARNING: storage of private keys in code or program memory MUST NOT be used in production.
48 // The below code is for testing purposes only. For production, secure key storage must be
49 // used to store private keys.
50 #define OT_CLI_TCAT_X509_CERT                                            \
51     "-----BEGIN CERTIFICATE-----\n"                                      \
52     "MIIB6TCCAZCgAwIBAgICNekwCgYIKoZIzj0EAwIwcTEmMCQGA1UEAwwdVGhyZWFk\n" \
53     "IENlcnRpZmljYXRpb24gRGV2aWNlQ0ExGTAXBgNVBAoMEFRocmVhZCBHcm91cCBJ\n" \
54     "bmMxEjAQBgNVBAcMCVNhbiBSYW1vbjELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVT\n" \
55     "MCAXDTI0MDUwNzA5Mzk0NVoYDzI5OTkxMjMxMDkzOTQ1WjA8MSEwHwYDVQQDDBhU\n" \
56     "Q0FUIEV4YW1wbGUgRGV2aWNlQ2VydDExFzAVBgNVBAUTDjQ3MjMtOTgzMy0wMDAx\n" \
57     "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE11h/4vKZXVXv+1GDZo066spItloT\n" \
58     "dpCi0bux0jvpQSHLdQBIc+40zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdqNLMEkw\n" \
59     "HwYDVR0jBBgwFoAUX6sbKWiIodS0MaiGYefnZlnt+BkwEAYJKwYBBAGC3yoCBAMC\n" \
60     "AQUwFAYJKwYBBAGC3yoDBAcEBSABAQEBMAoGCCqGSM49BAMCA0cAMEQCIHWu+Rd1\n" \
61     "VRlzrD8KbuyJcJFTXh2sQ9UIrFIA7+4e/GVcAiAVBdGqTxbt3TGkBBllpafAUB2/\n" \
62     "s0GJj7E33oblqy5eHQ==\n"                                             \
63     "-----END CERTIFICATE-----\n"
64 
65 #define OT_CLI_TCAT_PRIV_KEY                                             \
66     "-----BEGIN EC PRIVATE KEY-----\n"                                   \
67     "MHcCAQEEIIqKM1QTlNaquV74W6Viz/ggXoLqlPOP6LagSyaFO3oUoAoGCCqGSM49\n" \
68     "AwEHoUQDQgAE11h/4vKZXVXv+1GDZo066spItloTdpCi0bux0jvpQSHLdQBIc+40\n" \
69     "zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdg==\n"                             \
70     "-----END EC PRIVATE KEY-----\n"
71 
72 #define OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE                             \
73     "-----BEGIN CERTIFICATE-----\n"                                      \
74     "MIICOzCCAeGgAwIBAgIJAKOc2hehOGoBMAoGCCqGSM49BAMCMHExJjAkBgNVBAMM\n" \
75     "HVRocmVhZCBDZXJ0aWZpY2F0aW9uIERldmljZUNBMRkwFwYDVQQKDBBUaHJlYWQg\n" \
76     "R3JvdXAgSW5jMRIwEAYDVQQHDAlTYW4gUmFtb24xCzAJBgNVBAgMAkNBMQswCQYD\n" \
77     "VQQGEwJVUzAeFw0yNDA1MDMyMDAyMThaFw00NDA0MjgyMDAyMThaMHExJjAkBgNV\n" \
78     "BAMMHVRocmVhZCBDZXJ0aWZpY2F0aW9uIERldmljZUNBMRkwFwYDVQQKDBBUaHJl\n" \
79     "YWQgR3JvdXAgSW5jMRIwEAYDVQQHDAlTYW4gUmFtb24xCzAJBgNVBAgMAkNBMQsw\n" \
80     "CQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGy850VBIPTkN3oL\n" \
81     "x++zIUsZk2k26w4fuieFz9oNvjdb5W14+Yf3mvGWsl4NHyLxqhmamVAR4h7zWRlZ\n" \
82     "0XyMVpKjYjBgMB4GA1UdEQQXMBWBE3RvbUB0aHJlYWRncm91cC5vcmcwDgYDVR0P\n" \
83     "AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFF+rGyloiKHUtDGo\n" \
84     "hmHn52ZZ7fgZMAoGCCqGSM49BAMCA0gAMEUCIQCTq1qjPZs9fAJB6ppTXs588Pnu\n" \
85     "eVFOwC8bd//D99KiHAIgU84kwFHIyDvFqu6y+u1hFqBGsiuTmKwZ2PHhVe/xK1k=\n" \
86     "-----END CERTIFICATE-----\n"
87 
88 namespace ot {
89 
90 namespace Cli {
91 
92 otTcatAdvertisedDeviceId sAdvertisedDeviceIds[OT_TCAT_DEVICE_ID_MAX];
93 otTcatGeneralDeviceId    sGeneralDeviceId;
94 
95 const char kPskdVendor[] = "JJJJJJ";
96 const char kUrl[]        = "dummy_url";
97 
IsDeviceIdSet(void)98 static bool IsDeviceIdSet(void)
99 {
100     bool ret = false;
101     for (const otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds)
102     {
103         if (vendorDeviceId.mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY)
104         {
105             ExitNow(ret = true);
106         }
107     }
108 exit:
109     return ret;
110 }
111 
HandleBleSecureReceive(otInstance * aInstance,const otMessage * aMessage,int32_t aOffset,otTcatApplicationProtocol aTcatApplicationProtocol,const char * aServiceName,void * aContext)112 static void HandleBleSecureReceive(otInstance               *aInstance,
113                                    const otMessage          *aMessage,
114                                    int32_t                   aOffset,
115                                    otTcatApplicationProtocol aTcatApplicationProtocol,
116                                    const char               *aServiceName,
117                                    void                     *aContext)
118 {
119     OT_UNUSED_VARIABLE(aContext);
120     OT_UNUSED_VARIABLE(aTcatApplicationProtocol);
121     OT_UNUSED_VARIABLE(aServiceName);
122 
123     static constexpr int     kTextMaxLen   = 100;
124     static constexpr uint8_t kBufPrefixLen = 5;
125 
126     uint16_t nLen;
127     uint8_t  buf[kTextMaxLen];
128 
129     nLen =
130         otMessageRead(aMessage, static_cast<uint16_t>(aOffset), buf + kBufPrefixLen, sizeof(buf) - kBufPrefixLen - 1);
131 
132     memcpy(buf, "RECV:", kBufPrefixLen);
133 
134     buf[nLen + kBufPrefixLen] = 0;
135 
136     IgnoreReturnValue(otBleSecureSendApplicationTlv(aInstance, buf, (uint16_t)strlen((char *)buf)));
137     IgnoreReturnValue(otBleSecureFlush(aInstance));
138 }
139 
Process(Arg aArgs[])140 template <> otError Tcat::Process<Cmd("advid")>(Arg aArgs[])
141 {
142     otError                  error = OT_ERROR_NONE;
143     otTcatAdvertisedDeviceId devId;
144     static const char *const kVendorIdTypes[] = {"clear", "oui24", "oui36", "discriminator", "ianapen"};
145 
146     mVendorInfo.mAdvertisedDeviceIds = sAdvertisedDeviceIds;
147 
148     if (aArgs[0].IsEmpty())
149     {
150         if (mVendorInfo.mAdvertisedDeviceIds[0].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY)
151         {
152             OutputLine("Set advertisedIds:");
153             for (size_t i = 0; mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdType != OT_TCAT_DEVICE_ID_EMPTY; i++)
154             {
155                 OutputFormat("type %s, value: ", kVendorIdTypes[mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdType]);
156                 OutputBytesLine(const_cast<uint8_t *>(mVendorInfo.mAdvertisedDeviceIds[i].mDeviceId),
157                                 mVendorInfo.mAdvertisedDeviceIds[i].mDeviceIdLen);
158             }
159         }
160         ExitNow();
161     }
162 
163     if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_OUI24])
164     {
165         devId.mDeviceIdType = OT_TCAT_DEVICE_ID_OUI24;
166     }
167     else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_OUI36])
168     {
169         devId.mDeviceIdType = OT_TCAT_DEVICE_ID_OUI36;
170     }
171     else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_DISCRIMINATOR])
172     {
173         devId.mDeviceIdType = OT_TCAT_DEVICE_ID_DISCRIMINATOR;
174     }
175     else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_IANAPEN])
176     {
177         devId.mDeviceIdType = OT_TCAT_DEVICE_ID_IANAPEN;
178     }
179     else if (aArgs[0] == kVendorIdTypes[OT_TCAT_DEVICE_ID_EMPTY])
180     {
181         for (otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds)
182         {
183             vendorDeviceId.mDeviceIdType = OT_TCAT_DEVICE_ID_EMPTY;
184             vendorDeviceId.mDeviceIdLen  = 0;
185         }
186         ExitNow();
187     }
188     else
189     {
190         ExitNow(error = OT_ERROR_INVALID_ARGS);
191     }
192 
193     if (!aArgs[1].IsEmpty() && aArgs[1].GetLength() < (OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE * 2 + 1))
194     {
195         devId.mDeviceIdLen = OT_TCAT_MAX_ADVERTISED_DEVICEID_SIZE;
196         SuccessOrExit(error = aArgs[1].ParseAsHexString(devId.mDeviceIdLen, devId.mDeviceId));
197         for (otTcatAdvertisedDeviceId &vendorDeviceId : sAdvertisedDeviceIds)
198         {
199             if (vendorDeviceId.mDeviceIdType == devId.mDeviceIdType ||
200                 vendorDeviceId.mDeviceIdType == OT_TCAT_DEVICE_ID_EMPTY)
201             {
202                 vendorDeviceId = devId;
203                 break;
204             }
205         }
206     }
207     else
208     {
209         ExitNow(error = OT_ERROR_INVALID_ARGS);
210     }
211 exit:
212     return error;
213 }
214 
Process(Arg aArgs[])215 template <> otError Tcat::Process<Cmd("devid")>(Arg aArgs[])
216 {
217     otError error = OT_ERROR_NONE;
218 
219     if (aArgs[0].IsEmpty())
220     {
221         if (sGeneralDeviceId.mDeviceIdLen != 0)
222         {
223             OutputLine("TCAT DeviceId:");
224             OutputBytesLine(sGeneralDeviceId.mDeviceId, sGeneralDeviceId.mDeviceIdLen);
225         }
226         ExitNow();
227     }
228 
229     if (aArgs[0] == "clear")
230     {
231         ClearAllBytes(sGeneralDeviceId);
232     }
233     else
234     {
235         VerifyOrExit(aArgs[0].GetLength() < (OT_TCAT_MAX_DEVICEID_SIZE * 2 + 1), error = OT_ERROR_INVALID_ARGS);
236         sGeneralDeviceId.mDeviceIdLen = OT_TCAT_MAX_DEVICEID_SIZE;
237         SuccessOrExit(error = aArgs[0].ParseAsHexString(sGeneralDeviceId.mDeviceIdLen, sGeneralDeviceId.mDeviceId));
238     }
239 
240 exit:
241     return error;
242 }
243 
Process(Arg aArgs[])244 template <> otError Tcat::Process<Cmd("start")>(Arg aArgs[])
245 {
246     OT_UNUSED_VARIABLE(aArgs);
247 
248     otError error = OT_ERROR_NONE;
249 
250     ClearAllBytes(mVendorInfo);
251     mVendorInfo.mPskdString      = kPskdVendor;
252     mVendorInfo.mProvisioningUrl = kUrl;
253 
254     if (IsDeviceIdSet())
255     {
256         mVendorInfo.mAdvertisedDeviceIds = sAdvertisedDeviceIds;
257     }
258 
259     if (sGeneralDeviceId.mDeviceIdLen != 0)
260     {
261         mVendorInfo.mGeneralDeviceId = &sGeneralDeviceId;
262     }
263 
264     otBleSecureSetCertificate(GetInstancePtr(), reinterpret_cast<const uint8_t *>(OT_CLI_TCAT_X509_CERT),
265                               sizeof(OT_CLI_TCAT_X509_CERT), reinterpret_cast<const uint8_t *>(OT_CLI_TCAT_PRIV_KEY),
266                               sizeof(OT_CLI_TCAT_PRIV_KEY));
267 
268     otBleSecureSetCaCertificateChain(GetInstancePtr(),
269                                      reinterpret_cast<const uint8_t *>(OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE),
270                                      sizeof(OT_CLI_TCAT_TRUSTED_ROOT_CERTIFICATE));
271 
272     otBleSecureSetSslAuthMode(GetInstancePtr(), true);
273 
274     SuccessOrExit(error = otBleSecureSetTcatVendorInfo(GetInstancePtr(), &mVendorInfo));
275     SuccessOrExit(error = otBleSecureStart(GetInstancePtr(), nullptr, HandleBleSecureReceive, true, nullptr));
276     SuccessOrExit(error = otBleSecureTcatStart(GetInstancePtr(), nullptr));
277 
278 exit:
279     return error;
280 }
281 
Process(Arg aArgs[])282 template <> otError Tcat::Process<Cmd("stop")>(Arg aArgs[])
283 {
284     OT_UNUSED_VARIABLE(aArgs);
285 
286     otBleSecureStop(GetInstancePtr());
287 
288     return OT_ERROR_NONE;
289 }
290 
Process(Arg aArgs[])291 otError Tcat::Process(Arg aArgs[])
292 {
293 #define CmdEntry(aCommandString)                            \
294     {                                                       \
295         aCommandString, &Tcat::Process<Cmd(aCommandString)> \
296     }
297 
298     static constexpr Command kCommands[] = {CmdEntry("advid"), CmdEntry("devid"), CmdEntry("start"), CmdEntry("stop")};
299 
300     static_assert(BinarySearch::IsSorted(kCommands), "kCommands is not sorted");
301 
302 #undef CmdEntry
303 
304     otError        error = OT_ERROR_NONE;
305     const Command *command;
306 
307     if (aArgs[0].IsEmpty() || (aArgs[0] == "help"))
308     {
309         OutputCommandTable(kCommands);
310         ExitNow(error = aArgs[0].IsEmpty() ? error : OT_ERROR_NONE);
311     }
312 
313     command = BinarySearch::Find(aArgs[0].GetCString(), kCommands);
314     VerifyOrExit(command != nullptr);
315 
316     error = (this->*command->mHandler)(aArgs + 1);
317 
318 exit:
319     return error;
320 }
321 
322 } // namespace Cli
323 } // namespace ot
324 #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE && OPENTHREAD_CONFIG_CLI_BLE_SECURE_ENABLE
325