From 59b060eed18e27e1c2b5382acd8dec11d3a99f44 Mon Sep 17 00:00:00 2001 From: Michael Rupp <95718139+mykrupp@users.noreply.github.com> Date: Wed, 31 Jul 2024 22:31:44 -0400 Subject: [PATCH] [Silabs] Port platform specific Multi-Chip OTA work (#34440) * Pull request #1836: Cherry multi ota Merge in WMN_TOOLS/matter from cherry-multi-ota to silabs_slc_1.3 Squashed commit of the following: commit 4320bb46571658bc44fb82345348265def394991 Author: Michael Rupp Date: Fri May 10 14:26:07 2024 -0400 remove some unwanted diffs in provision files commit be160931dc600de7e7ead378b70d6a43c3945e46 Author: Michael Rupp Date: Fri May 10 14:24:25 2024 -0400 revert changes to generator.project.mak commit 14b6605887166e6d5284a61feb2bf407d850bdcf Author: Michael Rupp Date: Fri May 10 13:06:12 2024 -0400 revert NVM key changes and script changes ... and 8 more commits * Restyled by whitespace * Restyled by clang-format * Restyled by gn * Restyled by autopep8 * remove unused libs caught by linter * update doctree with new readmes * rerun CI, cirque failing for unknown reasons * fix include guards in provision examples * Restyled by clang-format --------- Co-authored-by: Restyled.io --- docs/tools/index.md | 2 + .../provision/ProvisionStorageDefault.cpp | 5 +- .../provision/ProvisionStorageFlash.cpp | 5 +- .../silabs/factory_data_generator/README.md | 56 ++++++ .../silabs/factory_data_generator/custom.py | 106 +++++++++++ .../silabs/factory_data_generator/default.py | 131 ++++++++++++++ scripts/tools/silabs/ota/README.md | 15 +- .../binaries/ext_flash_ota_entry_example.bin | Bin 0 -> 7056 bytes .../examples/ota_custom_entries_example.json | 67 +++++++ .../examples/ota_custom_entries_example2.json | 88 +++++++++ .../tools/silabs/ota/ota_multi_image_tool.py | 10 +- scripts/tools/silabs/ota/requirements.txt | 1 + src/platform/silabs/efr32/BUILD.gn | 21 ++- .../silabs/efr32/efr32-psa-crypto-config.h | 5 + .../silabs/multi-ota/OTACustomProcessor.cpp | 94 ++++++++++ .../silabs/multi-ota/OTACustomProcessor.h | 56 ++++++ .../multi-ota/OTAFactoryDataProcessor.cpp | 170 ++++++++++++++++++ .../multi-ota/OTAFactoryDataProcessor.h | 81 +++++++++ .../{efr32 => }/OTAFirmwareProcessor.cpp | 37 +++- .../{efr32 => }/OTAFirmwareProcessor.h | 0 .../silabs/multi-ota/{efr32 => }/OTAHooks.cpp | 27 ++- .../multi-ota/OTAMultiImageProcessorImpl.cpp | 3 - .../silabs/multi-ota/OTATlvProcessor.cpp | 70 +------- .../silabs/multi-ota/OTATlvProcessor.h | 10 ++ .../silabs/multi-ota/OtaTlvEncryptionKey.cpp | 130 ++++++++++++++ .../silabs/multi-ota/OtaTlvEncryptionKey.h | 34 ++++ third_party/silabs/efr32_sdk.gni | 10 ++ 27 files changed, 1145 insertions(+), 89 deletions(-) create mode 100644 scripts/tools/silabs/factory_data_generator/README.md create mode 100644 scripts/tools/silabs/factory_data_generator/custom.py create mode 100644 scripts/tools/silabs/factory_data_generator/default.py create mode 100755 scripts/tools/silabs/ota/examples/binaries/ext_flash_ota_entry_example.bin create mode 100644 scripts/tools/silabs/ota/examples/ota_custom_entries_example.json create mode 100644 scripts/tools/silabs/ota/examples/ota_custom_entries_example2.json create mode 100644 scripts/tools/silabs/ota/requirements.txt create mode 100644 src/platform/silabs/multi-ota/OTACustomProcessor.cpp create mode 100644 src/platform/silabs/multi-ota/OTACustomProcessor.h create mode 100644 src/platform/silabs/multi-ota/OTAFactoryDataProcessor.cpp create mode 100644 src/platform/silabs/multi-ota/OTAFactoryDataProcessor.h rename src/platform/silabs/multi-ota/{efr32 => }/OTAFirmwareProcessor.cpp (84%) rename src/platform/silabs/multi-ota/{efr32 => }/OTAFirmwareProcessor.h (100%) rename src/platform/silabs/multi-ota/{efr32 => }/OTAHooks.cpp (52%) create mode 100644 src/platform/silabs/multi-ota/OtaTlvEncryptionKey.cpp create mode 100644 src/platform/silabs/multi-ota/OtaTlvEncryptionKey.h diff --git a/docs/tools/index.md b/docs/tools/index.md index 003573ed5ebb14..a2ff8fd1587f89 100644 --- a/docs/tools/index.md +++ b/docs/tools/index.md @@ -49,6 +49,8 @@ Source files for these tools are located at `scripts/tools`. :maxdepth: 1 ../scripts/tools/silabs/README +../scripts/tools/silabs/ota/README +../scripts/tools/silabs/factory_data_generator/README ``` diff --git a/examples/platform/silabs/provision/ProvisionStorageDefault.cpp b/examples/platform/silabs/provision/ProvisionStorageDefault.cpp index 09f103592d2470..1162323cda3d51 100644 --- a/examples/platform/silabs/provision/ProvisionStorageDefault.cpp +++ b/examples/platform/silabs/provision/ProvisionStorageDefault.cpp @@ -28,6 +28,9 @@ #include #include #include +#ifdef OTA_ENCRYPTION_ENABLE +#include +#endif // OTA_ENCRYPTION_ENABLE #ifdef SLI_SI91X_MCU_INTERFACE #include #else @@ -659,7 +662,7 @@ CHIP_ERROR Storage::SetOtaTlvEncryptionKey(const ByteSpan & value) ReturnErrorOnFailure(key.Import(value.data(), value.size())); return SilabsConfig::WriteConfigValue(SilabsConfig::kOtaTlvEncryption_KeyId, key.GetId()); } -#endif +#endif // OTA_ENCRYPTION_ENABLE /** * @brief Reads the test event trigger key from NVM. If the key isn't present, returns default value if defined. diff --git a/examples/platform/silabs/provision/ProvisionStorageFlash.cpp b/examples/platform/silabs/provision/ProvisionStorageFlash.cpp index 5bd11108bd9cc6..35a32d6af8f5ec 100644 --- a/examples/platform/silabs/provision/ProvisionStorageFlash.cpp +++ b/examples/platform/silabs/provision/ProvisionStorageFlash.cpp @@ -25,6 +25,9 @@ #include #include #include +#ifdef OTA_ENCRYPTION_ENABLE +#include +#endif // OTA_ENCRYPTION_ENABLE using namespace chip::Credentials; @@ -708,7 +711,7 @@ CHIP_ERROR Storage::SetOtaTlvEncryptionKey(const ByteSpan & value) { return CHIP_ERROR_NOT_IMPLEMENTED; } -#endif +#endif // OTA_ENCRYPTION_ENABLE CHIP_ERROR Storage::GetTestEventTriggerKey(MutableByteSpan & keySpan) { diff --git a/scripts/tools/silabs/factory_data_generator/README.md b/scripts/tools/silabs/factory_data_generator/README.md new file mode 100644 index 00000000000000..c75c52f91d0333 --- /dev/null +++ b/scripts/tools/silabs/factory_data_generator/README.md @@ -0,0 +1,56 @@ +# Silabs Factory Data Generator + +## Tool implementation + +The tool comprises of two files: `default.py`, `custom.py` + +### `default.py` + +Defines the base `InputArgument` class and its derived classes that will be +referenced as **default classes**. + +`InputArgument` offers an abstract interface in the form of three methods: +`key()`, `length()`, `encode()`, that will be used to generate the `(K, L, V)` +tuple through the public `output()` method. Each custom class should implement +the abstract interface, if its direct parent does not offer a relevant +implementation. + +### `custom.py` + +Defines classes for each argument that should generate data in the output binary +(will be referenced as **custom classes**). Please note that each new class +should derive from a default class, not from `InputArgument` directly. + +### How to add a new argument + +Example of defining a new argument class in `custom.py`: + +``` +class FooArgument(BarArgument): + def __init__(self, arg): + super().__init__(arg) + + def key(self): + return + + def length(self): + return + + def encode(self): + return + + def custom_function(self): + pass +``` + +where `BarArgument` is one of the **default classes**. Please note that a user +can define additional methods if needed (e.g. `custom_function`; also see +`generate_private_key` from `DacPKey` class). + +Then use this class in `generate.py` to create a `FooArgument` object from an +option: + +``` +parser.add_argument("--foo", required=True, type=FooArgument, + help="[int | hex] Foo argument.") +``` diff --git a/scripts/tools/silabs/factory_data_generator/custom.py b/scripts/tools/silabs/factory_data_generator/custom.py new file mode 100644 index 00000000000000..a06d61ca3f8568 --- /dev/null +++ b/scripts/tools/silabs/factory_data_generator/custom.py @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 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. +# + +'''This file should contain custom classes derived any class from default.py. + +Each class implemented here should describe an input parameter and should +implement the InputArgument abstract interface, if its base class does not +already offer an implementation or if there is a need of a custom behavior. + +Example of defining a new argument class: + + class FooArgument(IntArgument): + def __init__(self, arg): + super().__init__(arg) + + def key(self): + return + + def length(self): + return + + def encode(self): + return + + def custom_function(self): + pass + +Then use this class in generate.py to create a FooArgument object from an +option: + + parser.add_argument("--foo", required=True, type=FooArgument, + help="[int | hex] Foo argument.") +''' + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives.serialization import load_der_private_key +from default import FileArgument + + +class DacPKey(FileArgument): + + def __init__(self, arg): + super().__init__(arg) + self.private_key = None + + def key(self): + return 1 + + def length(self): + assert (self.private_key is not None) + return len(self.private_key) + + def encode(self): + assert (self.private_key is not None) + return self.private_key + + def generate_private_key(self, password, use_sss_blob=True): + if use_sss_blob: + self.private_key = self.val + else: + keys = load_der_private_key(self.val, password, backend=default_backend()) + self.private_key = keys.private_numbers().private_value.to_bytes( + 32, byteorder='big' + ) + + +class DacCert(FileArgument): + + def __init__(self, arg): + super().__init__(arg) + + def key(self): + return 2 + + +class PaiCert(FileArgument): + + def __init__(self, arg): + super().__init__(arg) + + def key(self): + return 3 + + +class CertDeclaration(FileArgument): + + def __init__(self, arg): + super().__init__(arg) + + def key(self): + return 4 diff --git a/scripts/tools/silabs/factory_data_generator/default.py b/scripts/tools/silabs/factory_data_generator/default.py new file mode 100644 index 00000000000000..13dc0866aaed40 --- /dev/null +++ b/scripts/tools/silabs/factory_data_generator/default.py @@ -0,0 +1,131 @@ +#!/usr/bin/env python3 +# +# Copyright (c) 2022 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. +# +'''This file should contain default argument classes. + +Base class is InputArgument. It defines the abstract interface to be +implemented and offers a way to compute a KLV value through output(). +Other classes that derive InputArgument directly will be referenced +as default classes throughout the docstrings. + +The default classes should not be used to instantiate arguments. +If one wants to add another argument, a custom class should be derived +from one of the default classes. +''' + +import base64 +import logging + + +class InputArgument: + '''Base class for any input argument that will be added to KLV. + + The user will define its arguments as instances of InputArgument + by setting the "type" attribute of ArgumentParser add_argument to + an instance of a derived class. This means that all derived classes + must accept an additional "arg" parameter in the constructor. In the + end, the list of arguments will be parsed into objects derived from + InputArgument (or default derived classes), which decouples the object + creation from its processing. + + Abstract methods: + key: Should be overwritten by final classes to return a "magic number". + length: Can be overwritten by default classes to specify a default value + (e.g. int arguments with a default length value of 4); can also + be overwritten by final classes to specify a custom value for a + certain argument. + encode: Should be overwritten to generate the correct bytes array from + its internal value. + + Main usage is to iterate over an iterable entity of InputArguments and call + the output() method to generate the (K, L, V) tuple. Note that the output() + method should not be implemented, since its a common functionality across + all InputArgument classes. + ''' + + def __init__(self): + self.val = None + + def key(self): + logging.error("key() should be implemented in derived classes.") + + def length(self): + logging.error("length() should be implemented in derived classes.") + + def encode(self): + logging.error("encode() should be implemented in derived classes.") + + def output(self): + out = (self.key(), self.length(), self.encode()) + logging.info("'{}' length: {}".format(type(self).__name__, self.length())) + return out + + +class IntArgument(InputArgument): + + def __init__(self, arg): + super().__init__() + self.val = int(arg, 0) + + def length(self): + return 4 + + def encode(self): + return self.val.to_bytes(self.length(), "little") + + +class Base64Argument(InputArgument): + + def __init__(self, arg): + super().__init__() + self.val = base64.b64decode(arg) + + def length(self): + return len(self.encode()) + + def encode(self): + return base64.b64encode(self.val) + + +class StrArgument(InputArgument): + + def __init__(self, arg): + super().__init__() + self.val = str(arg) + + def length(self): + return len(self.encode()) + + def encode(self): + return str.encode(self.val) + + def max_length(self): + return 32 + + +class FileArgument(InputArgument): + + def __init__(self, arg): + super().__init__() + with open(arg, "rb") as _file: + self.val = _file.read() + + def length(self): + return len(self.val) + + def encode(self): + return self.val diff --git a/scripts/tools/silabs/ota/README.md b/scripts/tools/silabs/ota/README.md index d0c1bd39e068cd..0629d8650c1e8a 100644 --- a/scripts/tools/silabs/ota/README.md +++ b/scripts/tools/silabs/ota/README.md @@ -25,8 +25,7 @@ python3 ./scripts/tools/silabs/ota/ota_multi_image_tool.py create -v 0xDEAD -p 0 ``` followed by \*_custom options_- and a positional argument (should be last) that -specifies the output file. Please see the `create_ota_images.sh` for some -reference commands. +specifies the output file. The list of **custom options**: @@ -38,12 +37,6 @@ The list of **custom options**: --app-version-str --> Application version string. Same as above. --app-build-date --> Application build date. Same as above. -# SSBL options ---bl-input-file --> Path to the SSBL binary. ---bl-version --> SSBL version. ---bl-version-str --> SSBL version string. ---bl-build-date --> SSBL build date. - # Factory data options --factory-data --> If set, enables the generation of factory data. --cert_declaration --> Certification Declaration. @@ -51,6 +44,10 @@ The list of **custom options**: --dac_key --> DAC private key. --pai_cert --> PAI certificate. +# Encryption options +--enc_enable --> Enable ota encryption +--input_ota_key --> 16 Byte AES key + # Custom TLV options --json --> Path to a JSON file following ota_payload.schema ``` @@ -67,5 +64,5 @@ processing. When defining a custom processor, a user is able to also specify the custom format of the TLV by creating a JSON file based on the `ota_payload.schema`. The tool offers support for describing multiple TLV in the same JSON file. Please -see the `examples/ota_max_entries_example.json` for a multi-app + SSBL example. +see the `examples/ota_custom_entries_example.json` for a multi-binary example. Option `--json` must be used to specify the path to the JSON file. diff --git a/scripts/tools/silabs/ota/examples/binaries/ext_flash_ota_entry_example.bin b/scripts/tools/silabs/ota/examples/binaries/ext_flash_ota_entry_example.bin new file mode 100755 index 0000000000000000000000000000000000000000..5800a9860f70aae4d8f235a4371b31c6b77a498f GIT binary patch literal 7056 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 aOX=pJ+=kw7SL#0dc~SP73%^%zf&UFmw4W~k literal 0 HcmV?d00001 diff --git a/scripts/tools/silabs/ota/examples/ota_custom_entries_example.json b/scripts/tools/silabs/ota/examples/ota_custom_entries_example.json new file mode 100644 index 00000000000000..8f679a266da9bd --- /dev/null +++ b/scripts/tools/silabs/ota/examples/ota_custom_entries_example.json @@ -0,0 +1,67 @@ +{ + "inputs": [ + { + "tag": 8, + "descriptor": [ + { + "name": "binary_version", + "length": 4, + "value": 1003 + }, + { + "name": "binary_version_str", + "length": 64, + "value": "TestVersion" + }, + { + "name": "build_date", + "length": 64, + "value": "2024-03-25" + } + ], + "path": "./binaries/ext_flash_ota_entry_example.bin" + }, + { + "tag": 9, + "descriptor": [ + { + "name": "binary_version", + "length": 4, + "value": 1004 + }, + { + "name": "binary_version_str", + "length": 64, + "value": "TestVersion2" + }, + { + "name": "build_date", + "length": 64, + "value": "2024-03-25" + } + ], + "path": "./binaries/ext_flash_ota_entry_example.bin" + }, + { + "tag": 10, + "descriptor": [ + { + "name": "binary_version", + "length": 4, + "value": 1005 + }, + { + "name": "binary_version_str", + "length": 64, + "value": "TestVersion3" + }, + { + "name": "build_date", + "length": 64, + "value": "2024-03-25" + } + ], + "path": "./binaries/ext_flash_ota_entry_example.bin" + } + ] +} diff --git a/scripts/tools/silabs/ota/examples/ota_custom_entries_example2.json b/scripts/tools/silabs/ota/examples/ota_custom_entries_example2.json new file mode 100644 index 00000000000000..3b910f2d163156 --- /dev/null +++ b/scripts/tools/silabs/ota/examples/ota_custom_entries_example2.json @@ -0,0 +1,88 @@ +{ + "inputs": [ + { + "tag": 8, + "descriptor": [ + { + "name": "binary_version", + "length": 4, + "value": 1003 + }, + { + "name": "binary_version_str", + "length": 64, + "value": "TestVersion" + }, + { + "name": "build_date", + "length": 64, + "value": "2024-03-25" + } + ], + "path": "./binaries/ext_flash_ota_entry_example.bin" + }, + { + "tag": 9, + "descriptor": [ + { + "name": "binary_version", + "length": 4, + "value": 1004 + }, + { + "name": "binary_version_str", + "length": 64, + "value": "TestVersion2" + }, + { + "name": "build_date", + "length": 64, + "value": "2024-03-25" + } + ], + "path": "./binaries/ext_flash_ota_entry_example.bin" + }, + { + "tag": 10, + "descriptor": [ + { + "name": "binary_version", + "length": 4, + "value": 1005 + }, + { + "name": "binary_version_str", + "length": 64, + "value": "TestVersion3" + }, + { + "name": "build_date", + "length": 64, + "value": "2024-03-25" + } + ], + "path": "./binaries/ext_flash_ota_entry_example.bin" + }, + { + "tag": 11, + "descriptor": [ + { + "name": "binary_version", + "length": 4, + "value": 1006 + }, + { + "name": "binary_version_str", + "length": 64, + "value": "TestVersion4" + }, + { + "name": "build_date", + "length": 64, + "value": "2024-03-25" + } + ], + "path": "./binaries/ext_flash_ota_entry_example.bin" + } + ] +} diff --git a/scripts/tools/silabs/ota/ota_multi_image_tool.py b/scripts/tools/silabs/ota/ota_multi_image_tool.py index 64715d784aeecd..280c80ea516a5d 100755 --- a/scripts/tools/silabs/ota/ota_multi_image_tool.py +++ b/scripts/tools/silabs/ota/ota_multi_image_tool.py @@ -50,7 +50,6 @@ from chip.tlv import TLVWriter # noqa: E402 isort:skip from custom import CertDeclaration, DacCert, DacPKey, PaiCert # noqa: E402 isort:skip from default import InputArgument # noqa: E402 isort:skip -from generate import set_logger # noqa: E402 isort:skip OTA_APP_TLV_TEMP = os.path.join(os.path.dirname(__file__), "ota_temp_app_tlv.bin") OTA_BOOTLOADER_TLV_TEMP = os.path.join(os.path.dirname(__file__), "ota_temp_ssbl_tlv.bin") @@ -65,6 +64,15 @@ class TAG: FACTORY_DATA = 3 +def set_logger(): + stdout_handler = logging.StreamHandler(stream=sys.stdout) + logging.basicConfig( + level=logging.DEBUG, + format='[%(levelname)s] %(message)s', + handlers=[stdout_handler] + ) + + def write_to_temp(path: str, payload: bytearray): with open(path, "wb") as _handle: _handle.write(payload) diff --git a/scripts/tools/silabs/ota/requirements.txt b/scripts/tools/silabs/ota/requirements.txt new file mode 100644 index 00000000000000..9d440c9c5b780b --- /dev/null +++ b/scripts/tools/silabs/ota/requirements.txt @@ -0,0 +1 @@ +jsonschema>=3.2.0 diff --git a/src/platform/silabs/efr32/BUILD.gn b/src/platform/silabs/efr32/BUILD.gn index 5d661886d54f21..a71a0cd8b335e6 100644 --- a/src/platform/silabs/efr32/BUILD.gn +++ b/src/platform/silabs/efr32/BUILD.gn @@ -78,14 +78,29 @@ static_library("efr32") { } if (chip_enable_multi_ota_requestor) { + if (chip_enable_multi_ota_encryption) { + sources += [ + "${silabs_platform_dir}/multi-ota/OtaTlvEncryptionKey.cpp", + "${silabs_platform_dir}/multi-ota/OtaTlvEncryptionKey.h", + ] + } + + if (chip_enable_ota_custom_tlv_testing) { + sources += [ + "${silabs_platform_dir}/multi-ota/OTACustomProcessor.cpp", + "${silabs_platform_dir}/multi-ota/OTACustomProcessor.h", + ] + } sources += [ + "${silabs_platform_dir}/multi-ota/OTAFactoryDataProcessor.cpp", + "${silabs_platform_dir}/multi-ota/OTAFactoryDataProcessor.h", + "${silabs_platform_dir}/multi-ota/OTAFirmwareProcessor.cpp", + "${silabs_platform_dir}/multi-ota/OTAFirmwareProcessor.h", + "${silabs_platform_dir}/multi-ota/OTAHooks.cpp", "${silabs_platform_dir}/multi-ota/OTAMultiImageProcessorImpl.cpp", "${silabs_platform_dir}/multi-ota/OTAMultiImageProcessorImpl.h", "${silabs_platform_dir}/multi-ota/OTATlvProcessor.cpp", "${silabs_platform_dir}/multi-ota/OTATlvProcessor.h", - "${silabs_platform_dir}/multi-ota/efr32/OTAFirmwareProcessor.cpp", - "${silabs_platform_dir}/multi-ota/efr32/OTAFirmwareProcessor.h", - "${silabs_platform_dir}/multi-ota/efr32/OTAHooks.cpp", ] } else if (chip_enable_ota_requestor) { sources += [ diff --git a/src/platform/silabs/efr32/efr32-psa-crypto-config.h b/src/platform/silabs/efr32/efr32-psa-crypto-config.h index b5564f88bd3258..b389431f85ec0b 100644 --- a/src/platform/silabs/efr32/efr32-psa-crypto-config.h +++ b/src/platform/silabs/efr32/efr32-psa-crypto-config.h @@ -30,5 +30,10 @@ #define PSA_WANT_ALG_CBC_NO_PADDING #endif // SL_USE_COAP_CONFIG +// Multi-chip OTA encryption processing +#if OTA_ENCRYPTION_ENABLE +#define PSA_WANT_ALG_CTR +#endif // OTA_ENCRYPTION_ENABLE + // Include Generated fies #include "psa_crypto_config.h" diff --git a/src/platform/silabs/multi-ota/OTACustomProcessor.cpp b/src/platform/silabs/multi-ota/OTACustomProcessor.cpp new file mode 100644 index 00000000000000..85ceb7d44d4eaf --- /dev/null +++ b/src/platform/silabs/multi-ota/OTACustomProcessor.cpp @@ -0,0 +1,94 @@ +/* + * + * 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. + */ + +#include +#include +#include + +#include + +extern "C" { +#include "btl_interface.h" +#include "em_bus.h" // For CORE_CRITICAL_SECTION +#if SL_WIFI +#include "spi_multiplex.h" +#endif // SL_WIFI +} + +/// No error, operation OK +#define SL_BOOTLOADER_OK 0L + +namespace chip { + +// Define static memebers +uint8_t OTACustomProcessor::mSlotId = 0; +uint32_t OTACustomProcessor::mWriteOffset = 0; +uint16_t OTACustomProcessor::writeBufOffset = 0; +uint8_t OTACustomProcessor::writeBuffer[kAlignmentBytes] __attribute__((aligned(4))) = { 0 }; + +CHIP_ERROR OTACustomProcessor::Init() +{ + ReturnErrorCodeIf(mCallbackProcessDescriptor == nullptr, CHIP_OTA_PROCESSOR_CB_NOT_REGISTERED); + mAccumulator.Init(sizeof(Descriptor)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTACustomProcessor::Clear() +{ + OTATlvProcessor::ClearInternal(); + mAccumulator.Clear(); + mDescriptorProcessed = false; + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTACustomProcessor::ProcessInternal(ByteSpan & block) +{ + if (!mDescriptorProcessed) + { + ReturnErrorOnFailure(ProcessDescriptor(block)); + } + + ChipLogError(SoftwareUpdate, "Reached Custom Processor"); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTACustomProcessor::ProcessDescriptor(ByteSpan & block) +{ + ReturnErrorOnFailure(mAccumulator.Accumulate(block)); + ReturnErrorOnFailure(mCallbackProcessDescriptor(static_cast(mAccumulator.data()))); + + mDescriptorProcessed = true; + mAccumulator.Clear(); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTACustomProcessor::ApplyAction() +{ + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTACustomProcessor::FinalizeAction() +{ + return CHIP_NO_ERROR; +} + +} // namespace chip diff --git a/src/platform/silabs/multi-ota/OTACustomProcessor.h b/src/platform/silabs/multi-ota/OTACustomProcessor.h new file mode 100644 index 00000000000000..64610f4b41fa37 --- /dev/null +++ b/src/platform/silabs/multi-ota/OTACustomProcessor.h @@ -0,0 +1,56 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include + +namespace chip { + +class OTACustomProcessor : public OTATlvProcessor +{ +public: + struct Descriptor + { + uint32_t version; + char versionString[kVersionStringSize]; + char buildDate[kBuildDateSize]; + }; + + CHIP_ERROR Init() override; + CHIP_ERROR Clear() override; + CHIP_ERROR ApplyAction() override; + CHIP_ERROR FinalizeAction() override; + +private: + CHIP_ERROR ProcessInternal(ByteSpan & block) override; + CHIP_ERROR ProcessDescriptor(ByteSpan & block); + + OTADataAccumulator mAccumulator; + bool mDescriptorProcessed = false; + static constexpr size_t kAlignmentBytes = 64; + static uint32_t mWriteOffset; // End of last written block + static uint8_t mSlotId; // Bootloader storage slot + // Bootloader storage API requires the buffer size to be a multiple of 4. + static uint8_t writeBuffer[kAlignmentBytes] __attribute__((aligned(4))); + // Offset indicates how far the write buffer has been filled + static uint16_t writeBufOffset; +}; + +} // namespace chip diff --git a/src/platform/silabs/multi-ota/OTAFactoryDataProcessor.cpp b/src/platform/silabs/multi-ota/OTAFactoryDataProcessor.cpp new file mode 100644 index 00000000000000..8d38207df35542 --- /dev/null +++ b/src/platform/silabs/multi-ota/OTAFactoryDataProcessor.cpp @@ -0,0 +1,170 @@ +/* + * + * 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. + */ + +#include +#include +#include + +namespace chip { + +using FactoryProvider = DeviceLayer::Silabs::Provision::Storage; + +CHIP_ERROR OTAFactoryDataProcessor::Init() +{ + mAccumulator.Init(mLength); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::Clear() +{ + OTATlvProcessor::ClearInternal(); + mAccumulator.Clear(); + mPayload.Clear(); + + return CHIP_NO_ERROR; +} + +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) + { + // The factory data payload can contain a variable number of fields + // to be updated. CHIP_END_OF_TLV is returned if no more fields are + // found. + if (error == CHIP_END_OF_TLV) + { + return CHIP_NO_ERROR; + } + + Clear(); + } + + return error; +} + +CHIP_ERROR OTAFactoryDataProcessor::ApplyAction() +{ + CHIP_ERROR error = CHIP_NO_ERROR; + + SuccessOrExit(error = Update((uint8_t) FactoryTags::kDacKey, mPayload.mCertDacKey)); + SuccessOrExit(error = Update((uint8_t) FactoryTags::kDacCert, mPayload.mCertDac)); + SuccessOrExit(error = Update((uint8_t) FactoryTags::kPaiCert, mPayload.mCertPai)); + SuccessOrExit(error = Update((uint8_t) FactoryTags::kCdCert, mPayload.mCertDeclaration)); + +exit: + if (error != CHIP_NO_ERROR) + { + ChipLogError(SoftwareUpdate, "Failed to update factory data. Error: %s", ErrorStr(error)); + } + else + { + ChipLogProgress(SoftwareUpdate, "Factory data update finished."); + } + + return error; +} + +CHIP_ERROR OTAFactoryDataProcessor::FinalizeAction() +{ + ChipLogProgress(SoftwareUpdate, "Finalize Action\n"); + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::DecodeTlv() +{ + TLV::TLVReader tlvReader; + tlvReader.Init(mAccumulator.data(), mLength); + ReturnErrorOnFailure(tlvReader.Next(TLV::TLVType::kTLVType_Structure, TLV::AnonymousTag())); + + TLV::TLVType outerType; + ReturnErrorOnFailure(tlvReader.EnterContainer(outerType)); + ReturnErrorOnFailure(tlvReader.Next()); + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) FactoryTags::kDacKey)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDacKey.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) FactoryTags::kDacCert)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDac.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) FactoryTags::kPaiCert)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertPai.Emplace())); + ReturnErrorOnFailure(tlvReader.Next()); + } + + if (tlvReader.GetTag() == TLV::ContextTag((uint8_t) FactoryTags::kCdCert)) + { + ReturnErrorOnFailure(tlvReader.Get(mPayload.mCertDeclaration.Emplace())); + } + + ReturnErrorOnFailure(tlvReader.ExitContainer(outerType)); + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OTAFactoryDataProcessor::Update(uint8_t tag, Optional & optional) +{ + CHIP_ERROR error = CHIP_NO_ERROR; + if (optional.HasValue()) + { + error = UpdateValue(tag, optional.Value()); + } + + return error; +} + +CHIP_ERROR OTAFactoryDataProcessor::UpdateValue(uint8_t tag, ByteSpan & newValue) +{ + FactoryProvider factoryProvider; + switch (tag) + { + case (int) FactoryTags::kDacKey: + ChipLogProgress(SoftwareUpdate, "Set Device Attestation Key"); + return factoryProvider.FactoryProvider::SetDeviceAttestationKey(newValue); + case (int) FactoryTags::kDacCert: + ChipLogProgress(SoftwareUpdate, "Set Device Attestation Cert"); + return factoryProvider.FactoryProvider::SetDeviceAttestationCert(newValue); + case (int) FactoryTags::kPaiCert: + ChipLogProgress(SoftwareUpdate, "Set Product Attestionation Intermediate Cert"); + return factoryProvider.FactoryProvider::SetProductAttestationIntermediateCert(newValue); + case (int) FactoryTags::kCdCert: + ChipLogProgress(SoftwareUpdate, "Set Certification Declaration"); + return factoryProvider.FactoryProvider::SetCertificationDeclaration(newValue); + } + + ChipLogError(DeviceLayer, "Failed to find tag %d.", tag); + return CHIP_ERROR_NOT_FOUND; +} + +} // namespace chip diff --git a/src/platform/silabs/multi-ota/OTAFactoryDataProcessor.h b/src/platform/silabs/multi-ota/OTAFactoryDataProcessor.h new file mode 100644 index 00000000000000..f7fd106de17220 --- /dev/null +++ b/src/platform/silabs/multi-ota/OTAFactoryDataProcessor.h @@ -0,0 +1,81 @@ +/* + * + * 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. + */ + +#pragma once + +#include +#include +#include +#include +#include // nogncheck +#include // nogncheck + +namespace chip { + +/** + * OTA custom payload that uses Matter TLVs. + * The custom payload is used when factory data needs updating. + * Factory data will be encoded using Matter TLV format to make + * use of the ChipTlv reader. A payload contains metadata (size of + * TLVs) and the TLVs themselves contained in a structure. + * If no factory data need to be updated, the metadata will be 0 + */ +struct OTAFactoryPayload +{ + Optional mCertDacKey; + Optional mCertDac; + Optional mCertPai; + Optional mCertDeclaration; + + void Clear() + { + mCertDacKey.ClearValue(); + mCertDac.ClearValue(); + mCertPai.ClearValue(); + mCertDeclaration.ClearValue(); + } +}; + +enum class FactoryTags +{ + kDacKey = 1, + kDacCert = 2, + kPaiCert = 3, + kCdCert = 4 +}; + +class OTAFactoryDataProcessor : public OTATlvProcessor +{ +public: + CHIP_ERROR Init() override; + CHIP_ERROR Clear() override; + CHIP_ERROR ApplyAction() override; + CHIP_ERROR FinalizeAction() override; + +private: + CHIP_ERROR ProcessInternal(ByteSpan & block) override; + CHIP_ERROR DecodeTlv(); + CHIP_ERROR Update(uint8_t tag, Optional & optional); + CHIP_ERROR UpdateValue(uint8_t tag, ByteSpan & newValue); + + OTAFactoryPayload mPayload; + OTADataAccumulator mAccumulator; + uint8_t * mFactoryData = nullptr; +}; + +} // namespace chip diff --git a/src/platform/silabs/multi-ota/efr32/OTAFirmwareProcessor.cpp b/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp similarity index 84% rename from src/platform/silabs/multi-ota/efr32/OTAFirmwareProcessor.cpp rename to src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp index fdf4c3d0321262..3fc66e53913b87 100644 --- a/src/platform/silabs/multi-ota/efr32/OTAFirmwareProcessor.cpp +++ b/src/platform/silabs/multi-ota/OTAFirmwareProcessor.cpp @@ -17,8 +17,8 @@ */ #include +#include #include -#include #include @@ -32,8 +32,6 @@ extern "C" { /// No error, operation OK #define SL_BOOTLOADER_OK 0L -// TODO: more descriptive error codes -#define SL_OTA_ERROR 1L namespace chip { @@ -72,7 +70,33 @@ 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); + block = mBlock; +#endif uint32_t blockReadOffset = 0; while (blockReadOffset < block.size()) @@ -88,7 +112,7 @@ CHIP_ERROR OTAFirmwareProcessor::ProcessInternal(ByteSpan & block) if (err != SL_STATUS_OK) { ChipLogError(SoftwareUpdate, "sl_wfx_host_pre_bootloader_spi_transfer() error: %ld", err); - return; + return CHIP_ERROR_CANCELLED; } #endif // SL_BTLCTRL_MUX CORE_CRITICAL_SECTION(err = bootloader_eraseWriteStorage(mSlotId, mWriteOffset, writeBuffer, kAlignmentBytes);) @@ -97,15 +121,12 @@ CHIP_ERROR OTAFirmwareProcessor::ProcessInternal(ByteSpan & block) if (err != SL_STATUS_OK) { ChipLogError(SoftwareUpdate, "sl_wfx_host_post_bootloader_spi_transfer() error: %ld", err); - return; + return CHIP_ERROR_CANCELLED; } #endif // SL_BTLCTRL_MUX if (err) { ChipLogError(SoftwareUpdate, "bootloader_eraseWriteStorage() error: %ld", err); - // TODO: add this somewhere - // imageProcessor->mDownloader->EndDownload(CHIP_ERROR_WRITE_FAILED); - // TODO: Replace CHIP_ERROR_CANCELLED with new error statement return CHIP_ERROR_CANCELLED; } mWriteOffset += kAlignmentBytes; diff --git a/src/platform/silabs/multi-ota/efr32/OTAFirmwareProcessor.h b/src/platform/silabs/multi-ota/OTAFirmwareProcessor.h similarity index 100% rename from src/platform/silabs/multi-ota/efr32/OTAFirmwareProcessor.h rename to src/platform/silabs/multi-ota/OTAFirmwareProcessor.h diff --git a/src/platform/silabs/multi-ota/efr32/OTAHooks.cpp b/src/platform/silabs/multi-ota/OTAHooks.cpp similarity index 52% rename from src/platform/silabs/multi-ota/efr32/OTAHooks.cpp rename to src/platform/silabs/multi-ota/OTAHooks.cpp index cddb66980c27f9..6be17441be473f 100644 --- a/src/platform/silabs/multi-ota/efr32/OTAHooks.cpp +++ b/src/platform/silabs/multi-ota/OTAHooks.cpp @@ -21,7 +21,12 @@ #include -#include +#include +#include + +#if OTA_TEST_CUSTOM_TLVS +#include +#endif CHIP_ERROR chip::OTAMultiImageProcessorImpl::ProcessDescriptor(void * descriptor) { @@ -34,11 +39,29 @@ CHIP_ERROR chip::OTAMultiImageProcessorImpl::ProcessDescriptor(void * descriptor CHIP_ERROR chip::OTAMultiImageProcessorImpl::OtaHookInit() { static chip::OTAFirmwareProcessor sApplicationProcessor; + static chip::OTAFactoryDataProcessor sFactoryDataProcessor; sApplicationProcessor.RegisterDescriptorCallback(ProcessDescriptor); + sFactoryDataProcessor.RegisterDescriptorCallback(ProcessDescriptor); auto & imageProcessor = chip::OTAMultiImageProcessorImpl::GetDefaultInstance(); - ReturnErrorOnFailure(imageProcessor.RegisterProcessor(1, &sApplicationProcessor)); + ReturnErrorOnFailure( + imageProcessor.RegisterProcessor(static_cast(OTAProcessorTag::kApplicationProcessor), &sApplicationProcessor)); + ReturnErrorOnFailure( + imageProcessor.RegisterProcessor(static_cast(OTAProcessorTag::kFactoryDataProcessor), &sFactoryDataProcessor)); + +#if OTA_TEST_CUSTOM_TLVS + static chip::OTACustomProcessor customProcessor1; + static chip::OTACustomProcessor customProcessor2; + static chip::OTACustomProcessor customProcessor3; + + customProcessor1.RegisterDescriptorCallback(ProcessDescriptor); + customProcessor2.RegisterDescriptorCallback(ProcessDescriptor); + customProcessor3.RegisterDescriptorCallback(ProcessDescriptor); + ReturnErrorOnFailure(imageProcessor.RegisterProcessor(8, &customProcessor1)); + ReturnErrorOnFailure(imageProcessor.RegisterProcessor(9, &customProcessor2)); + ReturnErrorOnFailure(imageProcessor.RegisterProcessor(10, &customProcessor3)); +#endif return CHIP_NO_ERROR; } diff --git a/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp b/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp index 147dcdaf8317ee..9dfb42fc879c4b 100644 --- a/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp +++ b/src/platform/silabs/multi-ota/OTAMultiImageProcessorImpl.cpp @@ -420,9 +420,6 @@ void OTAMultiImageProcessorImpl::HandleApply(intptr_t context) ChipLogProgress(SoftwareUpdate, "HandleApply: Finished"); - // TODO: check where to put this - // ConfigurationManagerImpl().StoreSoftwareUpdateCompleted(); - // This reboots the device CORE_CRITICAL_SECTION(bootloader_rebootAndInstall();) } diff --git a/src/platform/silabs/multi-ota/OTATlvProcessor.cpp b/src/platform/silabs/multi-ota/OTATlvProcessor.cpp index a5da7eaba00c10..91a748c077ec79 100644 --- a/src/platform/silabs/multi-ota/OTATlvProcessor.cpp +++ b/src/platform/silabs/multi-ota/OTATlvProcessor.cpp @@ -23,9 +23,12 @@ #include #include #if OTA_ENCRYPTION_ENABLE -#include "OtaUtils.h" -#include "rom_aes.h" +#include +#include #endif + +using namespace ::chip::DeviceLayer::Internal; + namespace chip { #if OTA_ENCRYPTION_ENABLE @@ -105,65 +108,10 @@ CHIP_ERROR OTADataAccumulator::Accumulate(ByteSpan & block) #if OTA_ENCRYPTION_ENABLE CHIP_ERROR OTATlvProcessor::vOtaProcessInternalEncryption(MutableByteSpan & block) { - uint8_t iv[16]; - uint8_t key[kOTAEncryptionKeyLength]; - 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); - - if (Encoding::HexToBytes(OTA_ENCRYPTION_KEY, strlen(OTA_ENCRYPTION_KEY), key, kOTAEncryptionKeyLength) != - kOTAEncryptionKeyLength) - { - // Failed to convert the OTAEncryptionKey string to octstr type value - return CHIP_ERROR_INVALID_STRING_LENGTH; - } - - 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; - } + uint32_t keyId; + SilabsConfig::ReadConfigValue(SilabsConfig::kOtaTlvEncryption_KeyId, keyId); + chip::DeviceLayer::Silabs::OtaTlvEncryptionKey::OtaTlvEncryptionKey key(keyId); + key.Decrypt(block, mIVOffset); return CHIP_NO_ERROR; } diff --git a/src/platform/silabs/multi-ota/OTATlvProcessor.h b/src/platform/silabs/multi-ota/OTATlvProcessor.h index 9e8e56a2b35825..fe2070b75e5634 100644 --- a/src/platform/silabs/multi-ota/OTATlvProcessor.h +++ b/src/platform/silabs/multi-ota/OTATlvProcessor.h @@ -43,6 +43,8 @@ namespace chip { #define CHIP_OTA_PROCESSOR_START_IMAGE CHIP_ERROR_TLV_PROCESSOR(0x0E) #define SL_GENERIC_OTA_ERROR CHIP_ERROR_TLV_PROCESSOR(0x0E) +constexpr uint16_t requestedOtaMaxBlockSize = 1024; + // Descriptor constants inline constexpr size_t kVersionStringSize = 64; inline constexpr size_t kBuildDateSize = 64; @@ -60,6 +62,14 @@ struct OTATlvHeader uint32_t length; }; +// TLV tags synced with ota files generate by scripts/tools/silabs/ota/ota_image_tool.py +enum class OTAProcessorTag +{ + kApplicationProcessor = 1, + kBootloaderProcessor = 2, + kFactoryDataProcessor = 3 +}; + /** * This class defines an interface for a Matter TLV processor. * Instances of derived classes can be registered as processors diff --git a/src/platform/silabs/multi-ota/OtaTlvEncryptionKey.cpp b/src/platform/silabs/multi-ota/OtaTlvEncryptionKey.cpp new file mode 100644 index 00000000000000..10273e52f5a7a1 --- /dev/null +++ b/src/platform/silabs/multi-ota/OtaTlvEncryptionKey.cpp @@ -0,0 +1,130 @@ +#include "OtaTlvEncryptionKey.h" +#include +#include +#include +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Silabs { +namespace OtaTlvEncryptionKey { + +using SilabsConfig = chip::DeviceLayer::Internal::SilabsConfig; + +int destroyAESKey(uint32_t kid) +{ + psa_key_handle_t key_handle; + + int err = psa_open_key(kid, &key_handle); + if (err) + { + psa_close_key(kid); + } + else + { + err = psa_destroy_key(kid); + } + return err; +} + +CHIP_ERROR OtaTlvEncryptionKey::Import(const uint8_t * key, size_t key_len) +{ + destroyAESKey(mId); + + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_status_t status; + + psa_key_id_t key_id; + psa_set_key_id(&attributes, mId); + psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attributes, 128); + psa_set_key_algorithm(&attributes, PSA_ALG_CTR); + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DECRYPT); + + status = psa_import_key(&attributes, key, key_len, &key_id); + if (status != PSA_SUCCESS) + { + printf("Failed to import a key error:%ld\n", status); + return CHIP_ERROR_INTERNAL; + } + + return CHIP_NO_ERROR; +} + +CHIP_ERROR OtaTlvEncryptionKey::Decrypt(MutableByteSpan & block, uint32_t & mIVOffset) +{ + constexpr uint8_t au8Iv[] = { 0x00, 0x00, 0x00, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x00, 0x00, 0x00, 0x00 }; + uint8_t iv[16]; + psa_cipher_operation_t operation = PSA_CIPHER_OPERATION_INIT; + psa_status_t status; + uint8_t output[PSA_BLOCK_CIPHER_BLOCK_LENGTH(PSA_KEY_TYPE_AES)]; + size_t output_len; + size_t total_output; + uint32_t u32IVCount; + uint32_t Offset = 0; + + 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); + + while (Offset + 16 <= block.size()) + { + status = psa_cipher_decrypt_setup(&operation, static_cast(mId), PSA_ALG_CTR); + if (status != PSA_SUCCESS) + { + printf("Failed to begin cipher operation error:%ld\n", status); + return CHIP_ERROR_INTERNAL; + } + + status = psa_cipher_set_iv(&operation, iv, sizeof(iv)); + if (status != PSA_SUCCESS) + { + printf("Failed to set IV error:%ld\n", status); + return CHIP_ERROR_INTERNAL; + } + + status = psa_cipher_update(&operation, static_cast(&block[Offset]), 16, output, sizeof(output), &output_len); + if (status != PSA_SUCCESS) + { + printf("Failed to update cipher operation error:%ld\n", status); + return CHIP_ERROR_INTERNAL; + } + + /* 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); + + memcpy((void *) &block[Offset], &output, output_len); + + Offset += 16; /* increment the buffer offset */ + mIVOffset += 16; + status = psa_cipher_finish(&operation, output + total_output, sizeof(output) - total_output, &total_output); + if (status != PSA_SUCCESS) + { + printf("Failed to finish cipher operation\n"); + return CHIP_ERROR_INTERNAL; + } + } + + printf("Decrypted ciphertext\n"); + + psa_cipher_abort(&operation); + + return CHIP_NO_ERROR; +} + +} // namespace OtaTlvEncryptionKey +} // namespace Silabs +} // namespace DeviceLayer +} // namespace chip diff --git a/src/platform/silabs/multi-ota/OtaTlvEncryptionKey.h b/src/platform/silabs/multi-ota/OtaTlvEncryptionKey.h new file mode 100644 index 00000000000000..919e3b1285ac80 --- /dev/null +++ b/src/platform/silabs/multi-ota/OtaTlvEncryptionKey.h @@ -0,0 +1,34 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace chip { +namespace DeviceLayer { +namespace Silabs { +namespace OtaTlvEncryptionKey { + +static constexpr uint32_t kAES_KeyId_Default = (PSA_KEY_ID_USER_MIN + 2); + +class OtaTlvEncryptionKey +{ +public: + OtaTlvEncryptionKey(uint32_t id = 0) { mId = (id > 0) ? id : kAES_KeyId_Default; } + ~OtaTlvEncryptionKey() = default; + + uint32_t GetId() { return mId; } + CHIP_ERROR Import(const uint8_t * key, size_t key_len); + CHIP_ERROR Decrypt(MutableByteSpan & block, uint32_t & mIVOffset); + +protected: + uint32_t mId = 0; +}; + +} // namespace OtaTlvEncryptionKey +} // namespace Silabs +} // namespace DeviceLayer +} // namespace chip diff --git a/third_party/silabs/efr32_sdk.gni b/third_party/silabs/efr32_sdk.gni index 08695af1fc3800..ccbae7f1ec23ff 100644 --- a/third_party/silabs/efr32_sdk.gni +++ b/third_party/silabs/efr32_sdk.gni @@ -80,6 +80,8 @@ declare_args() { # Multi-chip OTA chip_enable_multi_ota_requestor = false + chip_enable_multi_ota_encryption = false + chip_enable_ota_custom_tlv_testing = false } examples_plat_dir = "${chip_root}/examples/platform/silabs/efr32" @@ -496,6 +498,14 @@ template("efr32_sdk") { ] } + if (chip_enable_multi_ota_encryption && chip_enable_multi_ota_requestor) { + defines += [ "OTA_ENCRYPTION_ENABLE=1" ] + } + + if (chip_enable_ota_custom_tlv_testing && chip_enable_multi_ota_requestor) { + defines += [ "OTA_TEST_CUSTOM_TLVS=1" ] + } + if (defined(invoker.chip_enable_wifi) && invoker.chip_enable_wifi) { if (enable_dic) { assert(chip_enable_wifi_ipv4, "enable chip_enable_wifi_ipv4")