]> granicus.if.org Git - esp-idf/commitdiff
nvs_flash: Add support for nvs encryption
authorSagar Bijwe <sagar@espressif.com>
Mon, 2 Jul 2018 11:10:43 +0000 (16:40 +0530)
committerSagar Bijwe <sagar@espressif.com>
Thu, 4 Oct 2018 13:55:12 +0000 (19:25 +0530)
43 files changed:
components/bootloader_support/src/bootloader_utility.c
components/bootloader_support/src/flash_encrypt.c
components/esp32/esp_err_to_name.c
components/esp32/hwcrypto/aes.c
components/esp32/include/esp_flash_data_types.h
components/esp32/include/hwcrypto/aes.h
components/mbedtls/port/include/aes_alt.h
components/mbedtls/port/include/mbedtls/esp_config.h
components/nvs_flash/CMakeLists.txt
components/nvs_flash/Kconfig
components/nvs_flash/README.rst
components/nvs_flash/component.mk
components/nvs_flash/include/nvs.h
components/nvs_flash/include/nvs_flash.h
components/nvs_flash/nvs_partition_generator/README.rst
components/nvs_flash/nvs_partition_generator/nvs_partition_gen.py
components/nvs_flash/nvs_partition_generator/testdata/encryption_keys.txt [new file with mode: 0644]
components/nvs_flash/src/intrusive_list.h
components/nvs_flash/src/nvs_api.cpp
components/nvs_flash/src/nvs_encr.cpp [new file with mode: 0644]
components/nvs_flash/src/nvs_encr.hpp [new file with mode: 0644]
components/nvs_flash/src/nvs_ops.cpp [new file with mode: 0644]
components/nvs_flash/src/nvs_ops.hpp [new file with mode: 0644]
components/nvs_flash/src/nvs_page.cpp
components/nvs_flash/src/nvs_pagemanager.hpp
components/nvs_flash/src/nvs_storage.cpp
components/nvs_flash/src/nvs_storage.hpp
components/nvs_flash/src/nvs_test_api.h
components/nvs_flash/test/component.mk
components/nvs_flash/test/encryption_keys.bin [new file with mode: 0644]
components/nvs_flash/test/partition_encrypted.bin [new file with mode: 0644]
components/nvs_flash/test/sample.bin [new file with mode: 0644]
components/nvs_flash/test/test_nvs.c
components/nvs_flash/test_nvs_host/Makefile
components/nvs_flash/test_nvs_host/test_nvs.cpp
components/partition_table/gen_esp32part.py
components/partition_table/test/test_partition.c
components/spi_flash/include/esp_partition.h
components/spi_flash/partition.c
components/wpa_supplicant/port/include/endian.h
docs/en/api-guides/partition-tables.rst
requirements.txt [new file with mode: 0644]
tools/unit-test-app/partition_table_unit_test_app.csv

index 4b23b14cb5f6396fde471ada63794a708de57701..4fc438f85f8001a43d95b796b0264482084c369a 100644 (file)
@@ -131,6 +131,9 @@ bool bootloader_utility_load_partition_table(bootloader_state_t* bs)
             case PART_SUBTYPE_DATA_WIFI:
                 partition_usage = "WiFi data";
                 break;
+            case PART_SUBTYPE_DATA_NVS_KEYS:
+                partition_usage = "NVS keys";
+                break;
             default:
                 partition_usage = "Unknown data";
                 break;
index a9e8f8f9bacd57fce8c6fec2deae89e7c7e85757..41b3a0c569261ae61ee030039b2a4e20d4f7fae0 100644 (file)
@@ -285,7 +285,8 @@ static esp_err_t encrypt_partition(int index, const esp_partition_info_t *partit
                            &partition->pos,
                            &data_ignored);
       should_encrypt = (err == ESP_OK);
-    } else if (partition->type == PART_TYPE_DATA && partition->subtype == PART_SUBTYPE_DATA_OTA) {
+    } else if ((partition->type == PART_TYPE_DATA && partition->subtype == PART_SUBTYPE_DATA_OTA)
+                || (partition->type == PART_TYPE_DATA && partition->subtype == PART_SUBTYPE_DATA_NVS_KEYS)) {
         /* check if we have ota data partition and the partition should be encrypted unconditionally */
         should_encrypt = true;
     }
index f012a5fb90453a80904f23dfb9c3d5b9ca816729..a6ab5cbc658abf6451250edad08486133a61ecbe 100644 (file)
@@ -162,6 +162,27 @@ static const esp_err_msg_t esp_err_msg_table[] = {
     ERR_TBL_IT(ESP_ERR_NVS_NEW_VERSION_FOUND),              /*  4368 0x1110 NVS partition contains data in new format
                                                                             and cannot be recognized by this version of
                                                                             code */
+#   endif
+#   ifdef      ESP_ERR_NVS_XTS_ENCR_FAILED
+    ERR_TBL_IT(ESP_ERR_NVS_XTS_ENCR_FAILED),                /*  4369 0x1111 XTS encryption failed while writing NVS entry */
+#   endif
+#   ifdef      ESP_ERR_NVS_XTS_DECR_FAILED
+    ERR_TBL_IT(ESP_ERR_NVS_XTS_DECR_FAILED),                /*  4370 0x1112 XTS decryption failed while reading NVS entry */
+#   endif
+#   ifdef      ESP_ERR_NVS_XTS_CFG_FAILED
+    ERR_TBL_IT(ESP_ERR_NVS_XTS_CFG_FAILED),                 /*  4371 0x1113 XTS configuration setting failed */
+#   endif
+#   ifdef      ESP_ERR_NVS_XTS_CFG_NOT_FOUND
+    ERR_TBL_IT(ESP_ERR_NVS_XTS_CFG_NOT_FOUND),              /*  4372 0x1114 XTS configuration not found */
+#   endif
+#   ifdef      ESP_ERR_NVS_ENCR_NOT_SUPPORTED
+    ERR_TBL_IT(ESP_ERR_NVS_ENCR_NOT_SUPPORTED),             /*  4373 0x1115 NVS encryption is not supported in this version */
+#   endif
+#   ifdef      ESP_ERR_NVS_KEYS_NOT_INITIALIZED
+    ERR_TBL_IT(ESP_ERR_NVS_KEYS_NOT_INITIALIZED),           /*  4374 0x1116 NVS key partition is uninitialized */
+#   endif
+#   ifdef      ESP_ERR_NVS_CORRUPT_KEY_PART
+    ERR_TBL_IT(ESP_ERR_NVS_CORRUPT_KEY_PART),               /*  4375 0x1117 NVS key partition is corrupt */
 #   endif
     // components/ulp/include/esp32/ulp.h
 #   ifdef      ESP_ERR_ULP_BASE
index bddaf1fcdaa272b1f8fb626d0adc3f2800de5572..9c449d821c92f69c97e47a7dceb84c41ab63fe26 100644 (file)
@@ -88,6 +88,8 @@ void esp_aes_free( esp_aes_context *ctx )
     bzero( ctx, sizeof( esp_aes_context ) );
 }
 
