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 /**
30  * @file
31  *  Implements the TCAT Agent service.
32  */
33 
34 #ifndef TCAT_AGENT_HPP_
35 #define TCAT_AGENT_HPP_
36 
37 #include "openthread-core-config.h"
38 
39 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
40 
41 #include <openthread/tcat.h>
42 #include <openthread/platform/ble.h>
43 
44 #include "common/as_core_type.hpp"
45 #include "common/callback.hpp"
46 #include "common/locator.hpp"
47 #include "common/log.hpp"
48 #include "common/message.hpp"
49 #include "common/non_copyable.hpp"
50 #include "mac/mac_types.hpp"
51 #include "meshcop/dataset.hpp"
52 #include "meshcop/meshcop.hpp"
53 #include "meshcop/meshcop_tlvs.hpp"
54 #include "meshcop/secure_transport.hpp"
55 
56 namespace ot {
57 
58 namespace Ble {
59 class BleSecure;
60 }
61 
62 namespace MeshCoP {
63 
64 class TcatAgent : public InstanceLocator, private NonCopyable
65 {
66 public:
67     /**
68      * Pointer to call when application data was received over the TLS connection.
69      *
70      *  Please see otHandleTcatApplicationDataReceive for details.
71      *
72      */
73     typedef otHandleTcatApplicationDataReceive AppDataReceiveCallback;
74 
75     /**
76      * Pointer to call to notify the completion of a join operation.
77      *
78      * Please see otHandleTcatJoin for details.
79      *
80      */
81     typedef otHandleTcatJoin JoinCallback;
82 
83     /**
84      * Represents a TCAT command class.
85      *
86      */
87     enum CommandClass
88     {
89         kGeneral            = OT_TCAT_COMMAND_CLASS_GENERAL,         ///< TCAT commands related to general operations
90         kCommissioning      = OT_TCAT_COMMAND_CLASS_COMMISSIONING,   ///< TCAT commands related to commissioning
91         kExtraction         = OT_TCAT_COMMAND_CLASS_EXTRACTION,      ///< TCAT commands related to key extraction
92         kTlvDecommissioning = OT_TCAT_COMMAND_CLASS_DECOMMISSIONING, ///< TCAT commands related to de-commissioning
93         kApplication        = OT_TCAT_COMMAND_CLASS_APPLICATION,     ///< TCAT commands related to application layer
94         kInvalid ///< TCAT command belongs to reserved pool or is invalid
95     };
96 
97     /**
98      * The certificate authorization field header type to indicate the type and version of the certificate.
99      *
100      */
101     enum CertificateAuthorizationFieldHeader : uint8_t
102     {
103         kCommissionerFlag = 1 << 0, ///< TCAT commissioner ('1') or device ('0')
104         kHeaderVersion    = 0xD0,   ///< Header version (3 bits)
105     };
106 
107     /**
108      * The command class flag type to indicate which requirements apply for a given command class.
109      *
110      */
111     enum CommandClassFlags : uint8_t
112     {
113         kAccessFlag        = 1 << 0, ///< Access to the command class (device: without without additional requirements).
114         kPskdFlag          = 1 << 1, ///< Access requires proof-of-possession of the device's PSKd
115         kNetworkNameFlag   = 1 << 2, ///< Access requires matching network name
116         kExtendedPanIdFlag = 1 << 3, ///< Access requires matching XPANID
117         kThreadDomainFlag  = 1 << 4, ///< Access requires matching XPANID
118         kPskcFlag          = 1 << 5, ///< Access requires proof-of-possession of the device's PSKc
119     };
120 
121     /**
122      *
123      * Represents a data structure for storing TCAT Commissioner authorization information in the
124      * certificate ASN.1 field 1.3.6.1.4.1.44970.3.
125      *
126      */
127     OT_TOOL_PACKED_BEGIN
128     struct CertificateAuthorizationField
129     {
130         CertificateAuthorizationFieldHeader mHeader;               ///< Typ and version
131         CommandClassFlags                   mCommissioningFlags;   ///< Command class flags
132         CommandClassFlags                   mExtractionFlags;      ///< Command class flags
133         CommandClassFlags                   mDecommissioningFlags; ///< Command class flags
134         CommandClassFlags                   mApplicationFlags;     ///< Command class flags
135 
136     } OT_TOOL_PACKED_END;
137 
138     typedef CertificateAuthorizationField CertificateAuthorizationField;
139 
140     /**
141      * Represents the TCAT vendor information.
142      *
143      */
144     class VendorInfo : public otTcatVendorInfo
145     {
146     public:
147         /**
148          * Validates whether the TCAT vendor information is valid.
149          *
150          * @returns Whether the parameters are valid.
151          *
152          */
153         bool IsValid(void) const;
154     };
155 
156     /**
157      * TCAT Command TLV Types.
158      *
159      */
160     enum CommandTlvType : uint8_t
161     {
162         // Command Class General
163         kTlvResponseWithStatus        = 1,  ///< TCAT response with status value TLV
164         kTlvResponseWithPayload       = 2,  ///< TCAT response with payload TLV
165         kTlvResponseEvent             = 3,  ///< TCAT response event TLV (reserved)
166         kTlvGetNetworkName            = 8,  ///< TCAT network name query TLV
167         kTlvDisconnect                = 9,  ///< TCAT disconnect request TLV
168         kTlvPing                      = 10, ///< TCAT ping request TLV
169         kTlvGetDeviceId               = 11, ///< TCAT device ID query TLV
170         kTlvGetExtendedPanID          = 12, ///< TCAT extended PAN ID query TLV
171         kTlvGetProvisioningURL        = 13, ///< TCAT provisioning URL query TLV
172         kTlvPresentPskdHash           = 16, ///< TCAT commissioner rights elevation request TLV using PSKd hash
173         kTlvPresentPskcHash           = 17, ///< TCAT commissioner rights elevation request TLV using PSKc hash
174         kTlvPresentInstallCodeHash    = 18, ///< TCAT commissioner rights elevation request TLV using install code
175         kTlvRequestRandomNumChallenge = 19, ///< TCAT random number challenge query TLV
176         kTlvRequestPskdHash           = 20, ///< TCAT PSKd hash request TLV
177 
178         // Command Class Commissioning
179         kTlvSetActiveOperationalDataset            = 32, ///< TCAT active operational dataset TLV
180         kTlvSetActiveOperationalDatasetAlternative = 33, ///< TCAT active operational dataset alternative #1 TLV
181         kTlvGetProvisioningTlvs                    = 36, ///< TCAT provisioning TLVs query TLV
182         kTlvGetCommissionerCertificate             = 37, ///< TCAT commissioner certificate query TLV
183         kTlvGetDiagnosticTlvs                      = 38, ///< TCAT diagnostics TLVs query TLV
184         kTlvStartThreadInterface                   = 39, ///< TCAT start thread interface request TLV
185         kTlvStopThreadInterface                    = 40, ///< TCAT stop thread interface request TLV
186 
187         // Command Class Extraction
188         kTlvGetActiveOperationalDataset            = 64, ///< TCAT active oerational dataset query TLV
189         kTlvGetActiveOperationalDatasetAlternative = 65, ///< TCAT active oerational dataset alternative #1 query TLV
190 
191         // Command Class Decommissioning
192         kTlvDecommission = 96, ///< TCAT decommission request TLV
193 
194         // Command Class Application
195         kTlvSelectApplicationLayerUdp = 128, ///< TCAT select UDP protocol application layer request TLV
196         kTlvSelectApplicationLayerTcp = 129, ///< TCAT select TCP protocol application layer request TLV
197         kTlvSendApplicationData       = 130, ///< TCAT send application data TLV
198         kTlvSendVendorSpecificData    = 159, ///< TCAT send vendor specific command or data TLV
199 
200         // Command Class CCM
201         kTlvSetLDevIdOperationalCert = 160, ///< TCAT LDevID operational certificate TLV
202         kTlvSetLDevIdPrivateKey      = 161, ///< TCAT LDevID operational certificate pricate key TLV
203         kTlvSetDomainCaCert          = 162, ///< TCAT domain CA certificate TLV
204     };
205 
206     /**
207      * TCAT Response Types.
208      *
209      */
210     enum StatusCode : uint8_t
211     {
212         kStatusSuccess      = OT_TCAT_STATUS_SUCCESS,       ///< Command or request was successfully processed
213         kStatusUnsupported  = OT_TCAT_STATUS_UNSUPPORTED,   ///< Requested command or received TLV is not supported
214         kStatusParseError   = OT_TCAT_STATUS_PARSE_ERROR,   ///< Request / command could not be parsed correctly
215         kStatusValueError   = OT_TCAT_STATUS_VALUE_ERROR,   ///< The value of the transmitted TLV has an error
216         kStatusGeneralError = OT_TCAT_STATUS_GENERAL_ERROR, ///< An error not matching any other category occurred
217         kStatusBusy         = OT_TCAT_STATUS_BUSY,          ///< Command cannot be executed because the resource is busy
218         kStatusUndefined    = OT_TCAT_STATUS_UNDEFINED,     ///< The requested value, data or service is not defined
219                                                             ///< (currently) or not present
220         kStatusHashError = OT_TCAT_STATUS_HASH_ERROR, ///< The hash value presented by the commissioner was incorrect
221         kStatusUnauthorized =
222             OT_TCAT_STATUS_UNAUTHORIZED, ///< Sender does not have sufficient authorization for the given command
223     };
224 
225     /**
226      * Represents TCAT application protocol.
227      *
228      */
229     enum TcatApplicationProtocol : uint8_t
230     {
231         kApplicationProtocolNone =
232             OT_TCAT_APPLICATION_PROTOCOL_NONE, ///< Message which has been sent without activating the TCAT agent
233         kApplicationProtocolUdp = OT_TCAT_APPLICATION_PROTOCOL_STATUS, ///< Message directed to a UDP service
234         kApplicationProtocolTcp = OT_TCAT_APPLICATION_PROTOCOL_TCP,    ///< Message directed to a TCP service
235     };
236 
237     /**
238      * Represents a TCAT certificate V3 extension attribute (OID 1.3.6.1.4.1.44970.x).
239      *
240      */
241     enum TcatCertificateAttribute
242     {
243         kCertificateDomainName         = 1,
244         kCertificateThreadVersion      = 2,
245         kCertificateAuthorizationField = 3,
246         kCertificateNetworkName        = 4,
247         kCertificateExtendedPanId      = 5,
248     };
249 
250     /**
251      * Represents TCAT status.
252      *
253      */
254     enum State : uint8_t
255     {
256         kStateDisabled,
257         kStateEnabled,
258         kStateConnected,
259     };
260 
261     /**
262      * Represents Device ID type.
263      *
264      */
265     enum TcatDeviceIdType : uint8_t
266     {
267         kTcatDeviceIdEmpty         = OT_TCAT_DEVICE_ID_EMPTY,
268         kTcatDeviceIdOui24         = OT_TCAT_DEVICE_ID_OUI24,
269         kTcatDeviceIdOui36         = OT_TCAT_DEVICE_ID_OUI36,
270         kTcatDeviceIdDiscriminator = OT_TCAT_DEVICE_ID_DISCRIMINATOR,
271         kTcatDeviceIdIanaPen       = OT_TCAT_DEVICE_ID_IANAPEN,
272     };
273 
274     /**
275      * Initializes the TCAT agent object.
276      *
277      * @param[in]  aInstance     A reference to the OpenThread instance.
278      *
279      */
280     explicit TcatAgent(Instance &aInstance);
281 
282     /**
283      * Enables the TCAT agent.
284      *
285      * @param[in] aAppDataReceiveCallback   A pointer to a function that is called when the user data is received.
286      * @param[in] aHandler                  A pointer to a function that is called when the join operation completes.
287      * @param[in] aContext                  A context pointer.
288      *
289      * @retval kErrorNone        Successfully started the TCAT agent.
290      * @retval kErrorFailed      Failed to start due to missing vendor info.
291      *
292      */
293     Error Start(AppDataReceiveCallback aAppDataReceiveCallback, JoinCallback aHandler, void *aContext);
294 
295     /**
296      * Stops the TCAT agent.
297      *
298      */
299     void Stop(void);
300 
301     /**
302      * Set the TCAT Vendor Info object
303      *
304      * @param[in] aVendorInfo A pointer to the Vendor Information (must remain valid after the method call).
305      *
306      */
307     Error SetTcatVendorInfo(const VendorInfo &aVendorInfo);
308 
309     /**
310      * Indicates whether or not the TCAT agent is enabled.
311      *
312      * @retval TRUE   The TCAT agent is enabled.
313      * @retval FALSE  The TCAT agent is not enabled.
314      *
315      */
IsEnabled(void) const316     bool IsEnabled(void) const { return mState != kStateDisabled; }
317 
318     /**
319      * Indicates whether or not the TCAT agent is connected.
320      *
321      * @retval TRUE   The TCAT agent is connected with a TCAT commissioner.
322      * @retval FALSE  The TCAT agent is not connected.
323      *
324      */
IsConnected(void) const325     bool IsConnected(void) const { return mState == kStateConnected; }
326 
327     /**
328      * Indicates whether or not a TCAT command class is authorized for use.
329      *
330      * @param[in] aCommandClass Command class to subject to authorization check.
331      *
332      * @retval TRUE   The command class is authorized for use by the present TCAT commissioner.
333      * @retval FALSE  The command class is not authorized for use.
334      *
335      */
336     bool IsCommandClassAuthorized(CommandClass aCommandClass) const;
337 
338     /**
339      * Gets TCAT advertisement data.
340      *
341      * @param[out] aLen               Advertisement data length (up to OT_TCAT_ADVERTISEMENT_MAX_LEN).
342      * @param[out] aAdvertisementData Advertisement data.
343      *
344      * @retval kErrorNone           Successfully retrieved the TCAT advertisement data.
345      * @retval kErrorInvalidArgs    The data could not be retrieved, or aAdvertisementData is null.
346      *
347      */
348     Error GetAdvertisementData(uint16_t &aLen, uint8_t *aAdvertisementData);
349 
350 private:
351     Error Connected(MeshCoP::SecureTransport &aTlsContext);
352     void  Disconnected(void);
353 
354     Error HandleSingleTlv(const Message &aIncomingMessage, Message &aOutgoingMessage);
355     Error HandleSetActiveOperationalDataset(const Message &aIncomingMessage, uint16_t aOffset, uint16_t aLength);
356     Error HandleDecomission(void);
357     Error HandlePing(const Message &aIncomingMessage,
358                      Message       &aOutgoingMessage,
359                      uint16_t       aOffset,
360                      uint16_t       aLength,
361                      bool          &response);
362     Error HandleGetNetworkName(Message &aOutgoingMessage, bool &response);
363     Error HandleGetDeviceId(Message &aOutgoingMessage, bool &response);
364     Error HandleGetExtPanId(Message &aOutgoingMessage, bool &response);
365     Error HandleGetProvisioningUrl(Message &aOutgoingMessage, bool &response);
366     Error HandleStartThreadInterface(void);
367 
368     bool         CheckCommandClassAuthorizationFlags(CommandClassFlags aCommissionerCommandClassFlags,
369                                                      CommandClassFlags aDeviceCommandClassFlags,
370                                                      Dataset          *aDataset) const;
371     bool         CanProcessTlv(uint8_t aTlvType) const;
372     CommandClass GetCommandClass(uint8_t aTlvType) const;
373 
374     static constexpr uint16_t kJoinerUdpPort            = OPENTHREAD_CONFIG_JOINER_UDP_PORT;
375     static constexpr uint16_t kPingPayloadMaxLength     = 512;
376     static constexpr uint16_t kProvisioningUrlMaxLength = 64;
377     static constexpr uint16_t kTcatMaxDeviceIdSize      = OT_TCAT_MAX_DEVICEID_SIZE;
378 
379     JoinerPskd                       mJoinerPskd;
380     const VendorInfo                *mVendorInfo;
381     Callback<JoinCallback>           mJoinCallback;
382     Callback<AppDataReceiveCallback> mAppDataReceiveCallback;
383     CertificateAuthorizationField    mCommissionerAuthorizationField;
384     CertificateAuthorizationField    mDeviceAuthorizationField;
385     TcatApplicationProtocol          mCurrentApplicationProtocol;
386     NetworkName                      mCommissionerNetworkName;
387     NetworkName                      mCommissionerDomainName;
388     ExtendedPanId                    mCommissionerExtendedPanId;
389     char                             mCurrentServiceName[OT_TCAT_MAX_SERVICE_NAME_LENGTH + 1];
390     State                            mState;
391     bool                             mCommissionerHasNetworkName : 1;
392     bool                             mCommissionerHasDomainName : 1;
393     bool                             mCommissionerHasExtendedPanId : 1;
394 
395     friend class Ble::BleSecure;
396 };
397 
398 } // namespace MeshCoP
399 
400 DefineCoreType(otTcatVendorInfo, MeshCoP::TcatAgent::VendorInfo);
401 
402 DefineMapEnum(otTcatApplicationProtocol, MeshCoP::TcatAgent::TcatApplicationProtocol);
403 DefineMapEnum(otTcatAdvertisedDeviceIdType, MeshCoP::TcatAgent::TcatDeviceIdType);
404 
405 // Command class TLVs
406 typedef UintTlvInfo<MeshCoP::TcatAgent::kTlvResponseWithStatus, uint8_t> ResponseWithStatusTlv;
407 
408 /**
409  * Represent Device Type and Status
410  *
411  */
412 struct DeviceTypeAndStatus
413 {
414     uint8_t mRsv : 1;
415     bool    mMultiradioSupport : 1;
416     bool    mStoresActiveOpertonalDataset : 1;
417     bool    mIsCommisionned : 1;
418     bool    mThreadNetworkActive : 1;
419     bool    mIsBorderRouter : 1;
420     bool    mRxOnWhenIdle : 1;
421     bool    mDeviceType : 1;
422 };
423 
424 static constexpr uint8_t kTlvVendorOui24Length         = 3;
425 static constexpr uint8_t kTlvVendorOui36Length         = 5;
426 static constexpr uint8_t kTlvDeviceDiscriminatorLength = 5;
427 static constexpr uint8_t kTlvBleLinkCapabilitiesLength = 1;
428 static constexpr uint8_t kTlvDeviceTypeAndStatusLength = 1;
429 static constexpr uint8_t kTlvVendorIanaPenLength       = 4;
430 
431 enum TcatAdvertisementTlvType : uint8_t
432 {
433     kTlvVendorOui24         = 1, ///< TCAT vendor OUI 24
434     kTlvVendorOui36         = 2, ///< TCAT vendor OUI 36
435     kTlvDeviceDiscriminator = 3, ///< TCAT random vendor discriminator
436     kTlvDeviceTypeAndStatus = 4, ///< TCAT Thread device type and status
437     kTlvBleLinkCapabilities = 5, ///< TCAT BLE link capabilities of device
438     kTlvVendorIanaPen       = 6, ///< TCAT Vendor IANA PEN
439 };
440 
441 } // namespace ot
442 
443 #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
444 
445 #endif // TCAT_AGENT_HPP_
446