Skip to content

Commit

Permalink
[Silabs] Port platform specific Multi-Chip OTA work (#34440)
Browse files Browse the repository at this point in the history
* 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 <michael.rupp@silabs.com>
Date:   Fri May 10 14:26:07 2024 -0400

    remove some unwanted diffs in provision files

commit be160931dc600de7e7ead378b70d6a43c3945e46
Author: Michael Rupp <michael.rupp@silabs.com>
Date:   Fri May 10 14:24:25 2024 -0400

    revert changes to generator.project.mak

commit 14b6605887166e6d5284a61feb2bf407d850bdcf
Author: Michael Rupp <michael.rupp@silabs.com>
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 <commits@restyled.io>
  • Loading branch information
2 people authored and pull[bot] committed Sep 13, 2024
1 parent b64bcce commit 59b060e
Show file tree
Hide file tree
Showing 27 changed files with 1,145 additions and 89 deletions.
2 changes: 2 additions & 0 deletions docs/tools/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,9 @@
#include <platform/silabs/MigrationManager.h>
#include <platform/silabs/SilabsConfig.h>
#include <silabs_creds.h>
#ifdef OTA_ENCRYPTION_ENABLE
#include <platform/silabs/multi-ota/OtaTlvEncryptionKey.h>
#endif // OTA_ENCRYPTION_ENABLE
#ifdef SLI_SI91X_MCU_INTERFACE
#include <sl_si91x_common_flash_intf.h>
#else
Expand Down Expand Up @@ -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.
Expand Down
5 changes: 4 additions & 1 deletion examples/platform/silabs/provision/ProvisionStorageFlash.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@
#include <platform/CHIPDeviceConfig.h>
#include <platform/silabs/SilabsConfig.h>
#include <string.h>
#ifdef OTA_ENCRYPTION_ENABLE
#include <platform/silabs/multi-ota/OtaTlvEncryptionKey.h>
#endif // OTA_ENCRYPTION_ENABLE

using namespace chip::Credentials;

Expand Down Expand Up @@ -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)
{
Expand Down
56 changes: 56 additions & 0 deletions scripts/tools/silabs/factory_data_generator/README.md
Original file line number Diff line number Diff line change
@@ -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 <unique key identifier>
def length(self):
return <actual length of data>
def encode(self):
return <data as encoded bytes>
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.")
```
106 changes: 106 additions & 0 deletions scripts/tools/silabs/factory_data_generator/custom.py
Original file line number Diff line number Diff line change
@@ -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 <unique key identifier>
def length(self):
return <actual length of data>
def encode(self):
return <data as encoded bytes>
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
131 changes: 131 additions & 0 deletions scripts/tools/silabs/factory_data_generator/default.py
Original file line number Diff line number Diff line change
@@ -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
15 changes: 6 additions & 9 deletions scripts/tools/silabs/ota/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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**:

Expand All @@ -38,19 +37,17 @@ 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.
--dac_cert --> DAC certificate.
--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
```
Expand All @@ -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.
Binary file not shown.
Loading

0 comments on commit 59b060e

Please sign in to comment.