From 8bc4d7d8f15e0c5517dde47c783c2ff1e5429e45 Mon Sep 17 00:00:00 2001 From: Marius Tache <102153746+marius-alex-tache@users.noreply.github.com> Date: Mon, 11 Dec 2023 16:21:27 +0200 Subject: [PATCH 1/9] [K32W0] SDK 2.6.13 changes (#30678) * [K32W0] Bring platform diff Signed-off-by: marius-alex-tache * [K32W0] Add SSBL version retrieval The applications will now print the SSBL version also. If the SSBL doesn't have a version defined, the expected value is 0. Signed-off-by: marius-alex-tache * [K32W0] Fix SSBL version retrieval SSBL will always be seen as booting from address 0, thanks to the remapping mechanism. This means the SSBL version will always offset from address 0. Signed-off-by: marius-alex-tache * [K32W0] Add support for simple hash verification Signed-off-by: marius-alex-tache * [K32W] Add support for BLE callback delegate Application can register callbacks for GAP/GATT events. Signed-off-by: marius-alex-tache * [K32W0] Add example for registering BLE event callbacks Signed-off-by: marius-alex-tache * [K32W0] replace to use SecLib API and SecLib mutex * [COMMON] Fix manufacturing flow Signed-off-by: marius-alex-tache * [COMMON] Add python script to generate certificates Signed-off-by: Axel Le Bourhis * [common] Update generate_certs script * Add valid_from and lifetime optional parameters * Updated parameters descriptions * Misc improvements Signed-off-by: Axel Le Bourhis * [COMMON] Update nxp_manufacturing_flow document Updated the document to make use of the new generate_certs.py script. Signed-off-by: Axel Le Bourhis * [K32W0] Fix function signature Signed-off-by: marius-alex-tache * [K32W0] Remove duplicate reboot cause set Signed-off-by: marius-alex-tache * [K32W] Set BLE manager impl pointer before calling InitHostController When adding app BLE callbacks, the sImplInstance pointer is used, so it needs to be set beforehand. Signed-off-by: marius-alex-tache * [COMMON] Update ot-nxp to release branch Signed-off-by: marius-alex-tache * [K32W0] Update west manifest to point to 2.6.13 SDK Signed-off-by: marius-alex-tache * [K32W0] Fix ICD parameters Signed-off-by: Doru Gucea * [NXP] Bump ot-nxp to latest Signed-off-by: marius-alex-tache * [NXP] Fix tools lint errors Signed-off-by: marius-alex-tache * Restyled by whitespace Restyled by clang-format Restyled by gn Restyled by prettier-markdown Restyled by autopep8 Restyled by isort * [NXP] Add extra condition for SDK gn variable to take into account CI Signed-off-by: marius-alex-tache * Restyled by gn * [NXP] Fix some lint errors Signed-off-by: marius-alex-tache * [K32W0] Fix include header Signed-off-by: marius-alex-tache * Restyled by autopep8 * [NXP] Fix lint error Signed-off-by: marius-alex-tache * [NXP] Update docker image used by the github actions workflow for K32W Signed-off-by: marius-alex-tache * [K32W1] Add example for registering BLE event callbacks Signed-off-by: marius-alex-tache * Restyled by clang-format * [NXP] Clarify usage of hash id in factory data. Update MATTER_ROOT to use a more generic path Signed-off-by: marius-alex-tache --------- Signed-off-by: marius-alex-tache Signed-off-by: Axel Le Bourhis Signed-off-by: Doru Gucea Co-authored-by: Ethan Tan Co-authored-by: Axel Le Bourhis Co-authored-by: Doru Gucea Co-authored-by: Restyled.io --- .github/workflows/examples-k32w.yaml | 2 +- docs/guides/nxp_manufacturing_flow.md | 198 ++++--- .../nxp/k32w/k32w0/BUILD.gn | 5 + .../nxp/k32w/k32w0/README.md | 22 + .../nxp/k32w/k32w0/args.gni | 4 + .../k32w/k32w0/include/CHIPProjectConfig.h | 8 +- .../nxp/k32w/k32w0/main/AppTask.cpp | 25 + .../nxp/k32w/k32w1/main/AppTask.cpp | 17 + examples/lighting-app/nxp/k32w/k32w0/BUILD.gn | 5 + .../lighting-app/nxp/k32w/k32w0/README.md | 22 + .../k32w/k32w0/include/CHIPProjectConfig.h | 5 + .../nxp/k32w/k32w0/main/AppTask.cpp | 25 +- examples/lock-app/nxp/k32w/k32w0/args.gni | 4 + .../k32w/k32w0/include/CHIPProjectConfig.h | 7 +- .../k32w0/app/ldscripts/chip-k32w0x-linker.ld | 9 +- .../k32w0/doc/images/ssbl_simple_hash.JPG | Bin 0 -> 56397 bytes .../nxp/k32w/k32w0/scripts/sign-outdir.py | 41 +- .../nxp/factory_data_generator/generate.py | 12 +- scripts/tools/nxp/generate_certs.py | 211 ++++++++ scripts/tools/nxp/ota/crypto_utils.py | 487 ++++++++++++++++++ scripts/tools/nxp/ota/ota_image_tool.py | 59 ++- .../nxp/k32w/common/BLEManagerCommon.cpp | 24 +- .../nxp/k32w/common/BLEManagerCommon.h | 22 +- .../CHIPDevicePlatformRamStorageConfig.h | 11 + .../nxp/k32w/common/FactoryDataProvider.cpp | 10 +- .../nxp/k32w/common/FactoryDataProvider.h | 4 +- .../nxp/k32w/common/OTAImageProcessorImpl.cpp | 2 +- .../nxp/k32w/common/OTATlvProcessor.cpp | 82 ++- .../nxp/k32w/common/OTATlvProcessor.h | 8 + .../nxp/k32w/k32w0/BLEManagerImpl.cpp | 2 +- src/platform/nxp/k32w/k32w0/BLEManagerImpl.h | 2 +- .../nxp/k32w/k32w0/CHIPDevicePlatformConfig.h | 12 + .../nxp/k32w/k32w0/CHIPPlatformConfig.h | 24 + .../k32w/k32w0/KeyValueStoreManagerImpl.cpp | 4 +- src/platform/nxp/k32w/k32w0/Logging.cpp | 2 +- .../k32w/k32w0/OTAFactoryDataProcessor.cpp | 4 + .../nxp/k32w/k32w0/OTAFirmwareProcessor.cpp | 37 +- .../nxp/k32w/k32w0/OTAFirmwareProcessor.h | 3 + .../nxp/k32w/k32w1/BLEManagerImpl.cpp | 2 +- src/platform/nxp/k32w/k32w1/BLEManagerImpl.h | 2 +- third_party/nxp/k32w0_sdk/k32w0_sdk.gni | 167 ++++-- .../nxp/k32w0_sdk/repo/manifest/west.yml | 8 +- third_party/openthread/ot-nxp | 2 +- 43 files changed, 1426 insertions(+), 176 deletions(-) create mode 100755 examples/platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG create mode 100644 scripts/tools/nxp/generate_certs.py create mode 100755 scripts/tools/nxp/ota/crypto_utils.py diff --git a/.github/workflows/examples-k32w.yaml b/.github/workflows/examples-k32w.yaml index 5d811a986c757a..e2854c2e297f38 100644 --- a/.github/workflows/examples-k32w.yaml +++ b/.github/workflows/examples-k32w.yaml @@ -37,7 +37,7 @@ jobs: if: github.actor != 'restyled-io[bot]' container: - image: ghcr.io/project-chip/chip-build-k32w:26 + image: ghcr.io/project-chip/chip-build-k32w:27 volumes: - "/tmp/bloat_reports:/tmp/bloat_reports" steps: diff --git a/docs/guides/nxp_manufacturing_flow.md b/docs/guides/nxp_manufacturing_flow.md index 141f90c8b8b57c..64bafd53c89329 100644 --- a/docs/guides/nxp_manufacturing_flow.md +++ b/docs/guides/nxp_manufacturing_flow.md @@ -11,108 +11,91 @@ using the procedure described below. ## 1. Prerequisites -Build `chip-cert` tool: - -``` -cd src/tools/chip-cert -gn gen out -ninja -C out -``` - -Build `spake2p` tool: +Generate build files from Matter root folder by running: ``` -cd src/tool/spake2p gn gen out -ninja -C out ``` -### Environment variables - -A user can customize the certificate generation by setting some environment -variables that are used within the utility scripts. Please note that the values -below are just an example and should be modified accordingly: +Build `chip-cert` tool: ``` -export FACTORY_DATA_DEST=path/factory/data/dest -export DEVICE_TYPE=100 -export DATE=$(date +"%F") -export TIME=$(date +"%T") -export LIFETIME="7305" -export VID="1037" -export PID="A220" +ninja -C out chip-cert ``` -`FACTORY_DATA_DEST` is the path where all factory related data is generated. - -`DEVICE_TYPE` should be updated according to the application device type (0x0100 -for the provided K32W0 lighting app). - -Additionally, `PAA_CERT` and `PAA_KEY` paths can be specified to use an already -existent **PAA**: +Build `spake2p` tool: ``` -export PAA_CERT=path/certs/Chip-PAA-NXP-Cert.pem -export PAA_KEY=path/certs/Chip-PAA-NXP-Key.pem +ninja -C out spake2p ``` ## 2. Generate ### a. Certificates -``` -./scripts/tools/nxp/generate_cert.sh ./src/tools/chip-cert/out/chip-cert -``` +To generate the different certificates, NXP provides a Python script +`scripts/tools/nxp/generate_certs.py`. This script will always generate the PAI +and DAC certificates/keys. It can also generate the Certification Declaration +and the PAA certificate/key depending on the parameters. -The output of the script is the **DAC**, **PAI** and **PAA** certificates. If -`FACTORY_DATA_DEST` is set, the certificates will be moved there. The **DAC** -and **PAI** certificates will be written in a special section of the internal -flash, while the **PAA** will be used by `chip-tool` as trust anchor. Please -note that for _real production manufacturing_ the "production PAA" is trusted -via the **DCL** rather than through the generated **PAA** certificate. The -**PAI** certificate may also have a different lifecycle. +| Parameter | Description | Type | Required | +| ------------------ | ------------------------------------------------------------------------------------------------- | ---------------------- | -------- | +| `--chip_cert_path` | Path to chip-cert executable | string | Yes | +| `--output` | Output path to store certificates | string | Yes | +| `--vendor_id` | Vendor Identification Number | integer or hex integer | Yes | +| `--product_id` | Product Identification Number | integer or hex integer | Yes | +| `--vendor_name` | Human-readable vendor name | string | Yes | +| `--product_name` | Human-readable product name | string | Yes | +| `--gen_cd` | Use this option to enable Certificate Declaration generation | boolean | No | +| `--cd_type` | Type of generated Certification Declaration: `0` - development, `1` - provisional, `2` - official | integer | No | +| `--device_type` | The primary device type implemented by the node | int | No | +| `--paa_cert` | Path to the Product Attestation Authority (PAA) certificate. Will be generated if not provided. | string | No | +| `--paa_key` | Path to the Product Attestation Authority (PAA) key. Will be generated if not provided. | string | No | +| `--valid_from` | The start date for the certificate's validity period. | string | No | +| `--lifetime` | The lifetime for the certificates, in whole days. | string | No | -### b. Certification declaration (CD) +You can also run the following command to get more details on the parameters and +their default value (if applicable): -``` -./src/tools/chip-cert/out/chip-cert gen-cd --key ./credentials/test/certification-declaration/Chip-Test-CD-Signing-Key.pem --cert ./credentials/test/certification-declaration/Chip-Test-CD-Signing-Cert.pem --out $FACTORY_DATA_DEST/Chip-Test-CD-$VID-$PID.der --format-version 1 --vendor-id "0x$VID" --product-id "0x$PID" --device-type-id "0x$DEVICE_TYPE" --certificate-id "ZIG20142ZB330003-24" --security-level 0 --security-info 0 --version-number 9876 --certification-type 1 +```shell +python scripts/tools/nxp/generate_certs.py --help ``` -The command above is extracted from `./credentials/test/gen-test-cds.sh` script. -The CD generation uses predefined key and certificate found in -`./credentials/test/certification-declaration`. This **CSA** certificate is also -hard-coded as Trust Anchor in the current `chip-tool` version. +Example of a command that will generate CD, PAA, PAI and DAC certificates and +keys in both .pem and .der formats: -By default, the CD is added to the factory data section. In order to have it -integrated in the application binary, set -`CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION` to 1 in the application's -CHIPProjectConfig.h file. +```shell +python scripts/tools/nxp/generate_certs.py --gen_cd --cd_type 1 --chip_cert_path ./out/chip-cert --vendor_id 0x1037 --product_id 0xA220 --vendor_name "NXP Semiconductors" --product_name all-clusters-app --device_type 65535 --output . +``` + +> **Note**: the commands provided in this guide are just for the example and +> shall be adapted to your use case accordingly ### c. Provisioning data Generate new provisioning data and convert all the data to a binary (unencrypted data): -``` -python3 ./scripts/tools/nxp/factory_data_generator/generate.py -i 10000 -s UXKLzwHdN3DZZLBaL2iVGhQi/OoQwIwJRQV4rpEalbA= -p 14014 -d 1000 --vid "0x$VID" --pid "0x$PID" --vendor_name "NXP Semiconductors" --product_name "Lighting app" --serial_num "12345678" --date "$DATE" --hw_version 1 --hw_version_str "1.0" --cert_declaration $FACTORY_DATA_DEST/Chip-Test-CD-$VID-$PID.der --dac_cert $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Cert.der --dac_key $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Key.der --pai_cert $FACTORY_DATA_DEST/Chip-PAI-NXP-$VID-$PID-Cert.der --spake2p_path ./src/tools/spake2p/out/spake2p --unique_id "00112233445566778899aabbccddeeff" --out $FACTORY_DATA_DEST/factory_data.bin +```shell +python3 ./scripts/tools/nxp/factory_data_generator/generate.py -i 10000 -s UXKLzwHdN3DZZLBaL2iVGhQi/OoQwIwJRQV4rpEalbA= -p 14014 -d 1000 --vid 0x1037 --pid 0xA220 --vendor_name "NXP Semiconductors" --product_name "Lighting app" --serial_num "12345678" --date "2023-01-01" --hw_version 1 --hw_version_str "1.0" --cert_declaration ./Chip-Test-CD-1037-A220.der --dac_cert ./Chip-DAC-NXP-1037-A220-Cert.der --dac_key ./Chip-DAC-NXP-1037-A220-Key.der --pai_cert ./Chip-PAI-NXP-1037-A220-Cert.der --spake2p_path ./out/spake2p --unique_id "00112233445566778899aabbccddeeff" --out ./factory_data.bin ``` Same example as above, but with an already generated verifier passed as input: -``` -python3 ./scripts/tools/nxp/factory_data_generator/generate.py -i 10000 -s UXKLzwHdN3DZZLBaL2iVGhQi/OoQwIwJRQV4rpEalbA= -p 14014 -d 1000 --vid "0x$VID" --pid "0x$PID" --vendor_name "NXP Semiconductors" --product_name "Lighting app" --serial_num "12345678" --date "$DATE" --hw_version 1 --hw_version_str "1.0" --cert_declaration $FACTORY_DATA_DEST/Chip-Test-CD-$VID-$PID.der --dac_cert $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Cert.der --dac_key $FACTORY_DATA_DEST/Chip-DAC-NXP-$VID-$PID-Key.der --pai_cert $FACTORY_DATA_DEST/Chip-PAI-NXP-$VID-$PID-Cert.der --spake2p_path ./src/tools/spake2p/out/spake2p --spake2p_verifier ivD5n3L2t5+zeFt6SjW7BhHRF30gFXWZVvvXgDxgCNcE+BGuTA5AUaVm3qDZBcMMKn1a6CakI4SxyPUnJr0CpJ4pwpr0DvpTlkQKqaRvkOQfAQ1XDyf55DuavM5KVGdDrg== --unique_id "00112233445566778899aabbccddeeff" --out $FACTORY_DATA_DEST/factory_data.bin +```shell +python3 ./scripts/tools/nxp/factory_data_generator/generate.py -i 10000 -s UXKLzwHdN3DZZLBaL2iVGhQi/OoQwIwJRQV4rpEalbA= -p 14014 -d 1000 --vid "0x1037" --pid "0xA220" --vendor_name "NXP Semiconductors" --product_name "Lighting app" --serial_num "12345678" --date "2023-01-01" --hw_version 1 --hw_version_str "1.0" --cert_declaration ./Chip-Test-CD-1037-A220.der --dac_cert ./Chip-DAC-NXP-1037-A220-Cert.der --dac_key ./Chip-DAC-NXP-1037-A220-Key.der --pai_cert ./Chip-PAI-NXP-1037-A220-Cert.der --spake2p_path ./out/spake2p --spake2p_verifier ivD5n3L2t5+zeFt6SjW7BhHRF30gFXWZVvvXgDxgCNcE+BGuTA5AUaVm3qDZBcMMKn1a6CakI4SxyPUnJr0CpJ4pwpr0DvpTlkQKqaRvkOQfAQ1XDyf55DuavM5KVGdDrg== --unique_id "00112233445566778899aabbccddeeff" --out ./factory_data.bin ``` Generate new provisioning data and convert all the data to a binary (encrypted data with the AES key). Add the following option to one of the above examples: -``` +```shell --aes128_key 2B7E151628AED2A6ABF7158809CF4F3C ``` Here is the interpretation of the **required** parameters: -``` +```shell -i -> SPAKE2+ iteration -s -> SPAKE2+ salt (passed as base64 encoded string) -p -> SPAKE2+ passcode @@ -127,7 +110,7 @@ Here is the interpretation of the **required** parameters: --dac_cert -> path to the DAC (der format) location --dac_key -> path to the DAC key (der format) location --pai_cert -> path to the PAI (der format) location ---spake2p_path -> path to the spake2p tool (compile it from ./src/tools/spake2p) +--spake2p_path -> path to the spake2p tool --out -> name of the binary that will be used for storing all the generated data @@ -135,18 +118,24 @@ Here is the interpretation of the **required** parameters: Here is the interpretation of the **optional** parameters: -``` ---dac_key_password -> Password to decode DAC key ---spake2p_verifier -> SPAKE2+ verifier (passed as base64 encoded string). If this option is set, - all SPAKE2+ inputs will be encoded in the final binary. The spake2p tool - will not be used to generate a new verifier on the fly. ---aes128_key -> 128 bits AES key used to encrypt the whole dataset ---date -> Manufacturing Date (YYYY-MM-DD format) ---part_number -> Part number as string ---product_url -> Product URL as string ---product_label -> Product label as string ---serial_num -> Serial Number ---unique_id -> Unique id used for rotating device id generation +```shell +--dac_key_password -> Password to decode DAC key +--dac_key_use_sss_blob -> Used when --dac_key contains a path to an encrypted blob, instead of the + actual DAC private key. The blob metadata size is 24, so the total length + of the resulting value is private key length (32) + 24 = 56. False by default. +--spake2p_verifier -> SPAKE2+ verifier (passed as base64 encoded string). If this option is set, + all SPAKE2+ inputs will be encoded in the final binary. The spake2p tool + will not be used to generate a new verifier on the fly. +--aes128_key -> 128 bits AES key used to encrypt the whole dataset. Please make sure + that the target application/board supports this feature: it has access to + the private key and implements a mechanism which can be used to decrypt + the factory data information. +--date -> Manufacturing Date (YYYY-MM-DD format) +--part_number -> Part number as string +--product_url -> Product URL as string +--product_label -> Product label as string +--serial_num -> Serial Number +--unique_id -> Unique id used for rotating device id generation ``` ## 3. Write provisioning data @@ -154,10 +143,20 @@ Here is the interpretation of the **optional** parameters: For the **K32W0x1** variants, the binary needs to be written in the internal flash at location **0x9D600** using `DK6Programmer.exe`: -``` +```shell DK6Programmer.exe -Y -V2 -s -P 1000000 -Y -p FLASH@0x9D600="factory_data.bin" ``` +For **K32W1** platform, the binary needs to be written in the internal flash at +location given by `__MATTER_FACTORY_DATA_START`, using `JLink`: + +``` +loadfile factory_data.bin 0xf4000 +``` + +where `0xf4000` is the value of `__MATTER_FACTORY_DATA_START` in the +corresponding .map file (can be different if using a custom linker script). + For the **RT1060**, **RT1170** and **RW61X** platform, the binary needs to be written using `MCUXpresso Flash Tool GUI` at the address value corresponding to `__FACTORY_DATA_START` (the map file of the application should be checked to get @@ -169,13 +168,13 @@ Use `chip_with_factory_data=1` when compiling to enable factory data usage. Run chip-tool with a new PAA: -``` +```shell ./chip-tool pairing ble-thread 2 hex: $hex_value 14014 1000 --paa-trust-store-path /home/ubuntu/certs/paa ``` Here is the interpretation of the parameters: -``` +```shell --paa-trust-store-path -> path to the generated PAA (der format) ``` @@ -195,4 +194,51 @@ certificates generated by `OpenSSL 1.1.1l`. Also, demo **DAC**, **PAI** and **PAA** certificates needed in case `chip_with_factory_data=1` is used can be found in -`./scripts/tools/nxp/demo_generated_certs`. +`./scripts/tools/nxp/demo_generated_certs`. + +## 6. Increased security for DAC private key + +Supported platforms: + +- K32W1 - `src/plaftorm/nxp/k32w/k32w1/FactoryDataProviderImpl.h` + +For platforms that have a secure subsystem (`SSS`), the DAC private key can be +converted to an encrypted blob. This blob will overwrite the DAC private key in +factory data and will be imported in the `SSS` at initialization, by the factory +data provider instance. + +The conversion process shall happen at manufacturing time and should be run one +time only: + +- Write factory data binary. +- Build the application with + `chip_with_factory_data=1 chip_convert_dac_private_key=1` set. +- Write the application to the board and let it run. + +After the conversion process: + +- Make sure the application is built with `chip_with_factory_data=1`, but + without `chip_convert_dac_private_key` arg, since conversion already + happened. +- Write the application to the board. + +If you are using Jlink, you can see a conversion script example in: + +```shell +./scripts/tools/nxp/factory_data_generator/k32w1/example_convert_dac_private_key.jlink +``` + +Factory data should now contain a corresponding encrypted blob instead of the +DAC private key. + +If an encrypted blob of the DAC private key is already available (e.g. obtained +previously, using other methods), then the conversion process shall be skipped. +Instead, option `--dac_key_use_sss_blob` can be used in the factory data +generation command: + +```shell +python3 ./scripts/tools/nxp/factory_data_generator/generate.py -i 10000 -s UXKLzwHdN3DZZLBaL2iVGhQi/OoQwIwJRQV4rpEalbA= -p 14014 -d 1000 --vid "0x1037" --pid "0xA221" --vendor_name "NXP Semiconductors" --product_name "Lighting app" --serial_num "12345678" --date "2023-01-01" --hw_version 1 --hw_version_str "1.0" --cert_declaration ./Chip-Test-CD-1037-A221.der --dac_cert ./Chip-DAC-NXP-1037-A221-Cert.der --dac_key ./Chip-DAC-NXP-1037-A221-Key-encrypted-blob.bin --pai_cert ./Chip-PAI-NXP-1037-A221-Cert.der --spake2p_path ./out/spake2p --unique_id "00112233445566778899aabbccddeeff" --dac_key_use_sss_blob --out ./factory_data_with_blob.bin +``` + +Please note that `--dac_key` now points to a binary file that contains the +encrypted blob. diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/BUILD.gn b/examples/contact-sensor-app/nxp/k32w/k32w0/BUILD.gn index e4c15436b3bd86..36f52d917f22db 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/BUILD.gn +++ b/examples/contact-sensor-app/nxp/k32w/k32w0/BUILD.gn @@ -25,6 +25,7 @@ import("${chip_root}/src/platform/device.gni") declare_args() { chip_software_version = 0 + chip_simple_hash_verification = 0 } if (chip_pw_tokenizer_logging) { @@ -177,6 +178,10 @@ action("binsign") { script = "${k32w0_platform_dir}/scripts/sign-outdir.py" output_name = "bignsign.log" outputs = [ "${root_build_dir}/${output_name}" ] + + if (chip_simple_hash_verification == 1) { + args = [ "--simple-hash" ] + } } group("default") { diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/README.md b/examples/contact-sensor-app/nxp/k32w/k32w0/README.md index 9f92bc0d795142..f616ddfeeca4f9 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/README.md +++ b/examples/contact-sensor-app/nxp/k32w/k32w0/README.md @@ -440,6 +440,10 @@ SSBL demo application can be imported from the `Quickstart panel`: ![SSBL Application Select](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_select.JPG) +### Features + +#### Multi image + To support multi-image OTA feature, the SSBL project must be compiled using the following defines: @@ -457,6 +461,24 @@ Optionally, add the following defines: ![SSBL_MULTI_IMAGE](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_multi_image.JPG) +#### Simple hash verification + +When secure boot is not used, a simple hash can be appended at the end of the +image for integrity check. Applications should be built with +`chip_simple_hash_verification=1`. + +To support simple hash verification feature, the SSBL project must be compiled +with: + +- `gSimpleHashVerification=1` + +and update the post-build command to use simple hash verification instead of the +default options. Go to +`Project -> Properties -> C/C++ Build -> Settings -> Build steps` and press +`Edit` under `Post-build steps` subsection. The command should look similar to: + +![SSBL_SIMPLE_HASH_VERIFICATION](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG) + Once compiled, the required SSBL file is called `k32w061dk6_ssbl.bin`. ![SSBL_BIN](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_bin.JPG) diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/args.gni b/examples/contact-sensor-app/nxp/k32w/k32w0/args.gni index 4f4bba4b47cf07..02a388daab9e1a 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/args.gni +++ b/examples/contact-sensor-app/nxp/k32w/k32w0/args.gni @@ -22,3 +22,7 @@ k32w0_sdk_target = get_label_info(":sdk", "label_no_toolchain") chip_enable_ota_requestor = true chip_stack_lock_tracking = "fatal" chip_enable_ble = true + +chip_enable_icd_server = true +chip_persist_subscriptions = true +chip_subscription_timeout_resumption = true diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h b/examples/contact-sensor-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h index 7d435215252b0e..b48b97c55e6f94 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h +++ b/examples/contact-sensor-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h @@ -59,6 +59,11 @@ #define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID 0x1037 #define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID 0xA220 +// Set the following define to use the Certification Declaration from below and not use it stored in factory data section +#ifndef CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION +#define CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION 0 +#endif + #ifndef CHIP_DEVICE_CONFIG_CERTIFICATION_DECLARATION //-> format_version = 1 //-> vendor_id = 0x1037 @@ -188,8 +193,7 @@ #define CHIP_CONFIG_MAX_FABRICS 5 // 5 is the minimum number of supported fabrics #define CHIP_DEVICE_CONFIG_ENABLE_SED 1 -#define CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL 1000_ms32 -#define CHIP_DEVICE_CONFIG_ICD_FAST_POLL_INTERVAL 100_ms32 + /** * @def CHIP_IM_MAX_NUM_COMMAND_HANDLER * diff --git a/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp b/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp index a3656550b9434c..484ddce1b7ce1b 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp +++ b/examples/contact-sensor-app/nxp/k32w/k32w0/main/AppTask.cpp @@ -45,6 +45,8 @@ #include #endif +#include "BLEManagerImpl.h" + #include "Keyboard.h" #include "LED.h" #include "LEDWidget.h" @@ -123,6 +125,18 @@ CHIP_ERROR AppTask::StartAppTask() return err; } +static void app_gap_callback(gapGenericEvent_t * event) +{ + /* This callback is called in the context of BLE task, so event processing + * should be posted to app task. */ +} + +static void app_gatt_callback(deviceId_t id, gattServerEvent_t * event) +{ + /* This callback is called in the context of BLE task, so event processing + * should be posted to app task. */ +} + #if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR static void CheckOtaEntry() { @@ -240,6 +254,17 @@ CHIP_ERROR AppTask::Init() K32W_LOG("Current Software Version: %s, %" PRIu32, currentSoftwareVer, currentVersion); +#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR + /* SSBL will always be seen as booting from address 0, thanks to the remapping mechanism. + * This means the SSBL version will always offset from address 0. */ + extern uint32_t __MATTER_SSBL_VERSION_START[]; + K32W_LOG("Current SSBL Version: %ld. Found at address 0x%lx", *((uint32_t *) __MATTER_SSBL_VERSION_START), + (uint32_t) __MATTER_SSBL_VERSION_START); +#endif + + auto & bleManager = chip::DeviceLayer::Internal::BLEMgrImpl(); + bleManager.RegisterAppCallbacks(app_gap_callback, app_gatt_callback); + return err; } diff --git a/examples/contact-sensor-app/nxp/k32w/k32w1/main/AppTask.cpp b/examples/contact-sensor-app/nxp/k32w/k32w1/main/AppTask.cpp index bce7374d7ea7ef..c7acba0f3ee1cb 100644 --- a/examples/contact-sensor-app/nxp/k32w/k32w1/main/AppTask.cpp +++ b/examples/contact-sensor-app/nxp/k32w/k32w1/main/AppTask.cpp @@ -46,6 +46,8 @@ #include #endif +#include + #include "K32W1PersistentStorageOpKeystore.h" #include "LEDWidget.h" @@ -106,6 +108,18 @@ static BDXDownloader gDownloader __attribute__((section(".data"))); constexpr uint16_t requestedOtaBlockSize = 1024; #endif +static void app_gap_callback(gapGenericEvent_t * event) +{ + /* This callback is called in the context of BLE task, so event processing + * should be posted to app task. */ +} + +static void app_gatt_callback(deviceId_t id, gattServerEvent_t * event) +{ + /* This callback is called in the context of BLE task, so event processing + * should be posted to app task. */ +} + CHIP_ERROR AppTask::StartAppTask() { CHIP_ERROR err = CHIP_NO_ERROR; @@ -188,6 +202,9 @@ CHIP_ERROR AppTask::Init() K32W_LOG("Current Software Version: %s, %d", currentSoftwareVer, currentVersion); + auto & bleManager = chip::DeviceLayer::Internal::BLEMgrImpl(); + bleManager.RegisterAppCallbacks(app_gap_callback, app_gatt_callback); + return err; } diff --git a/examples/lighting-app/nxp/k32w/k32w0/BUILD.gn b/examples/lighting-app/nxp/k32w/k32w0/BUILD.gn index 2ee2e42a24259e..a71bf63dc6d4e0 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/BUILD.gn +++ b/examples/lighting-app/nxp/k32w/k32w0/BUILD.gn @@ -25,6 +25,7 @@ import("${chip_root}/src/platform/device.gni") declare_args() { chip_software_version = 0 + chip_simple_hash_verification = 0 } if (chip_pw_tokenizer_logging) { @@ -170,6 +171,10 @@ action("binsign") { script = "${k32w0_platform_dir}/scripts/sign-outdir.py" output_name = "bignsign.log" outputs = [ "${root_build_dir}/${output_name}" ] + + if (chip_simple_hash_verification == 1) { + args = [ "--simple-hash" ] + } } group("default") { diff --git a/examples/lighting-app/nxp/k32w/k32w0/README.md b/examples/lighting-app/nxp/k32w/k32w0/README.md index 829d4bd1e70223..b43a08622eb330 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/README.md +++ b/examples/lighting-app/nxp/k32w/k32w0/README.md @@ -457,6 +457,10 @@ SSBL demo application can be imported from the `Quickstart panel`: ![SSBL Application Select](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_select.JPG) +### Features + +#### Multi image + To support multi-image OTA feature, the SSBL project must be compiled using the following defines: @@ -474,6 +478,24 @@ Optionally, add the following defines: ![SSBL_MULTI_IMAGE](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_multi_image.JPG) +#### Simple hash verification + +When secure boot is not used, a simple hash can be appended at the end of the +image for integrity check. Applications should be built with +`chip_simple_hash_verification=1`. + +To support simple hash verification feature, the SSBL project must be compiled +with: + +- `gSimpleHashVerification=1` + +and update the post-build command to use simple hash verification instead of the +default options. Go to +`Project -> Properties -> C/C++ Build -> Settings -> Build steps` and press +`Edit` under `Post-build steps` subsection. The command should look similar to: + +![SSBL_SIMPLE_HASH_VERIFICATION](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG) + Once compiled, the required SSBL file is called `k32w061dk6_ssbl.bin`. ![SSBL_BIN](../../../../platform/nxp/k32w/k32w0/doc/images/ssbl_bin.JPG) diff --git a/examples/lighting-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h b/examples/lighting-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h index 63abce6c8c0682..10019a4268cad2 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h +++ b/examples/lighting-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h @@ -59,6 +59,11 @@ #define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID 0x1037 #define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID 0xA220 +// Set the following define to use the Certification Declaration from below and not use it stored in factory data section +#ifndef CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION +#define CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION 0 +#endif + #ifndef CHIP_DEVICE_CONFIG_CERTIFICATION_DECLARATION //-> format_version = 1 //-> vendor_id = 0x1037 diff --git a/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp b/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp index 02656ae62299c7..e63c35a1489ee0 100644 --- a/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp +++ b/examples/lighting-app/nxp/k32w/k32w0/main/AppTask.cpp @@ -249,6 +249,14 @@ CHIP_ERROR AppTask::Init() K32W_LOG("Current Software Version: %s, %" PRIu32, currentSoftwareVer, currentVersion); +#if CHIP_DEVICE_CONFIG_ENABLE_OTA_REQUESTOR + /* SSBL will always be seen as booting from address 0, thanks to the remapping mechanism. + * This means the SSBL version will always offset from address 0. */ + extern uint32_t __MATTER_SSBL_VERSION_START[]; + K32W_LOG("Current SSBL Version: %ld. Found at address 0x%lx", *((uint32_t *) __MATTER_SSBL_VERSION_START), + (uint32_t) __MATTER_SSBL_VERSION_START); +#endif + return err; } @@ -897,11 +905,24 @@ void AppTask::PostTurnOnActionRequest(int32_t aActor, LightingManager::Action_t void AppTask::PostEvent(const AppEvent * aEvent) { + portBASE_TYPE taskToWake = pdFALSE; if (sAppEventQueue != NULL) { - if (!xQueueSend(sAppEventQueue, aEvent, 1)) + if (__get_IPSR()) { - K32W_LOG("Failed to post event to app task event queue"); + if (!xQueueSendToFrontFromISR(sAppEventQueue, aEvent, &taskToWake)) + { + K32W_LOG("Failed to post event to app task event queue"); + } + + portYIELD_FROM_ISR(taskToWake); + } + else + { + if (!xQueueSend(sAppEventQueue, aEvent, 1)) + { + K32W_LOG("Failed to post event to app task event queue"); + } } } } diff --git a/examples/lock-app/nxp/k32w/k32w0/args.gni b/examples/lock-app/nxp/k32w/k32w0/args.gni index dd8543b45f9f8f..a733921b547e38 100644 --- a/examples/lock-app/nxp/k32w/k32w0/args.gni +++ b/examples/lock-app/nxp/k32w/k32w0/args.gni @@ -21,3 +21,7 @@ k32w0_sdk_target = get_label_info(":sdk", "label_no_toolchain") chip_stack_lock_tracking = "fatal" chip_enable_ble = true + +chip_enable_icd_server = true +chip_persist_subscriptions = true +chip_subscription_timeout_resumption = true diff --git a/examples/lock-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h b/examples/lock-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h index 046b005f78da73..c74714bded920f 100644 --- a/examples/lock-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h +++ b/examples/lock-app/nxp/k32w/k32w0/include/CHIPProjectConfig.h @@ -59,6 +59,11 @@ #define CHIP_DEVICE_CONFIG_DEVICE_VENDOR_ID 0x1037 #define CHIP_DEVICE_CONFIG_DEVICE_PRODUCT_ID 0xA220 +// Set the following define to use the Certification Declaration from below and not use it stored in factory data section +#ifndef CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION +#define CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION 0 +#endif + #ifndef CHIP_DEVICE_CONFIG_CERTIFICATION_DECLARATION //-> format_version = 1 //-> vendor_id = 0x1037 @@ -179,8 +184,6 @@ #define CHIP_CONFIG_MAX_FABRICS 5 // 5 is the minimum number of supported fabrics #define CHIP_DEVICE_CONFIG_ENABLE_SED 1 -#define CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL 1000_ms32 -#define CHIP_DEVICE_CONFIG_ICD_FAST_POLL_INTERVAL 100_ms32 /** * CHIP_CONFIG_EVENT_LOGGING_DEFAULT_IMPORTANCE diff --git a/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld b/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld index c7a2e4009865e5..d3ea6ac41daf78 100644 --- a/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld +++ b/examples/platform/nxp/k32w/k32w0/app/ldscripts/chip-k32w0x-linker.ld @@ -432,8 +432,13 @@ SECTIONS __StackLimit = _vStackTop - STACK_SIZE; - __FACTORY_DATA_START = FACTORY_DATA_START_ADDRESS; - __FACTORY_DATA_SIZE = m_factory_data_size; + __MATTER_FACTORY_DATA_START = FACTORY_DATA_START_ADDRESS; + __MATTER_FACTORY_DATA_SIZE = m_factory_data_size; + + /* The .ro_version section inside SSBL is set after the .m_interrupts sections, + * which is assumed to never change, so the offset remains the same across different + * SSBL versions. This symbol is used in Matter Application to retrieve the SSBL version. */ + __MATTER_SSBL_VERSION_START = 0x00000120; ASSERT(((m_app_start + m_app_size + m_app_meta_data + m_factory_data_size + m_flash_config_size) <= m_int_flash_size), "Internal flash capacity exceeded") diff --git a/examples/platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG b/examples/platform/nxp/k32w/k32w0/doc/images/ssbl_simple_hash.JPG new file mode 100755 index 0000000000000000000000000000000000000000..9ec701b0bb1d86cac72c2df63702fde227d33f77 GIT binary patch literal 56397 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 z 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 j0)*B5AvD=)=h_3Arp0p6F($CYi#A94 /dev/null) && (dos2unix " + sign_images_path + ")", shell=True) + # Give execute permission if needed + if os.access(sign_images_path, os.X_OK) is False: + os.chmod(sign_images_path, 0o766) -# Call sign_images.sh script with the output directory -subprocess.call(sign_images_path + " " + os.getcwd(), shell=True) + # Convert script to unix format if needed + subprocess.call("(file " + sign_images_path + " | grep CRLF > /dev/null) && (dos2unix " + sign_images_path + ")", shell=True) + + # Call sign_images.sh script with the output directory + cmd = sign_images_path + " " + os.getcwd() + if args.simple_hash: + cmd = cmd + " -SimpleHashVerification" + + subprocess.call(cmd, shell=True) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser() + parser.add_argument( + "--simple-hash", + help="When enabled, adds a hash of the whole image at the end of the binary.", + action="store_true" + ) + args = parser.parse_args() + + main(args) diff --git a/scripts/tools/nxp/factory_data_generator/generate.py b/scripts/tools/nxp/factory_data_generator/generate.py index daf1b5d1267cb7..a203ac9dee749e 100755 --- a/scripts/tools/nxp/factory_data_generator/generate.py +++ b/scripts/tools/nxp/factory_data_generator/generate.py @@ -27,6 +27,14 @@ SetupPasscode, StrArgument, UniqueId, VendorId, VendorName, Verifier) from default import InputArgument +# A magic value used in the factory data integrity check. +# The value will be checked at runtime, before verifying the +# factory data integrity. Factory data header has the following format: +# | hash id (4 bytes) | size (4 bytes) | hash (4 bytes) | +# If the hash id check fails, it means the factory data is either missing +# or has become corrupted. +HASH_ID = "CE47BA5E" + def set_logger(): stdout_handler = logging.StreamHandler(stream=sys.stdout) @@ -155,9 +163,9 @@ def to_bin(self, klv, out, aes128_key): fullContentCipher = size.to_bytes(4, "little") + fullContentCipher # Add hash id - hashId = bytearray.fromhex("CE47BA5E") + hashId = bytearray.fromhex(HASH_ID) hashId.reverse() - fullContentCipher = hashId.reverse() + fullContentCipher + fullContentCipher = hashId + fullContentCipher size = len(fullContentCipher) diff --git a/scripts/tools/nxp/generate_certs.py b/scripts/tools/nxp/generate_certs.py new file mode 100644 index 00000000000000..7a98f7138ebe05 --- /dev/null +++ b/scripts/tools/nxp/generate_certs.py @@ -0,0 +1,211 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023 Project CHIP Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import argparse +import logging as log +import os +import subprocess + +MATTER_ROOT = os.path.dirname(os.path.realpath(f"{__file__}/../../../")) + + +def gen_test_certs(chip_cert_exe: str, + output: str, + vendor_id: int, + product_id: int, + device_name: str, + generate_cd: bool = False, + cd_type: int = 1, + device_type: int = 1, + paa_cert_path: str = None, + paa_key_path: str = None, + valid_from: str = "2023-01-01 00:00:00", + lifetime: str = "7305"): + """ + Generate Matter certificates according to given Vendor ID and Product ID using the chip-cert executable. + To use own Product Attestation Authority certificate provide paa_cert_path and paa_key_path arguments. + Without providing these arguments a PAA certificate will be generated in the output directory. + + Args: + chip_cert_exe (str): path to chip-cert executable + output (str): output path to store a newly generated certificates (CD, DAC, PAI) + vendor_id (int): an identification number specific to Vendor + product_id (int): an identification number specific to Product + device_name (str): human-readable device name + generate_cd (bool, optional): Generate Certificate Declaration and store it in the output directory. Defaults to False. + paa_cert_path (str, optional): provide PAA certification path. Defaults to None - the certificate and key will + be generated. + paa_key_path (str, optional): provide PAA key path. Defaults to None - the certificate and key will be generated. + """ + + CD_PATH = MATTER_ROOT + "/credentials/test/certification-declaration/Chip-Test-CD-Signing-Cert.pem" + CD_KEY_PATH = MATTER_ROOT + "/credentials/test/certification-declaration/Chip-Test-CD-Signing-Key.pem" + + log.info("Generating new certificates using chip-cert...") + + if generate_cd: + log.info("Generating Certification Declaration...") + cmd = [chip_cert_exe, "gen-cd", + "--key", CD_KEY_PATH, + "--cert", CD_PATH, + "--out", output + "/Chip-Test-CD-" + f'{vendor_id:X}' + "-" + f'{product_id:X}' + ".der", + "--format-version", "1", + "--vendor-id", hex(vendor_id), + "--product-id", hex(product_id), + "--device-type-id", hex(device_type), + "--certificate-id", "ZIG20142ZB330003-24", + "--security-level", "0", + "--security-info", "0", + "--certification-type", str(cd_type), + "--version-number", "9876", + ] + subprocess.run(cmd) + + new_certificates = { + "PAA_CERT": output + "/Chip-PAA-NXP-Cert", + "PAA_KEY": output + "/Chip-PAA-NXP-Key", + "PAI_CERT": output + "/Chip-PAI-NXP-" + f'{vendor_id:X}' + "-" + f'{product_id:X}' + "-Cert", + "PAI_KEY": output + "/Chip-PAI-NXP-" + f'{vendor_id:X}' + "-" + f'{product_id:X}' + "-Key", + "DAC_CERT": output + "/Chip-DAC-NXP-" + f'{vendor_id:X}' + "-" + f'{product_id:X}' + "-Cert", + "DAC_KEY": output + "/Chip-DAC-NXP-" + f'{vendor_id:X}' + "-" + f'{product_id:X}' + "-Key", + } + + if ((paa_cert_path is None) or (paa_key_path is None)): + log.info("Generating PAA certificate...") + cmd = [ + chip_cert_exe, "gen-att-cert", + "--type", "a", + "--subject-cn", device_name, + "--out", new_certificates["PAA_CERT"] + ".pem", + "--out-key", new_certificates["PAA_KEY"] + ".pem", + "--valid-from", valid_from, + "--lifetime", lifetime + ] + subprocess.run(cmd) + PAA_PATH = new_certificates["PAA_CERT"] + ".pem" + PAA_KEY_PATH = new_certificates["PAA_KEY"] + ".pem" + else: + PAA_PATH = paa_cert_path + PAA_KEY_PATH = paa_key_path + log.info("Using PAA certificate: " + PAA_PATH) + log.info("using PAA key: " + PAA_KEY_PATH) + + log.info("Generating PAI certificate...") + cmd = [ + chip_cert_exe, "gen-att-cert", + "--type", "i", + "--subject-cn", device_name, + "--subject-vid", hex(vendor_id), + "--ca-cert", PAA_PATH, + "--ca-key", PAA_KEY_PATH, + "--out", new_certificates["PAI_CERT"] + ".pem", + "--out-key", new_certificates["PAI_KEY"] + ".pem", + "--valid-from", valid_from, + "--lifetime", lifetime + ] + subprocess.run(cmd) + + log.info("Generating DAC certificate...") + cmd = [ + chip_cert_exe, "gen-att-cert", + "--type", "d", + "--subject-cn", device_name, + "--subject-vid", hex(vendor_id), + "--subject-pid", hex(product_id), + "--ca-cert", new_certificates["PAI_CERT"] + ".pem", + "--ca-key", new_certificates["PAI_KEY"] + ".pem", + "--out", new_certificates["DAC_CERT"] + ".pem", + "--out-key", new_certificates["DAC_KEY"] + ".pem", + "--valid-from", valid_from, + "--lifetime", lifetime + ] + subprocess.run(cmd) + + log.info("Converting to .der files...") + for cert_k, cert_v in new_certificates.items(): + action_type = "convert-cert" if cert_k.find("CERT") != -1 else "convert-key" + log.info(cert_v + ".der") + cmd = [ + chip_cert_exe, + action_type, + "--x509-der", + cert_v + ".pem", + cert_v + ".der", + ] + subprocess.run(cmd) + + +def main(): + parser = argparse.ArgumentParser(description="NXP CHIP Certificates generator") + + def allow_any_int(i): return int(i, 0) + + parser.add_argument("--chip_cert_path", type=str, required=True, + help=("This tool requires a path to chip-cert executable. " + "By default you can find chip-cert in connectedhomeip/src/tools/chip-cert directory " + "and build it there.")) + parser.add_argument("-o", "--output", type=str, required=True, + help="Output path to store certificates, e.g. /path/to/my/dir") + parser.add_argument("--vendor_id", type=allow_any_int, required=True, + help="[int | hex int] Provide Vendor Identification Number") + parser.add_argument("--product_id", type=allow_any_int, required=True, + help="[int | hex int] Provide Product Identification Number") + parser.add_argument("--vendor_name", type=str, required=True, + help="[string] provide human-readable vendor name") + parser.add_argument("--product_name", type=str, required=True, + help="[string] provide human-readable product name") + parser.add_argument("--gen_cd", action="store_true", default=False, + help=("Generate a new Certificate Declaration in .der format according to used Vendor ID " + "and Product ID.")) + parser.add_argument("--cd_type", type=int, default=1, + help=("[int] Type of generated Certification Declaration: " + "0 - development, 1 - provisional, 2 - official")) + parser.add_argument("--device_type", type=int, default=0, + help=("[int] Provides the primary device type implemented by the node. " + "This must be one of the device type identifiers defined in the Matter Device Library " + "specification.")) + parser.add_argument("--paa_cert", type=str, + help=("Provide a path to the Product Attestation Authority (PAA) certificate to generate " + "the PAI certificate. Without providing it, a testing PAA certificate will be generated.")) + parser.add_argument("--paa_key", type=str, + help=("Provide a path to the Product Attestation Authority (PAA) key to generate " + "the PAI certificate. Without providing it, a testing PAA key will be generated.")) + parser.add_argument("--valid_from", type=str, default="2023-01-01 00:00:00", + help=("The start date for the certificate's validity period in" + "--
[ :: ] format. Default to 2023-01-01 00:00:00")) + parser.add_argument("--lifetime", type=str, default="7305", + help=("The lifetime for the new certificate, in whole days. Default to 7305 days.")) + args = parser.parse_args() + + log.basicConfig(format='[%(levelname)s] %(message)s', level=log.INFO) + + gen_test_certs(args.chip_cert_path, + args.output, + args.vendor_id, + args.product_id, + args.vendor_name + " " + args.product_name, + args.gen_cd, + args.cd_type, + args.device_type, + args.paa_cert, + args.paa_key, + args.valid_from, + args.lifetime) + + +if __name__ == "__main__": + main() diff --git a/scripts/tools/nxp/ota/crypto_utils.py b/scripts/tools/nxp/ota/crypto_utils.py new file mode 100755 index 00000000000000..dbab140cf57cf9 --- /dev/null +++ b/scripts/tools/nxp/ota/crypto_utils.py @@ -0,0 +1,487 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2023 Project CHIP Authors +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +""" +A pure python (slow) implementation of rijndael with a decent interface + +To include - + +from rijndael import rijndael + +To do a key setup - + +r = rijndael(key, block_size = 16) + +key must be a string of length 16, 24, or 32 +blocksize must be 16, 24, or 32. Default is 16 + +To use - + +ciphertext = r.encrypt(plaintext) +plaintext = r.decrypt(ciphertext) + +If any strings are of the wrong length a ValueError is thrown +""" +# ported from the Java reference code by Bram Cohen, April 2001 +# this code is public domain, unless someone makes +# an intellectual property claim against the reference +# code, in which case it can be made public domain by +# deleting all the comments and renaming all the variables + +import copy +import logging +import struct + +shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]], + [[0, 0], [1, 5], [2, 4], [3, 3]], + [[0, 0], [1, 7], [3, 5], [4, 4]]] + +# [keysize][block_size] +num_rounds = {16: {16: 10, 24: 12, 32: 14}, 24: {16: 12, 24: 12, 32: 14}, 32: {16: 14, 24: 14, 32: 14}} + +A = [[1, 1, 1, 1, 1, 0, 0, 0], + [0, 1, 1, 1, 1, 1, 0, 0], + [0, 0, 1, 1, 1, 1, 1, 0], + [0, 0, 0, 1, 1, 1, 1, 1], + [1, 0, 0, 0, 1, 1, 1, 1], + [1, 1, 0, 0, 0, 1, 1, 1], + [1, 1, 1, 0, 0, 0, 1, 1], + [1, 1, 1, 1, 0, 0, 0, 1]] + +# produce log and alog tables, needed for multiplying in the +# field GF(2^m) (generator = 3) +alog = [1] +for i in range(255): + j = (alog[-1] << 1) ^ alog[-1] + if j & 0x100 != 0: + j ^= 0x11B + alog.append(j) + +log = [0] * 256 +for i in range(1, 255): + log[alog[i]] = i + + +# multiply two elements of GF(2^m) +def mul(a, b): + if a == 0 or b == 0: + return 0 + return alog[(log[a & 0xFF] + log[b & 0xFF]) % 255] # noqa: F821 + + +# substitution box based on F^{-1}(x) +box = [[0] * 8 for i in range(256)] +box[1][7] = 1 +for i in range(2, 256): + j = alog[255 - log[i]] + for t in range(8): + box[i][t] = (j >> (7 - t)) & 0x01 + +B = [0, 1, 1, 0, 0, 0, 1, 1] + +# affine transform: box[i] <- B + A*box[i] +cox = [[0] * 8 for i in range(256)] +for i in range(256): + for t in range(8): + cox[i][t] = B[t] + for j in range(8): + cox[i][t] ^= A[t][j] * box[i][j] + +# S-boxes and inverse S-boxes +S = [0] * 256 +Si = [0] * 256 +for i in range(256): + S[i] = cox[i][0] << 7 + for t in range(1, 8): + S[i] ^= cox[i][t] << (7-t) + Si[S[i] & 0xFF] = i + +# T-boxes +G = [[2, 1, 1, 3], + [3, 2, 1, 1], + [1, 3, 2, 1], + [1, 1, 3, 2]] + +AA = [[0] * 8 for i in range(4)] + +for i in range(4): + for j in range(4): + AA[i][j] = G[i][j] + AA[i][i+4] = 1 + +for i in range(4): + pivot = AA[i][i] + if pivot == 0: + t = i + 1 + while AA[t][i] == 0 and t < 4: + t += 1 + assert t != 4, 'G matrix must be invertible' + for j in range(8): + AA[i][j], AA[t][j] = AA[t][j], AA[i][j] + pivot = AA[i][i] + for j in range(8): + if AA[i][j] != 0: + AA[i][j] = alog[(255 + log[AA[i][j] & 0xFF] - log[pivot & 0xFF]) % 255] + for t in range(4): + if i != t: + for j in range(i+1, 8): + AA[t][j] ^= mul(AA[i][j], AA[t][i]) + AA[t][i] = 0 + +iG = [[0] * 4 for i in range(4)] + +for i in range(4): + for j in range(4): + iG[i][j] = AA[i][j + 4] + + +def mul4(a, bs): + if a == 0: + return 0 + r = 0 + for b in bs: + r <<= 8 + if b != 0: + r = r | mul(a, b) # noqa: F821 + return r + + +T1 = [] +T2 = [] +T3 = [] +T4 = [] +T5 = [] +T6 = [] +T7 = [] +T8 = [] +U1 = [] +U2 = [] +U3 = [] +U4 = [] + +for t in range(256): + s = S[t] + T1.append(mul4(s, G[0])) + T2.append(mul4(s, G[1])) + T3.append(mul4(s, G[2])) + T4.append(mul4(s, G[3])) + + s = Si[t] + T5.append(mul4(s, iG[0])) + T6.append(mul4(s, iG[1])) + T7.append(mul4(s, iG[2])) + T8.append(mul4(s, iG[3])) + + U1.append(mul4(t, iG[0])) + U2.append(mul4(t, iG[1])) + U3.append(mul4(t, iG[2])) + U4.append(mul4(t, iG[3])) + +# round constants +rcon = [1] +r = 1 +for t in range(1, 30): + r = mul(2, r) + rcon.append(r) + +del A +del AA +del pivot +del B +del G +del box +del log +del alog +del i +del j +del r +del s +del t +del mul +del mul4 +del cox +del iG + + +class rijndael: + def __init__(self, key, block_size=16): + if block_size != 16 and block_size != 24 and block_size != 32: + raise ValueError('Invalid block size: ' + str(block_size)) + if len(key) != 16 and len(key) != 24 and len(key) != 32: + raise ValueError('Invalid key size: ' + str(len(key))) + self.block_size = block_size + + ROUNDS = num_rounds[len(key)][block_size] + BC = int(block_size / 4) + + # encryption round keys + Ke = [[0] * BC for i in range(ROUNDS + 1)] + # decryption round keys + Kd = [[0] * BC for i in range(ROUNDS + 1)] + ROUND_KEY_COUNT = (ROUNDS + 1) * BC + KC = int(len(key) / 4) + + # copy user material bytes into temporary ints + tk = [] + for i in range(0, KC): + tk.append((key[i * 4] << 24) | (key[i * 4 + 1] << 16) | + (key[i * 4 + 2] << 8) | key[i * 4 + 3]) + + # copy values into round key arrays + t = 0 + j = 0 + while j < KC and t < ROUND_KEY_COUNT: + Ke[int(t / BC)][t % BC] = tk[j] + Kd[ROUNDS - int(t / BC)][t % BC] = tk[j] + j += 1 + t += 1 + tt = 0 + rconpointer = 0 + while t < ROUND_KEY_COUNT: + # extrapolate using phi (the round key evolution function) + tt = tk[KC - 1] + tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^ \ + (S[(tt >> 8) & 0xFF] & 0xFF) << 16 ^ \ + (S[tt & 0xFF] & 0xFF) << 8 ^ \ + (S[(tt >> 24) & 0xFF] & 0xFF) ^ \ + (rcon[rconpointer] & 0xFF) << 24 + rconpointer += 1 + if KC != 8: + for i in range(1, KC): + tk[i] ^= tk[i-1] + else: + for i in range(1, KC / 2): + tk[i] ^= tk[i-1] + tt = tk[KC / 2 - 1] + tk[KC / 2] ^= (S[tt & 0xFF] & 0xFF) ^ \ + (S[(tt >> 8) & 0xFF] & 0xFF) << 8 ^ \ + (S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \ + (S[(tt >> 24) & 0xFF] & 0xFF) << 24 + for i in range(KC / 2 + 1, KC): + tk[i] ^= tk[i-1] + # copy values into round key arrays + j = 0 + while j < KC and t < ROUND_KEY_COUNT: + Ke[int(t / BC)][t % BC] = tk[j] + Kd[ROUNDS - int(t / BC)][t % BC] = tk[j] + j += 1 + t += 1 + # inverse MixColumn where needed + for r in range(1, ROUNDS): + for j in range(BC): + tt = Kd[r][j] + Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \ + U2[(tt >> 16) & 0xFF] ^ \ + U3[(tt >> 8) & 0xFF] ^ \ + U4[tt & 0xFF] + self.Ke = Ke + self.Kd = Kd + + def encrypt(self, plaintext): + if len(plaintext) != self.block_size: + raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(plaintext))) + Ke = self.Ke + + BC = int(self.block_size / 4) + ROUNDS = len(Ke) - 1 + if BC == 4: + SC = 0 + elif BC == 6: + SC = 1 + else: + SC = 2 + s1 = shifts[SC][1][0] + s2 = shifts[SC][2][0] + s3 = shifts[SC][3][0] + a = [0] * BC + # temporary work array + t = [] + # plaintext to ints + key + for i in range(BC): + t.append((ord(plaintext[i * 4]) << 24 | + ord(plaintext[i * 4 + 1]) << 16 | + ord(plaintext[i * 4 + 2]) << 8 | + ord(plaintext[i * 4 + 3])) ^ Ke[0][i]) + # apply round transforms + for r in range(1, ROUNDS): + for i in range(BC): + a[i] = (T1[(t[i] >> 24) & 0xFF] ^ + T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^ + T3[(t[(i + s2) % BC] >> 8) & 0xFF] ^ + T4[t[(i + s3) % BC] & 0xFF]) ^ Ke[r][i] + t = copy.copy(a) + # last round is special + result = [] + for i in range(BC): + tt = Ke[ROUNDS][i] + result.append((S[(t[i] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((S[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((S[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((S[t[(i + s3) % BC] & 0xFF] ^ tt) & 0xFF) + return ''.join(list(map(chr, result))) + + def decrypt(self, ciphertext): + if len(ciphertext) != self.block_size: + raise ValueError('wrong block length, expected ' + str(self.block_size) + ' got ' + str(len(ciphertext))) + Kd = self.Kd + + BC = int(self.block_size / 4) + ROUNDS = len(Kd) - 1 + if BC == 4: + SC = 0 + elif BC == 6: + SC = 1 + else: + SC = 2 + s1 = shifts[SC][1][1] + s2 = shifts[SC][2][1] + s3 = shifts[SC][3][1] + a = [0] * BC + # temporary work array + t = [0] * BC + # ciphertext to ints + key + for i in range(BC): + t[i] = (ord(ciphertext[i * 4]) << 24 | + ord(ciphertext[i * 4 + 1]) << 16 | + ord(ciphertext[i * 4 + 2]) << 8 | + ord(ciphertext[i * 4 + 3])) ^ Kd[0][i] + # apply round transforms + for r in range(1, ROUNDS): + for i in range(BC): + a[i] = (T5[(t[i] >> 24) & 0xFF] ^ + T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^ + T7[(t[(i + s2) % BC] >> 8) & 0xFF] ^ + T8[t[(i + s3) % BC] & 0xFF]) ^ Kd[r][i] + t = copy.copy(a) + # last round is special + result = [] + for i in range(BC): + tt = Kd[ROUNDS][i] + result.append((Si[(t[i] >> 24) & 0xFF] ^ (tt >> 24)) & 0xFF) + result.append((Si[(t[(i + s1) % BC] >> 16) & 0xFF] ^ (tt >> 16)) & 0xFF) + result.append((Si[(t[(i + s2) % BC] >> 8) & 0xFF] ^ (tt >> 8)) & 0xFF) + result.append((Si[t[(i + s3) % BC] & 0xFF] ^ tt) & 0xFF) + return ''.join(map(chr, result)) + + +def encryptFlashData(nonce, key, data, imageLen): + encyptedBlock = '' + if (imageLen % 16) != 0: + for x in range(16 - (imageLen % 16)): + data = data + bytes([255]) + imageLen = len(data) + + r = rijndael(key, block_size=16) + + for x in range(int(imageLen / 16)): + # use nonce value to create encrypted chunk + encryptNonce = '' + for i in nonce: + tempString = "%08x" % i + y = 0 + while y < 8: + encryptNonce = encryptNonce + chr(int(tempString[y:y+2], 16)) + y = y + 2 + encChunk = r.encrypt(encryptNonce) + + # increment the nonce value + if (nonce[3] == 0xffffffff): + nonce[3] = 0 + else: + nonce[3] += 1 + + # xor encypted junk with data chunk + chunk = data[x*16:(x+1)*16] # Read 16 byte chucks. 128 bits + + lchunk = chunk + lencChunk = list(map(ord, encChunk)) + + loutChunk = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + for i in range(16): + loutChunk[i] = lchunk[i] ^ lencChunk[i] + encyptedBlock = encyptedBlock + chr(lchunk[i] ^ lencChunk[i]) + + return (encyptedBlock) + + +def aParsePassKeyString(sPassKey): + lstu32Passkey = [0, 0, 0, 0] + + try: + lstStrPassKey = sPassKey.split(",") + + except Exception: + sPassKey = "0x00000000, 0x00000000, 0x00000000, 0x00000000" + lstStrPassKey = sPassKey.split(",") + + if len(lstStrPassKey) == 4: + for i in range(4): + if "0x" in lstStrPassKey[i]: + lstu32Passkey[i] = int(lstStrPassKey[i], 16) + else: + lstu32Passkey[i] = int(lstStrPassKey[i], 10) + + logging.info(f"\t-key: {lstu32Passkey[0]}, {lstu32Passkey[1]}, {lstu32Passkey[2]}, {lstu32Passkey[3]}") + abEncryptKey = struct.pack(">LLLL", lstu32Passkey[0], + lstu32Passkey[1], + lstu32Passkey[2], + lstu32Passkey[3]) + return abEncryptKey + + +def aParseNonce(sNonceValue): + lstu32Nonce = [0, 0, 0, 0] + + try: + lstStrNonce = sNonceValue.split(",") + + except Exception: + sNonceValue = "0x00000000, 0x00000000, 0x00000000, 0x00000000" + lstStrNonce = sNonceValue.split(",") + + if len(lstStrNonce) == 4: + for i in range(4): + if "0x" in lstStrNonce[i]: + lstu32Nonce[i] = int(lstStrNonce[i], 16) + else: + lstu32Nonce[i] = int(lstStrNonce[i], 10) + + logging.info(f"Nonce : {lstu32Nonce[0]}, {lstu32Nonce[1]}, {lstu32Nonce[2]}, {lstu32Nonce[3]}") + + return lstu32Nonce + + +def encryptData(sSrcData, sPassKey, aPassIv): + + sKeyString = sPassKey.strip() + assert len(sKeyString) == 32, 'the length of encryption key should be equal to 32' + sPassString = "0x" + sKeyString[:8] + ',' + "0x" + sKeyString[8:16] + \ + ',' + "0x" + sKeyString[16:24] + ',' + "0x" + sKeyString[24:32] + aPassKey = aParsePassKeyString(sPassString) + + sIvString = aPassIv.strip() + sPassString = "0x" + sIvString[:8] + ',' + "0x" + sIvString[8:16] + \ + ',' + "0x" + sIvString[16:24] + ',' + "0x" + sIvString[24:32] + aNonce = aParseNonce(sPassString) + + logging.info("Started Encrypting with key[{}] ......".format(sPassKey)) + + encryptedData = encryptFlashData(aNonce, aPassKey, sSrcData, len(sSrcData)) + + logging.info("Done") + + return encryptedData diff --git a/scripts/tools/nxp/ota/ota_image_tool.py b/scripts/tools/nxp/ota/ota_image_tool.py index bf796f1010119f..64715d784aeecd 100755 --- a/scripts/tools/nxp/ota/ota_image_tool.py +++ b/scripts/tools/nxp/ota/ota_image_tool.py @@ -36,6 +36,7 @@ import os import sys +import crypto_utils import jsonschema sys.path.insert(0, os.path.join( @@ -55,6 +56,8 @@ OTA_BOOTLOADER_TLV_TEMP = os.path.join(os.path.dirname(__file__), "ota_temp_ssbl_tlv.bin") OTA_FACTORY_TLV_TEMP = os.path.join(os.path.dirname(__file__), "ota_temp_factory_tlv.bin") +INITIALIZATION_VECTOR = "00000010111213141516171800000000" + class TAG: APPLICATION = 1 @@ -92,8 +95,15 @@ def generate_factory_data(args: object): if fields: writer = TLVWriter() writer.put(None, fields) - payload = generate_header(TAG.FACTORY_DATA, len(writer.encoding)) - payload += writer.encoding + logging.info(f"factory data encryption enable: {args.enc_enable}") + if args.enc_enable: + enc_factory_data = crypto_utils.encryptData(writer.encoding, args.input_ota_key, INITIALIZATION_VECTOR) + enc_factory_data1 = bytes([ord(x) for x in enc_factory_data]) + payload = generate_header(TAG.FACTORY_DATA, len(enc_factory_data1)) + payload += enc_factory_data1 + else: + payload = generate_header(TAG.FACTORY_DATA, len(writer.encoding)) + payload += writer.encoding write_to_temp(OTA_FACTORY_TLV_TEMP, payload) @@ -126,12 +136,23 @@ def generate_app(args: object): logging.info("App descriptor information:") descriptor = generate_descriptor(args.app_version, args.app_version_str, args.app_build_date) - file_size = os.path.getsize(args.app_input_file) - payload = generate_header(TAG.APPLICATION, len(descriptor) + file_size) + descriptor + logging.info(f"App encryption enable: {args.enc_enable}") + if args.enc_enable: + inputFile = open(args.app_input_file, "rb") + enc_file = crypto_utils.encryptData(inputFile.read(), args.input_ota_key, INITIALIZATION_VECTOR) + enc_file1 = bytes([ord(x) for x in enc_file]) + file_size = len(enc_file1) + payload = generate_header(TAG.APPLICATION, len(descriptor) + file_size) + descriptor + enc_file1 + else: + file_size = os.path.getsize(args.app_input_file) + logging.info(f"file size: {file_size}") + payload = generate_header(TAG.APPLICATION, len(descriptor) + file_size) + descriptor write_to_temp(OTA_APP_TLV_TEMP, payload) - - return [OTA_APP_TLV_TEMP, args.app_input_file] + if args.enc_enable: + return [OTA_APP_TLV_TEMP] + else: + return [OTA_APP_TLV_TEMP, args.app_input_file] def generate_bootloader(args: object): @@ -141,12 +162,23 @@ def generate_bootloader(args: object): logging.info("SSBL descriptor information:") descriptor = generate_descriptor(args.bl_version, args.bl_version_str, args.bl_build_date) - file_size = os.path.getsize(args.bl_input_file) - payload = generate_header(TAG.BOOTLOADER, len(descriptor) + file_size) + descriptor + logging.info(f"Bootloader encryption enable: {args.enc_enable}") + if args.enc_enable: + inputFile = open(args.bl_input_file, "rb") + enc_file = crypto_utils.encryptData(inputFile.read(), args.input_ota_key, INITIALIZATION_VECTOR) + enc_file1 = bytes([ord(x) for x in enc_file]) + file_size = len(enc_file1) + payload = generate_header(TAG.BOOTLOADER, len(descriptor) + file_size) + descriptor + enc_file1 + else: + file_size = os.path.getsize(args.bl_input_file) + logging.info(f"file size: {file_size}") + payload = generate_header(TAG.BOOTLOADER, len(descriptor) + file_size) + descriptor write_to_temp(OTA_BOOTLOADER_TLV_TEMP, payload) - - return [OTA_BOOTLOADER_TLV_TEMP, args.bl_input_file] + if args.enc_enable: + return [OTA_BOOTLOADER_TLV_TEMP] + else: + return [OTA_BOOTLOADER_TLV_TEMP, args.bl_input_file] def validate_json(data: str): @@ -231,6 +263,9 @@ def create_image(args: object): for filename in glob.glob(os.path.dirname(__file__) + "/ota_temp_*"): os.remove(filename) + if args.enc_enable: + for filename in glob.glob(os.path.dirname(__file__) + "/enc_ota_temp_*"): + os.remove(filename) def main(): @@ -302,6 +337,10 @@ def any_base_int(s): return int(s, 0) # Path to input JSON file which describes custom TLVs. create_parser.add_argument('--json', help="[path] Path to the JSON describing custom TLVs") + create_parser.add_argument('--enc_enable', action="store_true", help='enable ota encryption') + create_parser.add_argument('--input_ota_key', type=str, default="1234567890ABCDEFA1B2C3D4E5F6F1B4", + help='Input OTA Encryption KEY (string:16Bytes)') + create_parser.add_argument('-i', '--input_files', default=list(), help='Path to input image payload file') create_parser.add_argument('output_file', help='Path to output image file') diff --git a/src/platform/nxp/k32w/common/BLEManagerCommon.cpp b/src/platform/nxp/k32w/common/BLEManagerCommon.cpp index 65b2f68cb20e90..5d20035aa99940 100644 --- a/src/platform/nxp/k32w/common/BLEManagerCommon.cpp +++ b/src/platform/nxp/k32w/common/BLEManagerCommon.cpp @@ -172,11 +172,11 @@ CHIP_ERROR BLEManagerCommon::_Init() xTimerCreate("bleTimeoutTmr", pdMS_TO_TICKS(CHIP_BLE_KW_CONN_TIMEOUT), pdFALSE, (void *) 0, blekw_connection_timeout_cb); VerifyOrExit(connectionTimeout != NULL, err = CHIP_ERROR_INCORRECT_STATE); + sImplInstance = GetImplInstance(); + /* BLE platform code initialization */ SuccessOrExit(err = InitHostController(&blekw_generic_cb)); - sImplInstance = GetImplInstance(); - /* Register the GATT server callback */ VerifyOrExit(GattServer_RegisterCallback(blekw_gatt_server_cb) == gBleSuccess_c, err = CHIP_ERROR_INCORRECT_STATE); @@ -914,6 +914,13 @@ void BLEManagerCommon::DoBleProcessing(void) } } +void BLEManagerCommon::RegisterAppCallbacks(BLECallbackDelegate::GapGenericCallback gapCallback, + BLECallbackDelegate::GattServerCallback gattCallback) +{ + callbackDelegate.gapCallback = gapCallback; + callbackDelegate.gattCallback = gattCallback; +} + void BLEManagerCommon::HandleConnectEvent(blekw_msg_t * msg) { uint8_t deviceId = msg->data.u8; @@ -1078,6 +1085,11 @@ void BLEManagerCommon::blekw_generic_cb(gapGenericEvent_t * pGenericEvent) /* Call BLE Conn Manager */ BleConnManager_GenericEvent(pGenericEvent); + if (sImplInstance->callbackDelegate.gapCallback) + { + sImplInstance->callbackDelegate.gapCallback(pGenericEvent); + } + switch (pGenericEvent->eventType) { case gInternalError_c: @@ -1162,6 +1174,9 @@ void BLEManagerCommon::blekw_gap_connection_cb(deviceId_t deviceId, gapConnectio if (pConnectionEvent->eventType == gConnEvtConnected_c) { +#if defined(chip_with_low_power) && (chip_with_low_power == 1) + PWR_DisallowDeviceToSleep(); +#endif #if CHIP_DEVICE_CONFIG_BLE_SET_PHY_2M_REQ ChipLogProgress(DeviceLayer, "BLE K32W: Trying to set the PHY to 2M"); @@ -1220,6 +1235,11 @@ void BLEManagerCommon::blekw_stop_connection_timeout(void) void BLEManagerCommon::blekw_gatt_server_cb(deviceId_t deviceId, gattServerEvent_t * pServerEvent) { + if (sImplInstance->callbackDelegate.gattCallback) + { + sImplInstance->callbackDelegate.gattCallback(deviceId, pServerEvent); + } + switch (pServerEvent->eventType) { case gEvtMtuChanged_c: { diff --git a/src/platform/nxp/k32w/common/BLEManagerCommon.h b/src/platform/nxp/k32w/common/BLEManagerCommon.h index d8a6561e50b380..32ccdd762cdea4 100644 --- a/src/platform/nxp/k32w/common/BLEManagerCommon.h +++ b/src/platform/nxp/k32w/common/BLEManagerCommon.h @@ -45,10 +45,20 @@ namespace chip { namespace DeviceLayer { namespace Internal { -typedef void (*ble_generic_cb_fp)(gapGenericEvent_t * pGenericEvent); - using namespace chip::Ble; +/** + * A delegate class that can be used by the application to subscribe to BLE events. + */ +struct BLECallbackDelegate +{ + using GapGenericCallback = void (*)(gapGenericEvent_t * event); + using GattServerCallback = void (*)(deviceId_t id, gattServerEvent_t * event); + + GapGenericCallback gapCallback = nullptr; + GattServerCallback gattCallback = nullptr; +}; + /** * Base class for different platform implementations (K32W0 and K32W1 for now). */ @@ -221,9 +231,13 @@ class BLEManagerCommon : public BLEManager, protected BleLayer, private BlePlatf static bool blekw_stop_connection_internal(BLE_CONNECTION_OBJECT conId); public: - virtual CHIP_ERROR InitHostController(ble_generic_cb_fp cb_fp) = 0; - virtual BLEManagerCommon * GetImplInstance() = 0; + virtual CHIP_ERROR InitHostController(BLECallbackDelegate::GapGenericCallback cb_fp) = 0; + virtual BLEManagerCommon * GetImplInstance() = 0; void DoBleProcessing(void); + + BLECallbackDelegate callbackDelegate; + void RegisterAppCallbacks(BLECallbackDelegate::GapGenericCallback gapCallback, + BLECallbackDelegate::GattServerCallback gattCallback); }; inline BLEManager::CHIPoBLEServiceMode BLEManagerCommon::_GetCHIPoBLEServiceMode(void) diff --git a/src/platform/nxp/k32w/common/CHIPDevicePlatformRamStorageConfig.h b/src/platform/nxp/k32w/common/CHIPDevicePlatformRamStorageConfig.h index 58c1c73bba5b73..33c68807ca7af9 100644 --- a/src/platform/nxp/k32w/common/CHIPDevicePlatformRamStorageConfig.h +++ b/src/platform/nxp/k32w/common/CHIPDevicePlatformRamStorageConfig.h @@ -145,3 +145,14 @@ #define kNvmId_FactoryDataBackup (uint16_t) 0x7000 #endif #endif // CONFIG_CHIP_LOAD_REAL_FACTORY_DATA + +/** + * @def kKVS_RamBufferSize + * + * Size of KVS values RAM storage buffer. + * This value is empirical, based on some minimal resource requirements tests. + * Applications should overwrite this value if necessary. + */ +#ifndef kKVS_RamBufferSize +#define kKVS_RamBufferSize (12 * 1024) +#endif diff --git a/src/platform/nxp/k32w/common/FactoryDataProvider.cpp b/src/platform/nxp/k32w/common/FactoryDataProvider.cpp index 98bb1653210601..315811cbf32442 100644 --- a/src/platform/nxp/k32w/common/FactoryDataProvider.cpp +++ b/src/platform/nxp/k32w/common/FactoryDataProvider.cpp @@ -40,8 +40,8 @@ static constexpr size_t kSpake2pSerializedVerifier_MaxBase64Len = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_VerifierSerialized_Length) + 1; static constexpr size_t kSpake2pSalt_MaxBase64Len = BASE64_ENCODED_LEN(chip::Crypto::kSpake2p_Max_PBKDF_Salt_Length) + 1; -uint32_t FactoryDataProvider::kFactoryDataStart = (uint32_t) __FACTORY_DATA_START; -uint32_t FactoryDataProvider::kFactoryDataSize = (uint32_t) __FACTORY_DATA_SIZE; +uint32_t FactoryDataProvider::kFactoryDataStart = (uint32_t) __MATTER_FACTORY_DATA_START; +uint32_t FactoryDataProvider::kFactoryDataSize = (uint32_t) __MATTER_FACTORY_DATA_SIZE; uint32_t FactoryDataProvider::kFactoryDataPayloadStart = kFactoryDataStart + sizeof(FactoryDataProvider::Header); FactoryDataProvider::FactoryDataProvider() @@ -123,11 +123,17 @@ CHIP_ERROR FactoryDataProvider::SearchForId(uint8_t searchedType, uint8_t * pBuf CHIP_ERROR FactoryDataProvider::GetCertificationDeclaration(MutableByteSpan & outBuffer) { +#if CHIP_USE_DEVICE_CONFIG_CERTIFICATION_DECLARATION + constexpr uint8_t kCdForAllExamples[] = CHIP_DEVICE_CONFIG_CERTIFICATION_DECLARATION; + + return CopySpanToMutableSpan(ByteSpan{ kCdForAllExamples }, outBuffer); +#else uint16_t declarationSize = 0; ReturnErrorOnFailure(SearchForId(FactoryDataId::kCertDeclarationId, outBuffer.data(), outBuffer.size(), declarationSize)); outBuffer.reduce_size(declarationSize); return CHIP_NO_ERROR; +#endif } CHIP_ERROR FactoryDataProvider::GetFirmwareInformation(MutableByteSpan & out_firmware_info_buffer) diff --git a/src/platform/nxp/k32w/common/FactoryDataProvider.h b/src/platform/nxp/k32w/common/FactoryDataProvider.h index db10a8f6accb23..99b9a1e039cf34 100644 --- a/src/platform/nxp/k32w/common/FactoryDataProvider.h +++ b/src/platform/nxp/k32w/common/FactoryDataProvider.h @@ -26,8 +26,8 @@ #include "CHIPPlatformConfig.h" /* Grab symbol for the base address from the linker file. */ -extern uint32_t __FACTORY_DATA_START[]; -extern uint32_t __FACTORY_DATA_SIZE[]; +extern uint32_t __MATTER_FACTORY_DATA_START[]; +extern uint32_t __MATTER_FACTORY_DATA_SIZE[]; namespace chip { namespace DeviceLayer { diff --git a/src/platform/nxp/k32w/common/OTAImageProcessorImpl.cpp b/src/platform/nxp/k32w/common/OTAImageProcessorImpl.cpp index 16493144c632be..c80bf64eaa357b 100644 --- a/src/platform/nxp/k32w/common/OTAImageProcessorImpl.cpp +++ b/src/platform/nxp/k32w/common/OTAImageProcessorImpl.cpp @@ -28,7 +28,7 @@ using namespace chip::DeviceLayer; using namespace ::chip::DeviceLayer::Internal; -#if USE_SMU2_AS_SYSTEM_MEMORY +#if USE_SMU2_STATIC // The attribute specifier should not be changed. static chip::OTAImageProcessorImpl gImageProcessor __attribute__((section(".smu2"))); #else diff --git a/src/platform/nxp/k32w/common/OTATlvProcessor.cpp b/src/platform/nxp/k32w/common/OTATlvProcessor.cpp index 40b068b446ead5..7571e693728c00 100644 --- a/src/platform/nxp/k32w/common/OTATlvProcessor.cpp +++ b/src/platform/nxp/k32w/common/OTATlvProcessor.cpp @@ -22,9 +22,15 @@ #include #include - +#if OTA_ENCRYPTION_ENABLE +#include "OtaUtils.h" +#include "rom_aes.h" +#endif namespace chip { +#if OTA_ENCRYPTION_ENABLE +constexpr uint8_t au8Iv[] = { 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x00, 0x00, 0x00, 0x00 }; +#endif CHIP_ERROR OTATlvProcessor::Process(ByteSpan & block) { CHIP_ERROR status = CHIP_NO_ERROR; @@ -57,6 +63,9 @@ void OTATlvProcessor::ClearInternal() mLength = 0; mProcessedLength = 0; mWasSelected = false; +#if OTA_ENCRYPTION_ENABLE + mIVOffset = 0; +#endif } bool OTATlvProcessor::IsError(CHIP_ERROR & status) @@ -93,4 +102,75 @@ CHIP_ERROR OTADataAccumulator::Accumulate(ByteSpan & block) return CHIP_NO_ERROR; } +#if OTA_ENCRYPTION_ENABLE +CHIP_ERROR OTATlvProcessor::vOtaProcessInternalEncryption(MutableByteSpan & block) +{ + uint8_t iv[16]; + uint8_t key[16]; + uint8_t dataOut[16] = { 0 }; + uint32_t u32IVCount; + uint32_t Offset = 0; + uint8_t data; + tsReg128 sKey; + aesContext_t Context; + + memcpy(iv, au8Iv, sizeof(au8Iv)); + + u32IVCount = (((uint32_t) iv[12]) << 24) | (((uint32_t) iv[13]) << 16) | (((uint32_t) iv[14]) << 8) | (iv[15]); + u32IVCount += (mIVOffset >> 4); + + iv[12] = (uint8_t) ((u32IVCount >> 24) & 0xff); + iv[13] = (uint8_t) ((u32IVCount >> 16) & 0xff); + iv[14] = (uint8_t) ((u32IVCount >> 8) & 0xff); + iv[15] = (uint8_t) (u32IVCount & 0xff); + + size_t len = strlen(OTA_ENCRYPTION_KEY); + + if (len != 32) + { + return CHIP_ERROR_INVALID_ARGUMENT; + } + + for (size_t i = 0; i < len; i += 2) + { + char hex[3] = { OTA_ENCRYPTION_KEY[i], OTA_ENCRYPTION_KEY[i + 1], '\0' }; + key[i / 2] = (uint8_t) strtol(hex, NULL, 16); + } + ByteSpan KEY = ByteSpan(key); + Encoding::LittleEndian::Reader reader_key(KEY.data(), KEY.size()); + ReturnErrorOnFailure(reader_key.Read32(&sKey.u32register0) + .Read32(&sKey.u32register1) + .Read32(&sKey.u32register2) + .Read32(&sKey.u32register3) + .StatusCode()); + + while (Offset + 16 <= block.size()) + { + /*Encrypt the IV*/ + Context.mode = AES_MODE_ECB_ENCRYPT; + Context.pSoftwareKey = (uint32_t *) &sKey; + AES_128_ProcessBlocks(&Context, (uint32_t *) &iv[0], (uint32_t *) &dataOut[0], 1); + + /* Decrypt a block of the buffer */ + for (uint8_t i = 0; i < 16; i++) + { + data = block[Offset + i] ^ dataOut[i]; + memcpy(&block[Offset + i], &data, sizeof(uint8_t)); + } + + /* increment the IV for the next block */ + u32IVCount++; + + iv[12] = (uint8_t) ((u32IVCount >> 24) & 0xff); + iv[13] = (uint8_t) ((u32IVCount >> 16) & 0xff); + iv[14] = (uint8_t) ((u32IVCount >> 8) & 0xff); + iv[15] = (uint8_t) (u32IVCount & 0xff); + + Offset += 16; /* increment the buffer offset */ + mIVOffset += 16; + } + + return CHIP_NO_ERROR; +} +#endif } // namespace chip diff --git a/src/platform/nxp/k32w/common/OTATlvProcessor.h b/src/platform/nxp/k32w/common/OTATlvProcessor.h index 1968f983eda14b..e412e1cdd1c5fa 100644 --- a/src/platform/nxp/k32w/common/OTATlvProcessor.h +++ b/src/platform/nxp/k32w/common/OTATlvProcessor.h @@ -90,6 +90,9 @@ class OTATlvProcessor void SetLength(uint32_t length) { mLength = length; } void SetWasSelected(bool selected) { mWasSelected = selected; } bool WasSelected() { return mWasSelected; } +#if OTA_ENCRYPTION_ENABLE + CHIP_ERROR vOtaProcessInternalEncryption(MutableByteSpan & block); +#endif protected: /** @@ -121,6 +124,10 @@ class OTATlvProcessor bool IsError(CHIP_ERROR & status); +#if OTA_ENCRYPTION_ENABLE + /*ota decryption*/ + uint32_t mIVOffset = 0; +#endif uint32_t mLength = 0; uint32_t mProcessedLength = 0; bool mWasSelected = false; @@ -140,6 +147,7 @@ class OTADataAccumulator CHIP_ERROR Accumulate(ByteSpan & block); inline uint8_t * data() { return mBuffer.Get(); } + inline uint32_t GetThreshold() { return mThreshold; } private: uint32_t mThreshold; diff --git a/src/platform/nxp/k32w/k32w0/BLEManagerImpl.cpp b/src/platform/nxp/k32w/k32w0/BLEManagerImpl.cpp index a7bc09d524360e..af56961cf23dc1 100644 --- a/src/platform/nxp/k32w/k32w0/BLEManagerImpl.cpp +++ b/src/platform/nxp/k32w/k32w0/BLEManagerImpl.cpp @@ -51,7 +51,7 @@ BLEManagerCommon * BLEManagerImpl::GetImplInstance() { return &BLEManagerImpl::sInstance; } -CHIP_ERROR BLEManagerImpl::InitHostController(ble_generic_cb_fp cb_fp) +CHIP_ERROR BLEManagerImpl::InitHostController(BLECallbackDelegate::GapGenericCallback cb_fp) { CHIP_ERROR err = CHIP_NO_ERROR; diff --git a/src/platform/nxp/k32w/k32w0/BLEManagerImpl.h b/src/platform/nxp/k32w/k32w0/BLEManagerImpl.h index 52fd69007d9aa0..eb8a25804804d1 100644 --- a/src/platform/nxp/k32w/k32w0/BLEManagerImpl.h +++ b/src/platform/nxp/k32w/k32w0/BLEManagerImpl.h @@ -44,7 +44,7 @@ class BLEManagerImpl : public BLEManagerCommon // the implementation methods provided by this class. friend BLEManager; - CHIP_ERROR InitHostController(ble_generic_cb_fp cb_fp) override; + CHIP_ERROR InitHostController(BLECallbackDelegate::GapGenericCallback cb_fp) override; BLEManagerCommon * GetImplInstance() override; private: diff --git a/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h b/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h index 0f6258fe9cd824..0c849ccc4989aa 100644 --- a/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h +++ b/src/platform/nxp/k32w/k32w0/CHIPDevicePlatformConfig.h @@ -176,6 +176,18 @@ #define CHIP_DEVICE_CONFIG_ENABLE_THREAD_DNS_CLIENT 1 #endif +#if CHIP_DEVICE_CONFIG_ENABLE_SED + +#ifndef CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL +#define CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL chip::System::Clock::Milliseconds32(NXP_OT_IDLE_INTERVAL) +#endif // CHIP_DEVICE_CONFIG_ICD_SLOW_POLL_INTERVAL + +#ifndef CHIP_DEVICE_CONFIG_ICD_FAST_POLL_INTERVAL +#define CHIP_DEVICE_CONFIG_ICD_FAST_POLL_INTERVAL chip::System::Clock::Milliseconds32(NXP_OT_ACTIVE_INTERVAL) +#endif // CHIP_DEVICE_CONFIG_ICD_FAST_POLL_INTERVAL + +#endif + #define CHIP_DEVICE_CONFIG_ENABLE_TEST_SETUP_PARAMS 1 #include diff --git a/src/platform/nxp/k32w/k32w0/CHIPPlatformConfig.h b/src/platform/nxp/k32w/k32w0/CHIPPlatformConfig.h index 6971c329fa37c6..2f582b28171996 100644 --- a/src/platform/nxp/k32w/k32w0/CHIPPlatformConfig.h +++ b/src/platform/nxp/k32w/k32w0/CHIPPlatformConfig.h @@ -77,4 +77,28 @@ #define WDM_PUBLISHER_MAX_NOTIFIES_IN_FLIGHT 2 #endif // WDM_PUBLISHER_MAX_NOTIFIES_IN_FLIGHT +#if NXP_ICD_ENABLED + +#ifndef CHIP_CONFIG_ICD_IDLE_MODE_INTERVAL_SEC +#define CHIP_CONFIG_ICD_IDLE_MODE_INTERVAL_SEC NXP_IDLE_MODE_INTERVAL +#endif // CHIP_CONFIG_ICD_IDLE_MODE_INTERVAL_SEC + +#ifndef CHIP_CONFIG_ICD_ACTIVE_MODE_INTERVAL_MS +#define CHIP_CONFIG_ICD_ACTIVE_MODE_INTERVAL_MS NXP_ACTIVE_MODE_INTERVAL +#endif // CHIP_CONFIG_ICD_ACTIVE_MODE_INTERVAL_MS + +#ifndef CHIP_CONFIG_ICD_ACTIVE_MODE_THRESHOLD_MS +#define CHIP_CONFIG_ICD_ACTIVE_MODE_THRESHOLD_MS NXP_ACTIVE_MODE_THRESHOLD +#endif // CHIP_CONFIG_ICD_ACTIVE_MODE_THRESHOLD_MS + +#ifndef CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC +#define CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC NXP_ICD_SUPPORTED_CLIENTS_PER_FABRIC +#endif // CHIP_CONFIG_ICD_CLIENTS_SUPPORTED_PER_FABRIC + +#ifndef CHIP_CONFIG_SYNCHRONOUS_REPORTS_ENABLED +#define CHIP_CONFIG_SYNCHRONOUS_REPORTS_ENABLED 1 +#endif + +#endif + #include "platform/nxp/common/CHIPNXPPlatformDefaultConfig.h" diff --git a/src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.cpp b/src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.cpp index 8b3e7491eec3bb..0cf81fa9bf1749 100644 --- a/src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.cpp +++ b/src/platform/nxp/k32w/k32w0/KeyValueStoreManagerImpl.cpp @@ -81,8 +81,8 @@ CHIP_ERROR KeyValueStoreManagerImpl::Init() { ChipLogProgress(DeviceLayer, "Cannot init KVS keys storage with id: %d. Error: %s", kNvmId_KvsKeys, ErrorStr(err)); } - // Set values storage to max RAM buffer size as a temporary fix for TC-RR-1.1. - err = sValuesStorage.Init(kRamBufferMaxAllocSize, true); + // Set values storage to a big RAM buffer size as a temporary fix for TC-RR-1.1. + err = sValuesStorage.Init(kKVS_RamBufferSize, true); if (err != CHIP_NO_ERROR) { ChipLogProgress(DeviceLayer, "Cannot init KVS values storage with id: %d. Error: %s", kNvmId_KvsValues, ErrorStr(err)); diff --git a/src/platform/nxp/k32w/k32w0/Logging.cpp b/src/platform/nxp/k32w/k32w0/Logging.cpp index b00366999023ee..537ceed1181694 100644 --- a/src/platform/nxp/k32w/k32w0/Logging.cpp +++ b/src/platform/nxp/k32w/k32w0/Logging.cpp @@ -45,7 +45,7 @@ void GetMessageString(char * buf, uint8_t bufLen, const char * module, uint8_t c */ assert(bufLen >= (timestamp_max_len_bytes + category_max_len_bytes + (strlen(module) + 2) + 1)); - writtenLen = snprintf(buf, bufLen, "[%" PRIu32, otPlatAlarmMilliGetNow()); + writtenLen = snprintf(buf, bufLen, "[%ld]", otPlatAlarmMilliGetNow()); bufLen -= writtenLen; buf += writtenLen; diff --git a/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp b/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp index dbf7d09c892de4..438623dbc97d2f 100644 --- a/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp +++ b/src/platform/nxp/k32w/k32w0/OTAFactoryDataProcessor.cpp @@ -49,6 +49,10 @@ CHIP_ERROR OTAFactoryDataProcessor::ProcessInternal(ByteSpan & block) CHIP_ERROR error = CHIP_NO_ERROR; ReturnErrorOnFailure(mAccumulator.Accumulate(block)); +#if OTA_ENCRYPTION_ENABLE + MutableByteSpan mBlock = MutableByteSpan(mAccumulator.data(), mAccumulator.GetThreshold()); + OTATlvProcessor::vOtaProcessInternalEncryption(mBlock); +#endif error = DecodeTlv(); if (error != CHIP_NO_ERROR) diff --git a/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.cpp b/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.cpp index cf26fc8c2149ea..760c9ef2eec8fc 100644 --- a/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.cpp +++ b/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.cpp @@ -18,6 +18,7 @@ #include #include +#include #include #include "OtaSupport.h" @@ -29,6 +30,9 @@ CHIP_ERROR OTAFirmwareProcessor::Init() { ReturnErrorCodeIf(mCallbackProcessDescriptor == nullptr, CHIP_OTA_PROCESSOR_CB_NOT_REGISTERED); mAccumulator.Init(sizeof(Descriptor)); +#if OTA_ENCRYPTION_ENABLE + mUnalignmentNum = 0; +#endif ReturnErrorCodeIf(gOtaSuccess_c != OTA_ClientInit(), CHIP_OTA_PROCESSOR_CLIENT_INIT); auto offset = OTA_GetCurrentEepromAddressOffset(); @@ -48,6 +52,9 @@ CHIP_ERROR OTAFirmwareProcessor::Clear() OTATlvProcessor::ClearInternal(); mAccumulator.Clear(); mDescriptorProcessed = false; +#if OTA_ENCRYPTION_ENABLE + mUnalignmentNum = 0; +#endif return CHIP_NO_ERROR; } @@ -57,7 +64,32 @@ CHIP_ERROR OTAFirmwareProcessor::ProcessInternal(ByteSpan & block) if (!mDescriptorProcessed) { ReturnErrorOnFailure(ProcessDescriptor(block)); +#if OTA_ENCRYPTION_ENABLE + /* 16 bytes to used to store undecrypted data because of unalignment */ + mAccumulator.Init(requestedOtaMaxBlockSize + 16); +#endif } +#if OTA_ENCRYPTION_ENABLE + MutableByteSpan mBlock = MutableByteSpan(mAccumulator.data(), mAccumulator.GetThreshold()); + memcpy(&mBlock[0], &mBlock[requestedOtaMaxBlockSize], mUnalignmentNum); + memcpy(&mBlock[mUnalignmentNum], block.data(), block.size()); + + if (mUnalignmentNum + block.size() < requestedOtaMaxBlockSize) + { + uint32_t mAlignmentNum = (mUnalignmentNum + block.size()) / 16; + mAlignmentNum = mAlignmentNum * 16; + mUnalignmentNum = (mUnalignmentNum + block.size()) % 16; + memcpy(&mBlock[requestedOtaMaxBlockSize], &mBlock[mAlignmentNum], mUnalignmentNum); + mBlock.reduce_size(mAlignmentNum); + } + else + { + mUnalignmentNum = mUnalignmentNum + block.size() - requestedOtaMaxBlockSize; + mBlock.reduce_size(requestedOtaMaxBlockSize); + } + + OTATlvProcessor::vOtaProcessInternalEncryption(mBlock); +#endif auto status = OTA_MakeHeadRoomForNextBlock(requestedOtaMaxBlockSize, OTAImageProcessorImpl::FetchNextData, 0); if (gOtaSuccess_c != status) @@ -65,8 +97,11 @@ CHIP_ERROR OTAFirmwareProcessor::ProcessInternal(ByteSpan & block) ChipLogError(SoftwareUpdate, "Failed to make room for next block. Status: %d", status); return CHIP_OTA_PROCESSOR_MAKE_ROOM; } - +#if OTA_ENCRYPTION_ENABLE + status = OTA_PushImageChunk((uint8_t *) mBlock.data(), (uint16_t) mBlock.size(), NULL, NULL); +#else status = OTA_PushImageChunk((uint8_t *) block.data(), (uint16_t) block.size(), NULL, NULL); +#endif if (gOtaSuccess_c != status) { ChipLogError(SoftwareUpdate, "Failed to write image block. Status: %d", status); diff --git a/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.h b/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.h index bc12394598ac89..885dc239dd22b3 100644 --- a/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.h +++ b/src/platform/nxp/k32w/k32w0/OTAFirmwareProcessor.h @@ -45,6 +45,9 @@ class OTAFirmwareProcessor : public OTATlvProcessor OTADataAccumulator mAccumulator; bool mDescriptorProcessed = false; +#if OTA_ENCRYPTION_ENABLE + uint32_t mUnalignmentNum; +#endif }; } // namespace chip diff --git a/src/platform/nxp/k32w/k32w1/BLEManagerImpl.cpp b/src/platform/nxp/k32w/k32w1/BLEManagerImpl.cpp index daac54fb5a16e7..95a69b717c4af0 100644 --- a/src/platform/nxp/k32w/k32w1/BLEManagerImpl.cpp +++ b/src/platform/nxp/k32w/k32w1/BLEManagerImpl.cpp @@ -41,7 +41,7 @@ BLEManagerCommon * BLEManagerImpl::GetImplInstance() return &BLEManagerImpl::sInstance; } -CHIP_ERROR BLEManagerImpl::InitHostController(ble_generic_cb_fp cb_fp) +CHIP_ERROR BLEManagerImpl::InitHostController(BLECallbackDelegate::GapGenericCallback cb_fp) { CHIP_ERROR err = CHIP_NO_ERROR; diff --git a/src/platform/nxp/k32w/k32w1/BLEManagerImpl.h b/src/platform/nxp/k32w/k32w1/BLEManagerImpl.h index 7aa4e9f1604b42..bef927c8275350 100644 --- a/src/platform/nxp/k32w/k32w1/BLEManagerImpl.h +++ b/src/platform/nxp/k32w/k32w1/BLEManagerImpl.h @@ -49,7 +49,7 @@ class BLEManagerImpl final : public BLEManagerCommon // the implementation methods provided by this class. friend BLEManager; - CHIP_ERROR InitHostController(ble_generic_cb_fp cb_fp) override; + CHIP_ERROR InitHostController(BLECallbackDelegate::GapGenericCallback cb_fp) override; BLEManagerCommon * GetImplInstance() override; private: diff --git a/third_party/nxp/k32w0_sdk/k32w0_sdk.gni b/third_party/nxp/k32w0_sdk/k32w0_sdk.gni index e8858022e20516..ab37141606f19a 100644 --- a/third_party/nxp/k32w0_sdk/k32w0_sdk.gni +++ b/third_party/nxp/k32w0_sdk/k32w0_sdk.gni @@ -47,6 +47,21 @@ declare_args() { chip_enable_ota_factory_data_processor = 0 chip_with_pdm_encryption = 1 ota_custom_entry_address = "0x000C1000" + use_antenna_diversity = 0 + + #ICD Matter Configuration flags + + chip_ot_idle_interval_ms = 2000 # 2s Idle Intervals + chip_ot_active_interval_ms = 500 # 500ms Active Intervals + + nxp_idle_mode_interval_s = 600 # 10min Idle Mode Interval + nxp_active_mode_interval_ms = 10000 # 10s Active Mode Interval + nxp_active_mode_threshold_ms = 1000 # 1s Active Mode Threshold + nxp_icd_supported_clients_per_fabric = 2 # 2 registration slots per fabric + + chip_with_ota_encryption = 0 + chip_with_ota_key = "1234567890ABCDEFA1B2C3D4E5F6F1B4" + chip_with_sdk_package = 1 } assert(k32w0_sdk_root != "", "k32w0_sdk_root must be specified") @@ -132,6 +147,12 @@ template("k32w0_sdk") { chip_with_ntag = 0 } + if (k32w0_sdk_root == "${chip_root}/third_party/nxp/k32w0_sdk/repo/core" || + k32w0_sdk_root == "/opt/sdk/core") { + chip_with_sdk_package = 0 + } else { + chip_with_sdk_package = 1 + } print("device:", device) print("board:", board) print("ntag:", chip_with_ntag) @@ -153,6 +174,7 @@ template("k32w0_sdk") { print("ECC crypto lib: ", chip_crypto_flavor) } + print("chip_with_sdk_package:", chip_with_sdk_package) device_lowercase = string_replace(board, "dk6", "") sdk_target_name = target_name @@ -204,26 +226,7 @@ template("k32w0_sdk") { "${k32w0_sdk_root}/components/serial_manager", "${k32w0_sdk_root}/components/uart", "${k32w0_sdk_root}/devices/${device}", - "${k32w0_sdk_root}/devices/K32W061/drivers", - "${k32w0_sdk_root}/drivers/common", - "${k32w0_sdk_root}/drivers/lpc_gpio", - "${k32w0_sdk_root}/drivers/aes", - "${k32w0_sdk_root}/drivers/jn_iocon", - "${k32w0_sdk_root}/drivers/lpc_adc", - "${k32w0_sdk_root}/drivers/aes", - "${k32w0_sdk_root}/drivers/jn_flash", - "${k32w0_sdk_root}/drivers/sha", - "${k32w0_sdk_root}/drivers/flexcomm", - "${k32w0_sdk_root}/drivers/lpc_dma", - "${k32w0_sdk_root}/drivers/pint", - "${k32w0_sdk_root}/drivers/inputmux", - "${k32w0_sdk_root}/drivers/spifi", - "${k32w0_sdk_root}/drivers/jn_rtc", - "${k32w0_sdk_root}/drivers/fmeas", - "${k32w0_sdk_root}/drivers/jn_rng", - "${k32w0_sdk_root}/drivers/ctimer", - "${k32w0_sdk_root}/drivers/wwdt", - "${k32w0_sdk_root}/drivers/gint", + "${k32w0_sdk_root}/utilities/debug_console/str", "${k32w0_sdk_root}/utilities/debug_console", "${k32w0_sdk_root}/devices/${device}/utilities", @@ -267,6 +270,32 @@ template("k32w0_sdk") { "${k32w0_sdk_root}/rtos/amazon-freertos/lib/include/private", "${k32w0_sdk_root}/rtos/amazon-freertos/lib/third_party/unity/src", ] + if (chip_with_sdk_package == 1) { + _sdk_include_dirs += [ "${k32w0_sdk_root}/devices/${device}/drivers" ] + } else { + _sdk_include_dirs += [ + "${k32w0_sdk_root}/devices/K32W061/drivers", + "${k32w0_sdk_root}/drivers/common", + "${k32w0_sdk_root}/drivers/lpc_gpio", + "${k32w0_sdk_root}/drivers/aes", + "${k32w0_sdk_root}/drivers/jn_iocon", + "${k32w0_sdk_root}/drivers/lpc_adc", + "${k32w0_sdk_root}/drivers/aes", + "${k32w0_sdk_root}/drivers/jn_flash", + "${k32w0_sdk_root}/drivers/sha", + "${k32w0_sdk_root}/drivers/flexcomm", + "${k32w0_sdk_root}/drivers/lpc_dma", + "${k32w0_sdk_root}/drivers/pint", + "${k32w0_sdk_root}/drivers/inputmux", + "${k32w0_sdk_root}/drivers/spifi", + "${k32w0_sdk_root}/drivers/jn_rtc", + "${k32w0_sdk_root}/drivers/fmeas", + "${k32w0_sdk_root}/drivers/jn_rng", + "${k32w0_sdk_root}/drivers/ctimer", + "${k32w0_sdk_root}/drivers/wwdt", + "${k32w0_sdk_root}/drivers/gint", + ] + } libs = [ "${k32w0_sdk_root}/middleware/wireless/ble_controller/lib/lib_ble_controller_peripheral_commissioning.a", @@ -317,6 +346,7 @@ template("k32w0_sdk") { "gAppUseBonding_d=0", "gAppUsePairing_d=0", "gAppUsePrivacy_d=0", + "gController_ReducedRxThoughput=1", "gPasskeyValue_c=999999", "gSupportBle=1", "SUPPORT_FOR_BLE=1", @@ -324,6 +354,9 @@ template("k32w0_sdk") { "gConnPhyUpdateReqTxPhySettings_c=(gLePhy2MFlag_c)", "gConnPhyUpdateReqRxPhySettings_c=(gLePhy2MFlag_c)", "gConnPhyUpdateReqPhyOptions_c=(gLeCodingNoPreference_c)", + "BLE_HIGH_TX_POWER=0", + "gAdvertisingPowerLeveldBm_c=0", + "gConnectPowerLeveldBm_c=0", "DUAL_MODE_APP=1", "gMainThreadStackSize_c=5096", "HEAP_SIZE=gTotalHeapSize_c", @@ -332,6 +365,15 @@ template("k32w0_sdk") { "gSecLibUseSha256Alt_d=1", "gOTA_UseSecLibAes=1", "gResetSystemReset_d=1", + + # TODO: move these platform specific defines to args.gni + "NXP_OT_IDLE_INTERVAL=${chip_ot_idle_interval_ms}", + "NXP_OT_ACTIVE_INTERVAL=${chip_ot_active_interval_ms}", + "NXP_ICD_ENABLED=1", + "NXP_ACTIVE_MODE_THRESHOLD=${nxp_active_mode_threshold_ms}", + "NXP_ACTIVE_MODE_INTERVAL=${nxp_active_mode_interval_ms}", + "NXP_IDLE_MODE_INTERVAL=${nxp_idle_mode_interval_s}", + "NXP_ICD_SUPPORTED_CLIENTS_PER_FABRIC=${nxp_icd_supported_clients_per_fabric}", ] # If OTA default processors are enabled, then OTA custom entry structure @@ -372,6 +414,15 @@ template("k32w0_sdk") { defines += [ "PDM_ENCRYPTION=0" ] } + if (chip_with_ota_encryption == 1) { + defines += [ + "OTA_ENCRYPTION_ENABLE=1", + "OTA_ENCRYPTION_KEY=\"${chip_with_ota_key}\"", + ] + } else { + defines += [ "OTA_ENCRYPTION_ENABLE=0" ] + } + if (chip_mdns == "platform") { defines += [ "OPENTHREAD_CONFIG_SRP_CLIENT_ENABLE=1", @@ -539,27 +590,6 @@ template("k32w0_sdk") { "${k32w0_sdk_root}/components/uart/usart_adapter.c", "${k32w0_sdk_root}/devices/${device}/mcuxpresso/startup_${device_lowercase}.c", "${k32w0_sdk_root}/devices/${device}/system_${device}.c", - "${k32w0_sdk_root}/devices/K32W061/drivers/fsl_clock.c", - "${k32w0_sdk_root}/devices/K32W061/drivers/fsl_power.c", - "${k32w0_sdk_root}/devices/K32W061/drivers/fsl_reset.c", - "${k32w0_sdk_root}/devices/K32W061/drivers/fsl_wtimer.c", - "${k32w0_sdk_root}/drivers/aes/fsl_aes.c", - "${k32w0_sdk_root}/drivers/common/fsl_common.c", - "${k32w0_sdk_root}/drivers/ctimer/fsl_ctimer.c", - "${k32w0_sdk_root}/drivers/flexcomm/fsl_flexcomm.c", - "${k32w0_sdk_root}/drivers/flexcomm/fsl_i2c.c", - "${k32w0_sdk_root}/drivers/flexcomm/fsl_i2c_freertos.c", - "${k32w0_sdk_root}/drivers/flexcomm/fsl_usart.c", - "${k32w0_sdk_root}/drivers/fmeas/fsl_fmeas.c", - "${k32w0_sdk_root}/drivers/inputmux/fsl_inputmux.c", - "${k32w0_sdk_root}/drivers/jn_flash/fsl_flash.c", - "${k32w0_sdk_root}/drivers/jn_rng/fsl_rng.c", - "${k32w0_sdk_root}/drivers/jn_rtc/fsl_rtc.c", - "${k32w0_sdk_root}/drivers/lpc_adc/fsl_adc.c", - "${k32w0_sdk_root}/drivers/lpc_gpio/fsl_gpio.c", - "${k32w0_sdk_root}/drivers/pint/fsl_pint.c", - "${k32w0_sdk_root}/drivers/sha/fsl_sha.c", - "${k32w0_sdk_root}/drivers/spifi/fsl_spifi.c", "${k32w0_sdk_root}/middleware/wireless/ble_controller/config/controller_config.c", "${k32w0_sdk_root}/middleware/wireless/bluetooth/application/common/ble_conn_manager.c", "${k32w0_sdk_root}/middleware/wireless/bluetooth/application/common/ble_host_tasks.c", @@ -599,9 +629,60 @@ template("k32w0_sdk") { "${k32w0_sdk_root}/rtos/amazon-freertos/lib/FreeRTOS/queue.c", "${k32w0_sdk_root}/rtos/amazon-freertos/lib/FreeRTOS/tasks.c", "${k32w0_sdk_root}/rtos/amazon-freertos/lib/FreeRTOS/timers.c", - "${k32w0_sdk_root}/utilities/debug_console/fsl_debug_console.c", - "${k32w0_sdk_root}/utilities/debug_console/str/fsl_str.c", ] + if (chip_with_sdk_package == 1) { + sources += [ + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_adc.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_aes.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_clock.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_common.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_ctimer.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_flash.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_flexcomm.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_fmeas.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_gpio.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_i2c.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_i2c_freertos.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_inputmux.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_pint.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_power.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_reset.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_rng.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_rtc.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_sha.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_spifi.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_usart.c", + "${k32w0_sdk_root}/devices/${device}/drivers/fsl_wtimer.c", + "${k32w0_sdk_root}/devices/${device}/utilities/debug_console/fsl_debug_console.c", + "${k32w0_sdk_root}/devices/${device}/utilities/str/fsl_str.c", + ] + } else { + sources += [ + "${k32w0_sdk_root}/devices/K32W061/drivers/fsl_clock.c", + "${k32w0_sdk_root}/devices/K32W061/drivers/fsl_power.c", + "${k32w0_sdk_root}/devices/K32W061/drivers/fsl_reset.c", + "${k32w0_sdk_root}/devices/K32W061/drivers/fsl_wtimer.c", + "${k32w0_sdk_root}/drivers/aes/fsl_aes.c", + "${k32w0_sdk_root}/drivers/common/fsl_common.c", + "${k32w0_sdk_root}/drivers/ctimer/fsl_ctimer.c", + "${k32w0_sdk_root}/drivers/flexcomm/fsl_flexcomm.c", + "${k32w0_sdk_root}/drivers/flexcomm/fsl_i2c.c", + "${k32w0_sdk_root}/drivers/flexcomm/fsl_i2c_freertos.c", + "${k32w0_sdk_root}/drivers/flexcomm/fsl_usart.c", + "${k32w0_sdk_root}/drivers/fmeas/fsl_fmeas.c", + "${k32w0_sdk_root}/drivers/inputmux/fsl_inputmux.c", + "${k32w0_sdk_root}/drivers/jn_flash/fsl_flash.c", + "${k32w0_sdk_root}/drivers/jn_rng/fsl_rng.c", + "${k32w0_sdk_root}/drivers/jn_rtc/fsl_rtc.c", + "${k32w0_sdk_root}/drivers/lpc_adc/fsl_adc.c", + "${k32w0_sdk_root}/drivers/lpc_gpio/fsl_gpio.c", + "${k32w0_sdk_root}/drivers/pint/fsl_pint.c", + "${k32w0_sdk_root}/drivers/sha/fsl_sha.c", + "${k32w0_sdk_root}/drivers/spifi/fsl_spifi.c", + "${k32w0_sdk_root}/utilities/debug_console/fsl_debug_console.c", + "${k32w0_sdk_root}/utilities/debug_console/str/fsl_str.c", + ] + } if (chip_with_DK6) { sources += [ diff --git a/third_party/nxp/k32w0_sdk/repo/manifest/west.yml b/third_party/nxp/k32w0_sdk/repo/manifest/west.yml index 3a00791908f057..0febc2e377bd1e 100644 --- a/third_party/nxp/k32w0_sdk/repo/manifest/west.yml +++ b/third_party/nxp/k32w0_sdk/repo/manifest/west.yml @@ -11,7 +11,7 @@ manifest: remote: nxpmicro projects: - name: mcux-sdk - revision: b1ce670e8433142a3c78dca7f4597326eb5f5342 + revision: 7219c54f5dbd5a8636242f9f1771ddb70be146c9 path: core - name: amazon-freertos url: https://github.com/NXP/amazon-freertos.git @@ -23,7 +23,7 @@ manifest: revision: 15458495823165de372f62c3dad621a8da6c86e3 - name: framework url: https://github.com/NXP/mcux-sdk-middleware-connectivity-framework.git - revision: a45ed24bc5f8d54312c4d9243c4423d226084412 + revision: 2ccc3df70ee5b73c451fceee13e6172518412518 path: core/middleware/wireless/framework - name: ble_controller url: https://github.com/NXP/mcux-sdk-middleware-bluetooth-controller.git @@ -35,9 +35,9 @@ manifest: path: core/middleware/wireless/bluetooth - name: ieee-802.15.4 url: https://github.com/NXP/mcux-sdk-middleware-ieee_802.15.4.git - revision: 7cca871d58e53c78a703a39b269c4366b03f26e4 + revision: e8c96197346a7e37ce3a872096a22bf4aee47e3d path: core/middleware/wireless/ieee-802.15.4 - name: examples url: https://github.com/nxp-mcuxpresso/mcux-sdk-examples.git - revision: 39eed09d6a8485dcf365a24cd6ef957e7cc6fbf8 + revision: 6735550f5a41dfb997d4dd3ce6eebfe1f1d41054 path: core/boards diff --git a/third_party/openthread/ot-nxp b/third_party/openthread/ot-nxp index 57422f917ad1ab..eb23212ca1e329 160000 --- a/third_party/openthread/ot-nxp +++ b/third_party/openthread/ot-nxp @@ -1 +1 @@ -Subproject commit 57422f917ad1abc65919435558cd836c8f7bada7 +Subproject commit eb23212ca1e329a29c95c29c09438c8f2256d5c6 From e15db829cde4a484a283c0e935df4738f572bb4c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:26:15 -0500 Subject: [PATCH 2/9] Bump actions/labeler from 4 to 5 (#30911) Bumps [actions/labeler](https://github.com/actions/labeler) from 4 to 5. - [Release notes](https://github.com/actions/labeler/releases) - [Commits](https://github.com/actions/labeler/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/labeler dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/labeler.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml index 057208eda328d2..0127be63320271 100644 --- a/.github/workflows/labeler.yaml +++ b/.github/workflows/labeler.yaml @@ -9,6 +9,6 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v4 + - uses: actions/labeler@v5 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" From b9a9c9a89e942630b15303010d72c3ab5fca5744 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:26:55 -0500 Subject: [PATCH 3/9] Bump actions/setup-python from 4 to 5 (#30912) Bumps [actions/setup-python](https://github.com/actions/setup-python) from 4 to 5. - [Release notes](https://github.com/actions/setup-python/releases) - [Commits](https://github.com/actions/setup-python/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/setup-python dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/docbuild.yaml | 2 +- .github/workflows/protocol_compatibility.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docbuild.yaml b/.github/workflows/docbuild.yaml index f02d704f348143..2d28a2a537d922 100644 --- a/.github/workflows/docbuild.yaml +++ b/.github/workflows/docbuild.yaml @@ -27,7 +27,7 @@ jobs: path: matter fetch-depth: 0 - name: Install Python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.8 cache-dependency-path: matter/docs/requirements.txt diff --git a/.github/workflows/protocol_compatibility.yaml b/.github/workflows/protocol_compatibility.yaml index 6b99b44317580c..6c8960072ec3be 100644 --- a/.github/workflows/protocol_compatibility.yaml +++ b/.github/workflows/protocol_compatibility.yaml @@ -28,7 +28,7 @@ jobs: - name: Checkout uses: actions/checkout@v4 - name: Setup python - uses: actions/setup-python@v4 + uses: actions/setup-python@v5 with: python-version: 3.11 - name: Install dependencies From fa269fcb037546b552849a31e8301e68db2cdd06 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 11 Dec 2023 09:29:50 -0500 Subject: [PATCH 4/9] Bump third_party/imgui/repo from `5288687` to `03298fe` (#30917) Bumps [third_party/imgui/repo](https://github.com/ocornut/imgui) from `5288687` to `03298fe`. - [Release notes](https://github.com/ocornut/imgui/releases) - [Commits](https://github.com/ocornut/imgui/compare/52886872f10656b299af4b90487e1036f582b812...03298fe875c0855097af6f79be631952054f984d) --- updated-dependencies: - dependency-name: third_party/imgui/repo dependency-type: direct:production ... Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- third_party/imgui/repo | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/third_party/imgui/repo b/third_party/imgui/repo index 52886872f10656..03298fe875c085 160000 --- a/third_party/imgui/repo +++ b/third_party/imgui/repo @@ -1 +1 @@ -Subproject commit 52886872f10656b299af4b90487e1036f582b812 +Subproject commit 03298fe875c0855097af6f79be631952054f984d From 86b53e147bac354ed4910cbce8ac793693267840 Mon Sep 17 00:00:00 2001 From: dinabenamar <108664279+dinabenamar@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:36:25 +0100 Subject: [PATCH 5/9] [NXP] Remove circular dependency with application code in platform file ConfigurationmanagerImpl.cpp (#30891) --- examples/all-clusters-app/nxp/common/main/AppTask.cpp | 4 +++- src/platform/nxp/common/ConfigurationManagerImpl.cpp | 8 +------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/examples/all-clusters-app/nxp/common/main/AppTask.cpp b/examples/all-clusters-app/nxp/common/main/AppTask.cpp index 3ca3af76b96349..9dc60db217cf9a 100644 --- a/examples/all-clusters-app/nxp/common/main/AppTask.cpp +++ b/examples/all-clusters-app/nxp/common/main/AppTask.cpp @@ -473,5 +473,7 @@ void AppTask::SwitchCommissioningStateHandler(void) void AppTask::FactoryResetHandler(void) { - ConfigurationMgr().InitiateFactoryReset(); + /* Emit the ShutDown event before factory reset */ + chip::Server::GetInstance().GenerateShutDownEvent(); + chip::Server::GetInstance().ScheduleFactoryReset(); } diff --git a/src/platform/nxp/common/ConfigurationManagerImpl.cpp b/src/platform/nxp/common/ConfigurationManagerImpl.cpp index b14f551df63654..e99f913369eda8 100644 --- a/src/platform/nxp/common/ConfigurationManagerImpl.cpp +++ b/src/platform/nxp/common/ConfigurationManagerImpl.cpp @@ -27,7 +27,6 @@ #include #include "NXPConfig.h" -#include #include #include #include @@ -148,12 +147,7 @@ bool ConfigurationManagerImpl::CanFactoryReset() void ConfigurationManagerImpl::InitiateFactoryReset() { - PlatformMgr().ScheduleWork([](intptr_t) { - // Emit ShutDown event and delete all fabrics. - PlatformMgr().HandleServerShuttingDown(); - Server::GetInstance().GetFabricTable().DeleteAllFabrics(); - DoFactoryReset(0); - }); + PlatformMgr().ScheduleWork(DoFactoryReset); } CHIP_ERROR ConfigurationManagerImpl::ReadPersistedStorageValue(::chip::Platform::PersistedStorage::Key persistedStorageKey, From 6e2aa2b73b26b289bced1ca399c181903615b6ba Mon Sep 17 00:00:00 2001 From: Tennessee Carmel-Veilleux Date: Mon, 11 Dec 2023 09:42:34 -0500 Subject: [PATCH 6/9] Ensure fields are always initialized in TLVWriters (#30876) * Ensure fields are always initialized in TLVWriters - It was found through static analysis that in some cases, TLVWriters did not have all fields initialized and this led to no errors, but possibly could lead to bad states. - None of the code making use of uninitialized TLVWriters was found in the SDK, but there may be usages. - In adding the checks, marked several areas that could be improved in TLVWriter data access/management. Issue #30825 Testing done: - All tests still pass. - There is never uninitialized used of a TLVWriter. --------- Co-authored-by: tennessee.carmelveilleux@gmail.com Co-authored-by: Restyled.io --- src/lib/core/TLVUpdater.cpp | 14 +- src/lib/core/TLVWriter.cpp | 98 +++++++++--- src/lib/core/TLVWriter.h | 56 ++++++- src/lib/core/tests/TestTLV.cpp | 277 +++++++++++++++++++++++++++++++++ 4 files changed, 420 insertions(+), 25 deletions(-) diff --git a/src/lib/core/TLVUpdater.cpp b/src/lib/core/TLVUpdater.cpp index c1793919bc7258..7df01cbad7b4a6 100644 --- a/src/lib/core/TLVUpdater.cpp +++ b/src/lib/core/TLVUpdater.cpp @@ -98,6 +98,8 @@ CHIP_ERROR TLVUpdater::Init(TLVReader & aReader, uint32_t freeLen) mUpdaterReader.ImplicitProfileId = aReader.ImplicitProfileId; mUpdaterReader.AppData = aReader.AppData; + // TODO(#30825): Need to ensure we use TLVWriter public API rather than touch the innards. + // Initialize the internal writer object mUpdaterWriter.mBackingStore = nullptr; mUpdaterWriter.mBufStart = buf - readDataLen; @@ -109,7 +111,8 @@ CHIP_ERROR TLVUpdater::Init(TLVReader & aReader, uint32_t freeLen) mUpdaterWriter.SetContainerOpen(false); mUpdaterWriter.SetCloseContainerReserved(false); - mUpdaterWriter.ImplicitProfileId = aReader.ImplicitProfileId; + mUpdaterWriter.ImplicitProfileId = aReader.ImplicitProfileId; + mUpdaterWriter.mInitializationCookie = TLVWriter::kExpectedInitializationCookie; // Cache element start address for internal use mElementStartAddr = buf + freeLen; @@ -142,6 +145,8 @@ CHIP_ERROR TLVUpdater::Next() CHIP_ERROR TLVUpdater::Move() { + VerifyOrReturnError(mUpdaterWriter.IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + const uint8_t * elementEnd; uint32_t copyLen; @@ -171,6 +176,8 @@ CHIP_ERROR TLVUpdater::Move() void TLVUpdater::MoveUntilEnd() { + VerifyOrDie(mUpdaterWriter.IsInitialized()); + const uint8_t * buffEnd = mUpdaterReader.GetReadPoint() + mUpdaterReader.GetRemainingLength(); uint32_t copyLen = static_cast(buffEnd - mElementStartAddr); @@ -178,6 +185,7 @@ void TLVUpdater::MoveUntilEnd() // Move all elements till end to output TLV memmove(mUpdaterWriter.mWritePoint, mElementStartAddr, copyLen); + // TODO(#30825): Need to ensure public API is used rather than touching the innards. // Adjust the updater state mElementStartAddr += copyLen; mUpdaterWriter.mWritePoint += copyLen; @@ -197,6 +205,8 @@ void TLVUpdater::MoveUntilEnd() CHIP_ERROR TLVUpdater::EnterContainer(TLVType & outerContainerType) { + VerifyOrReturnError(mUpdaterWriter.IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + TLVType containerType; VerifyOrReturnError(TLVTypeIsContainer(static_cast(mUpdaterReader.mControlByte & kTLVTypeMask)), @@ -232,6 +242,8 @@ CHIP_ERROR TLVUpdater::ExitContainer(TLVType outerContainerType) */ void TLVUpdater::AdjustInternalWriterFreeSpace() { + VerifyOrDie(mUpdaterWriter.IsInitialized()); + const uint8_t * nextElementStart = mUpdaterReader.mReadPoint; if (nextElementStart != mElementStartAddr) diff --git a/src/lib/core/TLVWriter.cpp b/src/lib/core/TLVWriter.cpp index eb93e781524677..1ac6433d1761fe 100644 --- a/src/lib/core/TLVWriter.cpp +++ b/src/lib/core/TLVWriter.cpp @@ -44,30 +44,56 @@ #define NO_INLINE __attribute__((noinline)) #endif // DOXYGEN +// You can enable this block manually to abort on usage of uninitialized writers in +// your codebase. There are no such usages in the SDK (outside of tests). +#if 0 +#define ABORT_ON_UNINITIALIZED_IF_ENABLED() VerifyOrDie(IsInitialized() == true) +#else +#define ABORT_ON_UNINITIALIZED_IF_ENABLED() \ + do \ + { \ + } while (0) +#endif + namespace chip { namespace TLV { using namespace chip::Encoding; +TLVWriter::TLVWriter() : + ImplicitProfileId(kProfileIdNotSpecified), AppData(nullptr), mBackingStore(nullptr), mBufStart(nullptr), mWritePoint(nullptr), + mRemainingLen(0), mLenWritten(0), mMaxLen(0), mReservedSize(0), mContainerType(kTLVType_NotSpecified), mInitializationCookie(0), + mContainerOpen(false), mCloseContainerReserved(true) +{} + NO_INLINE void TLVWriter::Init(uint8_t * buf, size_t maxLen) { // TODO: Maybe we can just make mMaxLen, mLenWritten, mRemainingLen size_t instead? uint32_t actualMaxLen = maxLen > UINT32_MAX ? UINT32_MAX : static_cast(maxLen); + + // TODO(#30825): Need to ensure a single init path for this complex data. + mInitializationCookie = 0; mBackingStore = nullptr; - mBufStart = mWritePoint = buf; - mRemainingLen = actualMaxLen; - mLenWritten = 0; - mMaxLen = actualMaxLen; - mContainerType = kTLVType_NotSpecified; - mReservedSize = 0; + mBufStart = buf; + mWritePoint = buf; + mRemainingLen = actualMaxLen; + mLenWritten = 0; + mMaxLen = actualMaxLen; + mContainerType = kTLVType_NotSpecified; + mReservedSize = 0; SetContainerOpen(false); SetCloseContainerReserved(true); - ImplicitProfileId = kProfileIdNotSpecified; + ImplicitProfileId = kProfileIdNotSpecified; + mInitializationCookie = kExpectedInitializationCookie; } -CHIP_ERROR TLVWriter::Init(TLVBackingStore & backingStore, uint32_t maxLen) +CHIP_ERROR TLVWriter::Init(TLVBackingStore & backingStore, uint32_t maxLen /* = UINT32_MAX */) { + // TODO(#30825): Need to ensure a single init path for this complex data. + Init(nullptr, maxLen); + mInitializationCookie = 0; + mBackingStore = &backingStore; mBufStart = nullptr; mRemainingLen = 0; @@ -75,25 +101,30 @@ CHIP_ERROR TLVWriter::Init(TLVBackingStore & backingStore, uint32_t maxLen) if (err != CHIP_NO_ERROR) return err; - mWritePoint = mBufStart; - mLenWritten = 0; - mMaxLen = maxLen; - mContainerType = kTLVType_NotSpecified; - mReservedSize = 0; - SetContainerOpen(false); - SetCloseContainerReserved(true); - - ImplicitProfileId = kProfileIdNotSpecified; + mWritePoint = mBufStart; + mInitializationCookie = kExpectedInitializationCookie; return CHIP_NO_ERROR; } CHIP_ERROR TLVWriter::Finalize() { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + CHIP_ERROR err = CHIP_NO_ERROR; if (IsContainerOpen()) return CHIP_ERROR_TLV_CONTAINER_OPEN; if (mBackingStore != nullptr) err = mBackingStore->FinalizeBuffer(*this, mBufStart, static_cast(mWritePoint - mBufStart)); + + // TODO(#30825) The following should be safe, but in some cases (without mBackingStore), there are incremental writes that + // start failing. +#if 0 + if (err == CHIP_NO_ERROR) + mInitializationCookie = 0; +#endif + return err; } @@ -463,6 +494,9 @@ CHIP_ERROR TLVWriter::CopyElement(Tag tag, TLVReader & reader) CHIP_ERROR TLVWriter::OpenContainer(Tag tag, TLVType containerType, TLVWriter & containerWriter) { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrReturnError(TLVTypeIsContainer(containerType), CHIP_ERROR_WRONG_TLV_TYPE); @@ -483,6 +517,7 @@ CHIP_ERROR TLVWriter::OpenContainer(Tag tag, TLVType containerType, TLVWriter & return err; } + // TODO(#30825): Clean-up this separate init path path. containerWriter.mBackingStore = mBackingStore; containerWriter.mBufStart = mBufStart; containerWriter.mWritePoint = mWritePoint; @@ -492,7 +527,8 @@ CHIP_ERROR TLVWriter::OpenContainer(Tag tag, TLVType containerType, TLVWriter & containerWriter.mContainerType = containerType; containerWriter.SetContainerOpen(false); containerWriter.SetCloseContainerReserved(IsCloseContainerReserved()); - containerWriter.ImplicitProfileId = ImplicitProfileId; + containerWriter.ImplicitProfileId = ImplicitProfileId; + containerWriter.mInitializationCookie = kExpectedInitializationCookie; SetContainerOpen(true); @@ -501,6 +537,10 @@ CHIP_ERROR TLVWriter::OpenContainer(Tag tag, TLVType containerType, TLVWriter & CHIP_ERROR TLVWriter::CloseContainer(TLVWriter & containerWriter) { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + if (!TLVTypeIsContainer(containerWriter.mContainerType)) return CHIP_ERROR_INCORRECT_STATE; @@ -526,6 +566,9 @@ CHIP_ERROR TLVWriter::CloseContainer(TLVWriter & containerWriter) CHIP_ERROR TLVWriter::StartContainer(Tag tag, TLVType containerType, TLVType & outerContainerType) { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); CHIP_ERROR err = CHIP_NO_ERROR; VerifyOrReturnError(TLVTypeIsContainer(containerType), CHIP_ERROR_WRONG_TLV_TYPE); @@ -555,6 +598,10 @@ CHIP_ERROR TLVWriter::StartContainer(Tag tag, TLVType containerType, TLVType & o CHIP_ERROR TLVWriter::EndContainer(TLVType outerContainerType) { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + if (!TLVTypeIsContainer(mContainerType)) return CHIP_ERROR_INCORRECT_STATE; @@ -585,6 +632,10 @@ CHIP_ERROR TLVWriter::CopyContainer(TLVReader & container) CHIP_ERROR TLVWriter::CopyContainer(Tag tag, TLVReader & container) { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + // NOTE: This function MUST be used with a TVLReader that is reading from a contiguous buffer. if (container.mBackingStore != nullptr) return CHIP_ERROR_INVALID_ARGUMENT; @@ -611,6 +662,8 @@ CHIP_ERROR TLVWriter::CopyContainer(Tag tag, TLVReader & container) CHIP_ERROR TLVWriter::CopyContainer(Tag tag, const uint8_t * encodedContainer, uint16_t encodedContainerLen) { + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); + TLVReader reader; reader.Init(encodedContainer, encodedContainerLen); @@ -624,6 +677,9 @@ CHIP_ERROR TLVWriter::CopyContainer(Tag tag, const uint8_t * encodedContainer, u CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_t lenOrVal) { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); uint8_t * p; uint8_t stagingBuf[17]; // 17 = 1 control byte + 8 tag bytes + 8 length/value bytes @@ -742,6 +798,9 @@ CHIP_ERROR TLVWriter::WriteElementHead(TLVElementType elemType, Tag tag, uint64_ CHIP_ERROR TLVWriter::WriteElementWithData(TLVType type, Tag tag, const uint8_t * data, uint32_t dataLen) { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); if (static_cast(type) & kTLVTypeSizeMask) { // We won't be able to recover this type properly! @@ -767,6 +826,9 @@ CHIP_ERROR TLVWriter::WriteElementWithData(TLVType type, Tag tag, const uint8_t CHIP_ERROR TLVWriter::WriteData(const uint8_t * p, uint32_t len) { + ABORT_ON_UNINITIALIZED_IF_ENABLED(); + + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError((mLenWritten + len) <= mMaxLen, CHIP_ERROR_BUFFER_TOO_SMALL); while (len > 0) diff --git a/src/lib/core/TLVWriter.h b/src/lib/core/TLVWriter.h index 59478f15e7123c..eb38d5a28d9932 100644 --- a/src/lib/core/TLVWriter.h +++ b/src/lib/core/TLVWriter.h @@ -27,6 +27,8 @@ #pragma once +#include + #include "TLVCommon.h" #include "TLVBackingStore.h" @@ -61,6 +63,18 @@ class DLL_EXPORT TLVWriter friend class TLVUpdater; public: + TLVWriter(); + + // TODO(#30825): We do not cleanly handle copies for all backing stores, but we don't disallow copy... +#if 0 + // Disable copy (and move) semantics. + TLVWriter(const TLVWriter&) = delete; + TLVWriter& operator=(const TLVWriter&) = delete; +#endif + + // Initialization cookie that is set when properly initialized. Randomly-picked 16 bit value. + static constexpr uint16_t kExpectedInitializationCookie = 0x52b1; + /** * Initializes a TLVWriter object to write into a single output buffer. * @@ -115,6 +129,7 @@ class DLL_EXPORT TLVWriter * (See @p OpenContainer()). * * @retval #CHIP_NO_ERROR If the encoding was finalized successfully. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -127,10 +142,12 @@ class DLL_EXPORT TLVWriter * Reserve some buffer for encoding future fields. * * @retval #CHIP_NO_ERROR Successfully reserved required buffer size. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_NO_MEMORY The reserved buffer size cannot fits into the remaining buffer size. */ CHIP_ERROR ReserveBuffer(uint32_t aBufferSize) { + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mRemainingLen >= aBufferSize, CHIP_ERROR_NO_MEMORY); mReservedSize += aBufferSize; mRemainingLen -= aBufferSize; @@ -141,10 +158,12 @@ class DLL_EXPORT TLVWriter * Release previously reserved buffer. * * @retval #CHIP_NO_ERROR Successfully released reserved buffer size. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_NO_MEMORY The released buffer is larger than previously reserved buffer size. */ CHIP_ERROR UnreserveBuffer(uint32_t aBufferSize) { + VerifyOrReturnError(IsInitialized(), CHIP_ERROR_INCORRECT_STATE); VerifyOrReturnError(mReservedSize >= aBufferSize, CHIP_ERROR_NO_MEMORY); mReservedSize -= aBufferSize; mRemainingLen += aBufferSize; @@ -161,6 +180,7 @@ class DLL_EXPORT TLVWriter * @param[in] v The value to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -193,6 +213,7 @@ class DLL_EXPORT TLVWriter * are strongly encouraged to set this parameter to false. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -251,6 +272,7 @@ class DLL_EXPORT TLVWriter * @param[in] v The value to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -341,6 +363,7 @@ class DLL_EXPORT TLVWriter * @param[in] v The value to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -374,6 +397,7 @@ class DLL_EXPORT TLVWriter * @param[in] data A ByteSpan object containing the bytes string to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -431,6 +455,7 @@ class DLL_EXPORT TLVWriter * @param[in] v The value to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -472,6 +497,7 @@ class DLL_EXPORT TLVWriter * @param[in] len The number of bytes to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -500,6 +526,7 @@ class DLL_EXPORT TLVWriter * @param[in] buf A pointer to the null-terminated UTF-8 string to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -529,6 +556,7 @@ class DLL_EXPORT TLVWriter * @param[in] len The length (in bytes) of the string to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -557,6 +585,7 @@ class DLL_EXPORT TLVWriter * @param[in] str A Span containing a pointer and a length of the string to be encoded. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -680,6 +709,7 @@ class DLL_EXPORT TLVWriter * ContextTag() or CommonTag(). * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -716,7 +746,7 @@ class DLL_EXPORT TLVWriter * * @retval #CHIP_NO_ERROR If the method succeeded. * @retval #CHIP_ERROR_INCORRECT_STATE - * If the supplied reader is not positioned on an element. + * If the supplied reader is not positioned on an element or if the writer is not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -765,7 +795,7 @@ class DLL_EXPORT TLVWriter * * @retval #CHIP_NO_ERROR If the method succeeded. * @retval #CHIP_ERROR_INCORRECT_STATE - * If the supplied reader is not positioned on an element. + * If the supplied reader is not positioned on an element or if the writer is not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -815,6 +845,7 @@ class DLL_EXPORT TLVWriter * writer. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_WRONG_TLV_TYPE * If the value specified for containerType is incorrect. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN @@ -855,7 +886,7 @@ class DLL_EXPORT TLVWriter * * @retval #CHIP_NO_ERROR If the method succeeded. * @retval #CHIP_ERROR_INCORRECT_STATE - * If a corresponding StartContainer() call was not made. + * If a corresponding StartContainer() call was not made or if the TLVWriter is not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -903,6 +934,7 @@ class DLL_EXPORT TLVWriter * associated with the supplied object is overwritten. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_WRONG_TLV_TYPE * If the value specified for containerType is incorrect. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN @@ -940,7 +972,8 @@ class DLL_EXPORT TLVWriter * * @retval #CHIP_NO_ERROR If the method succeeded. * @retval #CHIP_ERROR_INCORRECT_STATE - * If the supplied container writer is not in the correct state. + * If the supplied container writer is not in the correct state or if the TLVWriter is not + * initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If another container writer has been opened on the supplied * container writer and not yet closed. @@ -979,6 +1012,7 @@ class DLL_EXPORT TLVWriter * @param[in] dataLen The number of bytes in the @p data buffer. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_WRONG_TLV_TYPE * If the value specified for containerType is incorrect. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN @@ -1018,7 +1052,8 @@ class DLL_EXPORT TLVWriter * @retval #CHIP_ERROR_INVALID_ARGUMENT * If the supplied reader uses a TLVBackingStore rather than a simple buffer. * @retval #CHIP_ERROR_INCORRECT_STATE - * If the supplied reader is not positioned on a container element. + * If the supplied reader is not positioned on a container element or if the TLVWriter was not + * initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -1070,7 +1105,8 @@ class DLL_EXPORT TLVWriter * @retval #CHIP_ERROR_INVALID_ARGUMENT * If the supplied reader uses a TLVBackingStore rather than a simple buffer. * @retval #CHIP_ERROR_INCORRECT_STATE - * If the supplied reader is not positioned on a container element. + * If the supplied reader is not positioned on a container element or of the TLVWriter was not + * initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -1117,6 +1153,7 @@ class DLL_EXPORT TLVWriter * @param[in] encodedContainerLen The length in bytes of the pre-encoded container. * * @retval #CHIP_NO_ERROR If the method succeeded. + * @retval #CHIP_ERROR_INCORRECT_STATE If the TLVWriter was not initialized. * @retval #CHIP_ERROR_TLV_CONTAINER_OPEN * If a container writer has been opened on the current writer and not * yet closed. @@ -1165,6 +1202,12 @@ class DLL_EXPORT TLVWriter * @return the total remaining number of bytes. */ uint32_t GetRemainingFreeLength() const { return mRemainingLen; } + + /** + * @brief Returns true if this TLVWriter was properly initialized. + */ + bool IsInitialized() const { return mInitializationCookie == kExpectedInitializationCookie; } + /** * The profile id of tags that should be encoded in implicit form. * @@ -1197,6 +1240,7 @@ class DLL_EXPORT TLVWriter uint32_t mMaxLen; uint32_t mReservedSize; TLVType mContainerType; + uint16_t mInitializationCookie; private: bool mContainerOpen; diff --git a/src/lib/core/tests/TestTLV.cpp b/src/lib/core/tests/TestTLV.cpp index 9478a9186e6ce2..fce0a5d17ae484 100644 --- a/src/lib/core/tests/TestTLV.cpp +++ b/src/lib/core/tests/TestTLV.cpp @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -4646,6 +4647,281 @@ static void CheckTLVScopedBuffer(nlTestSuite * inSuite, void * inContext) } } +static void TestUninitializedWriter(nlTestSuite * inSuite, void * inContext) +{ + { + TLVWriter writer; + NL_TEST_ASSERT(inSuite, !writer.IsInitialized()); + } + + { + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.Finalize() == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.ReserveBuffer(123) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.UnreserveBuffer(123) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint8_t v = 3; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + int8_t v = 3; + bool preserveSize = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v, preserveSize) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + int16_t v = 3; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + int16_t v = 3; + bool preserveSize = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v, preserveSize) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + int32_t v = 3; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + int32_t v = 3; + bool preserveSize = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v, preserveSize) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + int64_t v = 3; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + int64_t v = 3; + bool preserveSize = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v, preserveSize) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint8_t v = 3; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint8_t v = 3; + bool preserveSize = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v, preserveSize) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint16_t v = 3; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint16_t v = 3; + bool preserveSize = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v, preserveSize) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint32_t v = 3; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint32_t v = 3; + bool preserveSize = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v, preserveSize) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint64_t v = 3; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + uint64_t v = 3; + bool preserveSize = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v, preserveSize) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + double v = 1.23; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + float v = 1.23f; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + bool v = true; + NL_TEST_ASSERT(inSuite, writer.PutBoolean(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + bool v = true; + NL_TEST_ASSERT(inSuite, writer.Put(ContextTag(1), v) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + const uint8_t buf[] = { 1, 2, 3 }; + NL_TEST_ASSERT(inSuite, + writer.PutBytes(ContextTag(1), buf, static_cast(sizeof(buf))) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + const char * buf = "abc"; + NL_TEST_ASSERT(inSuite, writer.PutString(ContextTag(1), buf) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + const char * buf = "abc"; + NL_TEST_ASSERT(inSuite, + writer.PutString(ContextTag(1), buf, static_cast(strlen(buf))) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + CharSpan str = "abc"_span; + NL_TEST_ASSERT(inSuite, writer.PutString(ContextTag(1), str) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.PutStringF(ContextTag(1), "%d", 1) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.PutNull(ContextTag(1)) == CHIP_ERROR_INCORRECT_STATE); + } + + { + const uint8_t buf[]{ 0, 0, 0 }; + TLVReader reader; + reader.Init(buf); + + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.CopyElement(reader) == CHIP_ERROR_INCORRECT_STATE); + } + + { + const uint8_t buf[]{ 0, 0, 0 }; + TLVReader reader; + reader.Init(buf); + + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.CopyElement(ContextTag(1), reader) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + TLVType outerContainerType; + + NL_TEST_ASSERT(inSuite, + writer.StartContainer(ContextTag(1), TLVType::kTLVType_Structure, outerContainerType) == + CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter writer; + TLVType outerContainerType = TLVType::kTLVType_Structure; + NL_TEST_ASSERT(inSuite, writer.EndContainer(outerContainerType) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter innerWriter; + uint8_t buf[]{ 0, 0, 0 }; + innerWriter.Init(buf); + NL_TEST_ASSERT(inSuite, innerWriter.IsInitialized()); + + TLVWriter writer; + NL_TEST_ASSERT(inSuite, + writer.OpenContainer(ContextTag(1), TLVType::kTLVType_Structure, innerWriter) == CHIP_ERROR_INCORRECT_STATE); + } + + { + TLVWriter innerWriter; + uint8_t buf[]{ 0, 0, 0 }; + innerWriter.Init(buf); + NL_TEST_ASSERT(inSuite, innerWriter.IsInitialized()); + + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.CloseContainer(innerWriter) == CHIP_ERROR_INCORRECT_STATE); + } + + { + uint8_t buf[]{ 0, 0, 0 }; + TLVWriter writer; + NL_TEST_ASSERT(inSuite, + writer.PutPreEncodedContainer(ContextTag(1), TLVType::kTLVType_Structure, buf, + static_cast(sizeof(buf))) == CHIP_ERROR_INCORRECT_STATE); + } + + { + const uint8_t buf[]{ 0, 0, 0 }; + TLVReader reader; + reader.Init(buf); + + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.CopyContainer(reader) == CHIP_ERROR_INCORRECT_STATE); + } + + { + const uint8_t buf[]{ 0, 0, 0 }; + TLVReader reader; + reader.Init(buf); + + TLVWriter writer; + NL_TEST_ASSERT(inSuite, writer.CopyContainer(ContextTag(1), reader) == CHIP_ERROR_INCORRECT_STATE); + } + + { + uint8_t buf[]{ 0, 0, 0 }; + + TLVWriter writer; + NL_TEST_ASSERT(inSuite, + writer.CopyContainer(ContextTag(1), buf, static_cast(sizeof(buf))) == CHIP_ERROR_INCORRECT_STATE); + } +} + // Test Suite /** @@ -4684,6 +4960,7 @@ static const nlTest sTests[] = NL_TEST_DEF("CHIP TLV GetStringView Test", CheckGetStringView), NL_TEST_DEF("CHIP TLV GetByteView Test", CheckGetByteView), NL_TEST_DEF("Int Min/Max Test", TestIntMinMax), + NL_TEST_DEF("Uninitialized Writer Test", TestUninitializedWriter), NL_TEST_SENTINEL() }; From e360187d892c031a8f039af03f90cfd191462d55 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Josefsen?= <69624991+ReneJosefsen@users.noreply.github.com> Date: Mon, 11 Dec 2023 15:43:14 +0100 Subject: [PATCH 7/9] Initial test scripts for BSENCFG and VCC test cases (#30841) * Initial python scripts for BSENCFG and VCC 2.1 test cases * Restyle --- .../python/chip/clusters/__init__.py | 34 ++--- src/python_testing/TC_BSENCFG_2_1.py | 72 +++++++++ src/python_testing/TC_VCC_2_1 .py | 142 ++++++++++++++++++ 3 files changed, 231 insertions(+), 17 deletions(-) create mode 100644 src/python_testing/TC_BSENCFG_2_1.py create mode 100644 src/python_testing/TC_VCC_2_1 .py diff --git a/src/controller/python/chip/clusters/__init__.py b/src/controller/python/chip/clusters/__init__.py index 456ee0a84057a8..04644cb5a4d109 100644 --- a/src/controller/python/chip/clusters/__init__.py +++ b/src/controller/python/chip/clusters/__init__.py @@ -25,29 +25,29 @@ from . import Attribute, CHIPClusters, Command from .Objects import (AccessControl, AccountLogin, Actions, ActivatedCarbonFilterMonitoring, AdministratorCommissioning, AirQuality, ApplicationBasic, ApplicationLauncher, AudioOutput, BallastConfiguration, BarrierControl, BasicInformation, - BinaryInputBasic, Binding, BooleanState, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, - CarbonMonoxideConcentrationMeasurement, Channel, ColorControl, ContentLauncher, Descriptor, - DeviceEnergyManagement, DiagnosticLogs, DishwasherAlarm, DishwasherMode, DoorLock, ElectricalMeasurement, - EnergyEvse, EthernetNetworkDiagnostics, FanControl, FaultInjection, FixedLabel, FlowMeasurement, - FormaldehydeConcentrationMeasurement, GeneralCommissioning, GeneralDiagnostics, GroupKeyManagement, Groups, - HepaFilterMonitoring, IcdManagement, Identify, IlluminanceMeasurement, KeypadInput, LaundryWasherControls, - LaundryWasherMode, LevelControl, LocalizationConfiguration, LowPower, MediaInput, MediaPlayback, ModeSelect, - NetworkCommissioning, NitrogenDioxideConcentrationMeasurement, OccupancySensing, OnOff, - OnOffSwitchConfiguration, OperationalCredentials, OperationalState, OtaSoftwareUpdateProvider, - OtaSoftwareUpdateRequestor, OzoneConcentrationMeasurement, Pm1ConcentrationMeasurement, - Pm10ConcentrationMeasurement, Pm25ConcentrationMeasurement, PowerSource, PowerSourceConfiguration, - PressureMeasurement, ProxyConfiguration, ProxyDiscovery, ProxyValid, PulseWidthModulation, - PumpConfigurationAndControl, RadonConcentrationMeasurement, RefrigeratorAlarm, + BinaryInputBasic, Binding, BooleanSensorConfiguration, BooleanState, BridgedDeviceBasicInformation, + CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, Channel, ColorControl, + ContentLauncher, Descriptor, DeviceEnergyManagement, DiagnosticLogs, DishwasherAlarm, DishwasherMode, + DoorLock, ElectricalMeasurement, EnergyEvse, EthernetNetworkDiagnostics, FanControl, FaultInjection, + FixedLabel, FlowMeasurement, FormaldehydeConcentrationMeasurement, GeneralCommissioning, GeneralDiagnostics, + GroupKeyManagement, Groups, HepaFilterMonitoring, IcdManagement, Identify, IlluminanceMeasurement, + KeypadInput, LaundryWasherControls, LaundryWasherMode, LevelControl, LocalizationConfiguration, LowPower, + MediaInput, MediaPlayback, ModeSelect, NetworkCommissioning, NitrogenDioxideConcentrationMeasurement, + OccupancySensing, OnOff, OnOffSwitchConfiguration, OperationalCredentials, OperationalState, + OtaSoftwareUpdateProvider, OtaSoftwareUpdateRequestor, OzoneConcentrationMeasurement, + Pm1ConcentrationMeasurement, Pm10ConcentrationMeasurement, Pm25ConcentrationMeasurement, PowerSource, + PowerSourceConfiguration, PressureMeasurement, ProxyConfiguration, ProxyDiscovery, ProxyValid, + PulseWidthModulation, PumpConfigurationAndControl, RadonConcentrationMeasurement, RefrigeratorAlarm, RefrigeratorAndTemperatureControlledCabinetMode, RelativeHumidityMeasurement, RvcCleanMode, RvcOperationalState, RvcRunMode, Scenes, SmokeCoAlarm, SoftwareDiagnostics, Switch, TargetNavigator, TemperatureControl, TemperatureMeasurement, Thermostat, ThermostatUserInterfaceConfiguration, ThreadNetworkDiagnostics, TimeFormatLocalization, TimeSynchronization, - TotalVolatileOrganicCompoundsConcentrationMeasurement, UnitLocalization, UnitTesting, UserLabel, WakeOnLan, - WiFiNetworkDiagnostics, WindowCovering) + TotalVolatileOrganicCompoundsConcentrationMeasurement, UnitLocalization, UnitTesting, UserLabel, + ValveConfigurationAndControl, WakeOnLan, WiFiNetworkDiagnostics, WindowCovering) __all__ = [Attribute, CHIPClusters, Command, AccessControl, AccountLogin, Actions, ActivatedCarbonFilterMonitoring, AdministratorCommissioning, AirQuality, ApplicationBasic, ApplicationLauncher, AudioOutput, BallastConfiguration, BarrierControl, BasicInformation, - BinaryInputBasic, Binding, BooleanState, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, + BinaryInputBasic, Binding, BooleanSensorConfiguration, BooleanState, BridgedDeviceBasicInformation, CarbonDioxideConcentrationMeasurement, CarbonMonoxideConcentrationMeasurement, Channel, ColorControl, ContentLauncher, Descriptor, DeviceEnergyManagement, DiagnosticLogs, DishwasherAlarm, DishwasherMode, DoorLock, ElectricalMeasurement, EnergyEvse, EthernetNetworkDiagnostics, FanControl, FaultInjection, FixedLabel, FlowMeasurement, @@ -65,4 +65,4 @@ Switch, TargetNavigator, TemperatureControl, TemperatureMeasurement, Thermostat, ThermostatUserInterfaceConfiguration, ThreadNetworkDiagnostics, TimeFormatLocalization, TimeSynchronization, TotalVolatileOrganicCompoundsConcentrationMeasurement, UnitLocalization, - UnitTesting, UserLabel, WakeOnLan, WiFiNetworkDiagnostics, WindowCovering] + UnitTesting, UserLabel, ValveConfigurationAndControl, WakeOnLan, WiFiNetworkDiagnostics, WindowCovering] diff --git a/src/python_testing/TC_BSENCFG_2_1.py b/src/python_testing/TC_BSENCFG_2_1.py new file mode 100644 index 00000000000000..f9a8c003d5be26 --- /dev/null +++ b/src/python_testing/TC_BSENCFG_2_1.py @@ -0,0 +1,72 @@ +# +# Copyright (c) 2023 Project CHIP Authors +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging + +import chip.clusters as Clusters +from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main +from mobly import asserts + + +class TC_BSENCFG_2_1(MatterBaseTest): + async def read_ts_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.BooleanSensorConfiguration + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + @async_test_body + async def test_TC_BSENCFG_2_1(self): + + endpoint = self.user_params.get("endpoint", 1) + + self.print_step(1, "Commissioning, already done") + attributes = Clusters.BooleanSensorConfiguration.Attributes + + self.print_step(2, "Read attribute list to determine supported attributes") + attribute_list = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + + self.print_step(3, "Read SensitivityLevel attribute, if supported") + if attributes.SensitivityLevel.attribute_id in attribute_list: + sensitivity_level_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.SensitivityLevel) + asserts.assert_less(sensitivity_level_dut, Clusters.Objects.BooleanSensorConfiguration.Enums.SensitivityEnum.kUnknownEnumValue, + "SensitivityLevel is not in valid range") + else: + logging.info("Test step skipped") + + self.print_step(4, "Read AlarmsActive attribute, if supported") + if attributes.AlarmsActive.attribute_id in attribute_list: + alarms_active_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsActive) + asserts.assert_less_equal(alarms_active_dut, 0b00000011, "AlarmsActive is not in valid range") + else: + logging.info("Test step skipped") + + self.print_step(5, "Read AlarmsSuppressed attribute, if supported") + if attributes.AlarmsSuppressed.attribute_id in attribute_list: + alarms_suppressed_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsSuppressed) + asserts.assert_less_equal(alarms_suppressed_dut, 0b00000011, "AlarmsSuppressed is not in valid range") + else: + logging.info("Test step skipped") + + self.print_step(5, "Read AlarmsEnabled attribute, if supported") + if attributes.AlarmsEnabled.attribute_id in attribute_list: + alarms_enabled_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.AlarmsEnabled) + asserts.assert_less_equal(alarms_enabled_dut, 0b00000011, "AlarmsEnabled is not in valid range") + else: + logging.info("Test step skipped") + + +if __name__ == "__main__": + default_matter_test_main() diff --git a/src/python_testing/TC_VCC_2_1 .py b/src/python_testing/TC_VCC_2_1 .py new file mode 100644 index 00000000000000..9d976e377e7e57 --- /dev/null +++ b/src/python_testing/TC_VCC_2_1 .py @@ -0,0 +1,142 @@ +# +# Copyright (c) 2023 Project CHIP Authors +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import logging + +import chip.clusters as Clusters +from chip.clusters.Types import NullValue +from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main +from mobly import asserts + + +class TC_VCC_2_1(MatterBaseTest): + async def read_ts_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.ValveConfigurationAndControl + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + @async_test_body + async def test_TC_VCC_2_1(self): + + endpoint = self.user_params.get("endpoint", 1) + + self.print_step(1, "Commissioning, already done") + attributes = Clusters.ValveConfigurationAndControl.Attributes + + self.print_step(2, "Read attribute list to determine supported attributes") + attribute_list = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.AttributeList) + + print(attribute_list) + + self.print_step(3, "Read OpenDuration attribute, if supported") + if attributes.OpenDuration.attribute_id in attribute_list: + open_duration_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.OpenDuration) + + if open_duration_dut is not NullValue: + asserts.assert_less_equal(open_duration_dut, 0xFFFFFFFE, "OpenDuration attribute is out of range") + asserts.assert_greater_equal(open_duration_dut, 1, "OpenDuration attribute is out of range") + else: + logging.info("Test step skipped") + + self.print_step(4, "Read AutoCloseTime attribute, if supported") + if attributes.AutoCloseTime.attribute_id in attribute_list: + auto_close_time_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.AutoCloseTime) + + if auto_close_time_dut is not NullValue: + asserts.assert_less_equal(auto_close_time_dut, 0xFFFFFFFFFFFFFFFE, "OpenDuration attribute is out of range") + else: + logging.info("Test step skipped") + + self.print_step(5, "Read RemainingDuration attribute, if supported") + if attributes.RemainingDuration.attribute_id in attribute_list: + remaining_duration_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.RemainingDuration) + + if remaining_duration_dut is not NullValue: + asserts.assert_less_equal(remaining_duration_dut, 0xFFFFFFFE, "RemainingDuration attribute is out of range") + asserts.assert_greater_equal(remaining_duration_dut, 1, "RemainingDuration attribute is out of range") + else: + logging.info("Test step skipped") + + self.print_step(6, "Read CurrentState attribute, if supported") + if attributes.CurrentState.attribute_id in attribute_list: + current_state_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentState) + + if current_state_dut is not NullValue: + asserts.assert_less(current_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kUnknownEnumValue, + "CurrentState is not in valid range") + else: + logging.info("Test step skipped") + + self.print_step(7, "Read TargetState attribute, if supported") + if attributes.TargetState.attribute_id in attribute_list: + target_state_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetState) + + if target_state_dut is not NullValue: + asserts.assert_less(target_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kUnknownEnumValue, + "TargetState is not in valid range") + else: + logging.info("Test step skipped") + + self.print_step(8, "Read StartUpState attribute, if supported") + if attributes.StartUpState.attribute_id in attribute_list: + start_up_state_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.StartUpState) + + asserts.assert_less(start_up_state_dut, Clusters.Objects.ValveConfigurationAndControl.Enums.ValveStateEnum.kUnknownEnumValue, + "StartUpState is not in valid range") + else: + logging.info("Test step skipped") + + self.print_step(9, "Read CurrentLevel attribute, if supported") + if attributes.CurrentLevel.attribute_id in attribute_list: + current_level_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.CurrentLevel) + + if current_level_dut is not NullValue: + asserts.assert_less_equal(current_level_dut, 100, "CurrentLevel attribute is out of range") + asserts.assert_greater_equal(current_level_dut, 0, "CurrentLevel attribute is out of range") + else: + logging.info("Test step skipped") + + self.print_step(10, "Read TargetLevel attribute, if supported") + if attributes.TargetLevel.attribute_id in attribute_list: + target_level_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TargetLevel) + + if target_level_dut is not NullValue: + asserts.assert_less_equal(target_level_dut, 100, "TargetLevel attribute is out of range") + asserts.assert_greater_equal(target_level_dut, 0, "TargetLevel attribute is out of range") + else: + logging.info("Test step skipped") + + self.print_step(11, "Read OpenLevel attribute, if supported") + if attributes.OpenLevel.attribute_id in attribute_list: + open_level_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.OpenLevel) + + if open_level_dut is not NullValue: + asserts.assert_less_equal(open_level_dut, 100, "OpenLevel attribute is out of range") + asserts.assert_greater_equal(open_level_dut, 1, "OpenLevel attribute is out of range") + else: + logging.info("Test step skipped") + + self.print_step(12, "Read ValveFault attribute, if supported") + if attributes.ValveFault.attribute_id in attribute_list: + valve_fault_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.ValveFault) + + asserts.assert_less_equal(valve_fault_dut, 0b00000111, "ValveFault is not in valid range") + else: + logging.info("Test step skipped") + + +if __name__ == "__main__": + default_matter_test_main() From 574dfd58819b3a219d0036ab8e941777d4a2c975 Mon Sep 17 00:00:00 2001 From: Andrei Litvin Date: Mon, 11 Dec 2023 09:55:52 -0500 Subject: [PATCH 8/9] Revert "Bump actions/labeler from 4 to 5 (#30911)" (#30922) This reverts commit e15db829cde4a484a283c0e935df4738f572bb4c. --- .github/workflows/labeler.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/labeler.yaml b/.github/workflows/labeler.yaml index 0127be63320271..057208eda328d2 100644 --- a/.github/workflows/labeler.yaml +++ b/.github/workflows/labeler.yaml @@ -9,6 +9,6 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/labeler@v5 + - uses: actions/labeler@v4 with: repo-token: "${{ secrets.GITHUB_TOKEN }}" From c4dd75ed2232b592d5d55c21a1588b1f20177a59 Mon Sep 17 00:00:00 2001 From: SAYON DEEP Date: Mon, 11 Dec 2023 20:30:26 +0530 Subject: [PATCH 9/9] ESP32: Update esp-idf to v5.1.2 (#30920) --- integrations/docker/images/base/chip-build/version | 2 +- integrations/docker/images/stage-2/chip-build-esp32/Dockerfile | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/integrations/docker/images/base/chip-build/version b/integrations/docker/images/base/chip-build/version index dc78b3d46f7ef2..deeec8d0ebf684 100644 --- a/integrations/docker/images/base/chip-build/version +++ b/integrations/docker/images/base/chip-build/version @@ -1 +1 @@ -28 : [NXP] Add support for RW61x SDK +29 : [ESP] Update esp-idf to v5.1.2 diff --git a/integrations/docker/images/stage-2/chip-build-esp32/Dockerfile b/integrations/docker/images/stage-2/chip-build-esp32/Dockerfile index a5f01eb0c340eb..7dd8d382b258ff 100644 --- a/integrations/docker/images/stage-2/chip-build-esp32/Dockerfile +++ b/integrations/docker/images/stage-2/chip-build-esp32/Dockerfile @@ -11,7 +11,7 @@ RUN set -x \ && : # last line RUN set -x \ - && git clone --recursive -b v5.1.1 --depth 1 --shallow-submodule https://github.com/espressif/esp-idf.git /tmp/esp-idf \ + && git clone --recursive -b v5.1.2 --depth 1 --shallow-submodule https://github.com/espressif/esp-idf.git /tmp/esp-idf \ && : # last line FROM ghcr.io/project-chip/chip-build:${VERSION}