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 00000000000000..5800a9860f70aa Binary files /dev/null and b/scripts/tools/silabs/ota/examples/binaries/ext_flash_ota_entry_example.bin differ 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")