Skip to content

Commit 6fcfdd3

Browse files
committed
Merge branch 'update/nvs_partition_util' into 'master'
Update for creation of unique filename for encryption keys See merge request idf/esp-idf!3808
2 parents 85fb54a + cd8c426 commit 6fcfdd3

File tree

4 files changed

+414
-181
lines changed

4 files changed

+414
-181
lines changed

components/nvs_flash/nvs_partition_generator/README.rst

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ Prerequisites
1212
To use this utility in encryption mode, the following packages need to be installed:
1313
- cryptography package
1414

15-
This dependency is already captured by including these packages in `requirement.txt` in top level IDF directory.
15+
These dependencies is already captured by including these packages in `requirement.txt` in top level IDF directory.
1616

1717
CSV file format
1818
---------------
@@ -28,7 +28,7 @@ Type
2828
Encoding
2929
Supported values are: ``u8``, ``i8``, ``u16``, ``u32``, ``i32``, ``string``, ``hex2bin``, ``base64`` and ``binary``. This specifies how actual data values are encoded in the resultant binary file. Difference between ``string`` and ``binary`` encoding is that ``string`` data is terminated with a NULL character, whereas ``binary`` data is not.
3030

31-
.. note:: For ``file`` type, only ``hex2bin``, ``base64``, ``string`` and ``binary`` is supported as of now.
31+
.. note:: For ``file`` type, only ``hex2bin``, ``base64``, ``string`` and ``binary`` is supported as of now.
3232

3333
Value
3434
Data value.
@@ -44,7 +44,7 @@ Below is an example dump of such CSV file::
4444
key1,data,u8,1
4545
key2,file,string,/path/to/file
4646

47-
.. note:: Make sure there are no spaces before and after ',' in CSV file.
47+
.. note:: Make sure there are no spaces before and after ',' or at the end of each line in CSV file.
4848

4949
NVS Entry and Namespace association
5050
-----------------------------------
@@ -71,7 +71,7 @@ Running the utility
7171
python nvs_partition_gen.py [-h] [--input INPUT] [--output OUTPUT]
7272
[--size SIZE] [--version {v1,v2}]
7373
[--keygen {true,false}] [--encrypt {true,false}]
74-
[--keyfile KEYFILE]
74+
[--keyfile KEYFILE] [--outdir OUTDIR]
7575

7676

7777
+------------------------+----------------------------------------------------------------------------------------------+
@@ -85,15 +85,14 @@ Running the utility
8585
+------------------------+----------------------------------------------------------------------------------------------+
8686
| --version {v1,v2} | Set version. Default: v2 |
8787
+------------------------+----------------------------------------------------------------------------------------------+
88-
| --keygen {true,false} | Generate keys for encryption. Creates an `encryption_keys.bin` file (in current directory). |
89-
| | Default: false |
88+
| --keygen {true,false} | Generate keys for encryption. |
9089
+------------------------+----------------------------------------------------------------------------------------------+
9190
| --encrypt {true,false} | Set encryption mode. Default: false |
9291
+------------------------+----------------------------------------------------------------------------------------------+
9392
| --keyfile KEYFILE | File having key for encryption (Applicable only if encryption mode is true) |
9493
+------------------------+----------------------------------------------------------------------------------------------+
95-
96-
94+
| --outdir OUTDIR | The output directory to store the files created (Default: current directory) |
95+
+------------------------+----------------------------------------------------------------------------------------------+
9796

9897
You can run this utility in two modes:
9998
- Default mode - Binary generated in this mode is an unencrypted binary file.
@@ -108,7 +107,7 @@ You can run this utility in two modes:
108107
python nvs_partition_gen.py [-h] --input INPUT --output OUTPUT
109108
--size SIZE [--version {v1,v2}]
110109
[--keygen {true,false}] [--encrypt {true,false}]
111-
[--keyfile KEYFILE]
110+
[--keyfile KEYFILE] [--outdir OUTDIR]
112111