+
+
 /*
  * AES key schedule (same for encryption or decryption, as hardware handles schedule)
  *
@@ -385,3 +387,246 @@ int esp_aes_crypt_ctr( esp_aes_context *ctx,
 
     return 0;
 }
+
+/* Below XTS implementation is copied aes.c of mbedtls library. 
+ * When MBEDTLS_AES_ALT is defined mbedtls expects alternate
+ * definition of XTS functions to be available. Even if this 
+ * could have been avoided, it is done for consistency reason.
+ */
+
+void esp_aes_xts_init( esp_aes_xts_context *ctx )
+{
+    esp_aes_init( &ctx->crypt );
+    esp_aes_init( &ctx->tweak );
+}
+
+void esp_aes_xts_free( esp_aes_xts_context *ctx )
+{
+    esp_aes_free( &ctx->crypt );
+    esp_aes_free( &ctx->tweak );
+}
+
+static int esp_aes_xts_decode_keys( const unsigned char *key,
+                                        unsigned int keybits,
+                                        const unsigned char **key1,
+                                        unsigned int *key1bits,
+                                        const unsigned char **key2,
+                                        unsigned int *key2bits )
+{
+    const unsigned int half_keybits = keybits / 2;
+    const unsigned int half_keybytes = half_keybits / 8;
+
+    switch( keybits )
+    {
+        case 256: break;
+        case 512: break;
+        default : return( MBEDTLS_ERR_AES_INVALID_KEY_LENGTH );
+    }
+
+    *key1bits = half_keybits;
+    *key2bits = half_keybits;
+    *key1 = &key[0];
+    *key2 = &key[half_keybytes];
+
+    return 0;
+}
+
+int esp_aes_xts_setkey_enc( mbedtls_aes_xts_context *ctx,
+                                const unsigned char *key,
+                                unsigned int keybits)
+{
+    int ret;
+    const unsigned char *key1, *key2;
+    unsigned int key1bits, key2bits;
+
+    ret = esp_aes_xts_decode_keys( key, keybits, &key1, &key1bits,
+                                       &key2, &key2bits );
+    if( ret != 0 )
+        return( ret );
+
+    /* Set the tweak key. Always set tweak key for the encryption mode. */
+    ret = esp_aes_setkey( &ctx->tweak, key2, key2bits );
+    if( ret != 0 )
+        return( ret );
+
+    /* Set crypt key for encryption. */
+    return esp_aes_setkey( &ctx->crypt, key1, key1bits );
+}
+
+int esp_aes_xts_setkey_dec( mbedtls_aes_xts_context *ctx,
+                                const unsigned char *key,
+                                unsigned int keybits)
+{
+    int ret;
+    const unsigned char *key1, *key2;
+    unsigned int key1bits, key2bits;
+
+    ret = esp_aes_xts_decode_keys( key, keybits, &key1, &key1bits,
+                                       &key2, &key2bits );
+    if( ret != 0 )
+        return( ret );
+
+    /* Set the tweak key. Always set tweak key for encryption. */
+    ret = esp_aes_setkey( &ctx->tweak, key2, key2bits );
+    if( ret != 0 )
+        return( ret );
+
+    /* Set crypt key for decryption. */
+    return esp_aes_setkey( &ctx->crypt, key1, key1bits );
+}
+
+/* Endianess with 64 bits values */
+#ifndef GET_UINT64_LE
+#define GET_UINT64_LE(n,b,i)                            \
+{                                                       \
+    (n) = ( (uint64_t) (b)[(i) + 7] << 56 )             \
+        | ( (uint64_t) (b)[(i) + 6] << 48 )             \
+        | ( (uint64_t) (b)[(i) + 5] << 40 )             \
+        | ( (uint64_t) (b)[(i) + 4] << 32 )             \
+        | ( (uint64_t) (b)[(i) + 3] << 24 )             \
+        | ( (uint64_t) (b)[(i) + 2] << 16 )             \
+        | ( (uint64_t) (b)[(i) + 1] <<  8 )             \
+        | ( (uint64_t) (b)[(i)    ]       );            \
+}
+#endif
+
+#ifndef PUT_UINT64_LE
+#define PUT_UINT64_LE(n,b,i)                            \
+{                                                       \
+    (b)[(i) + 7] = (unsigned char) ( (n) >> 56 );       \
+    (b)[(i) + 6] = (unsigned char) ( (n) >> 48 );       \
+    (b)[(i) + 5] = (unsigned char) ( (n) >> 40 );       \
+    (b)[(i) + 4] = (unsigned char) ( (n) >> 32 );       \
+    (b)[(i) + 3] = (unsigned char) ( (n) >> 24 );       \
+    (b)[(i) + 2] = (unsigned char) ( (n) >> 16 );       \
+    (b)[(i) + 1] = (unsigned char) ( (n) >>  8 );       \
+    (b)[(i)    ] = (unsigned char) ( (n)       );       \
+}
+#endif
+
+typedef unsigned char esp_be128[16];
+
+/*
+ * GF(2^128) multiplication function
+ *
+ * This function multiplies a field element by x in the polynomial field
+ * representation. It uses 64-bit word operations to gain speed but compensates
+ * for machine endianess and hence works correctly on both big and little
+ * endian machines.
+ */
+static void esp_gf128mul_x_ble( unsigned char r[16],
+                                    const unsigned char x[16] )
+{
+    uint64_t a, b, ra, rb;
+
+    GET_UINT64_LE( a, x, 0 );
+    GET_UINT64_LE( b, x, 8 );
+
+    ra = ( a << 1 )  ^ 0x0087 >> ( 8 - ( ( b >> 63 ) << 3 ) );
+    rb = ( a >> 63 ) | ( b << 1 );
+
+    PUT_UINT64_LE( ra, r, 0 );
+    PUT_UINT64_LE( rb, r, 8 );
+}
+
+/*
+ * AES-XTS buffer encryption/decryption
+ */
+int esp_aes_crypt_xts( mbedtls_aes_xts_context *ctx,
+                           int mode,
+                           size_t length,
+                           const unsigned char data_unit[16],
+                           const unsigned char *input,
+                           unsigned char *output )
+{
+    int ret;
+    size_t blocks = length / 16;
+    size_t leftover = length % 16;
+    unsigned char tweak[16];
+    unsigned char prev_tweak[16];
+    unsigned char tmp[16];
+
+    /* Sectors must be at least 16 bytes. */
+    if( length < 16 )
+        return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH;
+
+    /* NIST SP 80-38E disallows data units larger than 2**20 blocks. */
+    if( length > ( 1 << 20 ) * 16 )
+        return MBEDTLS_ERR_AES_INVALID_INPUT_LENGTH;
+
+    /* Compute the tweak. */
+    ret = esp_aes_crypt_ecb( &ctx->tweak, MBEDTLS_AES_ENCRYPT,
+                                 data_unit, tweak );
+    if( ret != 0 )
+        return( ret );
+
+    while( blocks-- )
+    {
+        size_t i;
+
+        if( leftover && ( mode == MBEDTLS_AES_DECRYPT ) && blocks == 0 )
+        {
+            /* We are on the last block in a decrypt operation that has
+             * leftover bytes, so we need to use the next tweak for this block,
+             * and this tweak for the lefover bytes. Save the current tweak for
+             * the leftovers and then update the current tweak for use on this,
+             * the last full block. */
+            memcpy( prev_tweak, tweak, sizeof( tweak ) );
+            esp_gf128mul_x_ble( tweak, tweak );
+        }
+
+        for( i = 0; i < 16; i++ )
+            tmp[i] = input[i] ^ tweak[i];
+
+        ret = esp_aes_crypt_ecb( &ctx->crypt, mode, tmp, tmp );
+        if( ret != 0 )
+            return( ret );
+
+        for( i = 0; i < 16; i++ )
+            output[i] = tmp[i] ^ tweak[i];
+
+        /* Update the tweak for the next block. */
+        esp_gf128mul_x_ble( tweak, tweak );
+
+        output += 16;
+        input += 16;
+    }
+
+    if( leftover )
+    {
+        /* If we are on the leftover bytes in a decrypt operation, we need to
+         * use the previous tweak for these bytes (as saved in prev_tweak). */
+        unsigned char *t = mode == MBEDTLS_AES_DECRYPT ? prev_tweak : tweak;
+
+        /* We are now on the final part of the data unit, which doesn't divide
+         * evenly by 16. It's time for ciphertext stealing. */
+        size_t i;
+        unsigned char *prev_output = output - 16;
+
+        /* Copy ciphertext bytes from the previous block to our output for each
+         * byte of cyphertext we won't steal. At the same time, copy the
+         * remainder of the input for this final round (since the loop bounds
+         * are the same). */
+        for( i = 0; i < leftover; i++ )
+        {
+            output[i] = prev_output[i];
+            tmp[i] = input[i] ^ t[i];
+        }
+
+        /* Copy ciphertext bytes from the previous block for input in this
+         * round. */
+        for( ; i < 16; i++ )
+            tmp[i] = prev_output[i] ^ t[i];
+
+        ret = esp_aes_crypt_ecb( &ctx->crypt, mode, tmp, tmp );
+        if( ret != 0 )
+            return ret;
+
+        /* Write the result back to the previous block, overriding the previous
+         * output we copied. */
+        for( i = 0; i < 16; i++ )
+            prev_output[i] = tmp[i] ^ t[i];
+    }
+
+    return( 0 );
+}
index bb5aa2998a31523665df70670b9826274d4419b4..9a26281b0a8d479042c80e21da14289056266199 100644 (file)
@@ -60,6 +60,7 @@ typedef struct {
 #define PART_SUBTYPE_DATA_OTA 0x00
 #define PART_SUBTYPE_DATA_RF  0x01
 #define PART_SUBTYPE_DATA_WIFI 0x02
+#define PART_SUBTYPE_DATA_NVS_KEYS 0x04
 
 #define PART_TYPE_END 0xff
 #define PART_SUBTYPE_END 0xff
index cc0f56086150ce74713f81683abdf8693f939cfd..1dd5291f07c06f37cd21ccc7f23b8520bff23997 100644 (file)
@@ -51,6 +51,19 @@ typedef struct {
     uint8_t key[32];
 } esp_aes_context;
 
+
+/**
+ * \brief The AES XTS context-type definition.
+ */
+typedef struct
+{
+    esp_aes_context crypt; /*!< The AES context to use for AES block
+                                        encryption or decryption. */
+    esp_aes_context tweak; /*!< The AES context used for tweak
+                                        computation. */
+} esp_aes_xts_context;
+
+
 /**
  * \brief Lock access to AES hardware unit
  *
@@ -86,6 +99,23 @@ void esp_aes_init( esp_aes_context *ctx );
  */
 void esp_aes_free( esp_aes_context *ctx );
 
+/**
+ * \brief          This function initializes the specified AES XTS context.
+ *
+ *                 It must be the first API called before using
+ *                 the context.
+ *
+ * \param ctx      The AES XTS context to initialize.
+ */
+void esp_aes_xts_init( esp_aes_xts_context *ctx );
+
+/**
+ * \brief          This function releases and clears the specified AES XTS context.
+ *
+ * \param ctx      The AES XTS context to clear.
+ */
+void esp_aes_xts_free( esp_aes_xts_context *ctx );
+
 /**
  * \brief          AES set key schedule (encryption or decryption)
  *
@@ -233,6 +263,42 @@ int esp_aes_crypt_ctr( esp_aes_context *ctx,
                        const unsigned char *input,
                        unsigned char *output );
 
+/**
+ * \brief          This function prepares an XTS context for encryption and
+ *                 sets the encryption key.
+ *
+ * \param ctx      The AES XTS context to which the key should be bound.
+ * \param key      The encryption key. This is comprised of the XTS key1
+ *                 concatenated with the XTS key2.
+ * \param keybits  The size of \p key passed in bits. Valid options are:
+ *                 <ul><li>256 bits (each of key1 and key2 is a 128-bit key)</li>
+ *                 <li>512 bits (each of key1 and key2 is a 256-bit key)</li></ul>
+ *
+ * \return         \c 0 on success.
+ * \return         #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
+ */
+int esp_aes_xts_setkey_enc( esp_aes_xts_context *ctx,
+                                const unsigned char *key,
+                                unsigned int keybits );
+
+/**
+ * \brief          This function prepares an XTS context for decryption and
+ *                 sets the decryption key.
+ *
+ * \param ctx      The AES XTS context to which the key should be bound.
+ * \param key      The decryption key. This is comprised of the XTS key1
+ *                 concatenated with the XTS key2.
+ * \param keybits  The size of \p key passed in bits. Valid options are:
+ *                 <ul><li>256 bits (each of key1 and key2 is a 128-bit key)</li>
+ *                 <li>512 bits (each of key1 and key2 is a 256-bit key)</li></ul>
+ *
+ * \return         \c 0 on success.
+ * \return         #MBEDTLS_ERR_AES_INVALID_KEY_LENGTH on failure.
+ */
+int esp_aes_xts_setkey_dec( esp_aes_xts_context *ctx,
+                                const unsigned char *key,
+                                unsigned int keybits );
+
 
 /**
  * \brief           Internal AES block encryption function
index 5c143f2c5a2c4810a384c8b062b6f5d126f63421..cf87ea5c15234bb679d62f7d71b160a814cb6d25 100644 (file)
@@ -47,6 +47,14 @@ typedef esp_aes_context mbedtls_aes_context;
 #if defined(MBEDTLS_CIPHER_MODE_CTR)
 #define mbedtls_aes_crypt_ctr       esp_aes_crypt_ctr
 #endif
+#if defined(MBEDTLS_CIPHER_MODE_XTS)
+typedef esp_aes_xts_context mbedtls_aes_xts_context;
+#define mbedtls_aes_xts_init            esp_aes_xts_init
+#define mbedtls_aes_xts_free            esp_aes_xts_free
+#define mbedtls_aes_xts_setkey_enc      esp_aes_xts_setkey_enc
+#define mbedtls_aes_xts_setkey_dec      esp_aes_xts_setkey_dec
+#define mbedtls_aes_crypt_xts       esp_aes_crypt_xts
+#endif
 #define mbedtls_internal_aes_encrypt         esp_internal_aes_encrypt
 #define mbedtls_internal_aes_decrypt         esp_internal_aes_decrypt
 #endif /* MBEDTLS_AES_ALT */
index c81bf1a06e2581d91014ce23f6cf9fa3979a549d..ec09782fd2691605c9b1ff1c3314134c77259883 100644 (file)
  */
 #define MBEDTLS_CIPHER_MODE_CTR
 
+/**
+ * \def MBEDTLS_CIPHER_MODE_XTS
+ *
+ * Enable Xor-encrypt-xor with ciphertext stealing mode (XTS) for AES.
+ */
+#define MBEDTLS_CIPHER_MODE_XTS
+
 /**
  * \def MBEDTLS_CIPHER_NULL_CIPHER
  *
index ce7efbb14b2b07dad49c1749e75abd890637b390..2c815b31ca598c7da41f963245629363c5b4f187 100644 (file)
@@ -1,11 +1,13 @@
 set(COMPONENT_SRCS "src/nvs_api.cpp"
+                   "src/nvs_encr.cpp"
                    "src/nvs_item_hash_list.cpp"
+                   "src/nvs_ops.cpp"
                    "src/nvs_page.cpp"
                    "src/nvs_pagemanager.cpp"
                    "src/nvs_storage.cpp"
                    "src/nvs_types.cpp")
 set(COMPONENT_ADD_INCLUDEDIRS include)
 
-set(COMPONENT_REQUIRES spi_flash)
+set(COMPONENT_REQUIRES spi_flash mbedtls)
 
 register_component()
index dc3efeaf50aa8e1607b5ea1fbd92da89f860c3ca..8c3c1101ae326eab9a260df46b9cfbd6aa915e97 100644 (file)
@@ -1,4 +1,4 @@
-menu "Non-volatile storage"
+menu NVS
 
 config MP_BLOB_SUPPORT
     bool "Enable multi-page blob support"
@@ -6,4 +6,15 @@ config MP_BLOB_SUPPORT
     help
         When enabled, blobs larger than the sector size are split and stored on multiple sectors.
         This removes the earlier limitation of 1984 bytes for storing data-blobs.
+
+config NVS_ENCRYPTION
+   bool "Enable NVS encryption"
+   default n
+   depends on FLASH_ENCRYPTION_ENABLED
+   help
+      This option enables encryption for NVS. When enabled, AES-XTS is used to encrypt
+      the complete NVS data, except the page headers. It requires XTS encryption keys 
+      to be stored in an encrypted partition. This means enabling flash encryption is 
+      a pre-requisite for this feature. 
+
 endmenu
index a7c50e0ae780056a58a71822d89343283098c0f0..11b8e8e3c0eba1f7d3e27833c68c3458bdcc6e6a 100644 (file)
@@ -243,3 +243,44 @@ To reduce the number of reads performed from flash memory, each member of Page c
 
 Each node in hash list contains a 24-bit hash and 8-bit item index. Hash is calculated based on item namespace, key name and ChunkIndex. CRC32 is used for calculation, result is truncated to 24 bits. To reduce overhead of storing 32-bit entries in a linked list, list is implemented as a doubly-linked list of arrays. Each array holds 29 entries, for the total size of 128 bytes, together with linked list pointers and 32-bit count field. Minimal amount of extra RAM useage per page is therefore 128 bytes, maximum is 640 bytes.
 
+.. _nvs_encryption:
+
+NVS Encryption
+--------------
+
+Data stored in NVS partitions can be encrypted using AES-XTS in the manner similar to one mentioned in disc encryption standard IEEE P1619. For the purpose of encryption, each entry is considered as one `sector` and relative address of the entry (w.r.t. partition-start) is fed to the encryption algorithm as `sector-number`. The keys required for nvs encryption are stored in yet another partition, which is protected using :doc:`Flash Encryption <../../security/flash-encryption>`. Therefore, enabling :doc:`Flash Encryption <../../security/flash-encryption>` is a prerequisite for NVS encryption.
+
+.. _nvs_key_partition:
+
+NVS key partition
+^^^^^^^^^^^^^^^^^
+
+An application requiring NVS encryption support needs to be compiled with a key-partition of type `data` and subtype `key`. This partition should be marked as `encrypted`. Refer to :doc:`Partition Tables <../../api-guides/partition-tables>` for more details. The size of the partition should be 4096 bytes (minimum partition size). The structure of this partition is depicted below. 
+
+::
+
+    +-----------+--------------+-------------+----+
+    |              XTS encryption key(32)         |
+    +---------------------------------------------+
+    |              XTS tweak key (32)             |
+    +---------------------------------------------+
+    |                  CRC32(4)                   |
+    +---------------------------------------------+
+
+This partition can be generated using `nvs partition generator` utility and flashed onto the device. Since the partition is marked `encrypted` and :doc:`Flash Encryption <../../security/flash-encryption>` is enabled, bootloader will encrypt this partition using flash encryption key on first boot. Alternatively, the keys can be generated after startup using ``nvs_flash_generate_keys`` API provided by ``nvs_flash.h``, which will then write those keys onto the key-partition in encrypted form.
+
+It is possible for an application to use different keys for different NVS partitions and thereby have multiple key-partitions. However, it is a responsibilty of the application to provide correct key-partition/keys for the purpose of encryption/decryption.
+
+Encrypted Read/Write
+^^^^^^^^^^^^^^^^^^^^
+
+The same NVS APIs ``nvs_read_*`` or ``nvs_write_*`` can be used for reading and writing of encrypted nvs partition as well. However, the APIs for initialising NVS partitions are different. ``nvs_flash_secure_init`` and ``nvs_flash_secure_init_partition`` are used for initialising instead of ``nvs_flash_init`` and ``nvs_flash_init_partition`` respectively. ``nvs_sec_cfg_t`` structure required for these APIs can be populated using ``nvs_flash_generate_keys`` or ``nvs_flash_read_security_cfg``.
+
+Applications are expected to follow the following steps in order to perform NVS read/write operations with encryption enabled.
+
+    1. Find key partition and NVS data partition using ``esp_partition_find*`` APIs.
+    2. Populate ``nvs_sec_cfg_t`` struct using ``nvs_flash_read_security_cfg`` or ``nvs_flash_generate_keys`` APIs.
+    3. Initialise NVS flash partition using ``nvs_flash_secure_init`` or ``nvs_flash_secure_init_partition`` APIs.
+    4. Open a namespace using ``nvs_open`` or ``nvs_open_from_part`` APIs
+    5. Perform NVS read/write operations using ``nvs_read_*`` or ``nvs_write_*``
+    6. Deinitialise NVS partition using ``nvs_flash_deinit``.
index a905ca689eeada9e41d5bd921f6c3e0bf370e136..2341ae7b4ff3424ae1571193f623a98e793b8833 100644 (file)
@@ -5,4 +5,3 @@
 COMPONENT_ADD_INCLUDEDIRS := include
 
 COMPONENT_SRCDIRS := src
-
index e7889ddea38cdd154586102c0e27ccd301339d17..74cb01b9e67ab7cb0f272574032d5a56c8833504 100644 (file)
@@ -28,23 +28,32 @@ extern "C" {
  */
 typedef uint32_t nvs_handle;
 
-#define ESP_ERR_NVS_BASE                0x1100                     /*!< Starting number of error codes */
-#define ESP_ERR_NVS_NOT_INITIALIZED     (ESP_ERR_NVS_BASE + 0x01)  /*!< The storage driver is not initialized */
-#define ESP_ERR_NVS_NOT_FOUND           (ESP_ERR_NVS_BASE + 0x02)  /*!< Id namespace doesn’t exist yet and mode is NVS_READONLY */
-#define ESP_ERR_NVS_TYPE_MISMATCH       (ESP_ERR_NVS_BASE + 0x03)  /*!< The type of set or get operation doesn't match the type of value stored in NVS */
-#define ESP_ERR_NVS_READ_ONLY           (ESP_ERR_NVS_BASE + 0x04)  /*!< Storage handle was opened as read only */
-#define ESP_ERR_NVS_NOT_ENOUGH_SPACE    (ESP_ERR_NVS_BASE + 0x05)  /*!< There is not enough space in the underlying storage to save the value */
-#define ESP_ERR_NVS_INVALID_NAME        (ESP_ERR_NVS_BASE + 0x06)  /*!< Namespace name doesn’t satisfy constraints */
-#define ESP_ERR_NVS_INVALID_HANDLE      (ESP_ERR_NVS_BASE + 0x07)  /*!< Handle has been closed or is NULL */
-#define ESP_ERR_NVS_REMOVE_FAILED       (ESP_ERR_NVS_BASE + 0x08)  /*!< The value wasn’t updated because flash write operation has failed. The value was written however, and update will be finished after re-initialization of nvs, provided that flash operation doesn’t fail again. */
-#define ESP_ERR_NVS_KEY_TOO_LONG        (ESP_ERR_NVS_BASE + 0x09)  /*!< Key name is too long */
-#define ESP_ERR_NVS_PAGE_FULL           (ESP_ERR_NVS_BASE + 0x0a)  /*!< Internal error; never returned by nvs API functions */
-#define ESP_ERR_NVS_INVALID_STATE       (ESP_ERR_NVS_BASE + 0x0b)  /*!< NVS is in an inconsistent state due to a previous error. Call nvs_flash_init and nvs_open again, then retry. */
-#define ESP_ERR_NVS_INVALID_LENGTH      (ESP_ERR_NVS_BASE + 0x0c)  /*!< String or blob length is not sufficient to store data */
-#define ESP_ERR_NVS_NO_FREE_PAGES       (ESP_ERR_NVS_BASE + 0x0d)  /*!< NVS partition doesn't contain any empty pages. This may happen if NVS partition was truncated. Erase the whole partition and call nvs_flash_init again. */
-#define ESP_ERR_NVS_VALUE_TOO_LONG      (ESP_ERR_NVS_BASE + 0x0e)  /*!< String or blob length is longer than supported by the implementation */
-#define ESP_ERR_NVS_PART_NOT_FOUND      (ESP_ERR_NVS_BASE + 0x0f)  /*!< Partition with specified name is not found in the partition table */
-#define ESP_ERR_NVS_NEW_VERSION_FOUND   (ESP_ERR_NVS_BASE + 0x10)  /*!< NVS partition contains data in new format and cannot be recognized by this version of code */
+#define ESP_ERR_NVS_BASE                    0x1100                     /*!< Starting number of error codes */
+#define ESP_ERR_NVS_NOT_INITIALIZED         (ESP_ERR_NVS_BASE + 0x01)  /*!< The storage driver is not initialized */
+#define ESP_ERR_NVS_NOT_FOUND               (ESP_ERR_NVS_BASE + 0x02)  /*!< Id namespace doesn’t exist yet and mode is NVS_READONLY */
+#define ESP_ERR_NVS_TYPE_MISMATCH           (ESP_ERR_NVS_BASE + 0x03)  /*!< The type of set or get operation doesn't match the type of value stored in NVS */
+#define ESP_ERR_NVS_READ_ONLY               (ESP_ERR_NVS_BASE + 0x04)  /*!< Storage handle was opened as read only */
+#define ESP_ERR_NVS_NOT_ENOUGH_SPACE        (ESP_ERR_NVS_BASE + 0x05)  /*!< There is not enough space in the underlying storage to save the value */
+#define ESP_ERR_NVS_INVALID_NAME            (ESP_ERR_NVS_BASE + 0x06)  /*!< Namespace name doesn’t satisfy constraints */
+#define ESP_ERR_NVS_INVALID_HANDLE          (ESP_ERR_NVS_BASE + 0x07)  /*!< Handle has been closed or is NULL */
+#define ESP_ERR_NVS_REMOVE_FAILED           (ESP_ERR_NVS_BASE + 0x08)  /*!< The value wasn’t updated because flash write operation has failed. The value was written however, and update will be finished after re-initialization of nvs, provided that flash operation doesn’t fail again. */
+#define ESP_ERR_NVS_KEY_TOO_LONG            (ESP_ERR_NVS_BASE + 0x09)  /*!< Key name is too long */
+#define ESP_ERR_NVS_PAGE_FULL               (ESP_ERR_NVS_BASE + 0x0a)  /*!< Internal error; never returned by nvs API functions */
+#define ESP_ERR_NVS_INVALID_STATE           (ESP_ERR_NVS_BASE + 0x0b)  /*!< NVS is in an inconsistent state due to a previous error. Call nvs_flash_init and nvs_open again, then retry. */
+#define ESP_ERR_NVS_INVALID_LENGTH          (ESP_ERR_NVS_BASE + 0x0c)  /*!< String or blob length is not sufficient to store data */
+#define ESP_ERR_NVS_NO_FREE_PAGES           (ESP_ERR_NVS_BASE + 0x0d)  /*!< NVS partition doesn't contain any empty pages. This may happen if NVS partition was truncated. Erase the whole partition and call nvs_flash_init again. */
+#define ESP_ERR_NVS_VALUE_TOO_LONG          (ESP_ERR_NVS_BASE + 0x0e)  /*!< String or blob length is longer than supported by the implementation */
+#define ESP_ERR_NVS_PART_NOT_FOUND          (ESP_ERR_NVS_BASE + 0x0f)  /*!< Partition with specified name is not found in the partition table */
+
+#define ESP_ERR_NVS_NEW_VERSION_FOUND       (ESP_ERR_NVS_BASE + 0x10)  /*!< NVS partition contains data in new format and cannot be recognized by this version of code */
+#define ESP_ERR_NVS_XTS_ENCR_FAILED         (ESP_ERR_NVS_BASE + 0x11)  /*!< XTS encryption failed while writing NVS entry */
+#define ESP_ERR_NVS_XTS_DECR_FAILED         (ESP_ERR_NVS_BASE + 0x12)  /*!< XTS decryption failed while reading NVS entry */
+#define ESP_ERR_NVS_XTS_CFG_FAILED          (ESP_ERR_NVS_BASE + 0x13)  /*!< XTS configuration setting failed */
+#define ESP_ERR_NVS_XTS_CFG_NOT_FOUND       (ESP_ERR_NVS_BASE + 0x14)  /*!< XTS configuration not found */
+#define ESP_ERR_NVS_ENCR_NOT_SUPPORTED      (ESP_ERR_NVS_BASE + 0x15)  /*!< NVS encryption is not supported in this version */
+#define ESP_ERR_NVS_KEYS_NOT_INITIALIZED    (ESP_ERR_NVS_BASE + 0x16)  /*!< NVS key partition is uninitialized */
+#define ESP_ERR_NVS_CORRUPT_KEY_PART        (ESP_ERR_NVS_BASE + 0x17)  /*!< NVS key partition is corrupt */
+
 
 #define NVS_DEFAULT_PART_NAME           "nvs"   /*!< Default partition name of the NVS partition in the partition table */
 /**
index a7ef7f4511efc564bd978a7a969618d8ccb4a0e5..55b218ec56ee9aafe50316dcf9bb33fc5f66b32c 100644 (file)
@@ -19,6 +19,18 @@ extern "C" {
 #endif
 
 #include "nvs.h"
+#include "esp_partition.h"
+
+
+#define NVS_KEY_SIZE 32 // AES-256
+
+/**
+ * @brief Key for encryption and decryption
+ */
+typedef struct {
+    uint8_t eky[NVS_KEY_SIZE]; /*!<  XTS encryption and decryption key*/
+    uint8_t tky[NVS_KEY_SIZE]; /*!<  XTS tweak key */
+} nvs_sec_cfg_t;
 
 /**
  * @brief Initialize the default NVS partition.
@@ -99,6 +111,81 @@ esp_err_t nvs_flash_erase(void);
  */
 esp_err_t nvs_flash_erase_partition(const char *part_name);
 
+
+/**
+ * @brief Initialize the default NVS partition.
+ *
+ * This API initialises the default NVS partition. The default NVS partition
+ * is the one that is labeled "nvs" in the partition table.
+ *
+ * @param[in]  cfg Security configuration (keys) to be used for NVS encryption/decryption. 
+ *                              If cfg is NULL, no encryption is used. 
+ *
+ * @return
+ *      - ESP_OK if storage was successfully initialized.
+ *      - ESP_ERR_NVS_NO_FREE_PAGES if the NVS storage contains no empty pages
+ *        (which may happen if NVS partition was truncated)
+ *      - ESP_ERR_NOT_FOUND if no partition with label "nvs" is found in the partition table
+ *      - one of the error codes from the underlying flash storage driver
+ */
+esp_err_t nvs_flash_secure_init(nvs_sec_cfg_t* cfg);
+
+/**
+ * @brief Initialize NVS flash storage for the specified partition.
+ *
+ * @param[in]  partition_label   Label of the partition. Note that internally a reference to
+ *                               passed value is kept and it should be accessible for future operations
+ *
+ * @param[in]  cfg Security configuration (keys) to be used for NVS encryption/decryption. 
+ *                              If cfg is null, no encryption/decryption is used. 
+ * @return
+ *      - ESP_OK if storage was successfully initialized.
+ *      - ESP_ERR_NVS_NO_FREE_PAGES if the NVS storage contains no empty pages
+ *        (which may happen if NVS partition was truncated)
+ *      - ESP_ERR_NOT_FOUND if specified partition is not found in the partition table
+ *      - one of the error codes from the underlying flash storage driver
+ */
+esp_err_t nvs_flash_secure_init_partition(const char *partition_label, nvs_sec_cfg_t* cfg);
+
+/**
+ * @brief Generate and store NVS keys in the provided esp partition
+ * 
+ * @param[in]  partition Pointer to partition structure obtained using
+ *                       esp_partition_find_first or esp_partition_get.
+ *                       Must be non-NULL.
+ * @param[out] cfg       Pointer to nvs security configuration structure.
+ *                       Pointer must be non-NULL. 
+ *                       Generated keys will be populated in this structure.
+ *
+ *
+ * @return 
+ *      -ESP_OK, if cfg was read successfully;
+ *      -or error codes from esp_partition_write/erase APIs.
+ */
+
+esp_err_t nvs_flash_generate_keys(const esp_partition_t* partition, nvs_sec_cfg_t* cfg);
+
+
+/**
+ * @brief Read NVS security configuration from a partition. 
+ * 
+ * @param[in]  partition Pointer to partition structure obtained using
+ *                       esp_partition_find_first or esp_partition_get.
+ *                       Must be non-NULL.
+ * @param[out] cfg       Pointer to nvs security configuration structure.
+ *                       Pointer must be non-NULL.
+ *
+ * @note  Provided parition is assumed to be marked 'encrypted'.
+ *
+ * @return 
+ *      -ESP_OK, if cfg was read successfully;
+ *      -ESP_ERR_NVS_KEYS_NOT_INITIALIZED, if the partition is not yet written with keys.
+ *      -ESP_ERR_NVS_CORRUPT_KEY_PART, if the partition containing keys is found to be corrupt
+ *      -or error codes from esp_partition_read API.
+ */
+
+esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partition, nvs_sec_cfg_t* cfg);
+
 #ifdef __cplusplus
 }
 #endif
