1 /*
2  *  Copyright (c) 2024, 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 "test_platform.h"
32 #include "test_util.h"
33 
34 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
35 
36 #include <openthread/ble_secure.h>
37 
38 #define OT_TCAT_X509_CERT                                                \
39     "-----BEGIN CERTIFICATE-----\n"                                      \
40     "MIIB6TCCAZCgAwIBAgICNekwCgYIKoZIzj0EAwIwcTEmMCQGA1UEAwwdVGhyZWFk\n" \
41     "IENlcnRpZmljYXRpb24gRGV2aWNlQ0ExGTAXBgNVBAoMEFRocmVhZCBHcm91cCBJ\n" \
42     "bmMxEjAQBgNVBAcMCVNhbiBSYW1vbjELMAkGA1UECAwCQ0ExCzAJBgNVBAYTAlVT\n" \
43     "MCAXDTI0MDUwNzA5Mzk0NVoYDzI5OTkxMjMxMDkzOTQ1WjA8MSEwHwYDVQQDDBhU\n" \
44     "Q0FUIEV4YW1wbGUgRGV2aWNlQ2VydDExFzAVBgNVBAUTDjQ3MjMtOTgzMy0wMDAx\n" \
45     "MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE11h/4vKZXVXv+1GDZo066spItloT\n" \
46     "dpCi0bux0jvpQSHLdQBIc+40zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdqNLMEkw\n" \
47     "HwYDVR0jBBgwFoAUX6sbKWiIodS0MaiGYefnZlnt+BkwEAYJKwYBBAGC3yoCBAMC\n" \
48     "AQUwFAYJKwYBBAGC3yoDBAcEBSABAQEBMAoGCCqGSM49BAMCA0cAMEQCIHWu+Rd1\n" \
49     "VRlzrD8KbuyJcJFTXh2sQ9UIrFIA7+4e/GVcAiAVBdGqTxbt3TGkBBllpafAUB2/\n" \
50     "s0GJj7E33oblqy5eHQ==\n"                                             \
51     "-----END CERTIFICATE-----\n"
52 
53 #define OT_TCAT_PRIV_KEY                                                 \
54     "-----BEGIN EC PRIVATE KEY-----\n"                                   \
55     "MHcCAQEEIIqKM1QTlNaquV74W6Viz/ggXoLqlPOP6LagSyaFO3oUoAoGCCqGSM49\n" \
56     "AwEHoUQDQgAE11h/4vKZXVXv+1GDZo066spItloTdpCi0bux0jvpQSHLdQBIc+40\n" \
57     "zVCxMDRUvbX//vJKGsSJKOVUlCojQ2wIdg==\n"                             \
58     "-----END EC PRIVATE KEY-----\n"
59 
60 #define OT_TCAT_TRUSTED_ROOT_CERTIFICATE                                 \
61     "-----BEGIN CERTIFICATE-----\n"                                      \
62     "MIICOzCCAeGgAwIBAgIJAKOc2hehOGoBMAoGCCqGSM49BAMCMHExJjAkBgNVBAMM\n" \
63     "HVRocmVhZCBDZXJ0aWZpY2F0aW9uIERldmljZUNBMRkwFwYDVQQKDBBUaHJlYWQg\n" \
64     "R3JvdXAgSW5jMRIwEAYDVQQHDAlTYW4gUmFtb24xCzAJBgNVBAgMAkNBMQswCQYD\n" \
65     "VQQGEwJVUzAeFw0yNDA1MDMyMDAyMThaFw00NDA0MjgyMDAyMThaMHExJjAkBgNV\n" \
66     "BAMMHVRocmVhZCBDZXJ0aWZpY2F0aW9uIERldmljZUNBMRkwFwYDVQQKDBBUaHJl\n" \
67     "YWQgR3JvdXAgSW5jMRIwEAYDVQQHDAlTYW4gUmFtb24xCzAJBgNVBAgMAkNBMQsw\n" \
68     "CQYDVQQGEwJVUzBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGy850VBIPTkN3oL\n" \
69     "x++zIUsZk2k26w4fuieFz9oNvjdb5W14+Yf3mvGWsl4NHyLxqhmamVAR4h7zWRlZ\n" \
70     "0XyMVpKjYjBgMB4GA1UdEQQXMBWBE3RvbUB0aHJlYWRncm91cC5vcmcwDgYDVR0P\n" \
71     "AQH/BAQDAgGGMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFF+rGyloiKHUtDGo\n" \
72     "hmHn52ZZ7fgZMAoGCCqGSM49BAMCA0gAMEUCIQCTq1qjPZs9fAJB6ppTXs588Pnu\n" \
73     "eVFOwC8bd//D99KiHAIgU84kwFHIyDvFqu6y+u1hFqBGsiuTmKwZ2PHhVe/xK1k=\n" \
74     "-----END CERTIFICATE-----\n"
75 
76 namespace ot {
77 
78 class TestBleSecure
79 {
80 public:
TestBleSecure(void)81     TestBleSecure(void)
82         : mIsConnected(false)
83         , mIsBleConnectionOpen(false)
84     {
85     }
86 
HandleBleSecureConnect(bool aConnected,bool aBleConnectionOpen)87     void HandleBleSecureConnect(bool aConnected, bool aBleConnectionOpen)
88     {
89         mIsConnected         = aConnected;
90         mIsBleConnectionOpen = aBleConnectionOpen;
91     }
92 
IsConnected(void) const93     bool IsConnected(void) const { return mIsConnected; }
IsBleConnectionOpen(void) const94     bool IsBleConnectionOpen(void) const { return mIsBleConnectionOpen; }
95 
96 private:
97     bool mIsConnected;
98     bool mIsBleConnectionOpen;
99 };
100 
HandleBleSecureConnect(otInstance * aInstance,bool aConnected,bool aBleConnectionOpen,void * aContext)101 static void HandleBleSecureConnect(otInstance *aInstance, bool aConnected, bool aBleConnectionOpen, void *aContext)
102 {
103     OT_UNUSED_VARIABLE(aInstance);
104 
105     static_cast<TestBleSecure *>(aContext)->HandleBleSecureConnect(aConnected, aBleConnectionOpen);
106 }
107 
TestTcat(void)108 void TestTcat(void)
109 {
110     const char         kPskdVendor[]                  = "J01NM3";
111     const char         kUrl[]                         = "dummy_url";
112     constexpr uint16_t kConnectionId                  = 0;
113     const int          kCertificateThreadVersion      = 2;
114     const int          kCertificateAuthorizationField = 3;
115     const uint8_t      expectedTcatAuthField[5]       = {0x20, 0x01, 0x01, 0x01, 0x01};
116     uint8_t            attributeBuffer[8];
117     size_t             attributeLen;
118 
119     TestBleSecure ble;
120     Instance     *instance = testInitInstance();
121 
122     otTcatVendorInfo vendorInfo = {.mProvisioningUrl = kUrl, .mPskdString = kPskdVendor};
123 
124     otBleSecureSetCertificate(instance, reinterpret_cast<const uint8_t *>(OT_TCAT_X509_CERT), sizeof(OT_TCAT_X509_CERT),
125                               reinterpret_cast<const uint8_t *>(OT_TCAT_PRIV_KEY), sizeof(OT_TCAT_PRIV_KEY));
126 
127     otBleSecureSetCaCertificateChain(instance, reinterpret_cast<const uint8_t *>(OT_TCAT_TRUSTED_ROOT_CERTIFICATE),
128                                      sizeof(OT_TCAT_TRUSTED_ROOT_CERTIFICATE));
129 
130     otBleSecureSetSslAuthMode(instance, true);
131 
132     // Validate BLE secure and Tcat start APIs
133     SuccessOrQuit(otBleSecureSetTcatVendorInfo(instance, &vendorInfo));
134     VerifyOrQuit(otBleSecureTcatStart(instance, nullptr) == kErrorInvalidState);
135     SuccessOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, &ble));
136     VerifyOrQuit(otBleSecureStart(instance, HandleBleSecureConnect, nullptr, true, nullptr) == kErrorAlready);
137     SuccessOrQuit(otBleSecureTcatStart(instance, nullptr));
138 
139     // Validate connection callbacks when platform informs that peer has connected/disconnected
140     VerifyOrQuit(!otBleSecureIsConnected(instance));
141     otPlatBleGapOnConnected(instance, kConnectionId);
142     VerifyOrQuit(!ble.IsConnected() && ble.IsBleConnectionOpen());
143     otPlatBleGapOnDisconnected(instance, kConnectionId);
144     VerifyOrQuit(!ble.IsConnected() && !ble.IsBleConnectionOpen());
145 
146     // Verify that Thread-attribute parsing isn't available yet when not connected as client or server.
147     attributeLen = sizeof(attributeBuffer);
148     VerifyOrQuit(otBleSecureGetThreadAttributeFromPeerCertificate(instance, kCertificateAuthorizationField,
149                                                                   &attributeBuffer[0],
150                                                                   &attributeLen) == kErrorInvalidState);
151     attributeLen = sizeof(attributeBuffer);
152     VerifyOrQuit(otBleSecureGetThreadAttributeFromOwnCertificate(
153                      instance, kCertificateThreadVersion, &attributeBuffer[0], &attributeLen) == kErrorInvalidState);
154 
155     // Validate connection callbacks when calling `otBleSecureDisconnect()`
156     otPlatBleGapOnConnected(instance, kConnectionId);
157     VerifyOrQuit(!ble.IsConnected() && ble.IsBleConnectionOpen());
158     otBleSecureDisconnect(instance);
159     VerifyOrQuit(!ble.IsConnected() && !ble.IsBleConnectionOpen());
160 
161     // Validate TLS connection can be started only when peer is connected
162     otPlatBleGapOnConnected(instance, kConnectionId);
163     SuccessOrQuit(otBleSecureConnect(instance));
164     VerifyOrQuit(otBleSecureIsConnectionActive(instance));
165 
166     // Once in TLS client connecting state, the below cert eval functions are available.
167     // Test that the Thread-specific attributes can be decoded properly.
168     attributeLen = 1;
169     SuccessOrQuit(otBleSecureGetThreadAttributeFromOwnCertificate(instance, kCertificateThreadVersion,
170                                                                   &attributeBuffer[0], &attributeLen));
171     VerifyOrQuit(attributeLen == 1 && attributeBuffer[0] >= kThreadVersion1p4);
172 
173     static_assert(5 == sizeof(expectedTcatAuthField), "expectedTcatAuthField size incorrect for test");
174     attributeLen = 5;
175     SuccessOrQuit(otBleSecureGetThreadAttributeFromOwnCertificate(instance, kCertificateAuthorizationField,
176                                                                   &attributeBuffer[0], &attributeLen));
177     VerifyOrQuit(attributeLen == 5 && memcmp(&expectedTcatAuthField, &attributeBuffer, attributeLen) == 0);
178 
179     // Validate TLS connection can be started only when peer is connected
180     otBleSecureDisconnect(instance);
181     VerifyOrQuit(otBleSecureConnect(instance) == kErrorInvalidState);
182 
183     // Validate Tcat state changes after stopping BLE secure
184     VerifyOrQuit(otBleSecureIsTcatEnabled(instance));
185     otBleSecureStop(instance);
186     VerifyOrQuit(!otBleSecureIsTcatEnabled(instance));
187 
188     testFreeInstance(instance);
189 }
190 
191 } // namespace ot
192 
193 #endif // OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
194 
main(void)195 int main(void)
196 {
197 #if OPENTHREAD_CONFIG_BLE_TCAT_ENABLE
198     ot::TestTcat();
199     printf("All tests passed\n");
200 #else
201     printf("Tcat is not enabled\n");
202     return -1;
203 #endif
204     return 0;
205 }
206