LCOV - code coverage report
Current view: top level - app/server - OnboardingCodesUtil.cpp (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 0 100 0.0 %
Date: 2024-02-15 08:20:41 Functions: 0 11 0.0 %

          Line data    Source code
       1             : /*
       2             :  *
       3             :  *    Copyright (c) 2020 Project CHIP Authors
       4             :  *    All rights reserved.
       5             :  *
       6             :  *    Licensed under the Apache License, Version 2.0 (the "License");
       7             :  *    you may not use this file except in compliance with the License.
       8             :  *    You may obtain a copy of the License at
       9             :  *
      10             :  *        http://www.apache.org/licenses/LICENSE-2.0
      11             :  *
      12             :  *    Unless required by applicable law or agreed to in writing, software
      13             :  *    distributed under the License is distributed on an "AS IS" BASIS,
      14             :  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
      15             :  *    See the License for the specific language governing permissions and
      16             :  *    limitations under the License.
      17             :  */
      18             : 
      19             : #include <app/server/OnboardingCodesUtil.h>
      20             : 
      21             : #include <algorithm>
      22             : #include <inttypes.h>
      23             : 
      24             : #include <lib/support/CodeUtils.h>
      25             : #include <lib/support/ScopedBuffer.h>
      26             : #include <lib/support/logging/CHIPLogging.h>
      27             : #include <platform/CHIPDeviceLayer.h>
      28             : #include <platform/CommissionableDataProvider.h>
      29             : #include <platform/DeviceInstanceInfoProvider.h>
      30             : #include <setup_payload/ManualSetupPayloadGenerator.h>
      31             : #include <setup_payload/QRCodeSetupPayloadGenerator.h>
      32             : 
      33             : constexpr char kQrCodeBaseUrl[]                   = "https://project-chip.github.io/connectedhomeip/qrcode.html";
      34             : constexpr char kUrlDataAssignmentPhrase[]         = "?data=";
      35             : constexpr char kSpecialCharsUnreservedInRfc3986[] = "-._~";
      36             : 
      37             : using namespace ::chip::DeviceLayer;
      38             : 
      39           0 : void PrintOnboardingCodes(chip::RendezvousInformationFlags aRendezvousFlags)
      40             : {
      41           0 :     chip::PayloadContents payload;
      42             : 
      43           0 :     CHIP_ERROR err = GetPayloadContents(payload, aRendezvousFlags);
      44           0 :     if (err != CHIP_NO_ERROR)
      45             :     {
      46           0 :         ChipLogError(AppServer, "GetPayloadContents() failed: %" CHIP_ERROR_FORMAT, err.Format());
      47             :     }
      48             : 
      49           0 :     PrintOnboardingCodes(payload);
      50           0 : }
      51             : 
      52           0 : void PrintOnboardingCodes(const chip::PayloadContents & payload)
      53             : {
      54             :     char payloadBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1];
      55           0 :     chip::MutableCharSpan qrCode(payloadBuffer);
      56             : 
      57           0 :     if (GetQRCode(qrCode, payload) == CHIP_NO_ERROR)
      58             :     {
      59           0 :         PrintQrCodeURL(qrCode);
      60             :     }
      61             :     else
      62             :     {
      63           0 :         ChipLogError(AppServer, "Getting QR code failed!");
      64             :     }
      65             : 
      66           0 :     chip::MutableCharSpan manualPairingCode(payloadBuffer);
      67           0 :     if (GetManualPairingCode(manualPairingCode, payload) == CHIP_NO_ERROR)
      68             :     {
      69           0 :         ChipLogProgress(AppServer, "Manual pairing code: [%s]", manualPairingCode.data());
      70             :     }
      71             :     else
      72             :     {
      73           0 :         ChipLogError(AppServer, "Getting manual pairing code failed!");
      74             :     }
      75           0 : }
      76             : 
      77           0 : void PrintQrCodeURL(const chip::MutableCharSpan qrCode)
      78             : {
      79           0 :     chip::Platform::ScopedMemoryBuffer<char> qrCodeBuffer;
      80           0 :     const size_t qrCodeBufferMaxSize = strlen(kQrCodeBaseUrl) + strlen(kUrlDataAssignmentPhrase) + 3 * qrCode.size() + 1;
      81           0 :     qrCodeBuffer.Alloc(qrCodeBufferMaxSize);
      82             : 
      83           0 :     ChipLogProgress(AppServer, "SetupQRCode: [%s]", qrCode.data());
      84           0 :     if (GetQRCodeUrl(qrCodeBuffer.Get(), qrCodeBufferMaxSize, qrCode) == CHIP_NO_ERROR)
      85             :     {
      86           0 :         ChipLogProgress(AppServer, "Copy/paste the below URL in a browser to see the QR Code:");
      87           0 :         ChipLogProgress(AppServer, "%s", qrCodeBuffer.Get());
      88             :     }
      89           0 : }
      90             : 
      91             : #if CHIP_DEVICE_CONFIG_ENABLE_NFC
      92             : void ShareQRCodeOverNFC(chip::RendezvousInformationFlags aRendezvousFlags)
      93             : {
      94             :     // Get QR Code and emulate its content using NFC tag
      95             :     char payloadBuffer[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1];
      96             :     chip::MutableCharSpan qrCode(payloadBuffer);
      97             : 
      98             :     ReturnOnFailure(GetQRCode(qrCode, chip::RendezvousInformationFlags(chip::RendezvousInformationFlag::kBLE)));
      99             : 
     100             :     ReturnOnFailure(NFCMgr().StartTagEmulation(qrCode.data(), qrCode.size()));
     101             : }
     102             : #endif
     103             : 
     104           0 : CHIP_ERROR GetPayloadContents(chip::PayloadContents & aPayload, chip::RendezvousInformationFlags aRendezvousFlags)
     105             : {
     106           0 :     CHIP_ERROR err   = CHIP_NO_ERROR;
     107           0 :     aPayload.version = 0;
     108           0 :     aPayload.rendezvousInformation.SetValue(aRendezvousFlags);
     109             : 
     110           0 :     err = GetCommissionableDataProvider()->GetSetupPasscode(aPayload.setUpPINCode);
     111           0 :     if (err != CHIP_NO_ERROR)
     112             :     {
     113             : #if defined(CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE) && CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE
     114           0 :         ChipLogProgress(AppServer, "*** Using default EXAMPLE passcode %u ***",
     115             :                         static_cast<unsigned>(CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE));
     116           0 :         aPayload.setUpPINCode = CHIP_DEVICE_CONFIG_USE_TEST_SETUP_PIN_CODE;
     117             : #else
     118             :         ChipLogError(AppServer, "GetCommissionableDataProvider()->GetSetupPasscode() failed: %" CHIP_ERROR_FORMAT, err.Format());
     119             :         return err;
     120             : #endif
     121             :     }
     122             : 
     123           0 :     uint16_t discriminator = 0;
     124           0 :     err                    = GetCommissionableDataProvider()->GetSetupDiscriminator(discriminator);
     125           0 :     if (err != CHIP_NO_ERROR)
     126             :     {
     127           0 :         ChipLogError(AppServer, "GetCommissionableDataProvider()->GetSetupDiscriminator() failed: %" CHIP_ERROR_FORMAT,
     128             :                      err.Format());
     129           0 :         return err;
     130             :     }
     131           0 :     aPayload.discriminator.SetLongValue(discriminator);
     132             : 
     133           0 :     err = chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(aPayload.vendorID);
     134           0 :     if (err != CHIP_NO_ERROR)
     135             :     {
     136           0 :         ChipLogError(AppServer, "GetDeviceInstanceInfoProvider()->GetVendorId() failed: %" CHIP_ERROR_FORMAT, err.Format());
     137           0 :         return err;
     138             :     }
     139             : 
     140           0 :     err = chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(aPayload.productID);
     141           0 :     if (err != CHIP_NO_ERROR)
     142             :     {
     143           0 :         ChipLogError(AppServer, "GetDeviceInstanceInfoProvider()->GetProductId() failed: %" CHIP_ERROR_FORMAT, err.Format());
     144           0 :         return err;
     145             :     }
     146             : 
     147           0 :     return err;
     148             : }
     149             : 
     150           0 : CHIP_ERROR GetQRCode(chip::MutableCharSpan & aQRCode, chip::RendezvousInformationFlags aRendezvousFlags)
     151             : {
     152           0 :     chip::PayloadContents payload;
     153             : 
     154           0 :     CHIP_ERROR err = GetPayloadContents(payload, aRendezvousFlags);
     155           0 :     if (err != CHIP_NO_ERROR)
     156             :     {
     157           0 :         ChipLogError(AppServer, "GetPayloadContents() failed: %" CHIP_ERROR_FORMAT, err.Format());
     158           0 :         return err;
     159             :     }
     160             : 
     161           0 :     return GetQRCode(aQRCode, payload);
     162           0 : }
     163             : 
     164           0 : CHIP_ERROR GetQRCode(chip::MutableCharSpan & aQRCode, const chip::PayloadContents & payload)
     165             : {
     166           0 :     CHIP_ERROR err = chip::QRCodeBasicSetupPayloadGenerator(payload).payloadBase38Representation(aQRCode);
     167           0 :     if (err != CHIP_NO_ERROR)
     168             :     {
     169           0 :         ChipLogError(AppServer, "Generating QR Code failed: %" CHIP_ERROR_FORMAT, err.Format());
     170           0 :         return err;
     171             :     }
     172             : 
     173           0 :     return CHIP_NO_ERROR;
     174             : }
     175             : 
     176           0 : CHIP_ERROR GetQRCodeUrl(char * aQRCodeUrl, size_t aUrlMaxSize, const chip::CharSpan & aQRCode)
     177             : {
     178           0 :     VerifyOrReturnError(aQRCodeUrl, CHIP_ERROR_INVALID_ARGUMENT);
     179           0 :     VerifyOrReturnError(aUrlMaxSize >= (strlen(kQrCodeBaseUrl) + strlen(kUrlDataAssignmentPhrase) + aQRCode.size() + 1),
     180             :                         CHIP_ERROR_BUFFER_TOO_SMALL);
     181             : 
     182           0 :     const int writtenDataSize = snprintf(aQRCodeUrl, aUrlMaxSize, "%s%s", kQrCodeBaseUrl, kUrlDataAssignmentPhrase);
     183           0 :     VerifyOrReturnError((writtenDataSize > 0) && (static_cast<size_t>(writtenDataSize) < aUrlMaxSize),
     184             :                         CHIP_ERROR_INVALID_STRING_LENGTH);
     185             : 
     186           0 :     return EncodeQRCodeToUrl(aQRCode.data(), aQRCode.size(), aQRCodeUrl + writtenDataSize,
     187           0 :                              aUrlMaxSize - static_cast<size_t>(writtenDataSize));
     188             : }
     189             : 
     190           0 : CHIP_ERROR GetManualPairingCode(chip::MutableCharSpan & aManualPairingCode, chip::RendezvousInformationFlags aRendezvousFlags)
     191             : {
     192           0 :     chip::PayloadContents payload;
     193             : 
     194           0 :     CHIP_ERROR err = GetPayloadContents(payload, aRendezvousFlags);
     195           0 :     if (err != CHIP_NO_ERROR)
     196             :     {
     197           0 :         ChipLogError(AppServer, "GetPayloadContents() failed: %" CHIP_ERROR_FORMAT, err.Format());
     198           0 :         return err;
     199             :     }
     200           0 :     return GetManualPairingCode(aManualPairingCode, payload);
     201           0 : }
     202             : 
     203           0 : CHIP_ERROR GetManualPairingCode(chip::MutableCharSpan & aManualPairingCode, const chip::PayloadContents & payload)
     204             : {
     205           0 :     CHIP_ERROR err = chip::ManualSetupPayloadGenerator(payload).payloadDecimalStringRepresentation(aManualPairingCode);
     206           0 :     if (err != CHIP_NO_ERROR)
     207             :     {
     208           0 :         ChipLogError(AppServer, "Generating Manual Pairing Code failed: %" CHIP_ERROR_FORMAT, err.Format());
     209           0 :         return err;
     210             :     }
     211             : 
     212           0 :     return CHIP_NO_ERROR;
     213             : }
     214             : 
     215           0 : static inline bool isCharUnreservedInRfc3986(const char c)
     216             : {
     217           0 :     return isalpha(c) || isdigit(c) || (strchr(kSpecialCharsUnreservedInRfc3986, c) != nullptr);
     218             : }
     219             : 
     220           0 : CHIP_ERROR EncodeQRCodeToUrl(const char * aQRCode, size_t aLen, char * aUrl, size_t aMaxSize)
     221             : {
     222             :     static const char upperHexDigits[] = "0123456789ABCDEF";
     223           0 :     size_t i = 0, j = 0;
     224             : 
     225           0 :     VerifyOrReturnError((aQRCode != nullptr) && (aUrl != nullptr), CHIP_ERROR_INVALID_ARGUMENT);
     226             : 
     227           0 :     for (i = 0; i < aLen; ++i)
     228             :     {
     229           0 :         char c = aQRCode[i];
     230           0 :         if (isCharUnreservedInRfc3986(c))
     231             :         {
     232             : 
     233           0 :             VerifyOrReturnError((j + 1) < aMaxSize, CHIP_ERROR_BUFFER_TOO_SMALL);
     234             : 
     235           0 :             aUrl[j++] = c;
     236             :         }
     237             :         else
     238             :         {
     239             : 
     240           0 :             VerifyOrReturnError((j + 3) < aMaxSize, CHIP_ERROR_BUFFER_TOO_SMALL);
     241             : 
     242           0 :             aUrl[j++] = '%';
     243           0 :             aUrl[j++] = upperHexDigits[(c & 0xf0) >> 4];
     244           0 :             aUrl[j++] = upperHexDigits[(c & 0x0f)];
     245             :         }
     246             :     }
     247             : 
     248           0 :     aUrl[j] = '\0';
     249             : 
     250           0 :     return CHIP_NO_ERROR;
     251             : }

Generated by: LCOV version 1.14