index 95b5d97bdbc5099ba75193172c453b2fd4dbf69e..cae8940b81b65109eb6a89b989e21636009bcdab 100644 (file)
@@ -7,6 +7,13 @@ Introduction
 :component_file:`nvs_flash/nvs_partition_generator/nvs_partition_gen.py` utility is designed to help create a binary file, compatible with NVS architecture defined in :doc:`Non-Volatile Storage </api-reference/storage/nvs_flash>`, based on user provided key-value pairs in a CSV file.
 Utility is ideally suited for generating a binary blob, containing data specific to ODM/OEM, which can be flashed externally at the time of device manufacturing. This helps manufacturers set unique value for various parameters for each device, e.g. serial number, while using same application firmaware for all devices.
 
+Prerequisites
+-------------
+To use this utility in encryption mode, the following packages need to be installed:
+    - cryptography package
+
+This dependency is already captured by including these packages in `requirement.txt` in top level IDF directory.
+
 CSV file format
 ---------------
 
@@ -52,53 +59,82 @@ Multipage Blob Support
 By default, binary blobs are allowed to span over multiple pages and written in the format mentioned in section :ref:`structure_of_entry`. 
 If older format is intended to be used, the utility provides an option to disable this feature.
 
-Running the utility
+Encryption Support
 -------------------
+This utility allows you to create an enrypted binary file also. Encryption used is AES-XTS encryption. Refer to :ref:`nvs_encryption` for more details.
 
-You can run the utility in two modes using below command:
-    - Multipage Blob Support Enabled (v2)
-    - Multipage Blob Support Disabled (v1)
-
+Running the utility
+-------------------
 
 *Usage*::
 
     python nvs_partition_gen.py [--version {v1,v2}] input output
 
+You can run this utility in two modes:
+    -   Normal mode - Binary generated in this mode is an unencrypted binary file.
+    -   Encryption mode - Binary generated in this mode is an encrypted binary file.
 
-Positional arguments:
+*In normal mode:*
 
-+------------------------+----------------------------------------------------------------------------------------------+
-|   Arguments            |                                     Description                                              |
-+========================+==============================================================================================+
-| input                  |  Path to CSV file to parse. Will use stdin if omitted (sample files are provided)            |
-+------------------------+----------------------------------------------------------------------------------------------+
-| output                 |  Path to output converted binary file. Will use stdout if omitted                            |
-+------------------------+----------------------------------------------------------------------------------------------+
+    A sample CSV file is provided with the utility. You can run the utility using below command::
 
-Optional arguments:
+        python nvs_partition_generator.py sample.csv sample.bin
 
-+-------------------------------+---------------------------------------------------------------------------------------+
-|   Arguments                   |                                     Description                                       |
-+===============================+=======================================================================================+
-| --version {v1,v2}             |  Set version. Default: v2                                                             |
-+-------------------------------+---------------------------------------------------------------------------------------+
+*In encryption mode:*
+
+    You can run the utility using below commands:
+
+            -   By taking encryption keys as an input file. A sample encryption keys file is provided with the utility::
+
+                   python nvs_partition_gen.py sample.csv sample_encrypted.bin --encrypt True --keyfile testdata/keys.txt
+
+            -   By enabling generation of encryption keys::
 
+                   python nvs_partition_gen.py sample.csv sample_encrypted.bin --encrypt True --keygen True
 
-*Multipage Blob Support Enabled Mode:*
 
-You can run the utility in this mode by setting the version parameter to v2, as shown below.
+.. note:: In encryption mode, this utility creates a binary file named `encryption_keys.bin` containing the encryption keys used. This binary file is compatible with NVS key-partition structure. Refer to :ref:`nvs_key_partition` for more details.
+
+
+You can also provide the format version number while running this utility:
+    - Multipage Blob Support Enabled (v2)
+    - Multipage Blob Support Disabled (v1)
+
+
+*Multipage Blob Support Enabled (v2):*
+
+You can run the utility in this format by setting the version parameter to v2, as shown below.
 A sample CSV file is provided with the utility::
 
     python nvs_partition_gen.py sample_multipage_blob.csv partition_multipage_blob.bin --version v2
 
 
-*Multipage Blob Support Disabled Mode:*
+*Multipage Blob Support Disabled (v1):*
 
-You can run the utility in this mode by setting the version parameter to v1, as shown below.
+You can run the utility in this format by setting the version parameter to v1, as shown below.
 A sample CSV file is provided with the utility::
 
     python nvs_partition_gen.py sample_singlepage_blob.csv partition_single_page.bin --version v1
 
++------------------------+----------------------------------------------------------------------------------------------+
+|   Arguments            |                                     Description                                              |
++========================+==============================================================================================+
+| input                  | Path to CSV file to parse. Will use stdin if omitted                                         |
++------------------------+----------------------------------------------------------------------------------------------+
+| output                 | Path to output converted binary file. Will use stdout if omitted                             |
++------------------------+----------------------------------------------------------------------------------------------+
+| size                   | Size of NVS Partition in KB. Eg. 12KB                                                        |
++------------------------+----------------------------------------------------------------------------------------------+
+| --version {v1,v2}      |  Set version. Default: v2                                                                    |
++-------------------------------+---------------------------------------------------------------------------------------+
+| --keygen {True,False}  |  Generate keys for encryption. Default: False                                                |
+|                        |  (Applicable only if encryption mode is true)                                                |
++------------------------+----------------------------------------------------------------------------------------------+
+| --encrypt {True,False} |  Set encryption mode. Default: False                                                         |
++------------------------+----------------------------------------------------------------------------------------------+
+| --keyfile KEYFILE      | File having key for encryption (Applicable only if encryption mode is true)                  |
++------------------------+----------------------------------------------------------------------------------------------+
+
 Caveats
 -------
 -  Utility doesn't check for duplicate keys and will write data pertaining to both keys. User needs to make sure keys are distinct.
index 4aac810c3ec5b77167ed34a58393d7190669bce8..683fc956f02185ef7f0af127e653e3da04114aae 100755 (executable)
 import sys
 import argparse
 import binascii
-import getopt
+import random
 import struct
 import os
 import array
 import csv
 import zlib
-from os import path
+from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+from cryptography.hazmat.backends import default_backend
 
 """ Class for standard NVS page structure """
 class Page(object):
@@ -63,6 +64,8 @@ class Page(object):
 
     def __init__(self, page_num):
         self.entry_num = 0
+        self.is_encrypt = False
+        self.encr_key = None
         self.bitmap_array = array.array('B')
         self.version = Page.VERSION1
         self.page_buf = bytearray(b'\xff')*Page.PAGE_PARAMS["max_size"]
@@ -89,6 +92,7 @@ class Page(object):
         page_header[28:32] = struct.pack('<I', crc & 0xFFFFFFFF)
         self.page_buf[0:len(page_header)] = page_header
 
+
     def create_bitmap_array(self):
         bitarray = array.array('B')
         charsize = 32 # bitmaparray has 256 bits, hence 32 bytes
@@ -96,6 +100,7 @@ class Page(object):
         bitarray.extend((fill,) * charsize)
         return bitarray
 
+
     def write_bitmaparray(self):
         bitnum = self.entry_num * 2
         byte_idx = bitnum / 8 # Find byte index in the array
@@ -106,16 +111,104 @@ class Page(object):
         end_idx = Page.BITMAPARRAY_OFFSET + Page.BITMAPARRAY_SIZE_IN_BYTES
         self.page_buf[start_idx:end_idx] = self.bitmap_array
 
-    def write_entry_to_buf(self, data, entrycount):
+
+    def encrypt_entry(self, data_arr, tweak_arr, encr_key):
+        # Encrypt 32 bytes of data using AES-XTS encryption
+        backend = default_backend()
+        plain_text = data_arr.decode('hex')
+        tweak = tweak_arr.decode('hex')
+        cipher = Cipher(algorithms.AES(encr_key), modes.XTS(tweak), backend=backend)
+        encryptor = cipher.encryptor()
+        encrypted_data = encryptor.update(plain_text)
+
+        return encrypted_data
+
+
+    def reverse_hexbytes(self, addr_tmp):
+        addr = []
+        reversed_bytes = ""
+        for i in range(0, len(addr_tmp), 2):
+            addr.append(addr_tmp[i:i+2])
+        reversed_bytes = "".join(reversed(addr))
+
+        return reversed_bytes
+
+
+    def encrypt_data(self, data_input, no_of_entries, nvs_obj):
+        # Set values needed for encryption and encrypt data byte wise
+        encr_data_to_write = ''
+        data_len_needed = 64 #in hex
+        tweak_len_needed = 32 #in hex
+        init_tweak_val = '0'
+        init_data_val = 'f'
+        tweak_tmp = ''
+        encr_key_input = None
+
+        # Extract encryption key and tweak key from given key input
+        encr_key_input = self.encr_key.decode('hex')
+
+        rel_addr = nvs_obj.page_num * Page.PAGE_PARAMS["max_size"] + Page.FIRST_ENTRY_OFFSET
+
+        if type(data_input) != bytearray:
+            byte_arr = bytearray('\xff') * 32
+            byte_arr[0:len(data_input)] = data_input
+            data_input = byte_arr
+
+        data_input = binascii.hexlify(bytearray(data_input))
+
+        entry_no = self.entry_num
+        start_idx = 0
+        end_idx = start_idx + 64
+
+        for _ in range(0, no_of_entries):
+            # Set tweak value
+            offset = entry_no * Page.SINGLE_ENTRY_SIZE
+            addr = hex(rel_addr + offset)[2:]
+            addr_len = len(addr)
+            if addr_len > 2:
+                if not addr_len % 2:
+                    addr_tmp = addr
+                    tweak_tmp = self.reverse_hexbytes(addr_tmp)
+                    tweak_val = tweak_tmp + (init_tweak_val * (tweak_len_needed - (len(tweak_tmp))))
+                else:
+                    addr_tmp = init_tweak_val + addr
+                    tweak_tmp = self.reverse_hexbytes(addr_tmp)
+                    tweak_val = tweak_tmp + (init_tweak_val * (tweak_len_needed - (len(tweak_tmp))))
+            else:
+                tweak_val = addr + (init_tweak_val * (tweak_len_needed - len(addr)))
+
+            # Encrypt data
+            data_bytes = data_input[start_idx:end_idx]
+            data_val = data_bytes + (init_data_val * (data_len_needed - len(data_bytes)))
+            encr_data_ret = self.encrypt_entry(data_val, tweak_val, encr_key_input)
+            encr_data_to_write = encr_data_to_write + encr_data_ret
+            # Update values for encrypting next set of data bytes
+            start_idx = end_idx
+            end_idx = start_idx + 64
+            entry_no += 1
+
+
+        return encr_data_to_write
+
+
+    def write_entry_to_buf(self, data, entrycount,nvs_obj):
+        encr_data = bytearray()
+        if self.is_encrypt:
+            encr_data_ret = self.encrypt_data(data, entrycount,nvs_obj)
+            encr_data[0:len(encr_data_ret)] = encr_data_ret
+            data = encr_data
+
         data_offset = Page.FIRST_ENTRY_OFFSET + (Page.SINGLE_ENTRY_SIZE * self.entry_num)
         start_idx = data_offset
         end_idx = data_offset + len(data)
         self.page_buf[start_idx:end_idx]  = data
+
         # Set bitmap array for entries in current page
         for i in range(0, entrycount):
             self.write_bitmaparray()
             self.entry_num += 1
 
+
     def set_crc_header(self, entry_struct):
         crc_data = bytearray(28)
         crc_data[0:4] = entry_struct[0:4]
@@ -174,9 +267,9 @@ class Page(object):
             entry_struct = self.set_crc_header(entry_struct)
 
             # write entry header
-            self.write_entry_to_buf(entry_struct, 1)
+            self.write_entry_to_buf(entry_struct, 1,nvs_obj)
             # write actual data
-            self.write_entry_to_buf(data_chunk, datachunk_entry_count)
+            self.write_entry_to_buf(data_chunk, datachunk_entry_count,nvs_obj)
 
             chunk_count = chunk_count + 1
 