113112
You can run the utility using below command::
114113

@@ -123,28 +122,34 @@ You can run the utility using below command::
123122

124123
python nvs_partition_gen.py [-h] --input INPUT --output OUTPUT
125124
--size SIZE --encrypt {true,false}
126-
--keygen {true,false} | --keyfile KEYFILE
127-
[--version {v1,v2}]
125+
--keygen {true,false} --keyfile KEYFILE
126+
[--version {v1,v2}] [--outdir OUTDIR]
128127

129128

130129
You can run the utility using below commands:
131130

131+
- By enabling generation of encryption keys::
132+
133+
python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true
134+
132135
- By taking encryption keys as an input file. A sample encryption keys binary file is provided with the utility::
133136

134137
python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keyfile testdata/sample_encryption_keys.bin
135138

136-
- By enabling generation of encryption keys::
139+
- By enabling generation of encryption keys and storing the keys in custom filename::
137140

138-
python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true
141+
python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true --keyfile encryption_keys_generated.bin
139142

143+
.. note:: If `--keygen` is given with `--keyfile` argument, generated keys will be stored in `--keyfile` file. If `--keygen` argument is absent, `--keyfile` is taken as input file having key for encryption.
140144

141145

142-
*To generate* **only** *encryption keys with this utility* ( Creates an `encryption_keys.bin` file in current directory ): ::
146+
*To generate* **only** *encryption keys with this utility*::
143147

144148
python nvs_partition_gen.py --keygen true
145149

146-
.. note:: This `encryption_keys.bin` file is compatible with NVS key-partition structure. Refer to :ref:`nvs_key_partition` for more details.
150+
This creates an `encryption_keys_<timestamp>.bin` file.
147151

152+
.. note:: This newly created file having encryption keys in `keys/` directory is compatible with NVS key-partition structure. Refer to :ref:`nvs_key_partition` for more details.
148153

149154

150155
You can also provide the format version number (in any of the two modes):
@@ -179,3 +184,4 @@ Caveats
179184
- Utility doesn't check for duplicate keys and will write data pertaining to both keys. User needs to make sure keys are distinct.
180185
- Once a new page is created, no data will be written in the space left in previous page. Fields in the CSV file need to be ordered in such a way so as to optimize memory.
181186
- 64-bit datatype is not yet supported.
187+

components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py

Lines changed: 124 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,16 @@
3131
import csv
3232
import zlib
3333
import codecs
34-
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
35-
from cryptography.hazmat.backends import default_backend
34+
import datetime
35+
import distutils.dir_util
36+
try:
37+
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
38+
from cryptography.hazmat.backends import default_backend
39+
except ImportError:
40+
print('The cryptography package is not installed.'
41+
'Please refer to the Get Started section of the ESP-IDF Programming Guide for '
42+
'setting up the required packages.')
43+
raise
3644

3745
VERSION1_PRINT = "v1 - Multipage Blob Support Disabled"
3846
VERSION2_PRINT = "v2 - Multipage Blob Support Enabled"
@@ -615,7 +623,7 @@ def write_entry(nvs_instance, key, datatype, encoding, value):
615623
if datatype == "file":
616624
abs_file_path = value
617625
if os.path.isabs(value) is False:
618-
script_dir = os.path.dirname(__file__)
626+
script_dir = os.getcwd()
619627
abs_file_path = os.path.join(script_dir, value)
620628

621629
with open(abs_file_path, 'rb') as f:
@@ -637,7 +645,8 @@ def nvs_close(nvs_instance):
637645

638646

639647
def check_input_args(input_filename=None, output_filename=None, input_part_size=None, is_key_gen=None,
640-
encrypt_mode=None, key_file=None, version_no=None, print_arg_str=None, print_encrypt_arg_str=None):
648+
encrypt_mode=None, key_file=None, version_no=None, print_arg_str=None, print_encrypt_arg_str=None,
649+
output_dir=None):
641650

