Skip to content

Commit ddae949

Browse files
committed
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
1 parent 3cb0ae9 commit ddae949

25 files changed

+1138
-87
lines changed

examples/platform/silabs/provision/ProvisionStorageDefault.cpp

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
*/
1717
#include "AttestationKey.h"
1818
#include "ProvisionStorage.h"
19+
#include <platform/silabs/multi-ota/OtaTlvEncryptionKey.h>
1920
#include <credentials/examples/DeviceAttestationCredsExample.h>
2021
#include <lib/support/BytesToHex.h>
2122
#include <lib/support/CHIPMemString.h>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
# Silabs Factory Data Generator
2+
3+
## Tool implementation
4+
5+
The tool comprises of two files: `default.py`, `custom.py`
6+
7+
### `default.py`
8+
9+
Defines the base `InputArgument` class and its derived classes that will be
10+
referenced as **default classes**.
11+
12+
`InputArgument` offers an abstract interface in the form of three methods:
13+
`key()`, `length()`, `encode()`, that will be used to generate the `(K, L, V)`
14+
tuple through the public `output()` method. Each custom class should implement
15+
the abstract interface, if its direct parent does not offer a relevant
16+
implementation.
17+
18+
### `custom.py`
19+
20+
Defines classes for each argument that should generate data in the output binary
21+
(will be referenced as **custom classes**). Please note that each new class
22+
should derive from a default class, not from `InputArgument` directly.
23+
24+
### How to add a new argument
25+
26+
Example of defining a new argument class in `custom.py`:
27+
28+
```
29+
class FooArgument(BarArgument):
30+
def __init__(self, arg):
31+
super().__init__(arg)
32+
33+
def key(self):
34+
return <unique key identifier>
35+
36+
def length(self):
37+
return <actual length of data>
38+
39+
def encode(self):
40+
return <data as encoded bytes>
41+
42+
def custom_function(self):
43+
pass
44+
```
45+
46+
where `BarArgument` is one of the **default classes**. Please note that a user
47+
can define additional methods if needed (e.g. `custom_function`; also see
48+
`generate_private_key` from `DacPKey` class).
49+
50+
Then use this class in `generate.py` to create a `FooArgument` object from an
51+
option:
52+
53+
```
54+
parser.add_argument("--foo", required=True, type=FooArgument,
55+
help="[int | hex] Foo argument.")
56+
```
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) 2022 Project CHIP Authors
4+
# All rights reserved.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
19+
'''This file should contain custom classes derived any class from default.py.
20+
21+
Each class implemented here should describe an input parameter and should
22+
implement the InputArgument abstract interface, if its base class does not
23+
already offer an implementation or if there is a need of a custom behavior.
24+
25+
Example of defining a new argument class:
26+
27+
class FooArgument(IntArgument):
28+
def __init__(self, arg):
29+
super().__init__(arg)
30+
31+
def key(self):
32+
return <unique key identifier>
33+
34+
def length(self):
35+
return <actual length of data>
36+
37+
def encode(self):
38+
return <data as encoded bytes>
39+
40+
def custom_function(self):
41+
pass
42+
43+
Then use this class in generate.py to create a FooArgument object from an
44+
option:
45+
46+
parser.add_argument("--foo", required=True, type=FooArgument,
47+
help="[int | hex] Foo argument.")
48+
'''
49+
50+
from cryptography.hazmat.backends import default_backend
51+
from cryptography.hazmat.primitives.serialization import load_der_private_key
52+
from default import Base64Argument, FileArgument, IntArgument, StrArgument
53+
54+
class DacPKey(FileArgument):
55+
56+
def __init__(self, arg):
57+
super().__init__(arg)
58+
self.private_key = None
59+
60+
def key(self):
61+
return 1
62+
63+
def length(self):
64+
assert (self.private_key is not None)
65+
return len(self.private_key)
66+
67+
def encode(self):
68+
assert (self.private_key is not None)
69+
return self.private_key
70+
71+
def generate_private_key(self, password, use_sss_blob=True):
72+
if use_sss_blob:
73+
self.private_key = self.val
74+
else:
75+
keys = load_der_private_key(self.val, password, backend=default_backend())
76+
self.private_key = keys.private_numbers().private_value.to_bytes(
77+
32, byteorder='big'
78+
)
79+
80+
81+
class DacCert(FileArgument):
82+
83+
def __init__(self, arg):
84+
super().__init__(arg)
85+
86+
def key(self):
87+
return 2
88+
89+
90+
class PaiCert(FileArgument):
91+
92+
def __init__(self, arg):
93+
super().__init__(arg)
94+
95+
def key(self):
96+
return 3
97+
98+
99+
class CertDeclaration(FileArgument):
100+
101+
def __init__(self, arg):
102+
super().__init__(arg)
103+
104+
def key(self):
105+
return 4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
#!/usr/bin/env python3
2+
#
3+
# Copyright (c) 2022 Project CHIP Authors
4+
# All rights reserved.
5+
#
6+
# Licensed under the Apache License, Version 2.0 (the "License");
7+
# you may not use this file except in compliance with the License.
8+
# You may obtain a copy of the License at
9+
#
10+
# http://www.apache.org/licenses/LICENSE-2.0
11+
#
12+
# Unless required by applicable law or agreed to in writing, software
13+
# distributed under the License is distributed on an "AS IS" BASIS,
14+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
# See the License for the specific language governing permissions and
16+
# limitations under the License.
17+
#
18+
'''This file should contain default argument classes.
19+
20+
Base class is InputArgument. It defines the abstract interface to be
21+
implemented and offers a way to compute a KLV value through output().
22+
Other classes that derive InputArgument directly will be referenced
23+
as default classes throughout the docstrings.
24+
25+
The default classes should not be used to instantiate arguments.
26+
If one wants to add another argument, a custom class should be derived
27+
from one of the default classes.
28+
'''
29+
30+
import base64
31+
import logging
32+
33+
34+
class InputArgument:
35+
'''Base class for any input argument that will be added to KLV.
36+
37+
The user will define its arguments as instances of InputArgument
38+
by setting the "type" attribute of ArgumentParser add_argument to
39+
an instance of a derived class. This means that all derived classes
40+
must accept an additional "arg" parameter in the constructor. In the
41+
end, the list of arguments will be parsed into objects derived from
42+
InputArgument (or default derived classes), which decouples the object
43+
creation from its processing.
44+
45+
Abstract methods:
46+
key: Should be overwritten by final classes to return a "magic number".
47+
length: Can be overwritten by default classes to specify a default value
48+
(e.g. int arguments with a default length value of 4); can also
49+
be overwritten by final classes to specify a custom value for a
50+
certain argument.
51+
encode: Should be overwritten to generate the correct bytes array from
52+
its internal value.
53+
54+
Main usage is to iterate over an iterable entity of InputArguments and call
55+
the output() method to generate the (K, L, V) tuple. Note that the output()
56+
method should not be implemented, since its a common functionality across
57+
all InputArgument classes.
58+
'''
59+
60+
def __init__(self):
61+
self.val = None
62+
63+
def key(self):
64+
logging.error("key() should be implemented in derived classes.")
65+
66+
def length(self):
67+
logging.error("length() should be implemented in derived classes.")
68+
69+
def encode(self):
70+
logging.error("encode() should be implemented in derived classes.")
71+
72+
def output(self):
73+
out = (self.key(), self.length(), self.encode())
74+
logging.info("'{}' length: {}".format(type(self).__name__, self.length()))
75+
return out
76+
77+
78+
class IntArgument(InputArgument):
79+
80+
def __init__(self, arg):
81+
super().__init__()
82+
self.val = int(arg, 0)
83+
84+
def length(self):
85+
return 4
86+
87+
def encode(self):
88+
return self.val.to_bytes(self.length(), "little")
89+
90+
91+
class Base64Argument(InputArgument):
92+
93+
def __init__(self, arg):
94+
super().__init__()
95+
self.val = base64.b64decode(arg)
96+
97+
def length(self):
98+
return len(self.encode())
99+
100+
def encode(self):
101+
return base64.b64encode(self.val)
102+
103+
104+
class StrArgument(InputArgument):
105+
106+
def __init__(self, arg):
107+
super().__init__()
108+
self.val = str(arg)
109+
110+
def length(self):
111+
return len(self.encode())
112+
113+
def encode(self):
114+
return str.encode(self.val)
115+
116+
def max_length(self):
117+
return 32
118+
119+
120+
class FileArgument(InputArgument):
121+
122+
def __init__(self, arg):
123+
super().__init__()
124+
with open(arg, "rb") as _file:
125+
self.val = _file.read()
126+
127+
def length(self):
128+
return len(self.val)
129+
130+
def encode(self):
131+
return self.val