@@ -189,8 +282,13 @@ class Page(object):
 
             offset = offset + chunk_size
 
+
             # All chunks are stored, now store the index
             if not remaining_size:
+                # Initialise data field to 0xff
+                data_array = bytearray('\xff')*8
+                entry_struct[24:32] = data_array
+
                 # change type of data to BLOB_IDX
                 entry_struct[1] = Page.BLOB_IDX
 
@@ -208,14 +306,14 @@ class Page(object):
                 # compute crc of entry header
                 entry_struct = self.set_crc_header(entry_struct)
 
-                # write entry header
-                self.write_entry_to_buf(entry_struct, 1)
+                # write last entry
+                self.write_entry_to_buf(entry_struct, 1,nvs_obj)
                 break
 
         return entry_struct
 
 
-    def write_single_page_entry(self, entry_struct, data, datalen, data_entry_count):
+    def write_single_page_entry(self, entry_struct, data, datalen, data_entry_count, nvs_obj):
         # compute CRC of data
         entry_struct[24:26] = struct.pack('<H', datalen)
         crc = zlib.crc32(data, 0xFFFFFFFF)
@@ -225,16 +323,16 @@ class Page(object):
         entry_struct = self.set_crc_header(entry_struct)
 
         # write entry header
-        self.write_entry_to_buf(entry_struct, 1)
+        self.write_entry_to_buf(entry_struct, 1, nvs_obj)
         # write actual data
-        self.write_entry_to_buf(data, data_entry_count)
+        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
     according to encoding specified.
     """
-    def write_varlen_data(self, key, data, encoding, ns_index, nvs_obj):
+    def write_varlen_data(self, key, data, encoding, ns_index,nvs_obj):
         # Set size of data
         datalen = len(data)
 
@@ -286,12 +384,12 @@ class Page(object):
                 entry_struct = self.write_varlen_binary_data(entry_struct,ns_index,key,data,\
                 datalen,total_entry_count, nvs_obj)
         else:
-            self.write_single_page_entry(entry_struct, data, datalen, data_entry_count)
+            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. """
-    def write_primitive_data(self, key, data, encoding, ns_index):
+    def write_primitive_data(self, key, data, encoding, ns_index,nvs_obj):
         # Check if entry exceeds max number of entries allowed per page
         if self.entry_num >= Page.PAGE_PARAMS["max_entries"]:
             raise PageFullError()
@@ -331,7 +429,7 @@ class Page(object):
         entry_struct[4:8] = struct.pack('<I', crc & 0xFFFFFFFF)
 
         # write to file
-        self.write_entry_to_buf(entry_struct, 1)
+        self.write_entry_to_buf(entry_struct, 1,nvs_obj)
 
     """ Get page buffer data of a given page """
     def get_data(self):
@@ -360,6 +458,8 @@ class NVS(object):
         self.page_num += 1
         new_page = Page(self.page_num)
         new_page.version = version
+        new_page.is_encrypt = is_encrypt_data
+        new_page.encr_key = key_input
         self.pages.append(new_page)
         self.cur_page = new_page
         return new_page
@@ -371,10 +471,10 @@ class NVS(object):
     def write_namespace(self, key):
         self.namespace_idx += 1
         try:
-            self.cur_page.write_primitive_data(key, self.namespace_idx, "u8", 0)
+            self.cur_page.write_primitive_data(key, self.namespace_idx, "u8", 0,self)
         except PageFullError:
             new_page = self.create_new_page()
-            new_page.write_primitive_data(key, self.namespace_idx, "u8", 0)
+            new_page.write_primitive_data(key, self.namespace_idx, "u8", 0,self)
             pass
 
     """
@@ -400,17 +500,17 @@ class NVS(object):
         primitive_encodings = ["u8", "i8", "u16", "u32", "i32"]
         if encoding in varlen_encodings:
             try:
-                self.cur_page.write_varlen_data(key, value, encoding, self.namespace_idx, self)
+                self.cur_page.write_varlen_data(key, value, encoding, self.namespace_idx,self)
             except PageFullError:
                 new_page = self.create_new_page()
-                new_page.write_varlen_data(key, value, encoding, self.namespace_idx,  self)
+                new_page.write_varlen_data(key, value, encoding, self.namespace_idx,self)
                 pass
         elif encoding in primitive_encodings:
             try:
-                self.cur_page.write_primitive_data(key, int(value), encoding, self.namespace_idx)
+                self.cur_page.write_primitive_data(key, int(value), encoding, self.namespace_idx,self)
             except PageFullError:
                 new_page = self.create_new_page()
-                new_page.write_primitive_data(key, int(value), encoding, self.namespace_idx)
+                new_page.write_primitive_data(key, int(value), encoding, self.namespace_idx,self)
                 sys.exc_clear()
                 pass
         else:
@@ -456,6 +556,7 @@ def write_entry(nvs_instance, key, datatype, encoding, value):
     :param value: Data value in ascii encoded string format for "data" datatype and filepath for "file" datatype
     :return: None
     """
+
     if datatype == "file":
         abs_file_path = value
         if os.path.isabs(value) == False:
@@ -477,21 +578,56 @@ def nvs_close(nvs_instance):
     """
     nvs_instance.__exit__(None, None, None)
 
-def nvs_part_gen(input_filename=None, output_filename=None, version_no=None):
+def nvs_part_gen(input_filename=None, output_filename=None, key_gen=None, encrypt_mode=None, key_file=None, version_no=None):
     """ Wrapper to generate nvs partition binary
 
     :param input_filename: Name of input file containing data
     :param output_filename: Name of output file to store generated binary