642651
global version, is_encrypt_data, input_size, key_gen
643652

@@ -646,6 +655,12 @@ def check_input_args(input_filename=None, output_filename=None, input_part_size=
646655
key_gen = is_key_gen
647656
input_size = input_part_size
648657

658+
if not output_dir == os.getcwd() and (key_file and os.path.isabs(key_file)):
659+
sys.exit("Error. Cannot provide --outdir argument as --keyfile is absolute path.")
660+
661+
if not os.path.isdir(output_dir):
662+
distutils.dir_util.mkpath(output_dir)
663+
649664
if is_encrypt_data.lower() == 'true':
650665
is_encrypt_data = True
651666
elif is_encrypt_data.lower() == 'false':
@@ -668,18 +683,25 @@ def check_input_args(input_filename=None, output_filename=None, input_part_size=
668683
elif any(arg is not None for arg in [input_filename, output_filename, input_size]):
669684
sys.exit(print_arg_str)
670685
else:
671-
if not input_size:
672-
if not all(arg is not None for arg in [input_filename, output_filename]):
673-
sys.exit(print_arg_str)
686+
if not (input_filename and output_filename and input_size):
687+
sys.exit(print_arg_str)
674688

675-
if is_encrypt_data and not key_gen and not key_file:
676-
sys.exit(print_encrypt_arg_str)
689+
if is_encrypt_data and not key_gen and not key_file:
690+
sys.exit(print_encrypt_arg_str)
677691

678-
if is_encrypt_data and key_gen and key_file:
679-
sys.exit(print_encrypt_arg_str)
692+
if not is_encrypt_data and key_file:
693+
sys.exit("Invalid. Cannot give --keyfile as --encrypt is set to false.")
680694

681-
if not is_encrypt_data and key_file:
682-
sys.exit("Invalid. Cannot give --keyfile as --encrypt is set to false.")
695+
if key_file:
696+
key_file_name, key_file_ext = os.path.splitext(key_file)
697+
if key_file_ext:
698+
if not key_file_ext == '.bin':
699+
sys.exit("--keyfile argument can be a filename with no extension or .bin extension only")
700+
701+
# If only one of the arguments - input_filename, output_filename, input_size is given
702+
if ((any(arg is None for arg in [input_filename, output_filename, input_size])) is True) and \
703+
((all(arg is None for arg in [input_filename, output_filename, input_size])) is False):
704+
sys.exit(print_arg_str)
683705

684706
if input_size:
685707
# Set size
@@ -695,7 +717,8 @@ def check_input_args(input_filename=None, output_filename=None, input_part_size=
695717
sys.exit("Minimum NVS partition size needed is 0x3000 bytes.")
696718

697719

698-
def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None, is_key_gen=None, encrypt_mode=None, key_file=None, version_no=None):
720+
def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None, is_key_gen=None, encrypt_mode=None,
721+
key_file=None, encr_key_prefix=None, version_no=None, output_dir=None):
699722
""" Wrapper to generate nvs partition binary
700723
701724
:param input_filename: Name of input file containing data
@@ -709,6 +732,9 @@ def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None
709732
"""
710733

711734
global key_input, key_len_needed
735+
encr_key_bin_file = None
736+
encr_keys_dir = None
737+
backslash = ['/','\\']
712738

713739
key_len_needed = 64
714740
key_input = bytearray()
@@ -720,6 +746,8 @@ def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None
720746
key_input = key_f.read(64)
721747

722748
if all(arg is not None for arg in [input_filename, output_filename, input_size]):
749+
if not os.path.isabs(output_filename) and not any(ch in output_filename for ch in backslash):
750+
output_filename = os.path.join(output_dir, '') + output_filename
723751
input_file = open(input_filename, 'rt', encoding='utf8')
724752
output_file = open(output_filename, 'wb')
725753

@@ -737,6 +765,8 @@ def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None
737765
input_file.close()
738766
output_file.close()
739767

768+
print("NVS binary created: " + output_filename)
769+
740770
if key_gen:
741771
keys_page_buf = bytearray(b'\xff') * Page.PAGE_PARAMS["max_size"]
742772
key_bytes = bytearray()
@@ -750,57 +780,87 @@ def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None
750780
crc_data = bytes(crc_data)
751781
crc = zlib.crc32(crc_data, 0xFFFFFFFF)
752782
struct.pack_into('<I', keys_page_buf, key_len, crc & 0xFFFFFFFF)
753-
with open("encryption_keys.bin",'wb') as output_keys_file:
783+
784+
if not key_file or (key_file and not os.path.isabs(key_file)):
785+
# Create encryption keys bin file with timestamp
786+
if not encr_key_prefix:
787+
timestamp = datetime.datetime.now().strftime('%m-%d_%H-%M')
788+
output_dir = os.path.join(output_dir, '')
789+
encr_keys_dir = output_dir + "keys"
790+
if not os.path.isdir(encr_keys_dir):
791+
distutils.dir_util.mkpath(encr_keys_dir)
792+
793+
# Add backslash to `keys` dir if it is not present
794+
encr_keys_dir = os.path.join(encr_keys_dir, '')
795+
796+
if key_file:
797+
key_file_name, ext = os.path.splitext(key_file)
798+
if ext:
799+
if ".bin" not in ext:
800+
sys.exit("Error: --keyfile must have .bin extension")
801+
encr_key_bin_file = os.path.basename(key_file)
802+
else:
803+
encr_key_bin_file = key_file_name + ".bin"
804+
if encr_keys_dir:
805+
encr_key_bin_file = encr_keys_dir + encr_key_bin_file
806+
else:
807+
if encr_key_prefix:
808+
encr_key_bin_file = encr_keys_dir + encr_key_prefix + "-keys" + ".bin"
809+
else:
810+
encr_key_bin_file = encr_keys_dir + "encryption_keys_" + timestamp + ".bin"
811+
812+
with open(encr_key_bin_file,'wb') as output_keys_file:
754813
output_keys_file.write(keys_page_buf)
755814

756-
print("Binary created.")
815+
print("Encryption keys binary created: " + encr_key_bin_file)
757816

758817

759818
def main():
760819
parser = argparse.ArgumentParser(description="ESP32 NVS partition generation utility")
761820
nvs_part_gen_group = parser.add_argument_group('To generate NVS partition')
762-
nvs_part_gen_group.add_argument(
763-
"--input",
764-
help="Path to CSV file to parse.",
765-
default=None)
766-
767-
nvs_part_gen_group.add_argument(
768-
"--output",
769-
help='Path to output converted binary file.',
770-
default=None)
771-
772-
nvs_part_gen_group.add_argument(
773-
"--size",
774-
help='Size of NVS Partition in bytes (must be multiple of 4096)')
775-
776-
nvs_part_gen_group.add_argument(
777-
"--version",
778-
help='Set version. Default: v2',
779-
choices=['v1','v2'],
780-
default='v2',
781-
type=str.lower)
782-
783-
keygen_action = nvs_part_gen_group.add_argument(
784-
"--keygen",
785-
help='Generate keys for encryption. Creates an `encryption_keys.bin` file. Default: false',
786-
choices=['true','false'],
787-
default='false',
788-
type=str.lower)
789-
790-
nvs_part_gen_group.add_argument(
791-
"--encrypt",
792-
help='Set encryption mode. Default: false',
793-
choices=['true','false'],
794-
default='false',
795-
type=str.lower)
796-
797-
nvs_part_gen_group.add_argument(
798-
"--keyfile",
799-
help='File having key for encryption (Applicable only if encryption mode is true)',
800-
default=None)
821+
nvs_part_gen_group.add_argument("--input",
822+
help="Path to CSV file to parse.",
823+
default=None)
824+
825+
nvs_part_gen_group.add_argument("--output",
826+
help='Path to output converted binary file.',
827+
default=None)
828+
829+
nvs_part_gen_group.add_argument("--size",
830+
help='Size of NVS Partition in bytes (must be multiple of 4096)')
831+
832+
nvs_part_gen_group.add_argument("--version",
833+
help='Set version. Default: v2',
834+
choices=['v1','v2'],
835+
default='v2',
836+
type=str.lower)
837+
838+
keygen_action_key = nvs_part_gen_group.add_argument("--keygen",
839+
help='Generate keys for encryption.',
840+
choices=['true','false'],
841+
default='false',
842+
type=str.lower)
843+
844+
nvs_part_gen_group.add_argument("--encrypt",
845+
help='Set encryption mode. Default: false',
846+
choices=['true','false'],
847+
default='false',
848+
type=str.lower)
849+
850+
keygen_action_file = nvs_part_gen_group.add_argument("--keyfile",
851+
help='File having key for encryption (Applicable only if encryption mode is true).',
852+
default=None)
853+
854+
keygen_action_dir = nvs_part_gen_group.add_argument('--outdir',
855+
dest='outdir',
856+
default=os.getcwd(),
857+
help='the output directory to store the files created\
858+
(Default: current directory)')
801859

802860
key_gen_group = parser.add_argument_group('To generate encryption keys')
803-
key_gen_group._group_actions.append(keygen_action)
861+
key_gen_group._group_actions.append(keygen_action_key)
862+
key_gen_group._group_actions.append(keygen_action_file)
863+
key_gen_group._group_actions.append(keygen_action_dir)
804864

805865
args = parser.parse_args()
806866
input_filename = args.input
@@ -810,13 +870,17 @@ def main():
810870
is_key_gen = args.keygen
811871
is_encrypt_data = args.encrypt
812872
key_file = args.keyfile
873+
output_dir_path = args.outdir
874+
encr_keys_prefix = None
813875

814-
print_arg_str = "Invalid.\nTo generate nvs partition binary --input, --output and --size arguments are mandatory.\n \
815-
To generate encryption keys --keygen argument is mandatory."
876+
print_arg_str = "Invalid.\nTo generate nvs partition binary --input, --output and --size arguments are mandatory.\
877+
\nTo generate encryption keys --keygen argument is mandatory."
816878
print_encrypt_arg_str = "Missing parameter. Enter --keyfile or --keygen."
817879

818-
check_input_args(input_filename,output_filename, part_size, is_key_gen, is_encrypt_data, key_file, version_no, print_arg_str, print_encrypt_arg_str)
819-
nvs_part_gen(input_filename, output_filename, part_size, is_key_gen, is_encrypt_data, key_file, version_no)
880+
check_input_args(input_filename,output_filename, part_size, is_key_gen, is_encrypt_data, key_file, version_no,
881+
print_arg_str, print_encrypt_arg_str, output_dir_path)
882+
nvs_part_gen(input_filename, output_filename, part_size, is_key_gen, is_encrypt_data, key_file,
883+
encr_keys_prefix, version_no, output_dir_path)
820884

821885

822886
if __name__ == "__main__":

components/nvs_flash/test_nvs_host/Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,5 +61,10 @@ clean:
6161
rm -f $(COVERAGE_FILES) *.gcov
6262
rm -rf coverage_report/
6363
rm -f coverage.info
64+
rm ../nvs_partition_generator/partition_single_page.bin
65+
rm ../nvs_partition_generator/partition_multipage_blob.bin
66+
rm ../nvs_partition_generator/partition_encrypted.bin
67+
rm ../nvs_partition_generator/partition_encrypted_using_keygen.bin
68+
rm ../nvs_partition_generator/partition_encrypted_using_keyfile.bin
6469

6570
.PHONY: clean all test long-test

0 commit comments

Comments
 (0)