Merge branch 'update/nvs_part_util_v3.2' into 'release/v3.2'

Update/nvs partition utility v3.2 (backport v3.2)

See merge request idf/esp-idf!4280
This commit is contained in:
Angus Gratton 2019-04-02 11:58:56 +08:00
commit b87e440c69
5 changed files with 454 additions and 244 deletions

View file

@ -12,7 +12,7 @@ Prerequisites
To use this utility in encryption mode, the following packages need to be installed: To use this utility in encryption mode, the following packages need to be installed:
- cryptography package - cryptography package
This dependency is already captured by including these packages in `requirement.txt` in top level IDF directory. These dependencies is already captured by including these packages in `requirement.txt` in top level IDF directory.
CSV file format CSV file format
--------------- ---------------
@ -28,7 +28,7 @@ Type
Encoding Encoding
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. 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.
.. note:: For ``file`` type, only ``hex2bin``, ``base64``, ``string`` and ``binary`` is supported as of now. .. note:: For ``file`` type, only ``hex2bin``, ``base64``, ``string`` and ``binary`` is supported as of now.
Value Value
Data value. Data value.
@ -44,7 +44,7 @@ Below is an example dump of such CSV file::
key1,data,u8,1 key1,data,u8,1
key2,file,string,/path/to/file key2,file,string,/path/to/file
.. note:: Make sure there are no spaces before and after ',' in CSV file. .. note:: Make sure there are no spaces before and after ',' or at the end of each line in CSV file.
NVS Entry and Namespace association NVS Entry and Namespace association
----------------------------------- -----------------------------------
@ -71,7 +71,7 @@ Running the utility
python nvs_partition_gen.py [-h] [--input INPUT] [--output OUTPUT] python nvs_partition_gen.py [-h] [--input INPUT] [--output OUTPUT]
[--size SIZE] [--version {v1,v2}] [--size SIZE] [--version {v1,v2}]
[--keygen {true,false}] [--encrypt {true,false}] [--keygen {true,false}] [--encrypt {true,false}]
[--keyfile KEYFILE] [--keyfile KEYFILE] [--outdir OUTDIR]
+------------------------+----------------------------------------------------------------------------------------------+ +------------------------+----------------------------------------------------------------------------------------------+
@ -85,15 +85,14 @@ Running the utility
+------------------------+----------------------------------------------------------------------------------------------+ +------------------------+----------------------------------------------------------------------------------------------+
| --version {v1,v2} | Set version. Default: v2 | | --version {v1,v2} | Set version. Default: v2 |
+------------------------+----------------------------------------------------------------------------------------------+ +------------------------+----------------------------------------------------------------------------------------------+
| --keygen {true,false} | Generate keys for encryption. Creates an `encryption_keys.bin` file (in current directory). | | --keygen {true,false} | Generate keys for encryption. |
| | Default: false |
+------------------------+----------------------------------------------------------------------------------------------+ +------------------------+----------------------------------------------------------------------------------------------+
| --encrypt {true,false} | Set encryption mode. Default: false | | --encrypt {true,false} | Set encryption mode. Default: false |
+------------------------+----------------------------------------------------------------------------------------------+ +------------------------+----------------------------------------------------------------------------------------------+
| --keyfile KEYFILE | File having key for encryption (Applicable only if encryption mode is true) | | --keyfile KEYFILE | File having key for encryption (Applicable only if encryption mode is true) |
+------------------------+----------------------------------------------------------------------------------------------+ +------------------------+----------------------------------------------------------------------------------------------+
| --outdir OUTDIR | The output directory to store the files created (Default: current directory) |
+------------------------+----------------------------------------------------------------------------------------------+
You can run this utility in two modes: You can run this utility in two modes:
- Default mode - Binary generated in this mode is an unencrypted binary file. - Default mode - Binary generated in this mode is an unencrypted binary file.
@ -108,7 +107,7 @@ You can run this utility in two modes:
python nvs_partition_gen.py [-h] --input INPUT --output OUTPUT python nvs_partition_gen.py [-h] --input INPUT --output OUTPUT
--size SIZE [--version {v1,v2}] --size SIZE [--version {v1,v2}]
[--keygen {true,false}] [--encrypt {true,false}] [--keygen {true,false}] [--encrypt {true,false}]
[--keyfile KEYFILE] [--keyfile KEYFILE] [--outdir OUTDIR]
You can run the utility using below command:: You can run the utility using below command::
@ -123,28 +122,34 @@ You can run the utility using below command::
python nvs_partition_gen.py [-h] --input INPUT --output OUTPUT python nvs_partition_gen.py [-h] --input INPUT --output OUTPUT
--size SIZE --encrypt {true,false} --size SIZE --encrypt {true,false}
--keygen {true,false} | --keyfile KEYFILE --keygen {true,false} --keyfile KEYFILE
[--version {v1,v2}] [--version {v1,v2}] [--outdir OUTDIR]
You can run the utility using below commands: You can run the utility using below commands:
- By taking encryption keys as an input file. A sample encryption keys binary file is provided with the utility::
python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keyfile testdata/sample_encryption_keys.bin
- By enabling generation of encryption keys:: - By enabling generation of encryption keys::
python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true
- By taking encryption keys as an input file. A sample encryption keys binary file is provided with the utility::
python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keyfile testdata/sample_encryption_keys.bin
- By enabling generation of encryption keys and storing the keys in custom filename::
python nvs_partition_gen.py --input sample.csv --output sample_encrypted.bin --size 0x3000 --encrypt true --keygen true --keyfile encryption_keys_generated.bin
.. 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.
*To generate* **only** *encryption keys with this utility* ( Creates an `encryption_keys.bin` file in current directory ): :: *To generate* **only** *encryption keys with this utility*::
python nvs_partition_gen.py --keygen true python nvs_partition_gen.py --keygen true
.. note:: This `encryption_keys.bin` file is compatible with NVS key-partition structure. Refer to :ref:`nvs_key_partition` for more details. This creates an `encryption_keys_<timestamp>.bin` file.
.. 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.
You can also provide the format version number (in any of the two modes): You can also provide the format version number (in any of the two modes):
@ -179,3 +184,4 @@ Caveats
- Utility doesn't check for duplicate keys and will write data pertaining to both keys. User needs to make sure keys are distinct. - Utility doesn't check for duplicate keys and will write data pertaining to both keys. User needs to make sure keys are distinct.
- 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. - 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.
- 64-bit datatype is not yet supported. - 64-bit datatype is not yet supported.

View file