scripts/tools/silabs/ota/README.md

+6-9
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,7 @@ python3 ./scripts/tools/silabs/ota/ota_multi_image_tool.py create -v 0xDEAD -p 0
2525
```
2626

2727
followed by \*_custom options_- and a positional argument (should be last) that
28-
specifies the output file. Please see the `create_ota_images.sh` for some
29-
reference commands.
28+
specifies the output file.
3029

3130
The list of **custom options**:
3231

@@ -38,19 +37,17 @@ The list of **custom options**:
3837
--app-version-str --> Application version string. Same as above.
3938
--app-build-date --> Application build date. Same as above.
4039
41-
# SSBL options
42-
--bl-input-file --> Path to the SSBL binary.
43-
--bl-version --> SSBL version.
44-
--bl-version-str --> SSBL version string.
45-
--bl-build-date --> SSBL build date.
46-
4740
# Factory data options
4841
--factory-data --> If set, enables the generation of factory data.
4942
--cert_declaration --> Certification Declaration.
5043
--dac_cert --> DAC certificate.
5144
--dac_key --> DAC private key.
5245
--pai_cert --> PAI certificate.
5346
47+
# Encryption options
48+
--enc_enable --> Enable ota encryption
49+
--input_ota_key --> 16 Byte AES key
50+
5451
# Custom TLV options
5552
--json --> Path to a JSON file following ota_payload.schema
5653
```
@@ -67,5 +64,5 @@ processing.
6764
When defining a custom processor, a user is able to also specify the custom
6865
format of the TLV by creating a JSON file based on the `ota_payload.schema`. The
6966
tool offers support for describing multiple TLV in the same JSON file. Please
70-
see the `examples/ota_max_entries_example.json` for a multi-app + SSBL example.
67+
see the `examples/ota_custom_entries_example.json` for a multi-binary example.
7168
Option `--json` must be used to specify the path to the JSON file.
Binary file not shown.

0 commit comments

Comments
 (0)