+    :param key_gen: Enable encryption key generation in encryption mode
+    :param encrypt_mode: Enable/Disable encryption mode
+    :param key_file: Input file having encryption keys in encryption mode
+    :param version_no: NVS format version
     :return: None
     """
-    global version
+    global version, is_encrypt_data, key_input
     version = version_no
+    key_input = None
+    is_encrypt_data = encrypt_mode
 
     if version == 'v1':
         version = Page.VERSION1
     elif version == 'v2':
         version = Page.VERSION2
     
+    if is_encrypt_data == 'True':
+        is_encrypt_data = True
+    elif is_encrypt_data == 'False':
+        is_encrypt_data = False
+
+    if key_gen == 'True':
+        key_gen = True
+    elif key_gen == 'False':
+        key_gen = False
+
+    if is_encrypt_data and not key_gen and not key_file:
+        sys.exit("Missing parameter. Enter --keyfile or --keygen.")
+
+    if is_encrypt_data and key_gen and key_file:
+        sys.exit("Only one input allowed. Enter --keyfile or --keygen.")
+
+    if not is_encrypt_data and key_gen:
+        sys.exit("Invalid. Cannot give --key_gen as --encrypt is set to False.")
+
+    if not is_encrypt_data and key_file:
+        sys.exit("Invalid. Cannot give --key_file as --encrypt is set to False.")
+
+    if key_gen:
+        key_input = ''.join(random.choice('0123456789abcdef') for _ in xrange(128))
+    elif key_file:
+        with open(key_file) as key_f:
+            key_input = key_f.readline()
+            key_input = key_input.strip()
+
     input_file = open(input_filename, 'rb')
     output_file = open(output_filename, 'wb')
 
@@ -500,7 +636,7 @@ def nvs_part_gen(input_filename=None, output_filename=None, version_no=None):
         for row in reader:
             try:
                 write_entry(nvs_obj, row["key"], row["type"], row["encoding"], row["value"])
-            except InputError as e:
+            except (InputError) as e:
                 print(e)
                 input_file.close()
                 output_file.close()
@@ -509,6 +645,19 @@ def nvs_part_gen(input_filename=None, output_filename=None, version_no=None):
     input_file.close()
     output_file.close()
 
+    if is_encrypt_data:
+        output_keys_file = open("encryption_keys.bin",'wb')
+        keys_page_buf = bytearray(b'\xff')*Page.PAGE_PARAMS["max_size"]
+        key_bytes = key_input.decode('hex')
+        key_len = len(key_bytes)
+        keys_page_buf[0:key_len] = key_bytes
+        crc_data = keys_page_buf[0:key_len]
+        crc = zlib.crc32(buffer(crc_data), 0xFFFFFFFF)
+        keys_page_buf[64:68] = struct.pack('<I', crc & 0xFFFFFFFF)
+        output_keys_file.write(keys_page_buf)
+
+
+
 def main():
     parser = argparse.ArgumentParser(description="ESP32 NVS partition generation utility")
     parser.add_argument(
@@ -527,14 +676,33 @@ def main():
             choices=['v1','v2'],
             default='v2')
 
+    parser.add_argument(
+            "--keygen",
+            help='Generate keys for encryption. Default: False (Applicable only if encryption mode is true)',
+            choices=['True','False'],
+            default= 'False')
+
+    parser.add_argument(
+            "--encrypt",
+            help='Set encryption mode. Default: False',
+            choices=['True','False'],
+            default='False')
+
+    parser.add_argument(
+            "--keyfile",
+            help='File having key for encryption (Applicable only if encryption mode is true)',
+            default = None)
 
     args = parser.parse_args()
     input_filename = args.input
     output_filename = args.output
     version_no = args.version
 
-    nvs_part_gen(input_filename, output_filename, version_no)
+    key_gen = args.keygen
+    is_encrypt_data = args.encrypt
+    key_file = args.keyfile
 
+    nvs_part_gen(input_filename, output_filename, key_gen, is_encrypt_data, key_file, version_no)
 
 
 if __name__ == "__main__":
diff --git a/components/nvs_flash/nvs_partition_generator/testdata/encryption_keys.txt b/components/nvs_flash/nvs_partition_generator/testdata/encryption_keys.txt
new file mode 100644 (file)
index 0000000..a52365a
--- /dev/null
@@ -0,0 +1 @@
+11111111111111111111111111111111111111111111111111111111111111112222222222222222222222222222222222222222222222222222222222222222
index 6bae6a657e5d4d90853d893f197aa3953a74f090..bb580502ec74a2ea135fa83c0369eb046b69ab21 100644 (file)
@@ -15,6 +15,7 @@
 #define intrusive_list_h
 
 #include <cassert>
+#include <unordered_map>
 
 template <typename T>
 class intrusive_list;
index 8d0e6c038935fcc686ccde7a65e5d2061361d465..35462dd33817713ed1548a17eb4cc7d3832c1e57 100644 (file)
 #include "nvs_platform.hpp"
 #include "esp_partition.h"
 #include "sdkconfig.h"
+#ifdef CONFIG_NVS_ENCRYPTION
+#include "nvs_encr.hpp"
+#endif
 
 #ifdef ESP_PLATFORM
+#include <rom/crc.h>
+
 // Uncomment this line to force output from this module
 // #define LOG_LOCAL_LEVEL ESP_LOG_DEBUG
 #include "esp_log.h"
 static const char* TAG = "nvs";
 #else
+#include "crc.h"
 #define ESP_LOGD(...)
 #endif
 
 extern "C" void nvs_dump(const char *partName);
 extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount);
 
+#ifdef CONFIG_NVS_ENCRYPTION
+extern "C" esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg);
+#endif
+
 class HandleEntry : public intrusive_list_node<HandleEntry>
 {
     static uint32_t s_nvs_next_handle;
@@ -91,6 +101,7 @@ extern "C" void nvs_dump(const char *partName)
 extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount)
 {
     ESP_LOGD(TAG, "nvs_flash_init_custom partition=%s start=%d count=%d", partName, baseSector, sectorCount);
+
     nvs::Storage* new_storage = NULL;
     nvs::Storage* storage = lookup_storage_from_name(partName);
     if (storage == NULL) {
@@ -109,6 +120,24 @@ extern "C" esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSe
     return err;
 }
 
+#ifdef CONFIG_NVS_ENCRYPTION
+extern "C" esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg)
+{
+    ESP_LOGD(TAG, "nvs_flash_secure_init_custom partition=%s start=%d count=%d", partName, baseSector, sectorCount);
+
+    if(cfg) {
+        auto encrMgr = EncrMgr::getInstance();
+        auto err = encrMgr->setSecurityContext(baseSector, sectorCount, cfg);
+        if(err != ESP_OK) {
+            return err;
+        }
+    }
+
+    return nvs_flash_init_custom(partName, baseSector, sectorCount);
+}
+#endif
+
+
 #ifdef ESP_PLATFORM
 extern "C" esp_err_t nvs_flash_init_partition(const char *part_name)
 {
@@ -136,6 +165,34 @@ extern "C" esp_err_t nvs_flash_init(void)
     return nvs_flash_init_partition(NVS_DEFAULT_PART_NAME);
 }
 
+#ifdef CONFIG_NVS_ENCRYPTION
+extern "C" esp_err_t nvs_flash_secure_init_partition(const char *part_name, nvs_sec_cfg_t* cfg)
+{
+    Lock::init();
+    Lock lock;
+    nvs::Storage* mStorage;
+
+    mStorage = lookup_storage_from_name(part_name);
+    if (mStorage) {
+        return ESP_OK;
+    }
+
+    const esp_partition_t* partition = esp_partition_find_first(
+            ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, part_name);
+    if (partition == NULL) {
+        return ESP_ERR_NOT_FOUND;
+    }
+
+    return nvs_flash_secure_init_custom(part_name, partition->address / SPI_FLASH_SEC_SIZE,
+            partition->size / SPI_FLASH_SEC_SIZE, cfg);
+}
+
+extern "C" esp_err_t nvs_flash_secure_init(nvs_sec_cfg_t* cfg)
+{
+    return nvs_flash_secure_init_partition(NVS_DEFAULT_PART_NAME, cfg);
+}
+#endif
+
 extern "C" esp_err_t nvs_flash_erase_partition(const char *part_name)
 {
     const esp_partition_t* partition = esp_partition_find_first(
@@ -163,6 +220,13 @@ extern "C" esp_err_t nvs_flash_deinit_partition(const char* partition_name)
         return ESP_ERR_NVS_NOT_INITIALIZED;
     }
 
+#ifdef CONFIG_NVS_ENCRYPTION
+    if(EncrMgr::isEncrActive()) {
+        auto encrMgr = EncrMgr::getInstance();
+        encrMgr->removeSecurityContext(storage->getBaseSector());
+    }
+#endif
+
     /* Clean up handles related to the storage being deinitialized */
     auto it = s_nvs_handles.begin();
     auto next = it;
@@ -507,3 +571,112 @@ extern "C" esp_err_t nvs_get_used_entry_count(nvs_handle handle, size_t* used_en
     }
     return err;
 }
+
+#if (defined CONFIG_NVS_ENCRYPTION) && (defined ESP_PLATFORM)
+
+extern "C" esp_err_t nvs_flash_generate_keys(const esp_partition_t* partition, nvs_sec_cfg_t* cfg)
+{
+    auto err = esp_partition_erase_range(partition, 0, partition->size);
+    if(err != ESP_OK) {
+        return err;
+    }
+
+    for(uint8_t cnt = 0; cnt < NVS_KEY_SIZE; cnt++) {
+        cfg->eky[cnt] = 0xff;
+        cfg->tky[cnt] = 0xee;
+    }
+
+    err = spi_flash_write(partition->address, cfg->eky, NVS_KEY_SIZE);
+    if(err != ESP_OK) { 
+        return err;
+    }
+
+    err = spi_flash_write(partition->address + NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE);
+    if(err != ESP_OK) {
+        return err;
+    }
+
+    err = esp_partition_read(partition, 0, cfg->eky, NVS_KEY_SIZE);
+    if(err != ESP_OK) { 
+        return err;
+    }
+
+    err = esp_partition_read(partition, NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE);
+    if(err != ESP_OK) { 
+        return err;
+    }
+
+    uint32_t crc_calc = crc32_le(0xffffffff, cfg->eky, NVS_KEY_SIZE);
+    crc_calc = crc32_le(crc_calc, cfg->tky, NVS_KEY_SIZE);
+
+    uint8_t crc_wr[16];
+    memset(crc_wr, 0xff, sizeof(crc_wr));
+    memcpy(crc_wr, &crc_calc, 4);
+
+    err = esp_partition_write(partition, 2 * NVS_KEY_SIZE, crc_wr, sizeof(crc_wr));
+    if(err != ESP_OK) { 
+        return err;
+    }
+    
+    return ESP_OK;
+
+}
+
+extern "C" esp_err_t nvs_flash_read_security_cfg(const esp_partition_t* partition, nvs_sec_cfg_t* cfg)
+{
+    uint8_t eky_raw[NVS_KEY_SIZE], tky_raw[NVS_KEY_SIZE];
+    uint32_t crc_raw, crc_read, crc_calc;
+
+    auto err = spi_flash_read(partition->address, eky_raw, NVS_KEY_SIZE);
+    if(err != ESP_OK) {
+        return err;
+    }
+
+    err = spi_flash_read(partition->address + NVS_KEY_SIZE, tky_raw, NVS_KEY_SIZE);
+    if(err != ESP_OK) {
+        return err;
+    }
+
+    err = spi_flash_read(partition->address + 2 * NVS_KEY_SIZE, &crc_raw, 4);
+    if(err != ESP_OK) {
+        return err;
+    }
+
+
+    uint8_t cnt = 0;
+
+    while(cnt < NVS_KEY_SIZE && eky_raw[cnt] == 0xff && tky_raw[cnt] == 0xff) cnt++;
+
+    if(cnt == NVS_KEY_SIZE && crc_raw == 0xffffffff) {
+        /* This is an uninitialized key partition*/
+        return ESP_ERR_NVS_KEYS_NOT_INITIALIZED;
+    }
+    
+    err = esp_partition_read(partition, 0, cfg->eky, NVS_KEY_SIZE);
+
+    if(err != ESP_OK) { 
+        return err;
+    }
+
+    err = esp_partition_read(partition, NVS_KEY_SIZE, cfg->tky, NVS_KEY_SIZE);
+    
+    if(err != ESP_OK) { 
+        return err;
+    }
+    err = esp_partition_read(partition, 2 * NVS_KEY_SIZE, &crc_read, 4);
+    
+    if(err != ESP_OK) { 
+        return err;
+    }
+    
+    crc_calc = crc32_le(0xffffffff, cfg->eky, NVS_KEY_SIZE);
+    crc_calc = crc32_le(crc_calc, cfg->tky, NVS_KEY_SIZE);
+
+    if(crc_calc != crc_read) { 
+        return ESP_ERR_NVS_CORRUPT_KEY_PART;
+    }
+
+    return ESP_OK;
+}
+#endif
diff --git a/components/nvs_flash/src/nvs_encr.cpp b/components/nvs_flash/src/nvs_encr.cpp
new file mode 100644 (file)
index 0000000..6f0b46c
--- /dev/null
@@ -0,0 +1,151 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "nvs_encr.hpp"
+#include "nvs_types.hpp"
+#include <string.h>
+
+namespace nvs
+{
+
+    bool EncrMgr::isActive = false;
+    EncrMgr* EncrMgr::instance = nullptr;
+
+
+    EncrMgr* EncrMgr::getInstance()
+    {
+        if(!isActive)
+        {
+            instance = new EncrMgr();
+            isActive = true;
+        }
+        return instance;
+    }
+
+    void EncrMgr::resetInstance()
+    {
+        if(isActive) {
+            delete instance;
+            instance = nullptr;
+            isActive = false;
+        }
+    }
+
+    bool EncrMgr::isEncrActive() {
+        return isActive;
+    }
+
+    XtsCtxt* EncrMgr::findXtsCtxtFromAddr(uint32_t addr) {
+
+        auto it = find_if(std::begin(xtsCtxtList), std::end(xtsCtxtList), [=](XtsCtxt& ctx) -> bool
+                { return (ctx.baseSector * SPI_FLASH_SEC_SIZE  <= addr)
+                && (addr < (ctx.baseSector + ctx.sectorCount) * SPI_FLASH_SEC_SIZE); });
+
+        if (it == std::end(xtsCtxtList)) {
+            return nullptr;
+        }
+        return it;
+    }
+
+    esp_err_t EncrMgr::setSecurityContext(uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg) {
+
+        uint8_t* eky = reinterpret_cast<uint8_t*>(cfg);
+
+        auto ctxt = new XtsCtxt();
+
+        ctxt->baseSector = baseSector;
+        ctxt->sectorCount = sectorCount;
+
+        mbedtls_aes_xts_init(ctxt->ectxt);
+        mbedtls_aes_xts_init(ctxt->dctxt);
+
+        if(mbedtls_aes_xts_setkey_enc(ctxt->ectxt, eky, 2 * NVS_KEY_SIZE * 8)) {
+            return ESP_ERR_NVS_XTS_CFG_FAILED;
+        }
+
+        if(mbedtls_aes_xts_setkey_dec(ctxt->dctxt, eky, 2 * NVS_KEY_SIZE * 8)) {
+            return ESP_ERR_NVS_XTS_CFG_FAILED;
+        }
+
+        xtsCtxtList.push_back(ctxt);
+
+        return ESP_OK;
+    }
+
+    esp_err_t EncrMgr::removeSecurityContext(uint32_t baseSector) {
+        auto xtsCtxt = findXtsCtxtFromAddr(baseSector * SPI_FLASH_SEC_SIZE);
+        if(!xtsCtxt) {
+            return ESP_ERR_NVS_XTS_CFG_NOT_FOUND;
+        }
+        xtsCtxtList.erase(xtsCtxt);
+        delete xtsCtxt;
+
+        if(!xtsCtxtList.size()) {
+            resetInstance();
+        }
+        return ESP_OK;
+    }
+
+
+    esp_err_t EncrMgr::encryptNvsData(uint8_t* ptxt, uint32_t addr, uint32_t ptxtLen, XtsCtxt* xtsCtxt) {
+
+        uint8_t entrySize = sizeof(Item);
+
+        //sector num required as an arr by mbedtls. Should have been just uint64/32.
+        uint8_t data_unit[16];
+
+        assert(ptxtLen % entrySize == 0);
+
+        /* Use relative address instead of absolute address (relocatable), so that host-generated
+         * encrypted nvs images can be used*/
+        uint32_t relAddr = addr - (xtsCtxt->baseSector * SPI_FLASH_SEC_SIZE);
+
+        memset(data_unit, 0, sizeof(data_unit));
+
+        for(uint8_t entry = 0; entry < (ptxtLen/entrySize); entry++)
+        {
+            uint32_t offset = entry * entrySize;
+            uint32_t *addr_loc = (uint32_t*) &data_unit[0];
+
+            *addr_loc = relAddr + offset;
+            if(mbedtls_aes_crypt_xts(xtsCtxt->ectxt, MBEDTLS_AES_ENCRYPT, entrySize, data_unit, ptxt + offset, ptxt + offset))  {
+                return ESP_ERR_NVS_XTS_ENCR_FAILED;
+            }
+        }
+        return ESP_OK;
+    }
+
+    esp_err_t EncrMgr::decryptNvsData(uint8_t* ctxt, uint32_t addr, uint32_t ctxtLen, XtsCtxt* xtsCtxt) {
+
+        //sector num required as an arr by mbedtls. Should have been just uint64/32.
+        uint8_t data_unit[16];
+
+
+        /** Currently upper layer of NVS reads entries one by one even for variable size
+        * multi-entry data types. So length should always be equal to size of an entry.*/
+        assert(ctxtLen == sizeof(Item));
+
+        uint32_t relAddr = addr - (xtsCtxt->baseSector * SPI_FLASH_SEC_SIZE);
+
+        memset(data_unit, 0, sizeof(data_unit));
+
+        memcpy(data_unit, &relAddr, sizeof(relAddr));
+
+        if(mbedtls_aes_crypt_xts(xtsCtxt->dctxt, MBEDTLS_AES_DECRYPT, ctxtLen, data_unit, ctxt, ctxt))  {
+            return ESP_ERR_NVS_XTS_DECR_FAILED;
+        }
+        return ESP_OK;
+    }
+
+} // namespace nvs
diff --git a/components/nvs_flash/src/nvs_encr.hpp b/components/nvs_flash/src/nvs_encr.hpp
new file mode 100644 (file)
index 0000000..3c136d3
--- /dev/null
@@ -0,0 +1,63 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef nvs_encr_hpp
+#define nvs_encr_hpp
+
+#include "esp_err.h"
+#include "mbedtls/aes.h"
+#include "intrusive_list.h"
+#include "nvs_flash.h"
+
+namespace nvs
+{
+
+struct XtsCtxt : public intrusive_list_node<XtsCtxt> {
+    public:
+        mbedtls_aes_xts_context ectxt[1];
+        mbedtls_aes_xts_context dctxt[1];
+        uint32_t baseSector;
+        uint32_t sectorCount;
+};
+
+
+/* A singleton class for managing nvs encryption*/
+class EncrMgr
+{
+    public:
+        static EncrMgr* getInstance();
+        static void resetInstance();
+        static bool isEncrActive();
+        esp_err_t setSecurityContext(uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg);
+        esp_err_t removeSecurityContext(uint32_t baseSector);
+        esp_err_t encryptNvsData(uint8_t* ptxt, uint32_t addr, uint32_t ptxtLen, XtsCtxt* xtsCtxt);
+        esp_err_t decryptNvsData(uint8_t* ctxt, uint32_t addr, uint32_t ctxtLen, XtsCtxt* xtsCtxt);
+        XtsCtxt* findXtsCtxtFromAddr(uint32_t addr);
+        ~EncrMgr() {}
+
+    protected:
+        static bool isActive;
+        static EncrMgr* instance;
+        intrusive_list<XtsCtxt> xtsCtxtList;
+        EncrMgr() {}
+
+}; // class EncrMgr
+
+esp_err_t nvs_flash_write(size_t destAddr, const void *srcAddr, size_t size);
+esp_err_t nvs_flash_read(size_t srcAddr, void *destAddr, size_t size);
+
+
+} // namespace nvs
+
+
+#endif /* nvs_encr_hpp */
diff --git a/components/nvs_flash/src/nvs_ops.cpp b/components/nvs_flash/src/nvs_ops.cpp
new file mode 100644 (file)
index 0000000..1ee32be
--- /dev/null
@@ -0,0 +1,73 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "esp_spi_flash.h"
+#include "nvs_ops.hpp"
+#ifdef CONFIG_NVS_ENCRYPTION
+#include "nvs_encr.hpp"
+#include <string.h>
+#endif
+
+namespace nvs
+{
+#ifdef CONFIG_NVS_ENCRYPTION
+esp_err_t nvs_flash_write(size_t destAddr, const void *srcAddr, size_t size) {
+
+    if(EncrMgr::isEncrActive()) {
+        auto encrMgr = EncrMgr::getInstance();
+        auto xtsCtxt = encrMgr->findXtsCtxtFromAddr(destAddr);
+
+        if(xtsCtxt) {
+            uint8_t* buf = static_cast<uint8_t*>(malloc(size));
+            memcpy(buf, srcAddr, size);
+            auto err = encrMgr->encryptNvsData(buf, destAddr, size, xtsCtxt);
+            if( err != ESP_OK) {
+                return err;
+            }
+            err = spi_flash_write(destAddr, buf, size);
+            delete buf;
+            return err;
+        }
+    }
+    return spi_flash_write(destAddr, srcAddr, size);
+}
+
+esp_err_t nvs_flash_read(size_t srcAddr, void *destAddr, size_t size) {
+    
+    auto err = spi_flash_read(srcAddr, destAddr, size);
+
+    if(err != ESP_OK) {
+        return err;
+    }
+
+    if(EncrMgr::isEncrActive()) {
+        auto encrMgr = EncrMgr::getInstance();
+        auto xtsCtxt = encrMgr->findXtsCtxtFromAddr(srcAddr);
+        if(xtsCtxt) {
+            return encrMgr->decryptNvsData(static_cast<uint8_t*>(destAddr),
+                    srcAddr, size, xtsCtxt);
+        }
+    }
+    return ESP_OK;
+}
+#else
+esp_err_t nvs_flash_write(size_t destAddr, const void *srcAddr, size_t size) {
+    return spi_flash_write(destAddr, srcAddr, size);
+}
+
+esp_err_t nvs_flash_read(size_t srcAddr, void *destAddr, size_t size) {
+    return spi_flash_read(srcAddr, destAddr, size);
+}
+#endif
+}
diff --git a/components/nvs_flash/src/nvs_ops.hpp b/components/nvs_flash/src/nvs_ops.hpp
new file mode 100644 (file)
index 0000000..20a9d14
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2015-2018 Espressif Systems (Shanghai) PTE LTD
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+#ifndef nvs_ops_hpp
+#define nvs_ops_hpp
+
+#include "esp_err.h"
+
+namespace nvs
+{
+    esp_err_t nvs_flash_write(size_t destAddr, const void *srcAddr, size_t size);
+    esp_err_t nvs_flash_read(size_t srcAddr, void *destAddr, size_t size);
+
+} // namespace nvs
+
+
+#endif /* nvs_ops_hpp */
index 73dabe8ea70f867232005f34fa68b14ed06d6d69..7b9de99baca0d807d56d5c2076a15e01cbb1df38 100644 (file)
@@ -20,6 +20,8 @@
 #include <cstdio>
 #include <cstring>
 
+#include "nvs_ops.hpp"
+
 namespace nvs
 {
 
@@ -91,13 +93,16 @@ esp_err_t Page::load(uint32_t sectorNumber)
 
 esp_err_t Page::writeEntry(const Item& item)
 {
-    auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), &item, sizeof(item));
-    if (rc != ESP_OK) {
+    esp_err_t err;
+
+    err = nvs_flash_write(getEntryAddress(mNextFreeEntry), &item, sizeof(item));
+
+    if (err != ESP_OK) {
         mState = PageState::INVALID;
-        return rc;
+        return err;
     }
 
-    auto err = alterEntryState(mNextFreeEntry, EntryState::WRITTEN);
+    err = alterEntryState(mNextFreeEntry, EntryState::WRITTEN);
     if (err != ESP_OK) {
         return err;
     }
@@ -137,7 +142,9 @@ esp_err_t Page::writeEntryData(const uint8_t* data, size_t size)
         memcpy((void*)buf, data, size);
     }
 #endif //ESP_PLATFORM
-    auto rc = spi_flash_write(getEntryAddress(mNextFreeEntry), buf, size);
+
+    auto rc = nvs_flash_write(getEntryAddress(mNextFreeEntry), buf, size);
+
 #ifdef ESP_PLATFORM
     if (buf != data) {
         free((void*)buf);
@@ -235,7 +242,7 @@ esp_err_t Page::writeItem(uint8_t nsIndex, ItemType datatype, const char* key, c
 
         size_t tail = dataSize - left;
         if (tail > 0) {
-            std::fill_n(item.rawData, ENTRY_SIZE / 4, 0xffffffff);
+            std::fill_n(item.rawData, ENTRY_SIZE, 0xff);
             memcpy(item.rawData, static_cast<const uint8_t*>(data) + left, tail);
             err = writeEntry(item);
             if (err != ESP_OK) {
@@ -707,7 +714,7 @@ esp_err_t Page::alterPageState(PageState state)
 
 esp_err_t Page::readEntry(size_t index, Item& dst) const
 {
-    auto rc = spi_flash_read(getEntryAddress(index), &dst, sizeof(dst));
+    auto rc = nvs_flash_read(getEntryAddress(index), &dst, sizeof(dst));
     if (rc != ESP_OK) {
         return rc;
     }
index 1a7ffbaabbe3ad7f629b801e0289e68534f07684..8cc9f904272fb27b7f532e3499ffe061f5653f4a 100644 (file)
@@ -56,6 +56,11 @@ public:
 
     esp_err_t fillStats(nvs_stats_t& nvsStats);
 
+    uint32_t getBaseSector()
+    {
+        return mBaseSector;
+    }
+
 protected:
     friend class Iterator;
 
index f029411160b85aa127943f0880b8510f24e4b0ba..5cb34b27a169b4289c40fc77beaf6a6ed87d9a0b 100644 (file)
@@ -225,6 +225,7 @@ esp_err_t Storage::writeMultiPageBlob(uint8_t nsIndex, const char* key, const vo
         if (!remainingSize) {
             /* All pages are stored. Now store the index.*/
             Item item;
+            std::fill_n(item.data, sizeof(item.data), 0xff);
             item.blobIndex.dataSize = dataSize;
             item.blobIndex.chunkCount = chunkCount;
             item.blobIndex.chunkStart = chunkStart;
index 769abc270ae2efc2d29fb16dd5e4e32658805e54..3a79e705cc925cde6287c17c951266051c832a40 100644 (file)
@@ -100,6 +100,10 @@ public:
     {
         return mPartitionName;
     }
+    uint32_t getBaseSector()
+    {
+        return mPageManager.getBaseSector();
+    }
 
     esp_err_t writeMultiPageBlob(uint8_t nsIndex, const char* key, const void* data, size_t dataSize, VerOffset chunkStart);
 
index 3cf3e7fa1897bfa308c86113c3ed016d34e611cd..7321ed25ef1b7b3ad3e9e6a6b7a510afd5a320aa 100644 (file)
@@ -33,6 +33,22 @@ extern "C" {
  */
 esp_err_t nvs_flash_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount);
 
+#ifdef CONFIG_NVS_ENCRYPTION
+/**
+ * @brief Initialize NVS flash storage with custom flash sector layout
+ *
+ * @note  This API is intended to be used in unit tests.
+ * 
+ * @param partName Partition name of the NVS partition as per partition table
+ * @param baseSector Flash sector (units of 4096 bytes) offset to start NVS
+ * @param sectorCount Length (in flash sectors) of NVS region. 
+                                         NVS partition must be at least 3 sectors long.
+ * @param[in]  cfg    Security configuration (keys) to be used for NVS encryption/decryption. 
+ *                    If cfg is null, no encryption/decryption is used. 
+ * @return ESP_OK if flash was successfully initialized
+ */
+esp_err_t nvs_flash_secure_init_custom(const char *partName, uint32_t baseSector, uint32_t sectorCount, nvs_sec_cfg_t* cfg);
+#endif
 
 /**
  * @brief Dump contents of NVS storage to stdout
index 5dd172bdb741ac981aca2ac446a56e62733f816a..a0ea7a406344c18d7150c6be19cb87e324a135b9 100644 (file)
@@ -3,3 +3,4 @@
 #
 
 COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive
+COMPONENT_EMBED_TXTFILES += encryption_keys.bin partition_encrypted.bin sample.bin
diff --git a/components/nvs_flash/test/encryption_keys.bin b/components/nvs_flash/test/encryption_keys.bin
new file mode 100644 (file)
index 0000000..9ef4439
--- /dev/null
@@ -0,0 +1 @@
+\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11\11"""""""""""""""""""""""""""""""",ïÏ<ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
\ No newline at end of file
diff --git a/components/nvs_flash/test/partition_encrypted.bin b/components/nvs_flash/test/partition_encrypted.bin
new file mode 100644 (file)
index 0000000..93c900f
Binary files /dev/null and b/components/nvs_flash/test/partition_encrypted.bin differ
diff --git a/components/nvs_flash/test/sample.bin b/components/nvs_flash/test/sample.bin
new file mode 100644 (file)
index 0000000..fe69eca
--- /dev/null
@@ -0,0 +1 @@
+start0000000000000000000000start0123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef00000000000000000123456789abcdef0000000000000000end00000000000000000000000000end
\ No newline at end of file
index e3684d71f7603672ea2ac6be22f103f9359b0095..4dfacc733b280c4da4013e4c0854e6701847f6c4 100644 (file)
@@ -7,10 +7,15 @@
 #include "nvs.h"
 #include "nvs_flash.h"
 #include "esp_partition.h"
+#include "esp_flash_encrypt.h"
 #include "esp_log.h"
 #include <string.h>
 #include "esp_system.h"
 
+#ifdef CONFIG_NVS_ENCRYPTION
+#include "mbedtls/aes.h"
+#endif
+
 static const char* TAG = "test_nvs";
 
 TEST_CASE("various nvs tests", "[nvs]")
@@ -250,3 +255,244 @@ TEST_CASE("check for memory leaks in nvs_set_blob", "[nvs]")
     printf("%d\n", esp_get_free_heap_size());
     /* heap leaks will be checked in unity_platform.c */
 }
+
+#ifdef CONFIG_NVS_ENCRYPTION
+TEST_CASE("check underlying xts code for 32-byte size sector encryption", "[nvs]")
+{
+    uint8_t eky_hex[2 * NVS_KEY_SIZE] = { 0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11, 
+        0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+        0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+        0x11,0x11,0x11,0x11,0x11,0x11,0x11,0x11,
+        /* Tweak key below*/
+        0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22, 
+        0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,
+        0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22,
+        0x22,0x22,0x22,0x22,0x22,0x22,0x22,0x22 };
+
+    uint8_t ba_hex[16] = { 0x33,0x33,0x33,0x33,0x33,0x00,0x00,0x00,
+        0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 };
+
+    uint8_t ptxt_hex[32] = { 0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44, 
+        0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,
+        0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44,
+        0x44,0x44,0x44,0x44,0x44,0x44,0x44,0x44 };
+
+
+    uint8_t ctxt_hex[32] = { 0xe6,0x22,0x33,0x4f,0x18,0x4b,0xbc,0xe1,
+        0x29,0xa2,0x5b,0x2a,0xc7,0x6b,0x3d,0x92,
+        0xab,0xf9,0x8e,0x22,0xdf,0x5b,0xdd,0x15,
+        0xaf,0x47,0x1f,0x3d,0xb8,0x94,0x6a,0x85 };
+
+    mbedtls_aes_xts_context ectx[1];
+    mbedtls_aes_xts_context dctx[1];
+
+    mbedtls_aes_xts_init(ectx);
+    mbedtls_aes_xts_init(dctx);
+
+    TEST_ASSERT_TRUE(!mbedtls_aes_xts_setkey_enc(ectx, eky_hex, 2 * NVS_KEY_SIZE * 8));
+    TEST_ASSERT_TRUE(!mbedtls_aes_xts_setkey_enc(dctx, eky_hex, 2 * NVS_KEY_SIZE * 8));
+
+    TEST_ASSERT_TRUE(!mbedtls_aes_crypt_xts(ectx, MBEDTLS_AES_ENCRYPT, 32, ba_hex, ptxt_hex, ptxt_hex));
+
+    TEST_ASSERT_TRUE(!memcmp(ptxt_hex, ctxt_hex, 32));
+}
+
+TEST_CASE("Check nvs key partition APIs (read and generate keys)", "[nvs]")
+{
+    nvs_sec_cfg_t cfg, cfg2;
+    
+    const esp_partition_t* key_part = esp_partition_find_first(
+            ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL);
+
+    if (!esp_flash_encryption_enabled()) {
+        TEST_IGNORE_MESSAGE("flash encryption disabled, skipping nvs_key partition related tests");
+    }
+
+    TEST_ESP_OK(esp_partition_erase_range(key_part, 0, key_part->size));
+    TEST_ESP_ERR(nvs_flash_read_security_cfg(key_part, &cfg), ESP_ERR_NVS_KEYS_NOT_INITIALIZED);
+    
+    TEST_ESP_OK(nvs_flash_generate_keys(key_part, &cfg));
+
+    TEST_ESP_OK(nvs_flash_read_security_cfg(key_part, &cfg2));
+
+    TEST_ASSERT_TRUE(!memcmp(&cfg, &cfg2, sizeof(nvs_sec_cfg_t)));
+}
+
+
+TEST_CASE("test nvs apis with encryption enabled", "[nvs]")
+{
+
+
+    if (!esp_flash_encryption_enabled()) {
+        TEST_IGNORE_MESSAGE("flash encryption disabled, skipping nvs_api tests with encryption enabled");
+    }
+    const esp_partition_t* key_part = esp_partition_find_first(
+            ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL);
+
+    const esp_partition_t* nvs_partition = esp_partition_find_first(
+            ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
+    assert(nvs_partition && "partition table must have an NVS partition");
+
+    ESP_ERROR_CHECK( esp_partition_erase_range(nvs_partition, 0, nvs_partition->size) );
+
+    nvs_sec_cfg_t cfg;
+    esp_err_t err = nvs_flash_read_security_cfg(key_part, &cfg);
+
+    if(err == ESP_ERR_NVS_KEYS_NOT_INITIALIZED) {
+        TEST_ESP_OK(nvs_flash_generate_keys(key_part, &cfg));
+    } else {
+        ESP_ERROR_CHECK(err);
+    }
+    TEST_ESP_OK(nvs_flash_secure_init(&cfg));
+    
+    nvs_handle handle_1;
+
+    TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND);
+
+
+    TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1));
+
+    TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678));
+    TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789));
+
+    nvs_handle handle_2;
+    TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2));
+    TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a));
+    const char* str = "value 0123456789abcdef0123456789abcdef";
+    TEST_ESP_OK(nvs_set_str(handle_2, "key", str));
+
+    int32_t v1;
+    TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1));
+    TEST_ASSERT_TRUE(0x23456789 == v1);
+
+    int32_t v2;
+    TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2));
+    TEST_ASSERT_TRUE(0x3456789a == v2);
+
+    char buf[strlen(str) + 1];
+    size_t buf_len = sizeof(buf);
+
+    size_t buf_len_needed;
+    TEST_ESP_OK(nvs_get_str(handle_2, "key", NULL, &buf_len_needed));
+    TEST_ASSERT_TRUE(buf_len_needed == buf_len);
+
+    size_t buf_len_short = buf_len - 1;
+    TEST_ESP_ERR(ESP_ERR_NVS_INVALID_LENGTH, nvs_get_str(handle_2, "key", buf, &buf_len_short));
+    TEST_ASSERT_TRUE(buf_len_short == buf_len);
+
+    size_t buf_len_long = buf_len + 1;
+    TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len_long));
+    TEST_ASSERT_TRUE(buf_len_long == buf_len);
+
+    TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len));
+
+    TEST_ASSERT_TRUE(0 == strcmp(buf, str));
+
+    nvs_close(handle_1);
+    nvs_close(handle_2);
+
+    TEST_ESP_OK(nvs_flash_deinit());
+}
+
+TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled", "[nvs_part_gen]")
+{
+
+    if (!esp_flash_encryption_enabled()) {
+        TEST_IGNORE_MESSAGE("flash encryption disabled, skipping nvs_api tests with encryption enabled");
+    }
+
+    nvs_handle handle;
+    nvs_sec_cfg_t xts_cfg;
+
+    extern const char nvs_key_start[] asm("_binary_encryption_keys_bin_start");
+    extern const char nvs_key_end[]   asm("_binary_encryption_keys_bin_end");
+
+    extern const char nvs_data_start[] asm("_binary_partition_encrypted_bin_start");
+
+    extern const char sample_bin_start[] asm("_binary_sample_bin_start");
+
+    const esp_partition_t* key_part = esp_partition_find_first(
+            ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS, NULL);
+
+    const esp_partition_t* nvs_part = esp_partition_find_first(
+            ESP_PARTITION_TYPE_DATA, ESP_PARTITION_SUBTYPE_DATA_NVS, NULL);
+
+    assert(key_part && "partition table must have a KEY partition");
+    TEST_ASSERT_TRUE((nvs_key_end - nvs_key_start - 1) == SPI_FLASH_SEC_SIZE);
+
+    assert(nvs_part && "partition table must have an NVS partition");
+    printf("\n nvs_part size:%d\n", nvs_part->size);
+
+    ESP_ERROR_CHECK(esp_partition_erase_range(key_part, 0, key_part->size));
+    ESP_ERROR_CHECK( esp_partition_erase_range(nvs_part, 0, nvs_part->size) );
+
+    for (int i = 0; i < key_part->size; i+= SPI_FLASH_SEC_SIZE) {
+        ESP_ERROR_CHECK( esp_partition_write(key_part, i, nvs_key_start + i, SPI_FLASH_SEC_SIZE) );
+    }
+
+    for (int i = 0; i < nvs_part->size; i+= SPI_FLASH_SEC_SIZE) {
+        ESP_ERROR_CHECK( spi_flash_write(nvs_part->address + i, nvs_data_start + i, SPI_FLASH_SEC_SIZE) );
+    }
+
+    esp_err_t err = nvs_flash_read_security_cfg(key_part, &xts_cfg);
+    ESP_ERROR_CHECK(err);
+
+    TEST_ESP_OK(nvs_flash_secure_init(&xts_cfg));
+
+    TEST_ESP_OK(nvs_open("dummyNamespace", NVS_READONLY, &handle));
+    uint8_t u8v;
+    TEST_ESP_OK( nvs_get_u8(handle, "dummyU8Key", &u8v));
+    TEST_ASSERT_TRUE(u8v == 127);
+    int8_t i8v;
+    TEST_ESP_OK( nvs_get_i8(handle, "dummyI8Key", &i8v));
+    TEST_ASSERT_TRUE(i8v == -128);
+    uint16_t u16v;
+    TEST_ESP_OK( nvs_get_u16(handle, "dummyU16Key", &u16v));
+    TEST_ASSERT_TRUE(u16v == 32768);
+    uint32_t u32v;
+    TEST_ESP_OK( nvs_get_u32(handle, "dummyU32Key", &u32v));
+    TEST_ASSERT_TRUE(u32v == 4294967295);
+    int32_t i32v;
+    TEST_ESP_OK( nvs_get_i32(handle, "dummyI32Key", &i32v));
+    TEST_ASSERT_TRUE(i32v == -2147483648);
+
+    char buf[64] = {0};
+    size_t buflen = 64;
+    TEST_ESP_OK( nvs_get_str(handle, "dummyStringKey", buf, &buflen));
+    TEST_ASSERT_TRUE(strncmp(buf, "0A:0B:0C:0D:0E:0F", buflen) == 0);
+
+    uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_blob(handle, "dummyHex2BinKey", buf, &buflen));
+    TEST_ASSERT_TRUE(memcmp(buf, hexdata, buflen) == 0);
+
+    uint8_t base64data[] = {'1', '2', '3', 'a', 'b', 'c'};
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen));
+    TEST_ASSERT_TRUE(memcmp(buf, base64data, buflen) == 0);
+
+    uint8_t hexfiledata[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_blob(handle, "hexFileKey", buf, &buflen));
+    TEST_ASSERT_TRUE(memcmp(buf, hexfiledata, buflen) == 0);
+
+    uint8_t base64filedata[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xab, 0xcd, 0xef};
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_blob(handle, "base64FileKey", buf, &buflen));
+    TEST_ASSERT_TRUE(memcmp(buf, base64filedata, buflen) == 0);
+
+    uint8_t strfiledata[64] = "abcdefghijklmnopqrstuvwxyz\0";
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_str(handle, "stringFileKey", buf, &buflen));
+    TEST_ASSERT_TRUE(memcmp(buf, strfiledata, buflen) == 0);
+
+    char bin_data[5120];
+    size_t bin_len = sizeof(bin_data);
+    TEST_ESP_OK( nvs_get_blob(handle, "binFileKey", bin_data, &bin_len));
+    TEST_ASSERT_TRUE(memcmp(bin_data, sample_bin_start, bin_len) == 0);
+
+    nvs_close(handle);
+    TEST_ESP_OK(nvs_flash_deinit());
+
+}
+#endif
index 01c021e6e94de7911b8b74d2bc15bf6d4b0e5e24..ff2b3c46547405117a405f3fce2ad6d428cba045 100644 (file)
@@ -10,6 +10,8 @@ SOURCE_FILES = \
                nvs_pagemanager.cpp \
                nvs_storage.cpp \
                nvs_item_hash_list.cpp \
+               nvs_encr.cpp \
+               nvs_ops.cpp \
        ) \
        spi_flash_emulation.cpp \
        test_compressed_enum_table.cpp \
@@ -19,7 +21,7 @@ SOURCE_FILES = \
        crc.cpp \
        main.cpp
 
-CPPFLAGS += -I../include -I../src -I./ -I../../esp32/include -I ../../spi_flash/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage
+CPPFLAGS += -I../include -I../src -I./ -I../../esp32/include -I ../../mbedtls/mbedtls/include -I ../../spi_flash/include -I ../../../tools/catch -fprofile-arcs -ftest-coverage -DCONFIG_NVS_ENCRYPTION
 CFLAGS += -fprofile-arcs -ftest-coverage
 CXXFLAGS += -std=c++11 -Wall -Werror
 LDFLAGS += -lstdc++ -Wall -fprofile-arcs -ftest-coverage
@@ -31,7 +33,8 @@ COVERAGE_FILES = $(OBJ_FILES:.o=.gc*)
 $(OBJ_FILES): %.o: %.cpp
 
 $(TEST_PROGRAM): $(OBJ_FILES)
-       g++ $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES)
+       $(MAKE) -C ../../mbedtls/mbedtls/ lib
+       g++ $(LDFLAGS) -o $(TEST_PROGRAM) $(OBJ_FILES) ../../mbedtls/mbedtls/library/libmbedcrypto.a
 
 $(OUTPUT_DIR):
        mkdir -p $(OUTPUT_DIR)
@@ -56,6 +59,7 @@ coverage_report: coverage.info
        @echo "Coverage report is in coverage_report/index.html"
 
 clean:
+       $(MAKE) -C ../../mbedtls/mbedtls/ clean
        rm -f $(OBJ_FILES) $(TEST_PROGRAM)
        rm -f $(COVERAGE_FILES) *.gcov
        rm -rf coverage_report/
index 13babf5b94a2fb46a29baddcb868f1a150834695..568abc0971a656d92a1db08e54fd3bcde780fe37 100644 (file)
@@ -14,6 +14,9 @@
 #include "catch.hpp"
 #include "nvs.hpp"
 #include "nvs_test_api.h"
+#ifdef CONFIG_NVS_ENCRYPTION
+#include "nvs_encr.hpp"
+#endif
 #include "spi_flash_emulation.h"
 #include <sstream>
 #include <iostream>
@@ -382,10 +385,10 @@ TEST_CASE("storage can find items on second page if first is not fully written a
     ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "1", bigdata, sizeof(bigdata)));
     // write another big chunk of data
     ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "2", bigdata, sizeof(bigdata)));
-    
+
     // write third one; it will not fit into the first page
     ESP_ERROR_CHECK(storage.writeItem(0, ItemType::BLOB, "3", bigdata, sizeof(bigdata)));
-    
+
     size_t size;
     ESP_ERROR_CHECK(storage.getItemDataSize(0, ItemType::BLOB, "1", size));
     CHECK(size == sizeof(bigdata));
@@ -505,7 +508,7 @@ TEST_CASE("nvs api tests", "[nvs]")
 {
     SpiFlashEmulator emu(10);
     emu.randomize(100);
-    
+
     nvs_handle handle_1;
     const uint32_t NVS_FLASH_SECTOR = 6;
     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
@@ -547,11 +550,11 @@ TEST_CASE("nvs api tests", "[nvs]")
     size_t buf_len_needed;
     TEST_ESP_OK(nvs_get_str(handle_2, "key", NULL, &buf_len_needed));
     CHECK(buf_len_needed == buf_len);
-    
+
     size_t buf_len_short = buf_len - 1;
     TEST_ESP_ERR(ESP_ERR_NVS_INVALID_LENGTH, nvs_get_str(handle_2, "key", buf, &buf_len_short));
     CHECK(buf_len_short == buf_len);
-    
+
     size_t buf_len_long = buf_len + 1;
     TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len_long));
     CHECK(buf_len_long == buf_len);
@@ -559,19 +562,21 @@ TEST_CASE("nvs api tests", "[nvs]")
     TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len));
 
     CHECK(0 == strcmp(buf, str));
+    nvs_close(handle_1);
+    nvs_close(handle_2);
 }
 
 TEST_CASE("wifi test", "[nvs]")
 {
     SpiFlashEmulator emu(10);
     emu.randomize(10);
-    
-    
+
+
     const uint32_t NVS_FLASH_SECTOR = 5;
     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
     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));
-    
+
     nvs_handle misc_handle;
     TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &misc_handle));
     char log[33];
@@ -579,31 +584,31 @@ TEST_CASE("wifi test", "[nvs]")
     TEST_ESP_ERR(nvs_get_str(misc_handle, "log", log, &log_size), ESP_ERR_NVS_NOT_FOUND);
     strcpy(log, "foobarbazfizzz");
     TEST_ESP_OK(nvs_set_str(misc_handle, "log", log));
-    
+
     nvs_handle net80211_handle;
     TEST_ESP_OK(nvs_open("nvs.net80211", NVS_READWRITE, &net80211_handle));
-    
+
     uint8_t opmode = 2;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.opmode", &opmode), ESP_ERR_NVS_NOT_FOUND);
-    
+
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.opmode", opmode));
-    
+
     uint8_t country = 0;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "wifi.country", &opmode), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "wifi.country", opmode));
-    
+
     char ssid[36];
     size_t size = sizeof(ssid);
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND);
     strcpy(ssid, "my android AP");
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.ssid", ssid, size));
-    
+
     char mac[6];
     size = sizeof(mac);
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND);
     memset(mac, 0xab, 6);
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.mac", mac, size));
-    
+
     uint8_t authmode = 1;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.authmode", authmode));
@@ -619,11 +624,11 @@ TEST_CASE("wifi test", "[nvs]")
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND);
     memset(pmk, 1, size);
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.pmk", pmk, size));
-    
+
     uint8_t chan = 1;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.chan", &chan), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.chan", chan));
-    
+
     uint8_t autoconn = 1;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "auto.conn", &autoconn), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "auto.conn", autoconn));
@@ -641,43 +646,43 @@ TEST_CASE("wifi test", "[nvs]")
     uint8_t phym = 3;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phym", &phym), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phym", phym));
-    
+
     uint8_t phybw = 2;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "sta.phybw", &phybw), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "sta.phybw", phybw));
-    
+
     char apsw[2];
     size = sizeof(apsw);
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apsw", apsw, &size), ESP_ERR_NVS_NOT_FOUND);
     memset(apsw, 0x2, size);
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apsw", apsw, size));
-    
+
     char apinfo[700];
     size = sizeof(apinfo);
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "sta.apinfo", apinfo, &size), ESP_ERR_NVS_NOT_FOUND);
     memset(apinfo, 0, size);
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "sta.apinfo", apinfo, size));
-    
+
     size = sizeof(ssid);
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.ssid", ssid, &size), ESP_ERR_NVS_NOT_FOUND);
     strcpy(ssid, "ESP_A2F340");
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.ssid", ssid, size));
-    
+
     size = sizeof(mac);
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.mac", mac, &size), ESP_ERR_NVS_NOT_FOUND);
     memset(mac, 0xac, 6);
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.mac", mac, size));
-    
+
     size = sizeof(pswd);
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.passwd", pswd, &size), ESP_ERR_NVS_NOT_FOUND);
     strcpy(pswd, "");
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.passwd", pswd, size));
-    
+
     size = sizeof(pmk);
     TEST_ESP_ERR(nvs_get_blob(net80211_handle, "ap.pmk", pmk, &size), ESP_ERR_NVS_NOT_FOUND);
     memset(pmk, 1, size);
     TEST_ESP_OK(nvs_set_blob(net80211_handle, "ap.pmk", pmk, size));
-    
+
     chan = 6;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.chan", &chan), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.chan", chan));
@@ -685,19 +690,19 @@ TEST_CASE("wifi test", "[nvs]")
     authmode = 0;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.authmode", &authmode), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.authmode", authmode));
-    
+
     uint8_t hidden = 0;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.hidden", &hidden), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.hidden", hidden));
-    
+
     uint8_t max_conn = 4;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "ap.max.conn", &max_conn), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "ap.max.conn", max_conn));
-    
+
     uint8_t bcn_interval = 2;
     TEST_ESP_ERR(nvs_get_u8(net80211_handle, "bcn_interval", &bcn_interval), ESP_ERR_NVS_NOT_FOUND);
     TEST_ESP_OK(nvs_set_u8(net80211_handle, "bcn_interval", bcn_interval));
-    
+
     s_perf << "Time to simulate nvs init with wifi libs: " << emu.getTotalTime() << " us (" << emu.getEraseOps() << "E " << emu.getWriteOps() << "W " << emu.getReadOps() << "R " << emu.getWriteBytes() << "Wb " << emu.getReadBytes() << "Rb)" << std::endl;
 
 }
@@ -707,15 +712,15 @@ TEST_CASE("can init storage from flash with random contents", "[nvs]")
 {
     SpiFlashEmulator emu(10);
     emu.randomize(42);
-    
+
     nvs_handle handle;
     const uint32_t NVS_FLASH_SECTOR = 5;
     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
     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_open("nvs.net80211", NVS_READWRITE, &handle));
-    
+
     uint8_t opmode = 2;
     if (nvs_get_u8(handle, "wifi.opmode", &opmode) != ESP_OK) {
         TEST_ESP_OK(nvs_set_u8(handle, "wifi.opmode", opmode));
@@ -735,16 +740,16 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]")
         }
         SpiFlashEmulator emu(10);
         emu.randomize(static_cast<uint32_t>(count));
-        
+
         const uint32_t NVS_FLASH_SECTOR = 6;
         const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
         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));
-        
+
         nvs_handle handle_1;
         TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND);
-        
+
         TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1));
         TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678));
         for (size_t i = 0; i < 500; ++i) {
@@ -756,20 +761,20 @@ TEST_CASE("nvs api tests, starting with random data in flash", "[nvs][long]")
             char str_buf[128];
             snprintf(str_buf, sizeof(str_buf), str, i + count * 1024);
             TEST_ESP_OK(nvs_set_str(handle_2, "key", str_buf));
-            
+
             int32_t v1;
             TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1));
             CHECK(0x23456789 % (i + 1) == v1);
-            
+
             int32_t v2;
             TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2));
             CHECK(static_cast<int32_t>(i) == v2);
-            
+
             char buf[128];
             size_t buf_len = sizeof(buf);
-            
+
             TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len));
-            
+
             CHECK(0 == strcmp(buf, str_buf));
             nvs_close(handle_2);
         }
@@ -797,7 +802,7 @@ class RandomTest {
     uint8_t v10[smallBlobLen], v11[largeBlobLen];
 #endif
     bool written[nKeys];
-    
+
 public:
     RandomTest()
     {
@@ -822,7 +827,7 @@ public:
         const size_t nKeys = sizeof(keys) / sizeof(keys[0]);
         static_assert(nKeys == sizeof(types) / sizeof(types[0]), "");
         static_assert(nKeys == sizeof(values) / sizeof(values[0]), "");
-        
+
         auto randomRead = [&](size_t index) -> esp_err_t {
             switch (types[index]) {
                 case ItemType::I32:
@@ -841,7 +846,7 @@ public:
                     }
                     break;
                 }
-                    
+
                 case ItemType::U64:
                 {
                     uint64_t val;
@@ -858,7 +863,7 @@ public:
                     }
                     break;
                 }
-                    
+
                 case ItemType::SZ:
                 {
                     char buf[strBufLen];
@@ -910,13 +915,13 @@ public:
             }
             return ESP_OK;
         };
-        
+
         auto randomWrite = [&](size_t index) -> esp_err_t {
             switch (types[index]) {
                 case ItemType::I32:
                 {
                     int32_t val = static_cast<int32_t>(gen());
-                    
+
                     auto err = nvs_set_i32(handle, keys[index], val);
                     if (err == ESP_ERR_FLASH_OP_FAIL) {
                         return err;
@@ -931,11 +936,11 @@ public:
                     *reinterpret_cast<int32_t*>(values[index]) = val;
                     break;
                 }
-                    
+
                 case ItemType::U64:
                 {
                     uint64_t val = static_cast<uint64_t>(gen());
-                    
+
                     auto err = nvs_set_u64(handle, keys[index], val);
                     if (err == ESP_ERR_FLASH_OP_FAIL) {
                         return err;
@@ -950,19 +955,19 @@ public:
                     *reinterpret_cast<uint64_t*>(values[index]) = val;
                     break;
                 }
-                    
+
                 case ItemType::SZ:
                 {
                     char buf[strBufLen];
                     size_t len = strBufLen;
-                    
+
                     size_t strLen = gen() % (strBufLen - 1);
                     std::generate_n(buf, strLen, [&]() -> char {
                         const char c = static_cast<char>(gen() % 127);
                         return (c < 32) ? 32 : c;
                     });
                     buf[strLen] = 0;
-                    
+
                     auto err = nvs_set_str(handle, keys[index], buf);
                     if (err == ESP_ERR_FLASH_OP_FAIL) {
                         return err;
@@ -1013,8 +1018,8 @@ public:
             }
             return ESP_OK;
         };
-       
-        
+
+
         for (; count != 0; --count) {
             size_t index = gen() % (nKeys);
             switch (gen() % 3) {
@@ -1023,7 +1028,7 @@ public:
                         return ESP_ERR_FLASH_OP_FAIL;
                     }
                     break;
-                    
+
                 default: // write, 2/3
                     if (randomWrite(index) == ESP_ERR_FLASH_OP_FAIL) {
                         return ESP_ERR_FLASH_OP_FAIL;
@@ -1056,7 +1061,7 @@ TEST_CASE("monkey test", "[nvs][monkey]")
     std::mt19937 gen(rd());
     uint32_t seed = 3;
     gen.seed(seed);
-    
+
     SpiFlashEmulator emu(10);
     emu.randomize(seed);
     emu.clearStats();
@@ -1064,15 +1069,15 @@ TEST_CASE("monkey test", "[nvs][monkey]")
     const uint32_t NVS_FLASH_SECTOR = 2;
     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 8;
     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));
-    
+
     nvs_handle handle;
     TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle));
     RandomTest test;
     size_t count = 1000;
     CHECK(test.doRandomThings(handle, gen, count) == ESP_OK);
-    
+
     s_perf << "Monkey test: nErase=" << emu.getEraseOps() << " nWrite=" << emu.getWriteOps() << std::endl;
 }
 
@@ -1083,13 +1088,14 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]")
     uint32_t seed = 3;
     gen.seed(seed);
     const size_t iter_count = 2000;
-    
+
     SpiFlashEmulator emu(10);
     
     const uint32_t NVS_FLASH_SECTOR = 2;
     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;
     int lastPercent = -1;
     for (uint32_t errDelay = 0; ; ++errDelay) {
@@ -1098,7 +1104,7 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]")
         emu.clearStats();
         emu.failAfter(errDelay);
         RandomTest test;
-        
+
         if (totalOps != 0) {
             int percent = errDelay * 100 / totalOps;
             if (percent > lastPercent) {
@@ -1106,7 +1112,7 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]")
                 lastPercent = percent;
             }
         }
-        
+
 
         nvs_handle handle;
         size_t count = iter_count;
@@ -1120,7 +1126,7 @@ TEST_CASE("test recovery from sudden poweroff", "[long][nvs][recovery][monkey]")
                 nvs_close(handle);
             }
         }
-        
+
         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));
         auto res = test.doRandomThings(handle, gen, count);
@@ -1139,7 +1145,7 @@ TEST_CASE("test for memory leaks in open/set", "[leaks]")
     const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
     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));
-    
+
     for (int i = 0; i < 100000; ++i) {
         nvs_handle light_handle = 0;
         char lightbulb[1024] = {12, 13, 14, 15, 16};
@@ -1195,12 +1201,12 @@ TEST_CASE("recovery after failure to write data", "[nvs]")
     {
         Storage storage;
         TEST_ESP_OK(storage.init(0, 3));
-        
+
         TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_FLASH_OP_FAIL);
-        
+
         // check that repeated operations cause an error
         TEST_ESP_ERR(storage.writeItem(1, ItemType::SZ, "key", str, strlen(str)), ESP_ERR_NVS_INVALID_STATE);
-        
+
         uint8_t val;
         TEST_ESP_ERR(storage.readItem(1, ItemType::U8, "key", &val, sizeof(val)), ESP_ERR_NVS_NOT_FOUND);
     }
@@ -1210,7 +1216,7 @@ TEST_CASE("recovery after failure to write data", "[nvs]")
         p.load(0);
         CHECK(p.getErasedEntryCount() == 3);
         CHECK(p.getUsedEntryCount() == 0);
-        
+
         // try to write again
         TEST_ESP_OK(p.writeItem(1, ItemType::SZ, "key", str, strlen(str)));
     }
@@ -1225,18 +1231,18 @@ TEST_CASE("crc errors in item header are handled", "[nvs]")
     TEST_ESP_OK(storage.writeItem(0, "ns1", static_cast<uint8_t>(1)));
     TEST_ESP_OK(storage.writeItem(1, "value1", static_cast<uint32_t>(1)));
     TEST_ESP_OK(storage.writeItem(1, "value2", static_cast<uint32_t>(2)));
-    
+
     // corrupt item header
     uint32_t val = 0;
     emu.write(32 * 3, &val, 4);
-    
+
     // check that storage can recover
     TEST_ESP_OK(storage.init(0, 3));
     TEST_ESP_OK(storage.readItem(1, "value2", val));
     CHECK(val == 2);
     // check that the corrupted item is no longer present
     TEST_ESP_ERR(ESP_ERR_NVS_NOT_FOUND, storage.readItem(1, "value1", val));
-    
+
     // add more items to make the page full
     for (size_t i = 0; i < Page::ENTRY_COUNT; ++i) {
         char item_name[Item::MAX_KEY_LENGTH + 1];
@@ -1247,7 +1253,7 @@ TEST_CASE("crc errors in item header are handled", "[nvs]")
     // corrupt another item on the full page
     val = 0;
     emu.write(32 * 4, &val, 4);
-    
+
     // check that storage can recover
     TEST_ESP_OK(storage.init(0, 3));
     // check that the corrupted item is no longer present
@@ -1301,7 +1307,7 @@ TEST_CASE("read/write failure (TW8406)", "[nvs]")
         char data[76] = {12, 13, 14, 15, 16};
         uint8_t number = 20;
         size_t data_len = sizeof(data);
-        
+
         ESP_ERROR_CHECK(nvs_open("LIGHT", NVS_READWRITE, &light_handle));
         ESP_ERROR_CHECK(nvs_set_u8(light_handle, "RecordNum", number));
         for (i = 0; i < number; ++i) {
@@ -1309,7 +1315,7 @@ TEST_CASE("read/write failure (TW8406)", "[nvs]")
             ESP_ERROR_CHECK(nvs_set_blob(light_handle, key, data, sizeof(data)));
         }
         nvs_commit(light_handle);
-        
+
         uint8_t get_number = 0;
         ESP_ERROR_CHECK(nvs_get_u8(light_handle, "RecordNum", &get_number));
         REQUIRE(number == get_number);
@@ -2038,8 +2044,7 @@ TEST_CASE("Recovery from power-off during modification of blob present in old-fo
     TEST_ESP_ERR(p3.findItem(1, ItemType::BLOB, "singlepage"), ESP_ERR_NVS_NOT_FOUND);
 }
 #endif
-/* Add new tests above */
-/* This test has to be the final one */
+
 
 TEST_CASE("check partition generation utility with multipage blob support disabled", "[nvs_part_gen]")
 {
@@ -2116,7 +2121,7 @@ TEST_CASE("read data from partition generated via partition generation utility w
     CHECK(memcmp(bin_data, binfiledata, bin_len) == 0);
 
     file.close();
-
+    nvs_close(handle);
 }
 
 #ifdef CONFIG_MP_BLOB_SUPPORT
@@ -2199,6 +2204,245 @@ TEST_CASE("read data from partition generated via partition generation utility w
 }
 #endif
 
+#if CONFIG_NVS_ENCRYPTION
+TEST_CASE("check underlying xts code for 32-byte size sector encryption", "[nvs]")
+{
+    auto toHex = [](char ch) {
+        if(ch >= '0' && ch <= '9')
+            return ch - '0';
+        else if(ch >= 'a' && ch <= 'f')
+            return ch - 'a' + 10;
+        else if(ch >= 'A' && ch <= 'F')
+            return ch - 'A' + 10;
+        else
+            return 0;
+    };
+
+    auto toHexByte = [toHex](char* c) {
+        return 16 * toHex(c[0]) + toHex(c[1]);
+    };
+
+    auto toHexStream = [toHexByte](char* src, uint8_t* dest) {
+        uint32_t cnt =0;
+        char* p = src;
+        while(*p != '\0' && *(p + 1) != '\0')
+        {
+            dest[cnt++] = toHexByte(p); p += 2;
+        }
+    };
+
+    uint8_t eky_hex[2 * NVS_KEY_SIZE];
+    uint8_t ptxt_hex[Page::ENTRY_SIZE], ctxt_hex[Page::ENTRY_SIZE], ba_hex[16];
+    mbedtls_aes_xts_context ectx[1];
+    mbedtls_aes_xts_context dctx[1];
+
+    char eky[][2 * NVS_KEY_SIZE + 1] = {
+        "0000000000000000000000000000000000000000000000000000000000000000",
+        "1111111111111111111111111111111111111111111111111111111111111111"
+    };
+    char tky[][2 * NVS_KEY_SIZE + 1] = {
+        "0000000000000000000000000000000000000000000000000000000000000000",
+        "2222222222222222222222222222222222222222222222222222222222222222"
+    };
+    char blk_addr[][2*16 + 1]  = {
+        "00000000000000000000000000000000",
+        "33333333330000000000000000000000"
+    };
+
+    char ptxt[][2 * Page::ENTRY_SIZE + 1] = {
+        "0000000000000000000000000000000000000000000000000000000000000000",
+        "4444444444444444444444444444444444444444444444444444444444444444"
+    };
+    char ctxt[][2 * Page::ENTRY_SIZE + 1] = {
+        "d456b4fc2e620bba6ffbed27b956c9543454dd49ebd8d8ee6f94b65cbe158f73",
+        "e622334f184bbce129a25b2ac76b3d92abf98e22df5bdd15af471f3db8946a85"
+    };
+
+    mbedtls_aes_xts_init(ectx);
+    mbedtls_aes_xts_init(dctx);
+
+    for(uint8_t cnt = 0; cnt < sizeof(eky)/sizeof(eky[0]); cnt++) {
+        toHexStream(eky[cnt], eky_hex);
+        toHexStream(tky[cnt], &eky_hex[NVS_KEY_SIZE]);
+        toHexStream(ptxt[cnt], ptxt_hex);
+        toHexStream(ctxt[cnt], ctxt_hex);
+        toHexStream(blk_addr[cnt], ba_hex);
+
+        CHECK(!mbedtls_aes_xts_setkey_enc(ectx, eky_hex, 2 * NVS_KEY_SIZE * 8));
+        CHECK(!mbedtls_aes_xts_setkey_enc(dctx, eky_hex, 2 * NVS_KEY_SIZE * 8));
+
+        CHECK(!mbedtls_aes_crypt_xts(ectx, MBEDTLS_AES_ENCRYPT, Page::ENTRY_SIZE, ba_hex, ptxt_hex, ptxt_hex));
+
+        CHECK(!memcmp(ptxt_hex, ctxt_hex, Page::ENTRY_SIZE));
+    }
+}
+
+TEST_CASE("test nvs apis with encryption enabled", "[nvs]")
+{
+    SpiFlashEmulator emu(10);
+    emu.randomize(100);
+
+    nvs_handle handle_1;
+    const uint32_t NVS_FLASH_SECTOR = 6;
+    const uint32_t NVS_FLASH_SECTOR_COUNT_MIN = 3;
+    emu.setBounds(NVS_FLASH_SECTOR, NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN);
+
+    nvs_sec_cfg_t xts_cfg;
+    for(int count = 0; count < NVS_KEY_SIZE; count++) {
+        xts_cfg.eky[count] = 0x11;
+        xts_cfg.tky[count] = 0x22;
+    }
+
+    for (uint16_t i = NVS_FLASH_SECTOR; i <NVS_FLASH_SECTOR + NVS_FLASH_SECTOR_COUNT_MIN; ++i) {
+        spi_flash_erase_sector(i);
+    }
+    TEST_ESP_OK(nvs_flash_secure_init_custom(NVS_DEFAULT_PART_NAME, NVS_FLASH_SECTOR, NVS_FLASH_SECTOR_COUNT_MIN, &xts_cfg));
+
+    TEST_ESP_ERR(nvs_open("namespace1", NVS_READONLY, &handle_1), ESP_ERR_NVS_NOT_FOUND);
+
+
+    TEST_ESP_OK(nvs_open("namespace1", NVS_READWRITE, &handle_1));
+    TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x12345678));
+    TEST_ESP_OK(nvs_set_i32(handle_1, "foo", 0x23456789));
+
+    nvs_handle handle_2;
+    TEST_ESP_OK(nvs_open("namespace2", NVS_READWRITE, &handle_2));
+    TEST_ESP_OK(nvs_set_i32(handle_2, "foo", 0x3456789a));
+    const char* str = "value 0123456789abcdef0123456789abcdef";
+    TEST_ESP_OK(nvs_set_str(handle_2, "key", str));
+
+    int32_t v1;
+    TEST_ESP_OK(nvs_get_i32(handle_1, "foo", &v1));
+    CHECK(0x23456789 == v1);
+
+    int32_t v2;
+    TEST_ESP_OK(nvs_get_i32(handle_2, "foo", &v2));
+    CHECK(0x3456789a == v2);
+
+    char buf[strlen(str) + 1];
+    size_t buf_len = sizeof(buf);
+
+    size_t buf_len_needed;
+    TEST_ESP_OK(nvs_get_str(handle_2, "key", NULL, &buf_len_needed));
+    CHECK(buf_len_needed == buf_len);
+
+    size_t buf_len_short = buf_len - 1;
+    TEST_ESP_ERR(ESP_ERR_NVS_INVALID_LENGTH, nvs_get_str(handle_2, "key", buf, &buf_len_short));
+    CHECK(buf_len_short == buf_len);
+
+    size_t buf_len_long = buf_len + 1;
+    TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len_long));
+    CHECK(buf_len_long == buf_len);
+
+    TEST_ESP_OK(nvs_get_str(handle_2, "key", buf, &buf_len));
+
+    CHECK(0 == strcmp(buf, str));
+    nvs_close(handle_1);
+    nvs_close(handle_2);
+    TEST_ESP_OK(nvs_flash_deinit());
+
+}
+
+#ifdef CONFIG_MP_BLOB_SUPPORT
+TEST_CASE("test nvs apis for nvs partition generator utility with encryption enabled", "[nvs_part_gen]")
+{
+    int childpid = fork();
+    if (childpid == 0) {
+        exit(execlp("python", "python",
+                "../nvs_partition_generator/nvs_partition_gen.py",
+                "../nvs_partition_generator/sample_multipage_blob.csv",
+                "../nvs_partition_generator/partition_encrypted.bin",
+                "12KB",
+                "--encrypt",
+                "True",
+                "--keyfile",
+                "../nvs_partition_generator/testdata/encryption_keys.txt",NULL));
+    } else {
+        CHECK(childpid > 0);
+        int status;
+        waitpid(childpid, &status, 0);
+        CHECK(WEXITSTATUS(status) != -1);
+    }
+
+    SpiFlashEmulator emu("../nvs_partition_generator/partition_encrypted.bin");
+    nvs_handle handle;
+
+    nvs_sec_cfg_t xts_cfg;
+
+    for(int count = 0; count < NVS_KEY_SIZE; count++) {
+        xts_cfg.eky[count] = 0x11;
+        xts_cfg.tky[count] = 0x22;
+    }
+
+    TEST_ESP_OK(nvs_flash_secure_init_custom(NVS_DEFAULT_PART_NAME, 0, 3, &xts_cfg));
+
+    TEST_ESP_OK(nvs_open_from_partition(NVS_DEFAULT_PART_NAME, "dummyNamespace", NVS_READONLY, &handle));
+
+    uint8_t u8v;
+    TEST_ESP_OK( nvs_get_u8(handle, "dummyU8Key", &u8v));
+    CHECK(u8v == 127);
+    int8_t i8v;
+    TEST_ESP_OK( nvs_get_i8(handle, "dummyI8Key", &i8v));
+    CHECK(i8v == -128);
+    uint16_t u16v;
+    TEST_ESP_OK( nvs_get_u16(handle, "dummyU16Key", &u16v));
+    CHECK(u16v == 32768);
+    uint32_t u32v;
+    TEST_ESP_OK( nvs_get_u32(handle, "dummyU32Key", &u32v));
+    CHECK(u32v == 4294967295);
+    int32_t i32v;
+    TEST_ESP_OK( nvs_get_i32(handle, "dummyI32Key", &i32v));
+    CHECK(i32v == -2147483648);
+
+    char buf[64] = {0};
+    size_t buflen = 64;
+    TEST_ESP_OK( nvs_get_str(handle, "dummyStringKey", buf, &buflen));
+    CHECK(strncmp(buf, "0A:0B:0C:0D:0E:0F", buflen) == 0);
+
+    uint8_t hexdata[] = {0x01, 0x02, 0x03, 0xab, 0xcd, 0xef};
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_blob(handle, "dummyHex2BinKey", buf, &buflen));
+    CHECK(memcmp(buf, hexdata, buflen) == 0);
+
+    uint8_t base64data[] = {'1', '2', '3', 'a', 'b', 'c'};
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_blob(handle, "dummyBase64Key", buf, &buflen));
+    CHECK(memcmp(buf, base64data, buflen) == 0);
+
+    uint8_t hexfiledata[] = {0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef};
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_blob(handle, "hexFileKey", buf, &buflen));
+    CHECK(memcmp(buf, hexfiledata, buflen) == 0);
+
+    uint8_t base64filedata[] = {0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0xab, 0xcd, 0xef};
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_blob(handle, "base64FileKey", buf, &buflen));
+    CHECK(memcmp(buf, base64filedata, buflen) == 0);
+
+    uint8_t strfiledata[64] = "abcdefghijklmnopqrstuvwxyz\0";
+    buflen = 64;
+    TEST_ESP_OK( nvs_get_str(handle, "stringFileKey", buf, &buflen));
+    CHECK(memcmp(buf, strfiledata, buflen) == 0);
+
+    char bin_data[5120];
+    size_t bin_len = sizeof(bin_data);
+    char binfiledata[5200];
+    ifstream file;
+    file.open("../nvs_partition_generator/testdata/sample_multipage_blob.bin");
+    file.read(binfiledata,5120);
+    TEST_ESP_OK( nvs_get_blob(handle, "binFileKey", bin_data, &bin_len));
+    CHECK(memcmp(bin_data, binfiledata, bin_len) == 0);
+
+    nvs_close(handle);
+    TEST_ESP_OK(nvs_flash_deinit());
+
+}
+#endif
+#endif
+
+/* Add new tests above */
+/* This test has to be the final one */
+
 TEST_CASE("dump all performance data", "[nvs]")
 {
     std::cout << "====================" << std::endl << "Dumping benchmarks" << std::endl;
index 92bf35a233592ad55d1f3c30ee674d0f3a34d3f5..ae24cbbbc8d62300db9eef1cde44985a44e1daf8 100755 (executable)
@@ -216,6 +216,7 @@ class PartitionDefinition(object):
             "phy" : 0x01,
             "nvs" : 0x02,
             "coredump" : 0x03,
+            "nvs_keys" : 0x04,
             "esphttpd" : 0x80,
             "fat" : 0x81,
             "spiffs" : 0x82,
index 5267b5bac728e34489f897005449075cd024c78e..e023132d71e9c3fac6d1e63dc3116f2754997f7f 100644 (file)
@@ -27,7 +27,7 @@ TEST_CASE("Can read partition table", "[partition]")
         ++count;
     }
     esp_partition_iterator_release(it);
-    TEST_ASSERT_EQUAL(4, count);
+    TEST_ASSERT_EQUAL(5, count);
 }
 
 TEST_CASE("Can write, read, mmap partition", "[partition][ignore]")
index f3d5a424af21f0398e85f741de78a3206b0d701a..5cba97a63c6b6309cf79d6798bb6ab6af6d54203 100644 (file)
@@ -70,6 +70,7 @@ typedef enum {
     ESP_PARTITION_SUBTYPE_DATA_PHY = 0x01,                                    //!< PHY init data partition
     ESP_PARTITION_SUBTYPE_DATA_NVS = 0x02,                                    //!< NVS partition
     ESP_PARTITION_SUBTYPE_DATA_COREDUMP = 0x03,                               //!< COREDUMP partition
+    ESP_PARTITION_SUBTYPE_DATA_NVS_KEYS = 0x04,                               //!< Partition for NVS keys
 
     ESP_PARTITION_SUBTYPE_DATA_ESPHTTPD = 0x80,                               //!< ESPHTTPD partition
     ESP_PARTITION_SUBTYPE_DATA_FAT = 0x81,                                    //!< FAT partition
index 4003291999fe0fa0f6626bb0546cd361d96f770e..a64175e1593192b3b20dcf1733685cf3170f8c89 100644 (file)
@@ -169,7 +169,8 @@ static esp_err_t load_partitions()
         item->info.encrypted = it->flags & PART_FLAG_ENCRYPTED;
         if (esp_flash_encryption_enabled() && (
                 it->type == PART_TYPE_APP
-                || (it->type == PART_TYPE_DATA && it->subtype == PART_SUBTYPE_DATA_OTA))) {
+                || (it->type == PART_TYPE_DATA && it->subtype == PART_SUBTYPE_DATA_OTA)
+                || (it->type == PART_TYPE_DATA && it->subtype == PART_SUBTYPE_DATA_NVS_KEYS))) {
             /* If encryption is turned on, all app partitions and OTA data
                are always encrypted */
             item->info.encrypted = true;
index 5e6a876fda3dd107f8e93c0125a15ed26a9ec02d..1e2453f1645b8733be7d2e1f56267837fff09b64 100644 (file)
@@ -29,6 +29,7 @@
 #ifndef _ENDIAN_H_
 #define _ENDIAN_H_
 
+#include <stdint.h>
 #include "byteswap.h"
 
 #ifndef BIG_ENDIAN
index 6363f58f894a35fbbe43af14a178053e7f5a34df..bbea011a059d2bcc7e0f8b8146f71add839cdfa1 100644 (file)
@@ -53,13 +53,14 @@ If you choose "Custom partition table CSV" in menuconfig then you can also enter
 
 The CSV format is the same format as printed in the summaries shown above. However, not all fields are required in the CSV. For example, here is the "input" CSV for the OTA partition table::
 
-  # Name,   Type, SubType, Offset,   Size
-  nvs,      data, nvs,     0x9000,  0x4000
-  otadata,  data, ota,     0xd000,  0x2000
-  phy_init, data, phy,     0xf000,  0x1000
-  factory,  app,  factory, 0x10000,  1M
-  ota_0,    app,  ota_0,   ,         1M
-  ota_1,    app,  ota_1,   ,         1M
+  # Name,   Type, SubType,  Offset,   Size,  Flags
+  nvs,      data, nvs,      0x9000,  0x4000
+  otadata,  data, ota,      0xd000,  0x2000
+  phy_init, data, phy,      0xf000,  0x1000
+  factory,  app,  factory,  0x10000,  1M
+  ota_0,    app,  ota_0,    ,         1M
+  ota_1,    app,  ota_1,    ,         1M
+  nvs_key,  data, nvs_keys, ,        0x1000
 
 * Whitespace between fields is ignored, and so is any line starting with # (comments).
 * Each non-comment line in the CSV file is a partition definition.
@@ -115,6 +116,9 @@ When type is "data", the subtype field can be specified as ota (0), phy (1), nvs
   - The NVS API can also be used for other application data.
   - It is strongly recommended that you include an NVS partition of at least 0x3000 bytes in your project.
   - If using NVS API to store a lot of data, increase the NVS partition size from the default 0x6000 bytes.
+- keys (4) is for the NVS key partition. See :doc:`Non-Volatile Storage (NVS) API <../api-reference/storage/nvs_flash>` for more details.
+  - It is used to store NVS encryption keys when `NVS Encryption` feature is enabled.
+  - The size of this partition should be 4096 bytes (minimum partition size).
 
 Other data subtypes are reserved for future esp-idf uses.
 
diff --git a/requirements.txt b/requirements.txt
new file mode 100644 (file)
index 0000000..7051195
--- /dev/null
@@ -0,0 +1,10 @@
+# This is a list of python packages needed for ESP-IDF. This file is used with pip.
+# Please see the Get Started section of the ESP-IDF Programming Guide for further information.
+#
+setuptools
+# The setuptools package is required to install source distributions and on some systems is not installed by default.
+# Please keep it as the first item of this list.
+#
+pyserial>=3.0
+future>=0.15.2
+cryptography
index 5f80c5e79fe6347c336a61932f4dd5a11c98d0ea..432553f2e73a356985a7e728a19955b98bb96a2c 100644 (file)
@@ -2,15 +2,16 @@
 #
 # Name,     Type, SubType, Offset,   Size, Flags
 # Note: if you change the phy_init or app partition offset, make sure to change the offset in Kconfig.projbuild
-nvs,        data, nvs,     0x9000,  0x4000
-otadata,    data, ota,     0xd000,  0x2000
-phy_init,   data, phy,     0xf000,  0x1000
-factory,    0,    0,       0x10000, 0x240000
+nvs,        data, nvs,      0x9000,  0x4000
+otadata,    data, ota,      0xd000,  0x2000
+phy_init,   data, phy,      0xf000,  0x1000
+factory,    0,    0,        0x10000, 0x240000
 # these OTA partitions are used for tests, but can't fit real OTA apps in them
 # (done this way to reduce total flash usage.)
-ota_0,      0,    ota_0,   ,        64K
-ota_1,      0,    ota_1,   ,        64K
+ota_0,      0,    ota_0,    ,        64K
+ota_1,      0,    ota_1,    ,        64K
 # flash_test partition used for SPI flash tests, WL FAT tests, and SPIFFS tests
-flash_test, data, fat,     ,        528K
+flash_test, data, fat,      ,        528K
+nvs_key,    data, nvs_keys, ,        0x1000, encrypted
 
 # Note: still 1MB of a 4MB flash left free for some other purpose