@ -31,14 +31,24 @@ import array
import csv import csv
import zlib import zlib
import codecs import codecs
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes import datetime
from cryptography.hazmat.backends import default_backend import distutils.dir_util
try:
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
except ImportError:
print('The cryptography package is not installed.'
'Please refer to the Get Started section of the ESP-IDF Programming Guide for '
'setting up the required packages.')
raise
VERSION1_PRINT = "v1 - Multipage Blob Support Disabled" VERSION1_PRINT = "v1 - Multipage Blob Support Disabled"
VERSION2_PRINT = "v2 - Multipage Blob Support Enabled" VERSION2_PRINT = "v2 - Multipage Blob Support Enabled"
""" Class for standard NVS page structure """ """ Class for standard NVS page structure """
class Page(object): class Page(object):
PAGE_PARAMS = { PAGE_PARAMS = {
"max_size": 4096, "max_size": 4096,
@ -68,8 +78,8 @@ class Page(object):
CHUNK_ANY = 0xFF CHUNK_ANY = 0xFF
ACTIVE = 0xFFFFFFFE ACTIVE = 0xFFFFFFFE
FULL = 0xFFFFFFFC FULL = 0xFFFFFFFC
VERSION1=0xFF VERSION1 = 0xFF
VERSION2=0xFE VERSION2 = 0xFE
def __init__(self, page_num, is_rsrv_page=False): def __init__(self, page_num, is_rsrv_page=False):
self.entry_num = 0 self.entry_num = 0
@ -77,7 +87,7 @@ class Page(object):
self.encr_key = None self.encr_key = None
self.bitmap_array = array.array('B') self.bitmap_array = array.array('B')
self.version = Page.VERSION2 self.version = Page.VERSION2
self.page_buf = bytearray(b'\xff')*Page.PAGE_PARAMS["max_size"] self.page_buf = bytearray(b'\xff') * Page.PAGE_PARAMS["max_size"]
if not is_rsrv_page: if not is_rsrv_page:
self.bitmap_array = self.create_bitmap_array() self.bitmap_array = self.create_bitmap_array()
self.set_header(page_num) self.set_header(page_num)
@ -86,7 +96,7 @@ class Page(object):
global page_header global page_header
# set page state to active # set page state to active
page_header= bytearray(b'\xff') *32 page_header = bytearray(b'\xff') * 32
page_state_active_seq = Page.ACTIVE page_state_active_seq = Page.ACTIVE
struct.pack_into('<I', page_header, 0, page_state_active_seq) struct.pack_into('<I', page_header, 0, page_state_active_seq)
# set page sequence number # set page sequence number
@ -102,26 +112,23 @@ class Page(object):
struct.pack_into('<I', page_header, 28, crc & 0xFFFFFFFF) struct.pack_into('<I', page_header, 28, crc & 0xFFFFFFFF)
self.page_buf[0:len(page_header)] = page_header self.page_buf[0:len(page_header)] = page_header
def create_bitmap_array(self): def create_bitmap_array(self):
bitarray = array.array('B') bitarray = array.array('B')
charsize = 32 # bitmaparray has 256 bits, hence 32 bytes charsize = 32 # bitmaparray has 256 bits, hence 32 bytes
fill = 255 # Fill all 8 bits with 1's fill = 255 # Fill all 8 bits with 1's
bitarray.extend((fill,) * charsize) bitarray.extend((fill,) * charsize)
return bitarray return bitarray
def write_bitmaparray(self): def write_bitmaparray(self):
bitnum = self.entry_num * 2 bitnum = self.entry_num * 2
byte_idx = bitnum // 8 # Find byte index in the array byte_idx = bitnum // 8 # Find byte index in the array
bit_offset = bitnum & 7 # Find bit offset in given byte index bit_offset = bitnum & 7 # Find bit offset in given byte index
mask = ~(1 << bit_offset) mask = ~(1 << bit_offset)
self.bitmap_array[byte_idx] &= mask self.bitmap_array[byte_idx] &= mask
start_idx = Page.BITMAPARRAY_OFFSET start_idx = Page.BITMAPARRAY_OFFSET
end_idx = Page.BITMAPARRAY_OFFSET + Page.BITMAPARRAY_SIZE_IN_BYTES end_idx = Page.BITMAPARRAY_OFFSET + Page.BITMAPARRAY_SIZE_IN_BYTES
self.page_buf[start_idx:end_idx] = self.bitmap_array self.page_buf[start_idx:end_idx] = self.bitmap_array
def encrypt_entry(self, data_arr, tweak_arr, encr_key): def encrypt_entry(self, data_arr, tweak_arr, encr_key):
# Encrypt 32 bytes of data using AES-XTS encryption # Encrypt 32 bytes of data using AES-XTS encryption
backend = default_backend() backend = default_backend()
@ -134,28 +141,25 @@ class Page(object):
return encrypted_data return encrypted_data
def reverse_hexbytes(self, addr_tmp): def reverse_hexbytes(self, addr_tmp):
addr = [] addr = []
reversed_bytes = "" reversed_bytes = ""
for i in range(0, len(addr_tmp), 2): for i in range(0, len(addr_tmp), 2):
addr.append(addr_tmp[i:i+2]) addr.append(addr_tmp[i:i + 2])
reversed_bytes = "".join(reversed(addr)) reversed_bytes = "".join(reversed(addr))
return reversed_bytes return reversed_bytes
def encrypt_data(self, data_input, no_of_entries, nvs_obj): def encrypt_data(self, data_input, no_of_entries, nvs_obj):
# Set values needed for encryption and encrypt data byte wise # Set values needed for encryption and encrypt data byte wise
encr_data_to_write = bytearray() encr_data_to_write = bytearray()
data_len_needed = 64 #in hex data_len_needed = 64 # in hex
tweak_len_needed = 32 #in hex tweak_len_needed = 32 # in hex
init_tweak_val = '0' init_tweak_val = '0'
init_data_val = 'f' init_data_val = 'f'
tweak_tmp = '' tweak_tmp = ''
encr_key_input = None encr_key_input = None
# Extract encryption key and tweak key from given key input # Extract encryption key and tweak key from given key input
if len(self.encr_key) == key_len_needed: if len(self.encr_key) == key_len_needed:
encr_key_input = self.encr_key encr_key_input = self.encr_key
@ -207,7 +211,6 @@ class Page(object):
return encr_data_to_write return encr_data_to_write
def write_entry_to_buf(self, data, entrycount,nvs_obj): def write_entry_to_buf(self, data, entrycount,nvs_obj):
encr_data = bytearray() encr_data = bytearray()
@ -226,7 +229,6 @@ class Page(object):
self.write_bitmaparray() self.write_bitmaparray()
self.entry_num += 1 self.entry_num += 1
def set_crc_header(self, entry_struct): def set_crc_header(self, entry_struct):
crc_data = bytearray(b'28') crc_data = bytearray(b'28')
crc_data[0:4] = entry_struct[0:4] crc_data[0:4] = entry_struct[0:4]
@ -236,7 +238,6 @@ class Page(object):
struct.pack_into('<I', entry_struct, 4, crc & 0xFFFFFFFF) struct.pack_into('<I', entry_struct, 4, crc & 0xFFFFFFFF)
return entry_struct return entry_struct
def write_varlen_binary_data(self, entry_struct, ns_index, key, data, data_size, total_entry_count, encoding, nvs_obj): def write_varlen_binary_data(self, entry_struct, ns_index, key, data, data_size, total_entry_count, encoding, nvs_obj):
chunk_start = 0 chunk_start = 0
chunk_count = 0 chunk_count = 0
@ -250,7 +251,7 @@ class Page(object):
# Get the size available in current page # Get the size available in current page
tailroom = (Page.PAGE_PARAMS["max_entries"] - self.entry_num - 1) * Page.SINGLE_ENTRY_SIZE tailroom = (Page.PAGE_PARAMS["max_entries"] - self.entry_num - 1) * Page.SINGLE_ENTRY_SIZE
assert tailroom >=0, "Page overflow!!" assert tailroom >= 0, "Page overflow!!"
# Split the binary data into two and store a chunk of available size onto curr page # Split the binary data into two and store a chunk of available size onto curr page
if tailroom < remaining_size: if tailroom < remaining_size:
@ -266,7 +267,7 @@ class Page(object):
# Calculate no. of entries data chunk will require # Calculate no. of entries data chunk will require
datachunk_rounded_size = (chunk_size + 31) & ~31 datachunk_rounded_size = (chunk_size + 31) & ~31
datachunk_entry_count = datachunk_rounded_size // 32 datachunk_entry_count = datachunk_rounded_size // 32
datachunk_total_entry_count = datachunk_entry_count + 1 # +1 for the entry header datachunk_total_entry_count = datachunk_entry_count + 1 # +1 for the entry header
# Set Span # Set Span
entry_struct[2] = datachunk_total_entry_count entry_struct[2] = datachunk_total_entry_count
@ -276,7 +277,7 @@ class Page(object):
entry_struct[3] = chunk_index entry_struct[3] = chunk_index
# Set data chunk # Set data chunk
data_chunk = data[offset:offset + chunk_size] data_chunk = data[offset:offset + chunk_size]
# Compute CRC of data chunk # Compute CRC of data chunk
struct.pack_into('<H', entry_struct, 24, chunk_size) struct.pack_into('<H', entry_struct, 24, chunk_size)
@ -306,11 +307,10 @@ class Page(object):
offset = offset + chunk_size offset = offset + chunk_size
# All chunks are stored, now store the index # All chunks are stored, now store the index
if not remaining_size: if not remaining_size:
# Initialise data field to 0xff # Initialise data field to 0xff
data_array = bytearray(b'\xff')*8 data_array = bytearray(b'\xff') * 8
entry_struct[24:32] = data_array entry_struct[24:32] = data_array
# change type of data to BLOB_IDX # change type of data to BLOB_IDX
@ -336,7 +336,6 @@ class Page(object):
return entry_struct return entry_struct
def write_single_page_entry(self, entry_struct, data, datalen, data_entry_count, nvs_obj): def write_single_page_entry(self, entry_struct, data, datalen, data_entry_count, nvs_obj):
# compute CRC of data # compute CRC of data
struct.pack_into('<H', entry_struct, 24, datalen) struct.pack_into('<H', entry_struct, 24, datalen)
@ -355,7 +354,6 @@ class Page(object):
# write actual data # write actual data
self.write_entry_to_buf(data, data_entry_count, nvs_obj) self.write_entry_to_buf(data, data_entry_count, nvs_obj)
""" """
Low-level function to write variable length data into page buffer. Data should be formatted Low-level function to write variable length data into page buffer. Data should be formatted
according to encoding specified. according to encoding specified.
@ -364,32 +362,27 @@ class Page(object):
# Set size of data # Set size of data
datalen = len(data) datalen = len(data)
if version == Page.VERSION1: if datalen > Page.PAGE_PARAMS["max_old_blob_size"]:
if datalen > Page.PAGE_PARAMS["max_old_blob_size"]: if version == Page.VERSION1:
raise InputError("Version %s\n%s: Size exceeds max allowed length." % (VERSION1_PRINT,key)) raise InputError("Version %s\n%s: Size exceeds max allowed length." % (VERSION1_PRINT,key))
else:
if version == Page.VERSION2: if encoding == "string":
if encoding == "string":
if datalen > Page.PAGE_PARAMS["max_new_blob_size"]:
raise InputError("Version %s\n%s: Size exceeds max allowed length." % (VERSION2_PRINT,key)) raise InputError("Version %s\n%s: Size exceeds max allowed length." % (VERSION2_PRINT,key))
# Calculate no. of entries data will require # Calculate no. of entries data will require
rounded_size = (datalen + 31) & ~31 rounded_size = (datalen + 31) & ~31
data_entry_count = rounded_size // 32 data_entry_count = rounded_size // 32
total_entry_count = data_entry_count + 1 # +1 for the entry header total_entry_count = data_entry_count + 1 # +1 for the entry header
# Check if page is already full and new page is needed to be created right away # Check if page is already full and new page is needed to be created right away
if version == Page.VERSION1: if self.entry_num >= Page.PAGE_PARAMS["max_entries"]:
if encoding in ["string", "hex2bin", "binary", "base64"]: raise PageFullError()
if (self.entry_num + total_entry_count) >= Page.PAGE_PARAMS["max_entries"]: elif (self.entry_num + total_entry_count) >= Page.PAGE_PARAMS["max_entries"]:
raise PageFullError() if not (version == Page.VERSION2 and encoding in ["hex2bin", "binary", "base64"]):
else: raise PageFullError()
if encoding == "string":
if (self.entry_num + total_entry_count) >= Page.PAGE_PARAMS["max_entries"]:
raise PageFullError()
# Entry header # Entry header
entry_struct = bytearray(b'\xff')*32 entry_struct = bytearray(b'\xff') * 32
# Set Namespace Index # Set Namespace Index
entry_struct[0] = ns_index entry_struct[0] = ns_index
# Set Span # Set Span
@ -414,27 +407,25 @@ class Page(object):
entry_struct[1] = Page.BLOB entry_struct[1] = Page.BLOB
if version == Page.VERSION2 and (encoding in ["hex2bin", "binary", "base64"]): if version == Page.VERSION2 and (encoding in ["hex2bin", "binary", "base64"]):
entry_struct = self.write_varlen_binary_data(entry_struct,ns_index,key,data,\ entry_struct = self.write_varlen_binary_data(entry_struct,ns_index,key,data,
datalen,total_entry_count, encoding, nvs_obj) datalen,total_entry_count, encoding, nvs_obj)
else: else:
self.write_single_page_entry(entry_struct, data, datalen, data_entry_count, nvs_obj) self.write_single_page_entry(entry_struct, data, datalen, data_entry_count, nvs_obj)
""" Low-level function to write data of primitive type into page buffer. """ """ Low-level function to write data of primitive type into page buffer. """
def write_primitive_data(self, key, data, encoding, ns_index,nvs_obj): def write_primitive_data(self, key, data, encoding, ns_index,nvs_obj):
# Check if entry exceeds max number of entries allowed per page # Check if entry exceeds max number of entries allowed per page
if self.entry_num >= Page.PAGE_PARAMS["max_entries"]: if self.entry_num >= Page.PAGE_PARAMS["max_entries"]:
raise PageFullError() raise PageFullError()
entry_struct = bytearray(b'\xff')*32 entry_struct = bytearray(b'\xff') * 32
entry_struct[0] = ns_index # namespace index entry_struct[0] = ns_index # namespace index
entry_struct[2] = 0x01 # Span entry_struct[2] = 0x01 # Span
chunk_index = Page.CHUNK_ANY chunk_index = Page.CHUNK_ANY
entry_struct[3] = chunk_index entry_struct[3] = chunk_index
# write key # write key
key_array = b'\x00' *16 key_array = b'\x00' * 16
entry_struct[8:24] = key_array entry_struct[8:24] = key_array
entry_struct[8:8 + len(key)] = key.encode() entry_struct[8:8 + len(key)] = key.encode()
@ -469,9 +460,13 @@ class Page(object):
def get_data(self): def get_data(self):
return self.page_buf return self.page_buf
""" """
NVS class encapsulates all NVS specific operations to create a binary with given key-value pairs. Binary can later be flashed onto device via a flashing utility. NVS class encapsulates all NVS specific operations to create a binary with given key-value pairs.
Binary can later be flashed onto device via a flashing utility.
""" """
class NVS(object): class NVS(object):
def __init__(self, fout, input_size): def __init__(self, fout, input_size):
self.size = input_size self.size = input_size
@ -485,11 +480,11 @@ class NVS(object):
return self return self
def __exit__(self, exc_type, exc_value, traceback): def __exit__(self, exc_type, exc_value, traceback):
if exc_type == None and exc_value == None: if exc_type is None and exc_value is None:
# Create pages for remaining available size # Create pages for remaining available size
while True: while True:
try: try:
new_page = self.create_new_page() self.create_new_page()
except InsufficientSizeError: except InsufficientSizeError:
self.size = None self.size = None
# Creating the last reserved page # Creating the last reserved page
@ -577,13 +572,15 @@ class NVS(object):
data += page.get_data() data += page.get_data()
return data return data
class PageFullError(RuntimeError): class PageFullError(RuntimeError):
""" """
Represents error when current page doesn't have sufficient entries left Represents error when current page doesn't have sufficient entries left
to accommodate current request to accommodate current request
""" """
def __init__(self): def __init__(self):
super(PageFullError, self).__init__() super(PageFullError, self).__init__()
class InputError(RuntimeError): class InputError(RuntimeError):
""" """
@ -592,6 +589,7 @@ class InputError(RuntimeError):
def __init__(self, e): def __init__(self, e):
super(InputError, self).__init__(e) super(InputError, self).__init__(e)
class InsufficientSizeError(RuntimeError): class InsufficientSizeError(RuntimeError):
""" """
Represents error when NVS Partition size given is insufficient Represents error when NVS Partition size given is insufficient
@ -600,6 +598,7 @@ class InsufficientSizeError(RuntimeError):
def __init__(self, e): def __init__(self, e):
super(InsufficientSizeError, self).__init__(e) super(InsufficientSizeError, self).__init__(e)
def nvs_open(result_obj, input_size): def nvs_open(result_obj, input_size):
""" Wrapper to create and NVS class object. This object can later be used to set key-value pairs """ Wrapper to create and NVS class object. This object can later be used to set key-value pairs
@ -609,6 +608,7 @@ def nvs_open(result_obj, input_size):
""" """
return NVS(result_obj, input_size) return NVS(result_obj, input_size)
def write_entry(nvs_instance, key, datatype, encoding, value): def write_entry(nvs_instance, key, datatype, encoding, value):
""" Wrapper to set key-value pair in NVS format """ Wrapper to set key-value pair in NVS format
@ -622,8 +622,8 @@ def write_entry(nvs_instance, key, datatype, encoding, value):
if datatype == "file": if datatype == "file":
abs_file_path = value abs_file_path = value
if os.path.isabs(value) == False: if os.path.isabs(value) is False:
script_dir = os.path.dirname(__file__) script_dir = os.getcwd()
abs_file_path = os.path.join(script_dir, value) abs_file_path = os.path.join(script_dir, value)
with open(abs_file_path, 'rb') as f: with open(abs_file_path, 'rb') as f:
@ -634,6 +634,7 @@ def write_entry(nvs_instance, key, datatype, encoding, value):
else: else:
nvs_instance.write_entry(key, value, encoding) nvs_instance.write_entry(key, value, encoding)
def nvs_close(nvs_instance): def nvs_close(nvs_instance):
""" Wrapper to finish writing to NVS and write data to file/stream object provided to nvs_open method """ Wrapper to finish writing to NVS and write data to file/stream object provided to nvs_open method
@ -643,8 +644,9 @@ def nvs_close(nvs_instance):
nvs_instance.__exit__(None, None, None) nvs_instance.__exit__(None, None, None)
def check_input_args(input_filename=None, output_filename=None, input_part_size=None, is_key_gen=None,\ def check_input_args(input_filename=None, output_filename=None, input_part_size=None, is_key_gen=None,
encrypt_mode=None, key_file=None, version_no=None, print_arg_str=None, print_encrypt_arg_str=None): encrypt_mode=None, key_file=None, version_no=None, print_arg_str=None, print_encrypt_arg_str=None,
output_dir=None):
global version, is_encrypt_data, input_size, key_gen global version, is_encrypt_data, input_size, key_gen
@ -653,12 +655,17 @@ encrypt_mode=None, key_file=None, version_no=None, print_arg_str=None, print_enc
key_gen = is_key_gen key_gen = is_key_gen
input_size = input_part_size input_size = input_part_size
if not output_dir == os.getcwd() and (key_file and os.path.isabs(key_file)):
sys.exit("Error. Cannot provide --outdir argument as --keyfile is absolute path.")
if not os.path.isdir(output_dir):
distutils.dir_util.mkpath(output_dir)
if is_encrypt_data.lower() == 'true': if is_encrypt_data.lower() == 'true':
is_encrypt_data = True is_encrypt_data = True
elif is_encrypt_data.lower() == 'false': elif is_encrypt_data.lower() == 'false':
is_encrypt_data = False is_encrypt_data = False
if version == 'v1': if version == 'v1':
version = Page.VERSION1 version = Page.VERSION1
elif version == 'v2': elif version == 'v2':
@ -669,7 +676,6 @@ encrypt_mode=None, key_file=None, version_no=None, print_arg_str=None, print_enc
elif key_gen.lower() == 'false': elif key_gen.lower() == 'false':
key_gen = False key_gen = False
if key_gen: if key_gen:
if all(arg is not None for arg in [input_filename, output_filename, input_size]): if all(arg is not None for arg in [input_filename, output_filename, input_size]):
if not is_encrypt_data: if not is_encrypt_data:
@ -677,25 +683,31 @@ encrypt_mode=None, key_file=None, version_no=None, print_arg_str=None, print_enc
elif any(arg is not None for arg in [input_filename, output_filename, input_size]): elif any(arg is not None for arg in [input_filename, output_filename, input_size]):
sys.exit(print_arg_str) sys.exit(print_arg_str)
else: else:
if not input_size: if not (input_filename and output_filename and input_size):
if not all(arg is not None for arg in [input_filename, output_filename]): sys.exit(print_arg_str)
sys.exit(print_arg_str)
if is_encrypt_data and not key_gen and not key_file:
sys.exit(print_encrypt_arg_str)
if is_encrypt_data and not key_gen and not key_file: if not is_encrypt_data and key_file:
sys.exit(print_encrypt_arg_str) sys.exit("Invalid. Cannot give --keyfile as --encrypt is set to false.")
if is_encrypt_data and key_gen and key_file: if key_file:
sys.exit(print_encrypt_arg_str) key_file_name, key_file_ext = os.path.splitext(key_file)
if key_file_ext:
if not key_file_ext == '.bin':
sys.exit("--keyfile argument can be a filename with no extension or .bin extension only")
if not is_encrypt_data and key_file: # If only one of the arguments - input_filename, output_filename, input_size is given
sys.exit("Invalid. Cannot give --keyfile as --encrypt is set to false.") if ((any(arg is None for arg in [input_filename, output_filename, input_size])) is True) and \
((all(arg is None for arg in [input_filename, output_filename, input_size])) is False):
sys.exit(print_arg_str)
if input_size: if input_size:
# Set size # Set size
input_size = int(input_size, 0) input_size = int(input_size, 0)
if input_size % 4096 !=0: if input_size % 4096 != 0:
sys.exit("Size of partition must be multiple of 4096") sys.exit("Size of partition must be multiple of 4096")
# Update size as a page needs to be reserved of size 4KB # Update size as a page needs to be reserved of size 4KB
@ -705,9 +717,8 @@ encrypt_mode=None, key_file=None, version_no=None, print_arg_str=None, print_enc
sys.exit("Minimum NVS partition size needed is 0x3000 bytes.") sys.exit("Minimum NVS partition size needed is 0x3000 bytes.")
def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None, is_key_gen=None, encrypt_mode=None,
key_file=None, encr_key_prefix=None, version_no=None, output_dir=None):
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):
""" Wrapper to generate nvs partition binary """ Wrapper to generate nvs partition binary
:param input_filename: Name of input file containing data :param input_filename: Name of input file containing data
@ -721,6 +732,9 @@ def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None
""" """
global key_input, key_len_needed global key_input, key_len_needed
encr_key_bin_file = None
encr_keys_dir = None
backslash = ['/','\\']
key_len_needed = 64 key_len_needed = 64
key_input = bytearray() key_input = bytearray()
@ -732,6 +746,8 @@ def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None
key_input = key_f.read(64) key_input = key_f.read(64)
if all(arg is not None for arg in [input_filename, output_filename, input_size]): if all(arg is not None for arg in [input_filename, output_filename, input_size]):
if not os.path.isabs(output_filename) and not any(ch in output_filename for ch in backslash):
output_filename = os.path.join(output_dir, '') + output_filename
input_file = open(input_filename, 'rt', encoding='utf8') input_file = open(input_filename, 'rt', encoding='utf8')
output_file = open(output_filename, 'wb') output_file = open(output_filename, 'wb')
@ -749,9 +765,10 @@ def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None
input_file.close() input_file.close()
output_file.close() output_file.close()
print("NVS binary created: " + output_filename)
if key_gen: if key_gen:
keys_page_buf = bytearray(b'\xff')*Page.PAGE_PARAMS["max_size"] keys_page_buf = bytearray(b'\xff') * Page.PAGE_PARAMS["max_size"]
key_bytes = bytearray() key_bytes = bytearray()
if len(key_input) == key_len_needed: if len(key_input) == key_len_needed:
key_bytes = key_input key_bytes = key_input
@ -763,57 +780,87 @@ def nvs_part_gen(input_filename=None, output_filename=None, input_part_size=None
crc_data = bytes(crc_data) crc_data = bytes(crc_data)
crc = zlib.crc32(crc_data, 0xFFFFFFFF) crc = zlib.crc32(crc_data, 0xFFFFFFFF)
struct.pack_into('<I', keys_page_buf, key_len, crc & 0xFFFFFFFF) struct.pack_into('<I', keys_page_buf, key_len, crc & 0xFFFFFFFF)
with open("encryption_keys.bin",'wb') as output_keys_file:
if not key_file or (key_file and not os.path.isabs(key_file)):
# Create encryption keys bin file with timestamp
if not encr_key_prefix:
timestamp = datetime.datetime.now().strftime('%m-%d_%H-%M')
output_dir = os.path.join(output_dir, '')
encr_keys_dir = output_dir + "keys"
if not os.path.isdir(encr_keys_dir):
distutils.dir_util.mkpath(encr_keys_dir)
# Add backslash to `keys` dir if it is not present
encr_keys_dir = os.path.join(encr_keys_dir, '')
if key_file:
key_file_name, ext = os.path.splitext(key_file)
if ext:
if ".bin" not in ext:
sys.exit("Error: --keyfile must have .bin extension")
encr_key_bin_file = os.path.basename(key_file)
else:
encr_key_bin_file = key_file_name + ".bin"
if encr_keys_dir:
encr_key_bin_file = encr_keys_dir + encr_key_bin_file
else:
if encr_key_prefix:
encr_key_bin_file = encr_keys_dir + encr_key_prefix + "-keys" + ".bin"
else:
encr_key_bin_file = encr_keys_dir + "encryption_keys_" + timestamp + ".bin"
with open(encr_key_bin_file,'wb') as output_keys_file:
output_keys_file.write(keys_page_buf) output_keys_file.write(keys_page_buf)
print("Binary created.") print("Encryption keys binary created: " + encr_key_bin_file)
def main(): def main():
parser = argparse.ArgumentParser(description="ESP32 NVS partition generation utility") parser = argparse.ArgumentParser(description="ESP32 NVS partition generation utility")
nvs_part_gen_group = parser.add_argument_group('To generate NVS partition') nvs_part_gen_group = parser.add_argument_group('To generate NVS partition')
nvs_part_gen_group.add_argument( nvs_part_gen_group.add_argument("--input",
"--input", help="Path to CSV file to parse.",
help="Path to CSV file to parse.", default=None)
default=None)
nvs_part_gen_group.add_argument( nvs_part_gen_group.add_argument("--output",
"--output", help='Path to output converted binary file.',
help='Path to output converted binary file.', default=None)
default=None)
nvs_part_gen_group.add_argument( nvs_part_gen_group.add_argument("--size",
"--size", help='Size of NVS Partition in bytes (must be multiple of 4096)')
help='Size of NVS Partition in bytes (must be multiple of 4096)')
nvs_part_gen_group.add_argument( nvs_part_gen_group.add_argument("--version",
"--version", help='Set version. Default: v2',
help='Set version. Default: v2', choices=['v1','v2'],
choices=['v1','v2'], default='v2',
default='v2', type=str.lower)
type=str.lower)
keygen_action=nvs_part_gen_group.add_argument( keygen_action_key = nvs_part_gen_group.add_argument("--keygen",
"--keygen", help='Generate keys for encryption.',
help='Generate keys for encryption. Creates an `encryption_keys.bin` file. Default: false', choices=['true','false'],
choices=['true','false'], default='false',
default= 'false', type=str.lower)
type=str.lower)
nvs_part_gen_group.add_argument( nvs_part_gen_group.add_argument("--encrypt",
"--encrypt", help='Set encryption mode. Default: false',
help='Set encryption mode. Default: false', choices=['true','false'],
choices=['true','false'], default='false',
default='false', type=str.lower)
type=str.lower)
nvs_part_gen_group.add_argument( keygen_action_file = nvs_part_gen_group.add_argument("--keyfile",
"--keyfile", help='File having key for encryption (Applicable only if encryption mode is true).',
help='File having key for encryption (Applicable only if encryption mode is true)', default=None)
default = None)
keygen_action_dir = nvs_part_gen_group.add_argument('--outdir',
dest='outdir',
default=os.getcwd(),
help='the output directory to store the files created\
(Default: current directory)')
key_gen_group = parser.add_argument_group('To generate encryption keys') key_gen_group = parser.add_argument_group('To generate encryption keys')
key_gen_group._group_actions.append(keygen_action) key_gen_group._group_actions.append(keygen_action_key)
key_gen_group._group_actions.append(keygen_action_file)
key_gen_group._group_actions.append(keygen_action_dir)
args = parser.parse_args() args = parser.parse_args()
input_filename = args.input input_filename = args.input
@ -823,13 +870,17 @@ def main():
is_key_gen = args.keygen is_key_gen = args.keygen
is_encrypt_data = args.encrypt is_encrypt_data = args.encrypt
key_file = args.keyfile key_file = args.keyfile
output_dir_path = args.outdir
encr_keys_prefix = None
print_arg_str = "Invalid.\nTo generate nvs partition binary --input, --output and --size arguments are mandatory.\nTo generate encryption keys --keygen argument is mandatory." print_arg_str = "Invalid.\nTo generate nvs partition binary --input, --output and --size arguments are mandatory.\
\nTo generate encryption keys --keygen argument is mandatory."
print_encrypt_arg_str = "Missing parameter. Enter --keyfile or --keygen." print_encrypt_arg_str = "Missing parameter. Enter --keyfile or --keygen."
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) check_input_args(input_filename,output_filename, part_size, is_key_gen, is_encrypt_data, key_file, version_no,
nvs_part_gen(input_filename, output_filename, part_size, is_key_gen, is_encrypt_data, key_file, version_no) print_arg_str, print_encrypt_arg_str, output_dir_path)
nvs_part_gen(input_filename, output_filename, part_size, is_key_gen, is_encrypt_data, key_file,
encr_keys_prefix, version_no, output_dir_path)
if __name__ == "__main__": if __name__ == "__main__":

