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