From e0715b240d0166e3ce0bd1a436e508246837aecd Mon Sep 17 00:00:00 2001 From: chendejin Date: Fri, 15 Mar 2024 16:33:44 +0800 Subject: [PATCH] matter_controller: support dynamic QR code --- .../matter_controller/sdkconfig.defaults | 2 + .../README.md | 8 +- .../main/CMakeLists.txt | 26 ++-- .../main/app_matter.cpp | 9 ++ .../{ui_matter_ctrl.c => ui_matter_ctrl.cpp} | 8 ++ .../main/dynamic_qrcode/dynamic_qrcode.cpp | 116 ++++++++++++++++++ .../main/dynamic_qrcode/dynamic_qrcode.h | 40 ++++++ .../sdkconfig.defaults | 5 + 8 files changed, 198 insertions(+), 16 deletions(-) rename examples/matter/matter_controller_with_touchscreen/main/box/gui/{ui_matter_ctrl.c => ui_matter_ctrl.cpp} (97%) create mode 100644 examples/matter/matter_controller_with_touchscreen/main/dynamic_qrcode/dynamic_qrcode.cpp create mode 100644 examples/matter/matter_controller_with_touchscreen/main/dynamic_qrcode/dynamic_qrcode.h diff --git a/examples/matter/matter_controller/sdkconfig.defaults b/examples/matter/matter_controller/sdkconfig.defaults index fb2b01d6..46da6077 100644 --- a/examples/matter/matter_controller/sdkconfig.defaults +++ b/examples/matter/matter_controller/sdkconfig.defaults @@ -77,3 +77,5 @@ CONFIG_LWIP_IPV6_NUM_ADDRESSES=6 # Increase attribute buffer largest CONFIG_ESP_MATTER_ATTRIBUTE_BUFFER_LARGEST=2050 + +CONFIG_ESP_MATTER_CONTROLLER_CUSTOM_CLUSTER_ENABLE=y diff --git a/examples/matter/matter_controller_with_touchscreen/README.md b/examples/matter/matter_controller_with_touchscreen/README.md index d05083af..0222fa77 100644 --- a/examples/matter/matter_controller_with_touchscreen/README.md +++ b/examples/matter/matter_controller_with_touchscreen/README.md @@ -8,13 +8,7 @@ ### [Claiming device certificates](../README.md#claiming-device-certificates) -### Generating the factory nvs binary - -For this example, make sure to configure the passcode as 125 and set the discriminator to 521 to match the QR code on UI. The command is: -``` -$ cd $ESP_MATTER_PATH/tools/mfg_tool -$ ./mfg_tool.py -v 0x131B -p 0x2 --passcode 125 --discriminator 521 -cd $RMAKER_PATH/examples/matter/mfg/cd_131B_0002.der --csv $RMAKER_PATH/examples/matter/mfg/keys.csv --mcsv $RMAKER_PATH/examples/matter/mfg/master.csv -``` +### [Generating the factory nvs binary](../README.md#generating-the-factory-nvs-binary) ### Building the example diff --git a/examples/matter/matter_controller_with_touchscreen/main/CMakeLists.txt b/examples/matter/matter_controller_with_touchscreen/main/CMakeLists.txt index f82edd11..cbc4f28d 100644 --- a/examples/matter/matter_controller_with_touchscreen/main/CMakeLists.txt +++ b/examples/matter/matter_controller_with_touchscreen/main/CMakeLists.txt @@ -1,14 +1,22 @@ set(ldfragments linker.lf) + +set(SRC_DIRS_LIST "." + "./box" + "./box/gui" + "./box/gui/image") + +set(PRIV_INCLUDE_DIRS_LIST "." + "./box" + "./box/gui") + +if (CONFIG_CUSTOM_COMMISSIONABLE_DATA_PROVIDER) + list(APPEND SRC_DIRS_LIST "./dynamic_qrcode") + list(APPEND PRIV_INCLUDE_DIRS_LIST "./dynamic_qrcode") +endif() + idf_component_register( - SRC_DIRS - "." - "./box" - "./box/gui" - "./box/gui/image" - PRIV_INCLUDE_DIRS - "." - "./box" - "./box/gui" + SRC_DIRS ${SRC_DIRS_LIST} + PRIV_INCLUDE_DIRS ${PRIV_INCLUDE_DIRS_LIST} LDFRAGMENTS "${ldfragments}") target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format") diff --git a/examples/matter/matter_controller_with_touchscreen/main/app_matter.cpp b/examples/matter/matter_controller_with_touchscreen/main/app_matter.cpp index 0513a61b..b5f52968 100644 --- a/examples/matter/matter_controller_with_touchscreen/main/app_matter.cpp +++ b/examples/matter/matter_controller_with_touchscreen/main/app_matter.cpp @@ -25,6 +25,11 @@ #include "app_matter_ctrl.h" #include "ui_matter_ctrl.h" +#if CONFIG_CUSTOM_COMMISSIONABLE_DATA_PROVIDER +#include +#include "dynamic_qrcode.h" +#endif + using namespace esp_matter; using namespace esp_matter::attribute; using namespace esp_matter::cluster; @@ -194,6 +199,10 @@ esp_err_t app_matter_pre_rainmaker_start() esp_err_t app_matter_start() { +#if CONFIG_CUSTOM_COMMISSIONABLE_DATA_PROVIDER + esp_matter::set_custom_commissionable_data_provider(&DynamicPasscodeCommissionableDataProvider::GetInstance()); +#endif + esp_err_t err = esp_matter::start(app_event_cb); if (err != ESP_OK) { ESP_LOGE(TAG, "Matter start failed: %d", err); diff --git a/examples/matter/matter_controller_with_touchscreen/main/box/gui/ui_matter_ctrl.c b/examples/matter/matter_controller_with_touchscreen/main/box/gui/ui_matter_ctrl.cpp similarity index 97% rename from examples/matter/matter_controller_with_touchscreen/main/box/gui/ui_matter_ctrl.c rename to examples/matter/matter_controller_with_touchscreen/main/box/gui/ui_matter_ctrl.cpp index d22f2837..f97472a9 100644 --- a/examples/matter/matter_controller_with_touchscreen/main/box/gui/ui_matter_ctrl.c +++ b/examples/matter/matter_controller_with_touchscreen/main/box/gui/ui_matter_ctrl.cpp @@ -9,6 +9,10 @@ #include "esp_log.h" #include "lvgl.h" +#if CONFIG_CUSTOM_COMMISSIONABLE_DATA_PROVIDER +#include "dynamic_qrcode.h" +#endif + static const char *TAG = "ui_matter_ctrl"; static bool IsCommission = false; @@ -288,7 +292,11 @@ void ui_matter_ctrl_start(void (*fn)(void)) if (!IsCommission) { QRcode = lv_qrcode_create(g_page, qrcode_width, lv_color_black(), lv_color_white()); lv_obj_align(QRcode, LV_ALIGN_TOP_MID, 0, qrcode_align_y); +#ifdef CONFIG_CUSTOM_COMMISSIONABLE_DATA_PROVIDER + const char *qrcode_data = DynamicPasscodeCommissionableDataProvider::GetInstance().GetDynamicQRcodeStr(); +#else const char *qrcode_data = "MT:U9VJ0EPJ01ZD6100000"; +#endif ESP_LOGI(TAG, "QR Data: %s", qrcode_data); lv_qrcode_update(QRcode, qrcode_data, strlen(qrcode_data)); lv_label_set_text_static(g_hint_label, "Scan the QR code on your phone"); diff --git a/examples/matter/matter_controller_with_touchscreen/main/dynamic_qrcode/dynamic_qrcode.cpp b/examples/matter/matter_controller_with_touchscreen/main/dynamic_qrcode/dynamic_qrcode.cpp new file mode 100644 index 00000000..3c382d92 --- /dev/null +++ b/examples/matter/matter_controller_with_touchscreen/main/dynamic_qrcode/dynamic_qrcode.cpp @@ -0,0 +1,116 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include +#include +#include + +#include "dynamic_qrcode.h" +#include "esp_log.h" + +static const char *TAG = "dynamic_qrcode"; + +CHIP_ERROR DynamicPasscodeCommissionableDataProvider::GetSetupDiscriminator(uint16_t &setupDiscriminator) +{ + if (discriminator >= 4096) { + chip::Crypto::DRBG_get_bytes(reinterpret_cast(&discriminator), sizeof(discriminator)); + discriminator %= 4096; + } + setupDiscriminator = discriminator; + return CHIP_NO_ERROR; +} + +CHIP_ERROR DynamicPasscodeCommissionableDataProvider::GetSpake2pIterationCount(uint32_t &iterationCount) +{ + return chip::DeviceLayer::Internal::ESP32Config::ReadConfigValue( + chip::DeviceLayer::Internal::ESP32Config::kConfigKey_Spake2pIterationCount, iterationCount); +} + +CHIP_ERROR DynamicPasscodeCommissionableDataProvider::GetSpake2pSalt(chip::MutableByteSpan &saltBuf) +{ + static constexpr size_t kSpake2pSalt_MaxBase64Len = + BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length) + 1; + CHIP_ERROR err = CHIP_NO_ERROR; + char saltB64[kSpake2pSalt_MaxBase64Len] = {0}; + size_t saltB64Len = 0; + err = chip::DeviceLayer::Internal::ESP32Config::ReadConfigValueStr( + chip::DeviceLayer::Internal::ESP32Config::kConfigKey_Spake2pSalt, saltB64, sizeof(saltB64), saltB64Len); + ReturnErrorOnFailure(err); + size_t saltLen = chip::Base64Decode32(saltB64, saltB64Len, reinterpret_cast(saltB64)); + ReturnErrorCodeIf(saltLen > saltBuf.size(), CHIP_ERROR_BUFFER_TOO_SMALL); + memcpy(saltBuf.data(), saltB64, saltLen); + saltBuf.reduce_size(saltLen); + return CHIP_NO_ERROR; +} + +CHIP_ERROR DynamicPasscodeCommissionableDataProvider::GetSpake2pVerifier(chip::MutableByteSpan &verifierBuf, + size_t &verifierLen) +{ + chip::Crypto::Spake2pVerifier verifier; + uint32_t iterationCount = 0; + uint32_t setupPasscode = 0; + uint8_t saltData[chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length] = {0}; + chip::MutableByteSpan saltBuf(saltData); + ReturnErrorOnFailure(DynamicPasscodeCommissionableDataProvider::GetSpake2pIterationCount(iterationCount)); + ReturnErrorOnFailure(DynamicPasscodeCommissionableDataProvider::GetSetupPasscode(setupPasscode)); + ReturnErrorOnFailure(DynamicPasscodeCommissionableDataProvider::GetSpake2pSalt(saltBuf)); + CHIP_ERROR err = chip::PASESession::GeneratePASEVerifier(verifier, iterationCount, saltBuf, false, setupPasscode); + ReturnErrorOnFailure(err); + err = verifier.Serialize(verifierBuf); + verifierLen = verifierBuf.size(); + ReturnErrorOnFailure(err); + return CHIP_NO_ERROR; +} + +CHIP_ERROR DynamicPasscodeCommissionableDataProvider::GetSetupPasscode(uint32_t &setupPasscode) +{ + if (passcode == 0) { + while (!chip::SetupPayload::IsValidSetupPIN(passcode)) { + chip::Crypto::DRBG_get_bytes(reinterpret_cast(&passcode), sizeof(passcode)); + passcode = (passcode % chip::kSetupPINCodeMaximumValue) + 1; + } + } + setupPasscode = passcode; + return CHIP_NO_ERROR; +} + +const char *DynamicPasscodeCommissionableDataProvider::GetDynamicQRcodeStr() +{ + static bool hasInitialized = false; + if (hasInitialized) { + return dynamicQrcodeData; + } + CHIP_ERROR ret = CHIP_NO_ERROR; + chip::PayloadContents payload; + uint16_t setupDiscriminator = 0; + char qrCodeData[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1] = {0}; + chip::MutableCharSpan qrCodeBuf(qrCodeData); + chip::RendezvousInformationFlags rendezvous(chip::RendezvousInformationFlag::kSoftAP, + chip::RendezvousInformationFlag::kBLE); + chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetVendorId(payload.vendorID); + chip::DeviceLayer::GetDeviceInstanceInfoProvider()->GetProductId(payload.productID); + GetInstance().GetSetupDiscriminator(setupDiscriminator); + payload.discriminator.SetLongValue(setupDiscriminator); + GetInstance().GetSetupPasscode(payload.setUpPINCode); + payload.rendezvousInformation.SetValue(rendezvous); + ESP_LOGI(TAG, "DeviceInfo vid:%d pid:%d discriminator:%d passcode:%d", payload.vendorID, payload.productID, + payload.discriminator.GetLongValue(), payload.setUpPINCode); + ret = GetQRCode(qrCodeBuf, payload); + for (size_t i = 0; i < qrCodeBuf.size(); ++i) { + *(dynamicQrcodeData + i) = *(qrCodeBuf.data() + i); + } + *(dynamicQrcodeData + qrCodeBuf.size()) = '\0'; + + if (ret != CHIP_NO_ERROR) { + return nullptr; + } + hasInitialized = true; + return dynamicQrcodeData; +} diff --git a/examples/matter/matter_controller_with_touchscreen/main/dynamic_qrcode/dynamic_qrcode.h b/examples/matter/matter_controller_with_touchscreen/main/dynamic_qrcode/dynamic_qrcode.h new file mode 100644 index 00000000..c7463398 --- /dev/null +++ b/examples/matter/matter_controller_with_touchscreen/main/dynamic_qrcode/dynamic_qrcode.h @@ -0,0 +1,40 @@ +/* + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#pragma once + +#include +#include +#include +#include + +class DynamicPasscodeCommissionableDataProvider : public chip::DeviceLayer::CommissionableDataProvider +{ +public: + // ===== Members functions that implement the CommissionableDataProvider + CHIP_ERROR GetSetupDiscriminator(uint16_t & setupDiscriminator) override; + CHIP_ERROR SetSetupDiscriminator(uint16_t setupDiscriminator) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + CHIP_ERROR GetSpake2pIterationCount(uint32_t & iterationCount) override; + CHIP_ERROR GetSpake2pSalt(chip::MutableByteSpan & saltBuf) override; + CHIP_ERROR GetSpake2pVerifier(chip::MutableByteSpan & verifierBuf, size_t & verifierLen) override; + CHIP_ERROR GetSetupPasscode(uint32_t & setupPasscode) override; + CHIP_ERROR SetSetupPasscode(uint32_t setupPasscode) override { return CHIP_ERROR_NOT_IMPLEMENTED; } + static DynamicPasscodeCommissionableDataProvider &GetInstance() + { + static DynamicPasscodeCommissionableDataProvider sCommissionableDataProvider; + return sCommissionableDataProvider; + } + const char *GetDynamicQRcodeStr(); + +private: + DynamicPasscodeCommissionableDataProvider() : + chip::DeviceLayer::CommissionableDataProvider() {} + uint32_t passcode = 0; + uint16_t discriminator = 4096; + char dynamicQrcodeData[chip::QRCodeBasicSetupPayloadGenerator::kMaxQRCodeBase38RepresentationLength + 1] = {0}; +}; diff --git a/examples/matter/matter_controller_with_touchscreen/sdkconfig.defaults b/examples/matter/matter_controller_with_touchscreen/sdkconfig.defaults index 811abc67..31d91f93 100644 --- a/examples/matter/matter_controller_with_touchscreen/sdkconfig.defaults +++ b/examples/matter/matter_controller_with_touchscreen/sdkconfig.defaults @@ -140,3 +140,8 @@ CONFIG_IDF_TARGET="esp32s3" # Select one of the boards CONFIG_BSP_BOARD_ESP32_S3_BOX_3=y + +# Enable custom commission data to use dynamic qrcode +CONFIG_CUSTOM_COMMISSIONABLE_DATA_PROVIDER=y + +CONFIG_ESP_MATTER_CONTROLLER_CUSTOM_CLUSTER_ENABLE=y