View file

@ -61,5 +61,10 @@ clean:
rm -f $(COVERAGE_FILES) *.gcov rm -f $(COVERAGE_FILES) *.gcov
rm -rf coverage_report/ rm -rf coverage_report/
rm -f coverage.info rm -f coverage.info
rm ../nvs_partition_generator/partition_single_page.bin
rm ../nvs_partition_generator/partition_multipage_blob.bin
rm ../nvs_partition_generator/partition_encrypted.bin
rm ../nvs_partition_generator/partition_encrypted_using_keygen.bin
rm ../nvs_partition_generator/partition_encrypted_using_keyfile.bin
.PHONY: clean all test long-test .PHONY: clean all test long-test

View file

@ -21,8 +21,10 @@
#include <sstream> #include <sstream>
#include <iostream> #include <iostream>
#include <fstream> #include <fstream>
#include <dirent.h>
#include <unistd.h> #include <unistd.h>
#include <sys/wait.h> #include <sys/wait.h>
#include <string.h>
#define TEST_ESP_ERR(rc, res) CHECK((rc) == (res)) #define TEST_ESP_ERR(rc, res) CHECK((rc) == (res))
#define TEST_ESP_OK(rc) CHECK((rc) == ESP_OK) #define TEST_ESP_OK(rc) CHECK((rc) == ESP_OK)
@ -825,7 +827,7 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]")
extern "C" void nvs_dump(const char *partName); extern "C" void nvs_dump(const char *partName);
class RandomTest { class RandomTest {
static const size_t nKeys = 11; static const size_t nKeys = 11;
int32_t v1 = 0, v2 = 0; int32_t v1 = 0, v2 = 0;
uint64_t v3 = 0, v4 = 0; uint64_t v3 = 0, v4 = 0;
@ -844,12 +846,12 @@ public:
template<typename TGen> template<typename TGen>
esp_err_t doRandomThings(nvs_handle handle, TGen gen, size_t& count) { esp_err_t doRandomThings(nvs_handle handle, TGen gen, size_t& count) {
const char* keys[] = {"foo", "bar", "longkey_0123456", "another key", "param1", "param2", "param3", "param4", "param5", "singlepage", "multipage"}; const char* keys[] = {"foo", "bar", "longkey_0123456", "another key", "param1", "param2", "param3", "param4", "param5", "singlepage", "multipage"};
const ItemType types[] = {ItemType::I32, ItemType::I32, ItemType::U64, ItemType::U64, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::BLOB, ItemType::BLOB}; const ItemType types[] = {ItemType::I32, ItemType::I32, ItemType::U64, ItemType::U64, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::SZ, ItemType::BLOB, ItemType::BLOB};
void* values[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9, &v10, &v11}; void* values[] = {&v1, &v2, &v3, &v4, &v5, &v6, &v7, &v8, &v9, &v10, &v11};
const size_t nKeys = sizeof(keys) / sizeof(keys[0]); const size_t nKeys = sizeof(keys) / sizeof(keys[0]);
static_assert(nKeys == sizeof(types) / sizeof(types[0]), ""); static_assert(nKeys == sizeof(types) / sizeof(types[0]), "");
static_assert(nKeys == sizeof(values) / sizeof(values[0]), ""); static_assert(nKeys == sizeof(values) / sizeof(values[0]), "");
@ -1065,7 +1067,7 @@ public:
return ESP_OK; return ESP_OK;
} }
esp_err_t handleExternalWriteAtIndex(uint8_t index, const void* value, const size_t len ) { esp_err_t handleExternalWriteAtIndex(uint8_t index, const void* value, const size_t len ) {
if(index == 9) { /* This is only done for small-page blobs for now*/ if(index == 9) { /* This is only done for small-page blobs for now*/
if(len > smallBlobLen) { if(len > smallBlobLen) {
return ESP_FAIL; return ESP_FAIL;
@ -1076,7 +1078,7 @@ public:
} else { } else {
return ESP_FAIL; return ESP_FAIL;
} }
} }
}; };
TEST_CASE("monkey test", "[nvs][monkey]") TEST_CASE("monkey test", "[nvs][monkey]")
@ -1089,7 +1091,7 @@ TEST_CASE("monkey test", "[nvs][monkey]")
SpiFlashEmulator emu(10); SpiFlashEmulator emu(10);
emu.randomize(seed); emu.randomize(seed);
emu.clearStats(); emu.clearStats();
const uint32_t NVS_FLASH_SECTOR = 2; const uint32_t NVS_FLASH_SECTOR = 2;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
@ -1114,10 +1116,10 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]")
const size_t iter_count = 2000; const size_t iter_count = 2000;
SpiFlashEmulator emu(10); SpiFlashEmulator emu(10);
const uint32_t NVS_FLASH_SECTOR = 2; const uint32_t NVS_FLASH_SECTOR = 2;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
size_t totalOps = 0; size_t totalOps = 0;
@ -1786,7 +1788,7 @@ TEST_CASE("nvs code handles errors properly when partition is near to full", "[n
SpiFlashEmulator emu(5); SpiFlashEmulator emu(5);
Storage storage; Storage storage;
char nvs_key[16] = ""; char nvs_key[16] = "";
TEST_ESP_OK(storage.init(0, 5)); TEST_ESP_OK(storage.init(0, 5));
/* Four pages should fit roughly 12 blobs*/ /* Four pages should fit roughly 12 blobs*/
@ -1794,7 +1796,7 @@ TEST_CASE("nvs code handles errors properly when partition is near to full", "[n
sprintf(nvs_key, "key:%u", count); sprintf(nvs_key, "key:%u", count);
TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob))); TEST_ESP_OK(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob)));
} }
for(uint8_t count = 13; count <= 20; count++) { for(uint8_t count = 13; count <= 20; count++) {
sprintf(nvs_key, "key:%u", count); sprintf(nvs_key, "key:%u", count);
TEST_ESP_ERR(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE); TEST_ESP_ERR(storage.writeItem(1, ItemType::BLOB, nvs_key, blob, sizeof(blob)), ESP_ERR_NVS_NOT_ENOUGH_SPACE);
@ -1821,7 +1823,7 @@ TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]")
TEST_ESP_OK( nvs_flash_init_custom("test", 0, 2) ); TEST_ESP_OK( nvs_flash_init_custom("test", 0, 2) );
TEST_ESP_OK( nvs_open_from_partition("test", "dummyNamespace", NVS_READONLY, &handle)); TEST_ESP_OK( nvs_open_from_partition("test", "dummyNamespace", NVS_READONLY, &handle));
char buf[64] = {0}; char buf[64] = {0};
size_t buflen = 64; size_t buflen = 64;
uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef}; uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
@ -1853,7 +1855,7 @@ TEST_CASE("Check that NVS supports old blob format without blob index", "[nvs]")
TEST_ESP_OK(p2.findItem(1, ItemType::BLOB_IDX, "dummyHex2BinKey")); TEST_ESP_OK(p2.findItem(1, ItemType::BLOB_IDX, "dummyHex2BinKey"));
/* Read the blob in new format and check the contents*/ /* Read the blob in new format and check the contents*/
buflen = 64; buflen = 64;
TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen)); TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen));
CHECK(memcmp(buf, base64data, buflen) == 0); CHECK(memcmp(buf, base64data, buflen) == 0);
@ -1865,29 +1867,29 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]")
std::mt19937 gen(rd()); std::mt19937 gen(rd());
uint32_t seed = 3; uint32_t seed = 3;
gen.seed(seed); gen.seed(seed);
SpiFlashEmulator emu(10); SpiFlashEmulator emu(10);
emu.randomize(seed); emu.randomize(seed);
emu.clearStats(); emu.clearStats();
const uint32_t NVS_FLASH_SECTOR = 2; const uint32_t NVS_FLASH_SECTOR = 2;
const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8; const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8;
static const size_t smallBlobLen = Page::CHUNK_MAX_SIZE / 3; static const size_t smallBlobLen = Page::CHUNK_MAX_SIZE / 3;
emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN); emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN));
nvs_handle handle; nvs_handle handle;
TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
RandomTest test; RandomTest test;
for ( uint8_t it = 0; it < 10; it++) { for ( uint8_t it = 0; it < 10; it++) {
size_t count = 200; size_t count = 200;
/* Erase index and chunks for the blob with "singlepage" key */ /* Erase index and chunks for the blob with "singlepage" key */
for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) {
Page p; Page p;
p.load(num); p.load(num);
p.eraseItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY); p.eraseItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY);
p.eraseItem(1, ItemType::BLOB_IDX, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY); p.eraseItem(1, ItemType::BLOB_IDX, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY);
@ -1896,7 +1898,7 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]")
/* Now write "singlepage" blob in old format*/ /* Now write "singlepage" blob in old format*/
for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) {
Page p; Page p;
p.load(num); p.load(num);
if (p.state() == Page::PageState::ACTIVE) { if (p.state() == Page::PageState::ACTIVE) {
uint8_t buf[smallBlobLen]; uint8_t buf[smallBlobLen];
@ -1905,7 +1907,7 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]")
if(blobLen > p.getVarDataTailroom()) { if(blobLen > p.getVarDataTailroom()) {
blobLen = p.getVarDataTailroom(); blobLen = p.getVarDataTailroom();
} }
std::generate_n(buf, blobLen, [&]() -> uint8_t { std::generate_n(buf, blobLen, [&]() -> uint8_t {
return static_cast<uint8_t>(gen() % 256); return static_cast<uint8_t>(gen() % 256);
}); });
@ -1913,14 +1915,14 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]")
TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", buf, blobLen, Item::CHUNK_ANY)); TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", buf, blobLen, Item::CHUNK_ANY));
TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage")); TEST_ESP_OK(p.findItem(1, ItemType::BLOB, "singlepage"));
test.handleExternalWriteAtIndex(9, buf, blobLen); // This assumes "singlepage" is always at index 9 test.handleExternalWriteAtIndex(9, buf, blobLen); // This assumes "singlepage" is always at index 9
break; break;
} }
} }
/* Initialize again */ /* Initialize again */
TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN)); TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN));
TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
/* Perform random things */ /* Perform random things */
auto res = test.doRandomThings(handle, gen, count); auto res = test.doRandomThings(handle, gen, count);
if (res != ESP_OK) { if (res != ESP_OK) {
@ -1933,7 +1935,7 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]")
bool oldVerPresent = false, newVerPresent = false; bool oldVerPresent = false, newVerPresent = false;
for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) { for (uint8_t num = NVS_FLASH_SECTOR; num < NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; num++) {
Page p; Page p;
p.load(num); p.load(num);
if(!oldVerPresent && p.findItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY) == ESP_OK) { if(!oldVerPresent && p.findItem(1, ItemType::BLOB, "singlepage", Item::CHUNK_ANY, VerOffset::VER_ANY) == ESP_OK) {
oldVerPresent = true; oldVerPresent = true;
@ -1945,7 +1947,7 @@ TEST_CASE("monkey test with old-format blob present", "[nvs][monkey]")
} }
CHECK(oldVerPresent != newVerPresent); CHECK(oldVerPresent != newVerPresent);
} }
s_perf << "Monkey test: nErase=" << emu.getEraseOps() << " nWrite=" << emu.getWriteOps() << std::endl; s_perf << "Monkey test: nErase=" << emu.getEraseOps() << " nWrite=" << emu.getWriteOps() << std::endl;
} }
@ -1955,23 +1957,23 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
std::mt19937 gen(rd()); std::mt19937 gen(rd());
uint32_t seed = 3; uint32_t seed = 3;
gen.seed(seed); gen.seed(seed);
SpiFlashEmulator emu(3); SpiFlashEmulator emu(3);
emu.clearStats(); emu.clearStats();
TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3)); TEST_ESP_OK(nvs_flash_init_custom(NVS_DEFAULT_PART_NAME, 0, 3));
nvs_handle handle; nvs_handle handle;
TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle)); TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef}; uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee}; uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee};
size_t buflen = sizeof(hexdata); size_t buflen = sizeof(hexdata);
uint8_t buf[Page::CHUNK_MAX_SIZE]; uint8_t buf[Page::CHUNK_MAX_SIZE];
/* Power-off when blob was being written on the same page where its old version in old format /* Power-off when blob was being written on the same page where its old version in old format
* was present*/ * was present*/
Page p; Page p;
p.load(0); p.load(0);
/* Write blob in old-format*/ /* Write blob in old-format*/
TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old))); TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old)));
@ -1995,7 +1997,7 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen)); TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen));
CHECK(memcmp(buf, hexdata, buflen) == 0); CHECK(memcmp(buf, hexdata, buflen) == 0);
Page p2; Page p2;
p2.load(0); p2.load(0);
TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_TYPE_MISMATCH); TEST_ESP_ERR(p2.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_TYPE_MISMATCH);
@ -2007,7 +2009,7 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
std::mt19937 gen(rd()); std::mt19937 gen(rd());
uint32_t seed = 3; uint32_t seed = 3;
gen.seed(seed); gen.seed(seed);
SpiFlashEmulator emu(3); SpiFlashEmulator emu(3);
emu.clearStats(); emu.clearStats();
@ -2018,13 +2020,13 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef}; uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee}; uint8_t hexdata_old[] = {0x11, 0x12, 0x13, 0xbb, 0xcc, 0xee};
size_t buflen = sizeof(hexdata); size_t buflen = sizeof(hexdata);
uint8_t buf[Page::CHUNK_MAX_SIZE]; uint8_t buf[Page::CHUNK_MAX_SIZE];
/* Power-off when blob was being written on the different page where its old version in old format /* Power-off when blob was being written on the different page where its old version in old format
* was present*/ * was present*/
Page p; Page p;
p.load(0); p.load(0);
/* Write blob in old-format*/ /* Write blob in old-format*/
TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old))); TEST_ESP_OK(p.writeItem(1, ItemType::BLOB, "singlepage", hexdata_old, sizeof(hexdata_old)));
@ -2037,7 +2039,7 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
item.blobIndex.chunkCount = 1; item.blobIndex.chunkCount = 1;
item.blobIndex.chunkStart = VerOffset::VER_0_OFFSET; item.blobIndex.chunkStart = VerOffset::VER_0_OFFSET;
p.markFull(); p.markFull();
Page p2; Page p2;
p2.load(1); p2.load(1);
p2.setSeqNumber(1); p2.setSeqNumber(1);
@ -2052,7 +2054,7 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen)); TEST_ESP_OK( nvs_get_blob(handle, "singlepage", buf, &buflen));
CHECK(memcmp(buf, hexdata, buflen) == 0); CHECK(memcmp(buf, hexdata, buflen) == 0);
Page p3; Page p3;
p3.load(0); p3.load(0);
TEST_ESP_ERR(p3.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_NOT_FOUND); TEST_ESP_ERR(p3.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_NOT_FOUND);
} }
@ -2060,12 +2062,12 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
static void check_nvs_part_gen_args(char const *part_name, int size, char const *filename, bool is_encr, nvs_sec_cfg_t* xts_cfg) static void check_nvs_part_gen_args(char const *part_name, int size, char const *filename, bool is_encr, nvs_sec_cfg_t* xts_cfg)
{ {
nvs_handle handle; nvs_handle handle;
if (is_encr) if (is_encr)
TEST_ESP_OK(nvs_flash_secure_init_custom(part_name, 0, size, xts_cfg)); TEST_ESP_OK(nvs_flash_secure_init_custom(part_name, 0, size, xts_cfg));
else else
TEST_ESP_OK( nvs_flash_init_custom(part_name, 0, size) ); TEST_ESP_OK( nvs_flash_init_custom(part_name, 0, size) );
TEST_ESP_OK( nvs_open_from_partition(part_name, "dummyNamespace", NVS_READONLY, &handle)); TEST_ESP_OK( nvs_open_from_partition(part_name, "dummyNamespace", NVS_READONLY, &handle));
uint8_t u8v; uint8_t u8v;
TEST_ESP_OK( nvs_get_u8(handle, "dummyU8Key", &u8v)); TEST_ESP_OK( nvs_get_u8(handle, "dummyU8Key", &u8v));
@ -2093,7 +2095,7 @@ static void check_nvs_part_gen_args(char const *part_name, int size, char const
int j; int j;
TEST_ESP_OK( nvs_get_blob(handle, "dummyHex2BinKey", buf, &buflen)); TEST_ESP_OK( nvs_get_blob(handle, "dummyHex2BinKey", buf, &buflen));
CHECK(memcmp(buf, hexdata, buflen) == 0); CHECK(memcmp(buf, hexdata, buflen) == 0);
uint8_t base64data[] = {'1', '2', '3', 'a', 'b', 'c'}; uint8_t base64data[] = {'1', '2', '3', 'a', 'b', 'c'};
TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen)); TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen));
CHECK(memcmp(buf, base64data, buflen) == 0); CHECK(memcmp(buf, base64data, buflen) == 0);
@ -2118,65 +2120,112 @@ static void check_nvs_part_gen_args(char const *part_name, int size, char const
CHECK(memcmp(bin_data, binfiledata, bin_len) == 0); CHECK(memcmp(bin_data, binfiledata, bin_len) == 0);
file.close(); file.close();
nvs_close(handle); nvs_close(handle);
} }
TEST_CASE("check and read data from partition generated via partition generation utility with multipage blob support disabled", "[nvs_part_gen]") TEST_CASE("check and read data from partition generated via partition generation utility with multipage blob support disabled", "[nvs_part_gen]")
{ {
int status;
int childpid = fork(); int childpid = fork();
if (childpid == 0) { if (childpid == 0) {
exit(execlp("python", "python", exit(execlp("cp", " cp",
"../nvs_partition_generator/nvs_partition_gen.py", "-rf",
"--input", "../nvs_partition_generator/testdata",
"../nvs_partition_generator/sample_singlepage_blob.csv", ".",NULL));
"--output",
"../nvs_partition_generator/partition_single_page.bin",
"--size",
"0x3000",
"--version",
"v1",NULL));
} else { } else {
CHECK(childpid > 0); CHECK(childpid > 0);
int status;
waitpid(childpid, &status, 0); waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1); CHECK(WEXITSTATUS(status) != -1);
childpid = fork();
if (childpid == 0) {
exit(execlp("python", "python",
"../nvs_partition_generator/nvs_partition_gen.py",
"--input",
"../nvs_partition_generator/sample_singlepage_blob.csv",
"--output",
"../nvs_partition_generator/partition_single_page.bin",
"--size",
"0x3000",
"--version",
"v1",NULL));
} else {
CHECK(childpid > 0);
int status;
waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1);
}
} }
SpiFlashEmulator emu("../nvs_partition_generator/partition_single_page.bin"); SpiFlashEmulator emu("../nvs_partition_generator/partition_single_page.bin");
TEST_ESP_OK(nvs_flash_deinit());
check_nvs_part_gen_args("test", 3, "../nvs_partition_generator/testdata/sample_singlepage_blob.bin", false, NULL);
}
TEST_ESP_OK(nvs_flash_deinit());
check_nvs_part_gen_args("test", 3, "../nvs_partition_generator/testdata/sample_singlepage_blob.bin", false, NULL);
if (childpid == 0) {
exit(execlp("rm", " rm",
"-rf",
"testdata",NULL));
} else {
CHECK(childpid > 0);
waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1);
}
}
TEST_CASE("check and read data from partition generated via partition generation utility with multipage blob support enabled", "[nvs_part_gen]") TEST_CASE("check and read data from partition generated via partition generation utility with multipage blob support enabled", "[nvs_part_gen]")
{ {
int status;
int childpid = fork(); int childpid = fork();
if (childpid == 0) { if (childpid == 0) {
exit(execlp("python", "python", exit(execlp("cp", " cp",
"../nvs_partition_generator/nvs_partition_gen.py", "-rf",
"--input", "../nvs_partition_generator/testdata",
"../nvs_partition_generator/sample_multipage_blob.csv", ".",NULL));
"--output",
"../nvs_partition_generator/partition_multipage_blob.bin",
"--size",
"0x4000",
"--version",
"v2",NULL));
} else { } else {
CHECK(childpid > 0); CHECK(childpid > 0);
int status;
waitpid(childpid, &status, 0); waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1); CHECK(WEXITSTATUS(status) != -1);
childpid = fork();
if (childpid == 0) {
exit(execlp("python", "python",
"../nvs_partition_generator/nvs_partition_gen.py",
"--input",
"../nvs_partition_generator/sample_multipage_blob.csv",
"--output",
"../nvs_partition_generator/partition_multipage_blob.bin",
"--size",
"0x4000",
"--version",
"v2",NULL));
} else {
CHECK(childpid > 0);
waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1);
}
} }
SpiFlashEmulator emu("../nvs_partition_generator/partition_multipage_blob.bin"); SpiFlashEmulator emu("../nvs_partition_generator/partition_multipage_blob.bin");
check_nvs_part_gen_args("test", 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin",false,NULL); check_nvs_part_gen_args("test", 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin",false,NULL);
if (childpid == 0) {
exit(execlp("rm", " rm",
"-rf",
"testdata",NULL));
} else {
CHECK(childpid > 0);
waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1);
}
} }
#if CONFIG_NVS_ENCRYPTION #if CONFIG_NVS_ENCRYPTION
@ -2320,29 +2369,42 @@ TEST_CASE("test nvs apis with encryption enabled", "[nvs]")
TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled", "[nvs_part_gen]") TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled", "[nvs_part_gen]")
{ {
int status;
int childpid = fork(); int childpid = fork();
if (childpid == 0) { if (childpid == 0) {
exit(execlp("python", "python", exit(execlp("cp", " cp",
"../nvs_partition_generator/nvs_partition_gen.py", "-rf",
"--input", "../nvs_partition_generator/testdata",
"../nvs_partition_generator/sample_multipage_blob.csv", ".",NULL));
"--output",
"../nvs_partition_generator/partition_encrypted.bin",
"--size",
"0x4000",
"--encrypt",
"True",
"--keyfile",
"../nvs_partition_generator/testdata/sample_encryption_keys.bin",NULL));
} else { } else {
CHECK(childpid > 0); CHECK(childpid > 0);
int status;
waitpid(childpid, &status, 0); waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1); CHECK(WEXITSTATUS(status) != -1);
childpid = fork();
if (childpid == 0) {
exit(execlp("python", "python",
"../nvs_partition_generator/nvs_partition_gen.py",
"--input",
"../nvs_partition_generator/sample_multipage_blob.csv",
"--output",
"../nvs_partition_generator/partition_encrypted.bin",
"--size",
"0x4000",
"--encrypt",
"True",
"--keyfile",
"../nvs_partition_generator/testdata/sample_encryption_keys.bin",NULL));
} else {
CHECK(childpid > 0);
waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1);
}
} }
SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted.bin"); SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted.bin");
nvs_sec_cfg_t cfg; nvs_sec_cfg_t cfg;
for(int count = 0; count < NVS_KEY_SIZE; count++) { for(int count = 0; count < NVS_KEY_SIZE; count++) {
cfg.eky[count] = 0x11; cfg.eky[count] = 0x11;
@ -2350,7 +2412,19 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena
} }
check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg);
childpid = fork();
if (childpid == 0) {
exit(execlp("rm", " rm",
"-rf",
"testdata",NULL));
} else {
CHECK(childpid > 0);
waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1);
}
} }
@ -2358,32 +2432,69 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena
{ {
int childpid = fork(); int childpid = fork();
int status; int status;
if (childpid == 0) {
exit(execlp("python", "python",
"../nvs_partition_generator/nvs_partition_gen.py",
"--input",
"../nvs_partition_generator/sample_multipage_blob.csv",
"--output",
"../nvs_partition_generator/partition_encrypted_using_keygen.bin",
"--size",
"0x4000",
"--encrypt",
"True",
"--keygen",
"true",NULL));
if (childpid == 0) {
exit(execlp("cp", " cp",
"-rf",
"../nvs_partition_generator/testdata",
".",NULL));
} else { } else {
CHECK(childpid > 0); CHECK(childpid > 0);
waitpid(childpid, &status, 0); waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1); CHECK(WEXITSTATUS(status) != -1);
childpid = fork();
if (childpid == 0) {
exit(execlp("python", "python",
"../nvs_partition_generator/nvs_partition_gen.py",
"--input",
"../nvs_partition_generator/sample_multipage_blob.csv",
"--output",
"../nvs_partition_generator/partition_encrypted_using_keygen.bin",
"--size",
"0x4000",
"--encrypt",
"True",
"--keygen",
"true",NULL));
} else {
CHECK(childpid > 0);
waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1);
}
} }
DIR *dir;
struct dirent *file;
char *filename;
char *files;
char *file_ext;
dir = opendir("keys");
while ((file = readdir(dir)) != NULL)
{
filename = file->d_name;
files = strrchr(filename, '.');
if (files != NULL)
{
file_ext = files+1;
if (strncmp(file_ext,"bin",3) == 0)
{
break;
}
}
}
std::string encr_file = std::string("keys/") + std::string(filename);
SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted_using_keygen.bin"); SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted_using_keygen.bin");
char buffer[64]; char buffer[64];
FILE *fp; FILE *fp;
fp = fopen("encryption_keys.bin","rb"); fp = fopen(encr_file.c_str(),"rb");
fread(buffer,sizeof(buffer),1,fp); fread(buffer,sizeof(buffer),1,fp);
fclose(fp); fclose(fp);
@ -2398,14 +2509,37 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena
check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg); check_nvs_part_gen_args(NVS_DEFAULT_PART_NAME, 4, "../nvs_partition_generator/testdata/sample_multipage_blob.bin", true, &cfg);
} }
TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled using keyfile", "[nvs_part_gen]") TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled using keyfile", "[nvs_part_gen]")
{ {
int childpid = fork(); int childpid = fork();
int status; int status;
if (childpid == 0) {
DIR *dir;
struct dirent *file;
char *filename;
char *files;
char *file_ext;
dir = opendir("keys");
while ((file = readdir(dir)) != NULL)
{
filename = file->d_name;
files = strrchr(filename, '.');
if (files != NULL)
{
file_ext = files+1;
if (strncmp(file_ext,"bin",3) == 0)
{
break;
}
}
}
std::string encr_file = std::string("keys/") + std::string(filename);
if (childpid == 0) {
exit(execlp("python", "python", exit(execlp("python", "python",
"../nvs_partition_generator/nvs_partition_gen.py", "../nvs_partition_generator/nvs_partition_gen.py",
"--input", "--input",
@ -2417,7 +2551,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena
"--encrypt", "--encrypt",
"True", "True",
"--keyfile", "--keyfile",
"encryption_keys.bin",NULL)); encr_file.c_str(),NULL));
} else { } else {
CHECK(childpid > 0); CHECK(childpid > 0);
@ -2430,7 +2564,7 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena
char buffer[64]; char buffer[64];
FILE *fp; FILE *fp;
fp = fopen("encryption_keys.bin","rb"); fp = fopen(encr_file.c_str(),"rb");
fread(buffer,sizeof(buffer),1,fp); fread(buffer,sizeof(buffer),1,fp);
fclose(fp); fclose(fp);
@ -2448,12 +2582,24 @@ TEST_CASE("test nvs apis for nvs partition generator utility with encryption ena
childpid = fork(); childpid = fork();
if (childpid == 0) { if (childpid == 0) {
exit(execlp("rm", " rm", exit(execlp("rm", " rm",
"encryption_keys.bin",NULL)); "-rf",
"keys",NULL));
} else { } else {
CHECK(childpid > 0); CHECK(childpid > 0);
waitpid(childpid, &status, 0); waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1); CHECK(WEXITSTATUS(status) != -1);
childpid = fork();
if (childpid == 0) {
exit(execlp("rm", " rm",
"-rf",
"testdata",NULL));
} else {
CHECK(childpid > 0);
waitpid(childpid, &status, 0);
CHECK(WEXITSTATUS(status) != -1);
}
} }
} }

View file

@ -2,8 +2,10 @@
# Regexp for matching job names which are incompatible with Python 3 # Regexp for matching job names which are incompatible with Python 3
# - assign_test, nvs_compatible_test, IT - auto_test_script causes the incompatibility # - assign_test, nvs_compatible_test, IT - auto_test_script causes the incompatibility
# - UT_009_ - RS485 multi-device test is not started properly # - UT_009_ - multi-device tests are not compatible
py3_incomp='assign_test|nvs_compatible_test|IT|UT_009_' # - UT_014_ - multi-device tests are not compatible
# - UT_017_ - multi-device tests are not compatible
py3_incomp='assign_test|nvs_compatible_test|IT|UT_009_|UT_013_|UT_014_|UT_017_'
if [ -z ${PYTHON_VER+x} ] || [[ $CI_JOB_NAME =~ $py3_incomp ]]; then if [ -z ${PYTHON_VER+x} ] || [[ $CI_JOB_NAME =~ $py3_incomp ]]; then
# Use this version of the Python interpreter if it was not defined before or # Use this version of the Python interpreter if it was not defined before or