From 61cfe5c4a7343321480a4039b71775f72a6d1e7e Mon Sep 17 00:00:00 2001 From: alvinzhou Date: Thu, 13 Jul 2023 11:01:25 +0800 Subject: [PATCH] Implementation of COMPONENT_SECUREF with TG424_3 for secure flash --- cmsis/device/rtos/mbed_lib.json | 2 +- storage/blockdevice/CMakeLists.txt | 3 + .../COMPONENT_SECUREF/CMakeLists.txt | 20 + .../COMPONENT_SECUREF/TG424_3/CMakeLists.txt | 27 + .../TG424_3/JEDEC_security_HAL/CMakeLists.txt | 24 + .../JEDEC_security_HAL/crypto_wrapper.h | 67 + .../JEDEC_security_HAL/include/crypto_defs.h | 288 +++ .../JEDEC_security_HAL/include/error.h | 47 + .../JEDEC_security_HAL/include/jedec_defs.h | 83 + .../JEDEC_security_HAL/jedec_security_hal.c | 587 ++++++ .../JEDEC_security_HAL/jedec_security_hal.h | 216 +++ .../TG424_3/JEDEC_security_HAL/queue.c | 218 +++ .../TG424_3/JEDEC_security_HAL/queue.h | 144 ++ .../JEDEC_security_HAL/vendor_security_impl.h | 471 +++++ .../COMPONENT_SECUREF/TG424_3/README.md | 92 + .../TG424_3/vendor_impl/CMakeLists.txt | 12 + .../macronix/armorflash_mx75/CMakeLists.txt | 1 + .../macronix/armorflash_mx78/CMakeLists.txt | 85 + .../crypto_wrapper/crypto_wrapper_mbedtls.c | 763 ++++++++ .../crypto_wrapper/crypto_wrapper_psa.c | 742 ++++++++ .../armorflash_mx78/libmx78_armor_lib.a | Bin 0 -> 378188 bytes .../macronix/armorflash_mx78/mx78_armor.c | 1603 +++++++++++++++++ .../macronix/armorflash_mx78/mx78_armor.h | 139 ++ .../macronix/armorflash_mx78/mx78_armor_lib.h | 251 +++ .../libmx78_armor_provision_lib.a | Bin 0 -> 365928 bytes .../provisioning/mx78_armor_provision.h | 288 +++ .../armorflash_mx78/secureflash_layout.h | 82 + .../vendor_impl/vendor_provisioning_impl.h | 82 + .../TG424_3/vendor_impl/vendor_secureflash.h | 37 + .../vendor_impl/vendor_secureflash_defs.h | 124 ++ .../vendor_impl/vendor_template/vendor.c | 384 ++++ .../vendor_impl/vendor_template/vendor.h | 88 + .../vendor_crypto_wrapper.c | 123 ++ .../include/SECUREF/SecureFBlockDevice.h | 312 ++++ .../COMPONENT_SECUREF/mbed_lib.json | 93 + .../platform/include/plat_secure_flash.h | 31 + .../platform/plat_secure_flash.cpp | 158 ++ .../source/SecureFBlockDevice.cpp | 540 ++++++ .../COMPONENT_SECUREF/spi_nor_flash/spi_nor.c | 286 +++ .../COMPONENT_SECUREF/spi_nor_flash/spi_nor.h | 165 ++ .../include/blockdevice/BlockDevice.h | 21 + .../general_block_device/CMakeLists.txt | 4 + .../blockdevice/general_block_device/main.cpp | 358 +++- .../mx78_armor2_provision_example.h | 18 + targets/targets.json | 3 + 45 files changed, 9020 insertions(+), 62 deletions(-) create mode 100644 storage/blockdevice/COMPONENT_SECUREF/CMakeLists.txt create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/CMakeLists.txt create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/CMakeLists.txt create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/crypto_wrapper.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/crypto_defs.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/error.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/jedec_defs.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/jedec_security_hal.c create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/jedec_security_hal.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/queue.c create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/queue.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/vendor_security_impl.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/README.md create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/CMakeLists.txt create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx75/CMakeLists.txt create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/CMakeLists.txt create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/crypto_wrapper/crypto_wrapper_mbedtls.c create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/crypto_wrapper/crypto_wrapper_psa.c create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/libmx78_armor_lib.a create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor.c create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor_lib.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/provisioning/libmx78_armor_provision_lib.a create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/provisioning/mx78_armor_provision.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/secureflash_layout.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_provisioning_impl.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_secureflash.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_secureflash_defs.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor.c create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor_crypto_wrapper/vendor_crypto_wrapper.c create mode 100644 storage/blockdevice/COMPONENT_SECUREF/include/SECUREF/SecureFBlockDevice.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/mbed_lib.json create mode 100644 storage/blockdevice/COMPONENT_SECUREF/platform/include/plat_secure_flash.h create mode 100644 storage/blockdevice/COMPONENT_SECUREF/platform/plat_secure_flash.cpp create mode 100644 storage/blockdevice/COMPONENT_SECUREF/source/SecureFBlockDevice.cpp create mode 100644 storage/blockdevice/COMPONENT_SECUREF/spi_nor_flash/spi_nor.c create mode 100644 storage/blockdevice/COMPONENT_SECUREF/spi_nor_flash/spi_nor.h create mode 100644 storage/blockdevice/tests/TESTS/blockdevice/general_block_device/mx78_armor2_provision_example.h diff --git a/cmsis/device/rtos/mbed_lib.json b/cmsis/device/rtos/mbed_lib.json index 231ee3c0755..b707834e66b 100644 --- a/cmsis/device/rtos/mbed_lib.json +++ b/cmsis/device/rtos/mbed_lib.json @@ -4,7 +4,7 @@ "present": 1, "main-thread-stack-size": { "help": "The size of the main thread's stack", - "value": 4096 + "value": 16384 }, "timer-thread-stack-size": { "help": "The size of the timer thread's stack", diff --git a/storage/blockdevice/CMakeLists.txt b/storage/blockdevice/CMakeLists.txt index 8b77f0fbc05..3a8a12bee28 100644 --- a/storage/blockdevice/CMakeLists.txt +++ b/storage/blockdevice/CMakeLists.txt @@ -38,6 +38,9 @@ if("SPIF" IN_LIST MBED_TARGET_LABELS) add_subdirectory(COMPONENT_SPIF) endif() +if("SECUREF" IN_LIST MBED_TARGET_LABELS) + add_subdirectory(COMPONENT_SECUREF) +endif() target_include_directories(mbed-storage-blockdevice INTERFACE diff --git a/storage/blockdevice/COMPONENT_SECUREF/CMakeLists.txt b/storage/blockdevice/COMPONENT_SECUREF/CMakeLists.txt new file mode 100644 index 00000000000..c7288188bf6 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (c) 2020 ARM Limited. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +target_sources(mbed-storage-securef + INTERFACE + source/SecureFBlockDevice.cpp + PRIVATE + platform/plat_secure_flash.cpp + spi_nor_flash/spi_nor.c +) + +target_include_directories(mbed-storage-securef + INTERFACE + include + include/SECUREF + PRIVATE + platform/include/ + spi_nor_flash/ +) +add_subdirectory(JEDEC_security_HAL) diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/CMakeLists.txt b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/CMakeLists.txt new file mode 100644 index 00000000000..f4bf0c4c952 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/CMakeLists.txt @@ -0,0 +1,27 @@ +#------------------------------------------------------------------------------- +# Copyright (c) 2020-2023 Macronix International Co. LTD. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +#------------------------------------------------------------------------------- + +cmake_minimum_required(VERSION 3.15) + +cmake_policy(SET CMP0079 NEW) + +add_library(jedec_security_hal STATIC) + +target_sources(jedec_security_hal + PRIVATE + JEDEC_security_HAL/jedec_security_hal.c + JEDEC_security_HAL/queue.c +) + +target_include_directories(jedec_security_hal + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR}/JEDEC_security_HAL + ${CMAKE_CURRENT_SOURCE_DIR}/JEDEC_security_HAL/include + ${CMAKE_CURRENT_SOURCE_DIR}/vendor_impl +) + +add_subdirectory(vendor_impl) \ No newline at end of file diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/CMakeLists.txt b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/CMakeLists.txt new file mode 100644 index 00000000000..52611c3dd71 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/CMakeLists.txt @@ -0,0 +1,24 @@ +#------------------------------------------------------------------------------- +# Copyright (c) 2020-2023 Macronix International Co. LTD. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +#------------------------------------------------------------------------------- + +cmake_minimum_required(VERSION 3.15) + +cmake_policy(SET CMP0079 NEW) + +add_library(jedec_security_hal STATIC) + +target_sources(jedec_security_hal + PRIVATE + jedec_security_hal.c + queue.c +) + +target_include_directories(jedec_security_hal + PUBLIC + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/include +) \ No newline at end of file diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/crypto_wrapper.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/crypto_wrapper.h new file mode 100644 index 00000000000..753d077fc90 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/crypto_wrapper.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _CRYPTO_WRAPPER_H_ +#define _CRYPTO_WRAPPER_H_ + +#include +#include "include/crypto_defs.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef int (*init_t)(void); +typedef int (*deinit_t)(void); +typedef int (*algorithm_support_t)(int alg); +typedef int (*crypto_func_t)(crypto_indicator_t *indicator); +typedef int (*key_derive_t)(crypto_indicator_t *indicator, uint32_t *output_key_id); +typedef int (*generate_random_t)(uint8_t *odata, uint32_t odata_len); +typedef int (*ecdh_gen_key_pair_t)(crypto_indicator_t *indicator); +typedef int (*ecdh_gen_shared_secret_t)(crypto_indicator_t *indicator); + +typedef int (*open_key_t)(uint32_t key_id); +typedef int (*close_key_t)(uint32_t key_id); +typedef int (*destroy_key_t)(uint32_t key_id); +typedef int (*export_public_key_t)(uint32_t key_id, uint8_t *key_buf, uint32_t buf_size, uint32_t *actual_size); +typedef int (*export_key_t)(uint32_t key_id, uint8_t *key_buf, uint32_t buf_size, uint32_t *actual_size); +typedef int (*import_key_t)(uint32_t *key_id, uint8_t *key_buf, uint32_t buf_size, KeyLifeTime lifetime); + + +typedef struct { + init_t init; + deinit_t deinit; + algorithm_support_t algorithm_support; + crypto_func_t crypto_func; + key_derive_t key_derive; + generate_random_t generate_random; + ecdh_gen_key_pair_t ecdh_gen_key_pair; + ecdh_gen_shared_secret_t ecdh_gen_shared_secret; + + open_key_t open_key; + close_key_t close_key; + destroy_key_t destroy_key; + export_public_key_t export_public_key; + export_key_t export_key; + import_key_t import_key; + +} crypto_wrapper_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _CRYPTO_WRAPPER_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/crypto_defs.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/crypto_defs.h new file mode 100644 index 00000000000..96d5627c0d4 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/crypto_defs.h @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _CRYPTO_DEFS_H_ +#define _CRYPTO_DEFS_H_ + +#include +#include "error.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * \brief Cryptographic service error code + * + */ + +#define JEDEC_ERROR_NOTHING -0x1001 +#define JEDEC_ERROR_AEAD_ENC -0x1002 +#define JEDEC_ERROR_AEAD_DEC -0x1003 +#define JEDEC_ERROR_CIPHER_ENC -0x1004 +#define JEDEC_ERROR_CIPHER_DEC -0x1005 +#define JEDEC_ERROR_AEAD -0x1006 +#define JEDEC_ERROR_HMAC -0x1007 +#define JEDEC_ERROR_KDF -0x1008 +#define JEDEC_ERROR_MAC_COMPUTE -0x1009 +#define JEDEC_ERROR_MAC_VERIFY -0x100A +#define JEDEC_ERROR_GENERATE_RANDOM -0x100B +#define JEDEC_ERROR_OPEN_KEY -0x100C +#define JEDEC_ERROR_CLOSE_KEY -0x100D +#define JEDEC_ERROR_EXPORT_KEY -0x100E +#define JEDEC_ERROR_IMPORT_KEY -0x100F +#define JEDEC_ERROR_DESTROY_KEY -0x1010 +#define JEDEC_ERROR_INVALID_PARAM -0x1011 +#define JEDEC_ERROR_NOT_SUPPORT -0x1012 +#define JEDEC_ERROR_GENERATE_KEY -0x1013 +#define JEDEC_ERROR_KEY_ID_CONFLICT -0x1014 +#define JEDEC_ERROR_KEY_ID_NOT_FOUND -0x1015 +#define JEDEC_ERROR_KEY_OPERATION -0x1016 +#define JEDEC_ERROR_RANDOM_GEN -0x1017 +#define JEDEC_ERROR_MAC -0x1018 +#define JEDEC_ERROR_CRYPTO -0x1019 + +#define MAX_CIPHER_DATA_SIZE 1024 + +#define INVALID_KEY_ID 0 /*!< Invalid key id */ +#define RESVD_KEY_ID 0xFFFFFFFF /*!< Reserved key id */ + +#define ALG_TYPE(ALG) (ALG & 0xFFFFFFF0) + +/* \brief Supported cipher types and modes + * + */ +typedef enum { + ALG_NONE = 0x00, // 0x00 + + ALG_AES_CCM = 0x10, // 0x10 + ALG_AES_CCM_128, // 0x11 + ALG_AES_CCM_192, // 0x12 + ALG_AES_CCM_256, // 0x13 + ALG_AES_GCM = 0x20, // 0x20 + ALG_AES_GCM_128, // 0x21 + ALG_AES_GCM_192, // 0x22 + ALG_AES_GCM_256, // 0x23 + ALG_AES_ECB = 0x30, // 0x30 + ALG_AES_ECB_128, // 0x31 + ALG_AES_ECB_192, // 0x32 + ALG_AES_ECB_256, // 0x33 + ALG_AES_CBC = 0x40, // 0x40 + ALG_AES_CBC_128, // 0x41 + ALG_AES_CBC_192, // 0x42 + ALG_AES_CBC_256, // 0x43 + ALG_AES_OFB = 0x50, // 0x50 + ALG_AES_OFB_128, // 0x51 + ALG_AES_OFB_192, // 0x52 + ALG_AES_OFB_256, // 0x53 + ALG_AES_CTR = 0x60, // 0x60 + ALG_AES_CTR_128, // 0x61 + ALG_AES_CTR_192, // 0x62 + ALG_AES_CTR_256, // 0x63 + + ALG_ECDSA = 0x70, // 0x70 + ALG_ECDSA_SECP192R1, // 0x71 + ALG_ECDSA_SECP224R1, // 0x72 + ALG_ECDSA_SECP256R1, // 0x73 + ALG_ECDSA_SECP384R1, // 0x74 + ALG_ECDSA_SECP521R1, // 0x75 + ALG_ECDSA_BP256R1, // 0x76 + ALG_ECDSA_BP384R1, // 0x77 + ALG_ECDSA_BP512R1, // 0x78 + ALG_ECDSA_CURVE25519, // 0x79 + ALG_ECDSA_SECP192K1, // 0x7A + ALG_ECDSA_SECP224K1, // 0x7B + ALG_ECDSA_SECP256K1, // 0x7C + ALG_ECDSA_CURVE448, // 0x7D + + ALG_ECDH = 0x80, // 0x80 + ALG_ECDH_SECP192R1, // 0x81 + ALG_ECDH_SECP224R1, // 0x82 + ALG_ECDH_SECP256R1, // 0x83 + ALG_ECDH_SECP384R1, // 0x84 + ALG_ECDH_SECP521R1, // 0x85 + ALG_ECDH_BP256R1, // 0x86 + ALG_ECDH_BP384R1, // 0x87 + ALG_ECDH_BP512R1, // 0x88 + ALG_ECDH_CURVE25519, // 0x89 + ALG_ECDH_SECP192K1, // 0x8A + ALG_ECDH_SECP224K1, // 0x8B + ALG_ECDH_SECP256K1, // 0x8C + + ALG_HMAC = 0x90, // 0x90 + ALG_HMAC_SHA_1, // 0x91 + ALG_HMAC_SHA_224, // 0x92 + ALG_HMAC_SHA_256, // 0x93 + ALG_HMAC_SHA_384, // 0x94 + ALG_HMAC_SHA_512, // 0x95 + + ALG_HKDF = 0xA0, // 0xA0 + ALG_HKDF_SHA_1, // 0xA1 + ALG_HKDF_SHA_224, // 0xA2 + ALG_HKDF_SHA_256, // 0xA3 + ALG_HKDF_SHA_384, // 0xA4 + ALG_HKDF_SHA_512, // 0xA5 +} CryptoAlgorithm; + +/** + * \brief Supported cryptographic operation properties + * + */ +typedef enum { + PROP_NO_SECURITY_OPERATION , /*!< No security operation. */ + + PROP_AUTHEN_TAG_DECRYPT_DATA , /*!< Authenticate tag and decrypt data */ + PROP_AUTHEN_TAG , /*!< Authenticate tag only */ + PROP_DECRYPT_DATA , /*!< Decrypt data only */ + + PROP_ENCRYPT_TAG_DATA , /*!< Encrypt data and generate authenticate tag */ + PROP_ENCRYPT_TAG , /*!< Generate authenticate tag only */ + PROP_ENCRYPT_DATA , /*!< Encrypt data only */ + + PROP_HMAC_COMPUTE , /*!< Compute Hash-based MAC */ + PROP_HMAC_VERIFY , /*!< Verify Hash-based MAC */ + + PROP_HKDF , /*!< HKDF Extract-Expand */ + PROP_HKDF_EXTRACT , /*!< HKDF Extract */ + PROP_HKDF_EXPAND , /*!< HKDF Expand */ + + PROP_SIGNATURE_SIGN , /*!< Generate signature */ + PROP_SIGNATURE_VERIFY , /*!< Verify signature */ + + PROP_GEN_KEY_PAIR , /*!< Generate key pair */ + PROP_GEN_SHARED_SECRET , /*!< Compute shared secret */ +} CryptoOpProperty; + +typedef enum { + RAW_PUB_KEY, + UNCOMPRESSED_PUB_KEY, + COMPRESSED_PUB_KEY +} EcdhPubKeyType; + +typedef enum { + KEY_LIFETIME_VOLATILE, + KEY_LIFETIME_PERSISTENT, +} KeyLifeTime; +/** + * \brief Cryptographic operation indicator + * + */ +typedef struct { + union { + /** + * \struct aead + * + * \brief Structure containing AES-CCM/AES-GCM operation parameters. + */ + struct { + uint32_t key_id; + uint32_t key_len; + uint8_t *iv; + uint32_t iv_len; + uint8_t *add; + uint32_t add_len; + uint8_t *plain_text; + uint8_t *cipher_text; + uint32_t text_len; + uint8_t *tag; + uint32_t tag_len; + } aead; + /** + * \struct hkdf + * + * \brief Structure containing HKDF operation parameters. + */ + struct { + uint32_t ikm_id; /*!< input key material id */ + uint32_t ikm_len; + uint8_t *salt; /*!< optional salt value (a non-secret random value) */ + uint32_t salt_len; + uint32_t prk_id; + uint8_t *prk; /*!< pseudo random key (of HashLen octets) */ + uint32_t prk_len; + uint8_t *info; /*!< optional context and application specific information */ + uint32_t info_len; + uint32_t okm_id; + uint8_t *okm; /*!< output keying material (of okm_len octets) */ + uint32_t okm_len; /*!< length of output keying material in octets */ + } hkdf; + /** + * \struct cipher + * + * \brief Structure containing AES CBC/ECB operation parameters. + */ + struct { + uint32_t key_id; + uint8_t *iv; + uint32_t iv_len; + uint8_t *plain_text; + uint8_t *cipher_text; + uint32_t text_len; + } enc_cipher; + /** + * \struct ecdh + * + * \brief Structure containing ECDH operation parameters. + */ + struct { + EcdhPubKeyType pub_key_type; + uint8_t *pub_key; + uint32_t pub_key_id; + uint32_t pub_key_len; + uint32_t pri_key_id; + uint32_t pri_key_len; + uint8_t *secret; + uint32_t secret_id; + uint32_t secret_len; + } ecdh; + /** + * \struct hmac + * + * \brief Structure containing HMAC operation parameters. + */ + struct { + uint8_t *key; + uint32_t key_id; + uint32_t key_len; + uint8_t *idata; + uint32_t idata_len; + uint8_t *mac; + uint32_t mac_id; + uint32_t mac_len; + } hmac; + }; + struct { + uint8_t import_req; + int flag; + KeyLifeTime lifetime; + int type; + } key_attr; + uint8_t buf[MAX_CIPHER_DATA_SIZE]; + /** + * \brief Crypto algorithm type. + */ + CryptoAlgorithm algorithm; + /** + * \brief Crypto operation property. + */ + CryptoOpProperty property; +} crypto_indicator_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _CRYPTO_DEFS_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/error.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/error.h new file mode 100644 index 00000000000..a4b2768a881 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/error.h @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _ERROR_H_ +#define _ERROR_H_ + +#include + +#ifdef __cplusplus + extern "C" { +#endif + +typedef int32_t jedec_error_t; + +#define JEDEC_ERROR_NONE 0 +#define JEDEC_ERROR_SESSION -0x0001 +#define JEDEC_ERROR_INV_ARGS -0x0002 +#define JEDEC_ERROR_AUTH_FAIL -0x0003 +#define JEDEC_ERROR_COMM_FAIL -0x0004 +#define JEDEC_ERROR_INVALID_ADDR -0x0005 +#define JEDEC_ERROR_MAX_SESSIONS_REACHED -0x0006 +#define JEDEC_ERROR_CMD_NOT_SUPPORTED_ON_DEVICE -0x000a +#define JEDEC_ERROR_INIT_FAIL -0x000b +#define JEDEC_ERROR_NOT_PERMITTED -0x000c +#define JEDEC_ERROR_DEVICE_BUSY -0x0011 +#define JEDEC_ERROR_CMD_PACKET -0x0012 +#define JEDEC_ERROR_INSUFFICIENT_MEMORY -0x0013 +#define JEDEC_ERROR_GENERIC -0x0014 + +#ifdef __cplusplus +} +#endif + +#endif /* _ERROR_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/jedec_defs.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/jedec_defs.h new file mode 100644 index 00000000000..fcfbd7ddc8e --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/include/jedec_defs.h @@ -0,0 +1,83 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _JEDEC_DEFS_H_ +#define _JEDEC_DEFS_H_ + +#ifdef __cplusplus + extern "C" { +#endif + +/* secure read is supported */ +#define SECURE_READ +/* fixme: These macro could be defined in jedec_config.cmake file */ +#define PACKET_MAX_LEN 0x400 +#define MAX_RANDOM_SIZE 0x20 +#define MAX_REGION_NUM 0x10 + +/* Persistent key ids */ +/* fixme: 0X55AA55 is just a example key id */ +#define SECUREFLASH_AUTHEN_KEY_ID 0x55AA55 /*!< Authentication key id for secure init/uninit */ +/** + * \brief Region access restriction + * + */ +typedef enum { + NONE_PROC, /*!< no access restriction */ + WR_PROC, /*!< write protection */ + RD_PROC, /*!< read protection */ + RW_PROC, /*!< read&write protection */ + RD_ONLY, /*!< readonly, write is not allowed */ +} access_restr_t; + +/** + * \brief Region protection type + * + */ +typedef enum { + LOCK, /*!< need unlock before read/write */ + CONTINUOUS, /*!< need sign each read/write command */ +} proc_type_t; + +/** + * \brief region attributes + * + */ +typedef struct secure_region_attributes { + uint32_t address; /*!< region start address */ + uint32_t length; /*!< region size */ + uint32_t root_key_id; /*!< region root key id */ + uint8_t auth_algo; /*!< region authentication algorithm */ + uint8_t encr_algo; /*!< region encryption algorithm */ + uint8_t access_restr; /*!< region access restriction */ + uint8_t prot_type; /*!< region protection type */ +} secure_region_attributes_t; + +/** + * \brief region link list node + * + */ +typedef struct region_ll_node{ + secure_region_attributes_t attributes; /*!< region attributes */ + uint32_t session_key_id; /*!< region session key id */ + struct region_ll_node *next; /*!< pointer to next node */ +} region_ll_node_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _JEDEC_DEFS_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/jedec_security_hal.c b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/jedec_security_hal.c new file mode 100644 index 00000000000..0bd79d3fdaa --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/jedec_security_hal.c @@ -0,0 +1,587 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 +#include +#include +#include +#include "jedec_security_hal.h" +#include "include/error.h" + +static jedec_ctx_t ctx = {}; + +jedec_error_t jedec_set_vendor(vendor_security_op_t *vendor, + crypto_wrapper_t *crypto_wrapper, + void *vendor_ctx) +{ + ctx.vendor_security_op = vendor; + ctx.crypto_wrapper = crypto_wrapper, + ctx.vendor_ctx = vendor_ctx; +} + +jedec_error_t jedec_secure_init(uint32_t auth_key_id) +{ + jedec_error_t err = JEDEC_ERROR_NONE; + crypto_indicator_t indicator = {}; + uint8_t random[MAX_RANDOM_SIZE]; + + if (ctx.is_initialized) { + return JEDEC_ERROR_NONE; + } + /* Check whether auth_key_id is valid */ + if (auth_key_id == INVALID_KEY_ID || ctx.vendor_security_op == NULL || + ctx.crypto_wrapper == NULL || ctx.vendor_ctx == NULL) { + return JEDEC_ERROR_INV_ARGS; + } + err = ctx.crypto_wrapper->open_key(auth_key_id); + if (err != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_KEY_OPERATION; + } + queue_clear(&ctx.q); + err = ctx.crypto_wrapper->generate_random(random, MAX_RANDOM_SIZE); + if (err != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_RANDOM_GEN; + } + err = ctx.vendor_security_op->pre_secure_init(ctx.vendor_ctx, + random, MAX_RANDOM_SIZE, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->secure_init_packet(ctx.vendor_ctx, auth_key_id, + ctx.packet, &ctx.packet_len, + &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, ctx.packet_len, + NULL, + 0, + indicator.hmac.mac, + indicator.hmac.mac_len); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->post_secure_init(ctx.vendor_ctx, &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + ctx.is_initialized = true; + return JEDEC_ERROR_NONE; +} + +jedec_error_t jedec_secure_uninit(uint32_t auth_key_id) +{ + jedec_error_t err = JEDEC_ERROR_NONE; + crypto_indicator_t indicator = {}; + uint8_t random[MAX_RANDOM_SIZE]; + + if (!ctx.is_initialized) { + return JEDEC_ERROR_NONE; + } + if (auth_key_id == INVALID_KEY_ID) { + return JEDEC_ERROR_INV_ARGS; + } + queue_clear(&ctx.q); + err = ctx.crypto_wrapper->generate_random(random, MAX_RANDOM_SIZE); + if (err != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_RANDOM_GEN; + } + err = ctx.vendor_security_op->pre_secure_uninit(ctx.vendor_ctx, + random, MAX_RANDOM_SIZE, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->secure_uninit_packet(ctx.vendor_ctx, + auth_key_id, + ctx.packet, &ctx.packet_len, + &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, ctx.packet_len, + NULL, 0, + indicator.hmac.mac, + indicator.hmac.mac_len); + if (err != JEDEC_ERROR_NONE) { + return err; + } + queue_clear(&ctx.q); + err = ctx.vendor_security_op->post_secure_uninit(ctx.vendor_ctx, &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + /* Close root_key_id */ + err = ctx.crypto_wrapper->close_key(auth_key_id); + if (err != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_KEY_OPERATION; + } + ctx.is_initialized = false; + memset(&ctx, 0x00, sizeof (jedec_ctx_t)); + return JEDEC_ERROR_NONE; +} + +jedec_error_t jedec_create_session(uint32_t root_key_id, bool verify, uint32_t *session_key_id) +{ + jedec_error_t err = JEDEC_ERROR_NONE; + crypto_indicator_t indicator = {}; + uint8_t random[MAX_RANDOM_SIZE]; + + if (!ctx.is_initialized) { + return JEDEC_ERROR_NOT_PERMITTED; + } + queue_clear(&ctx.q); + err = ctx.crypto_wrapper->generate_random(random, MAX_RANDOM_SIZE); + if (err != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_RANDOM_GEN; + } + err = ctx.vendor_security_op->pre_create_session(ctx.vendor_ctx, + random, MAX_RANDOM_SIZE, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->create_session_packet(ctx.vendor_ctx, + root_key_id, + ctx.packet, &ctx.packet_len, + &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, ctx.packet_len, + NULL, 0, + NULL, 0); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* derive session key from root key */ + err = ctx.crypto_wrapper->key_derive(&indicator, session_key_id); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.crypto_wrapper->generate_random(random, MAX_RANDOM_SIZE); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->post_create_session(ctx.vendor_ctx, + root_key_id, + *session_key_id, + random, MAX_RANDOM_SIZE, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + return JEDEC_ERROR_NONE; +} + +jedec_error_t jedec_terminate_session(uint32_t session_key_id) +{ + jedec_error_t err = JEDEC_ERROR_NONE; + crypto_indicator_t indicator = {}; + uint8_t random[MAX_RANDOM_SIZE]; + + if (!ctx.is_initialized) { + return JEDEC_ERROR_NOT_PERMITTED; + } + queue_clear(&ctx.q); + err = ctx.crypto_wrapper->generate_random(random, MAX_RANDOM_SIZE); + if (err != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_RANDOM_GEN; + } + err = ctx.vendor_security_op->pre_terminate_session(ctx.vendor_ctx, + random, MAX_RANDOM_SIZE, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->terminate_session_packet(ctx.vendor_ctx, + session_key_id, + ctx.packet, &ctx.packet_len, + &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, ctx.packet_len, + NULL, 0, + indicator.hmac.mac, + indicator.hmac.mac_len); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->post_terminate_session(ctx.vendor_ctx, + session_key_id, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + /* Destroy companion crypto system session keys */ + err = ctx.crypto_wrapper->destroy_key(session_key_id); + if (err != JEDEC_ERROR_NONE) { + return err; + } + return JEDEC_ERROR_NONE; +} + + +jedec_error_t jedec_secure_program(uint32_t addr, const uint8_t *data, uint32_t len, + uint32_t session_key_id, uint32_t *bytes_programmed) +{ + jedec_error_t err = JEDEC_ERROR_NONE; + crypto_indicator_t indicator = {}; + + if (!ctx.is_initialized) { + return JEDEC_ERROR_NOT_PERMITTED; + } + queue_clear(&ctx.q); + err = ctx.vendor_security_op->pre_secure_program(ctx.vendor_ctx, addr, + session_key_id, &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + err = ctx.vendor_security_op->secure_program_packet(ctx.vendor_ctx, + addr, data, len, + session_key_id, + ctx.packet, &ctx.packet_len, + &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, ctx.packet_len, + indicator.aead.cipher_text, + indicator.aead.text_len, + indicator.aead.tag, + indicator.aead.tag_len); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Get secure program response from secure Flash */ + err = ctx.vendor_security_op->post_secure_program(ctx.vendor_ctx, + session_key_id, + ctx.packet, &ctx.packet_len, + &indicator, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from secure Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Parse response from secure Flash */ + err = ctx.vendor_security_op->parse_secure_program_response(ctx.vendor_ctx, + &indicator, + bytes_programmed); + if (err != JEDEC_ERROR_NONE) { + return err; + } + return JEDEC_ERROR_NONE; +} + +jedec_error_t jedec_secure_erase(uint32_t addr, uint32_t len, uint32_t session_key_id) +{ + jedec_error_t err = JEDEC_ERROR_NONE; + crypto_indicator_t indicator = {}; + + if (!ctx.is_initialized) { + return JEDEC_ERROR_NOT_PERMITTED; + } + queue_clear(&ctx.q); + err = ctx.vendor_security_op->pre_secure_erase(ctx.vendor_ctx, addr, len, + session_key_id, &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + err = ctx.vendor_security_op->secure_erase_packet(ctx.vendor_ctx, + addr, len, + session_key_id, + ctx.packet, &ctx.packet_len, + &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, ctx.packet_len, + indicator.aead.cipher_text, + indicator.aead.text_len, + indicator.aead.tag, + indicator.aead.tag_len); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Get secure program response from secure Flash */ + err = ctx.vendor_security_op->post_secure_erase(ctx.vendor_ctx, + session_key_id, + ctx.packet, &ctx.packet_len, + &indicator, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from secure Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Parse response from secure Flash */ + err = ctx.vendor_security_op->parse_secure_erase_response(ctx.vendor_ctx, &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + return JEDEC_ERROR_NONE; +} + +jedec_error_t jedec_secure_read(uint32_t addr, uint8_t *data, uint32_t len, + uint32_t session_key_id, uint32_t *bytes_read) +{ + jedec_error_t err = JEDEC_ERROR_NONE; + crypto_indicator_t indicator = {}; + + if (!ctx.is_initialized) { + return JEDEC_ERROR_NOT_PERMITTED; + } + queue_clear(&ctx.q); + err = ctx.vendor_security_op->pre_secure_read(ctx.vendor_ctx, addr, + session_key_id, &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + err = ctx.vendor_security_op->secure_read_packet(ctx.vendor_ctx, + addr, len, + session_key_id, + ctx.packet, &ctx.packet_len, + &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, ctx.packet_len, + indicator.aead.cipher_text, + indicator.aead.text_len, + indicator.aead.tag, + indicator.aead.tag_len); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Get secure program response from secure Flash */ + err = ctx.vendor_security_op->post_secure_read(ctx.vendor_ctx, + session_key_id, + ctx.packet, &ctx.packet_len, + &indicator, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from secure Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Parse response from secure Flash */ + err = ctx.vendor_security_op->parse_secure_read_response(ctx.vendor_ctx, + &indicator, + data, len, + bytes_read); + if (err != JEDEC_ERROR_NONE) { + return err; + } + return JEDEC_ERROR_NONE; +} + +jedec_error_t jedec_secure_copy(uint32_t src_addr, uint32_t dst_addr, uint32_t len, + uint32_t session_key_id) +{ + //TODO + return JEDEC_ERROR_NONE; +} + +jedec_error_t jedec_secure_get_regions_info(region_ll_node_t *region_descr_p, + uint32_t session_key_id, int8_t region_index) +{ + jedec_error_t err = JEDEC_ERROR_NONE; + crypto_indicator_t indicator = {}; + bool session_key_valid_flag; + uint16_t region_index_start, region_index_end; + uint8_t random[MAX_RANDOM_SIZE]; + + if (!ctx.is_initialized) { + return JEDEC_ERROR_NOT_PERMITTED; + } + region_ll_node_t *node_ptr = region_descr_p; + region_ll_node_t *next_node_ptr = region_descr_p; + memset(&indicator, 0x00, sizeof(indicator)); + queue_clear(&ctx.q); + err = ctx.vendor_security_op->pre_secure_get_regions_info(ctx.vendor_ctx, + session_key_id, + &session_key_valid_flag, + &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + if (session_key_valid_flag) { + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + } + if (region_index < 0) { + region_index_start = 0; + region_index_end = MAX_REGION_NUM - 1; + } else if (region_index < MAX_REGION_NUM) { + region_index_start = region_index; + region_index_end = region_index; + } else { + return JEDEC_ERROR_INV_ARGS; + } + for (uint16_t index = region_index_start; index <= region_index_end; index++) { + if (session_key_valid_flag) { + err = ctx.crypto_wrapper->generate_random(random, MAX_RANDOM_SIZE); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->secure_get_regions_info_packet(ctx.vendor_ctx, + session_key_id, + index, + random, MAX_RANDOM_SIZE, + ctx.packet, &ctx.packet_len, + &indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.crypto_wrapper->crypto_func(&indicator); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, + ctx.packet_len, + NULL, + 0, + indicator.hmac.mac, + indicator.hmac.mac_len); + if (err != JEDEC_ERROR_NONE) { + return err; + } + queue_clear(&ctx.q); + err = ctx.vendor_security_op->post_secure_get_regions_info(ctx.vendor_ctx, node_ptr, &ctx.q); + if (err != JEDEC_ERROR_NONE) { + return err; + } + /* Verify response from secure Flash */ + if (queue_verify(ctx.crypto_wrapper, &ctx.q) == false) { + return JEDEC_ERROR_AUTH_FAIL; + } + } else { + err = ctx.vendor_security_op->secure_get_regions_info_packet(ctx.vendor_ctx, + 0, + index, + NULL, 0, + ctx.packet, &ctx.packet_len, + NULL); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->packet_send(ctx.vendor_ctx, + ctx.packet, ctx.packet_len, + NULL, 0, + NULL, 0); + if (err != JEDEC_ERROR_NONE) { + return err; + } + err = ctx.vendor_security_op->post_secure_get_regions_info(ctx.vendor_ctx, node_ptr, NULL); + if (err != JEDEC_ERROR_NONE) { + return err; + } + } + next_node_ptr = node_ptr + 1; + node_ptr->next = next_node_ptr; + node_ptr = next_node_ptr; + next_node_ptr->next = NULL; + } + return JEDEC_ERROR_NONE; +} + +jedec_error_t jedec_secure_manage_region(region_ll_node_t *head) +{ + //TODO + return JEDEC_ERROR_NONE; +} diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/jedec_security_hal.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/jedec_security_hal.h new file mode 100644 index 00000000000..813ce140c90 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/jedec_security_hal.h @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _JEDEC_SECURITY_HAL_H_ +#define _JEDEC_SECURITY_HAL_H_ + +#include +#include +#include "vendor_security_impl.h" +#include "crypto_wrapper.h" + +#ifdef __cplusplus + extern "C" { +#endif + +typedef struct { + bool is_initialized; /*!< Secure Flash state:Initialized or uninitialized */ + void *vendor_ctx; /*!< Vendor context */ + vendor_security_op_t *vendor_security_op; /*!< Vendor specific security operations */ + crypto_wrapper_t *crypto_wrapper; /*!< Crypto wrapper functions */ + uint8_t packet[PACKET_MAX_LEN]; /*!< Buffer holding command packet */ + uint32_t packet_len; /*!< Command packet length */ + uint8_t verify; /*!< Verification indication of responses from secure Flash */ + jqueue_t q; /*!< Response queue */ +} jedec_ctx_t; + +/** + * \brief Bind JEDEC context with vendor specific implementation. + * + * \param[in] vendor Vendor specific implementation + * \param[in] vendor_ctx Vendor specific context + * + */ +jedec_error_t jedec_set_vendor(vendor_security_op_t *vendor, + crypto_wrapper_t *crypto_wrapper, + void *vendor_ctx); + +/** + * \brief Set verify flag of packets returned in the queue. + * + * \param[in] verify Verify indication + * + */ +jedec_error_t jedec_set_verify(uint8_t verify); + +/** + * \brief Move the secure Flash's status to initialized status. + * + * \param[in] auth_key_id Input authenticatoin key id for secure initialization + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + */ +jedec_error_t jedec_secure_init(uint32_t auth_key_id); +/** + * \brief Security uninitialization of secure Flash. + * + * \param[in] auth_key_id Input authentication key id for secure uninitialization + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_SESSION - no session has been established + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + */ +jedec_error_t jedec_secure_uninit(uint32_t auth_key_id); +/** + * \brief Establish a cryptographic session with secure Flash. + * + * \param[in] root_key_id Preshared root key ID + * \param[in] verify Indicate whether subsequent flash responses + * within session should be verified + * \param[out] session_key_id Generated session key ID + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + * JEDEC_ERROR_MAX_SESSIONS_REACHED - no more sessions may be created + */ +jedec_error_t jedec_create_session(uint32_t root_key_id, bool verify, uint32_t *session_key_id); +/** + * \brief Terminate a cryptographic session with secure Flash. + * + * \param[in] session_key_id Corresponding session key ID + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_COMM_FAIL - communication error + */ +jedec_error_t jedec_terminate_session(uint32_t session_key_id); +/** + * \brief Program data to secure Flash. + * + * \param[in] secureflash Secure Flash to access + * \param[in] addr Target address in secure Flash + * \param[in] data Data to be programmed to secure Flash + * \param[in] len Number of bytes requested to be programmed + * \param[in] session_key_id Session key ID + * \param[out] bytes_programmed Number of bytes that have been programmed + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_SESSION - no session has been established + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + * JEDEC_ERROR_INVALID_ADDR -address is outside addressable flash address space + */ +jedec_error_t jedec_secure_program(uint32_t addr, const uint8_t *data, uint32_t len, + uint32_t session_key_id, uint32_t *bytes_programmed); +/** + * \brief Erase a sector/block specified by a base address of secure Flash. + * + * \param[in] secureflash Secure Flash to access + * \param[in] addr Address from which to start erase + * \param[in] len Number of bytes requested to be programmed + * \param[in] session_key_id Session key ID + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_SESSION - no session has been established + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + */ +jedec_error_t jedec_secure_erase(uint32_t addr, uint32_t len, uint32_t session_key_id); +/** + * \brief Read protected data from secure Flash. + * + * \param[in] secureflash Secure Flash to access + * \param[in] addr Target (starting) address in secure Flash + * \param[in] data Data read from secure Flash + * \param[in] len Number of bytes requested to be read + * \param[in] session_key_id Session key ID + * \param[out] bytes_read Number of bytes that have been read + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_SESSION - no session has been established + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + * JEDEC_ERROR_INVALID_ADDR -address is outside addressable flash address space + * JEDEC_ERROR_CMD_NOT_SUPPORTED_ON_DEVICE -command not supported by vendor's flash + */ +jedec_error_t jedec_secure_read(uint32_t addr, uint8_t *data, uint32_t len, + uint32_t session_key_id, uint32_t *bytes_read); +/** + * \brief Copy data from secure Flash one location to another. + * + * \param[in] secureflash Secure Flash to access + * \param[in] src_addr Source address in secure Flash + * \param[in] dst_addr Destination address in secure Flash + * \param[in] len Number of bytes to copy + * \param[in] session_key_id Session key ID + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_SESSION - no session has been established + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + * JEDEC_ERROR_INVALID_ADDR -address is outside addressable flash address space + * JEDEC_ERROR_CMD_NOT_SUPPORTED_ON_DEVICE -command not supported by vendor's flash + */ +jedec_error_t jedec_secure_copy(uint32_t src_addr, uint32_t dst_addr, uint32_t len, + uint32_t session_key_id); +/** + * \brief Obtain current secure configuration parameters of secure Flash. + * + * \param[in] secureflash Secure Flash to access + * \param[in/out] region_descr_p Pointer to secure region configuration linked list. + * \param[in] session_key_id Session key ID + * \param[in] region_idx Region index + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_SESSION - no session has been established + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + */ +jedec_error_t jedec_secure_get_regions_info(region_ll_node_t *region_descr_p, + uint32_t session_key_id, int8_t region_idx); +/** + * \brief Configure secure Flash regions. + * + * \param[in] secureflash Secure Flash to access + * \param[in] head Pointer to the header of secure regions' + * configuration parameters link list + * + * \return JEDEC_ERROR_NONE - success + * JEDEC_ERROR_SESSION - no session has been established + * JEDEC_ERROR_INV_ARGS - invalid input arguments + * JEDEC_ERROR_AUTH_FAIL - authentication failure + * JEDEC_ERROR_COMM_FAIL - communication error + * JEDEC_ERROR_CMD_NOT_SUPPORTED_ON_DEVICE - command not supported by vendor's flash + */ +jedec_error_t jedec_secure_manage_region(region_ll_node_t *head); + +#ifdef __cplusplus +} +#endif + +#endif /* _JEDEC_SECURITY_HAL_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/queue.c b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/queue.c new file mode 100644 index 00000000000..a04ea1f50f4 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/queue.c @@ -0,0 +1,218 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 +#include +#include "queue.h" + +void queue_clear(jqueue_t *q) +{ + q->list.head.next = &q->list.head; + q->list.head.prev = &q->list.head; + memset(q->buf, 0x00, sizeof(q->buf)); + q->offset = 0; +} + +bool queue_is_empty(jqueue_t *q) +{ + if ((q->list.head.next == NULL) || + (q->list.head.next == &(q->list.head))) { + return 1; + } else { + return 0; + } +} + +void queue_add(jqueue_t *q, resp_param_t *item) +{ + if ((q->offset + sizeof(jresp_t)) > BUF_SIZE) { + return; + } + jresp_t *r = (jresp_t *)&q->buf[q->offset]; + q->offset += sizeof(jresp_t); + r->node.next = NULL; + r->node.prev = NULL; + + r->param.alg = item->alg; + r->param.property = item->property; + switch (ALG_TYPE(item->alg)) { + case ALG_HMAC: + r->param.hmac.flag = item->hmac.flag; + r->param.hmac.key_id = item->hmac.key_id; + r->param.hmac.key_len = item->hmac.key_len; + //input data + if (((q->offset + item->hmac.idata_len) <= BUF_SIZE) && + (item->hmac.idata != NULL) && (item->hmac.idata_len > 0)) { + memcpy(&q->buf[q->offset], item->hmac.idata, item->hmac.idata_len); + r->param.hmac.idata = &q->buf[q->offset]; + r->param.hmac.idata_len = item->hmac.idata_len; + q->offset += item->hmac.idata_len; + } + //mac + if (((q->offset + item->hmac.mac_len) <= BUF_SIZE) && + (item->hmac.mac != NULL) && (item->hmac.mac_len > 0)) { + memcpy(&q->buf[q->offset], item->hmac.mac, item->hmac.mac_len); + r->param.hmac.mac = &q->buf[q->offset]; + r->param.hmac.mac_len = item->hmac.mac_len; + q->offset += item->hmac.mac_len; + } + break; + case ALG_AES_GCM: + r->param.aead.key_id = item->aead.key_id; + //iv + if (((q->offset + item->aead.iv_len) <= BUF_SIZE) && + (item->aead.iv != NULL) && (item->aead.iv_len > 0)) { + memcpy(&q->buf[q->offset], item->aead.iv, item->aead.iv_len); + r->param.aead.iv = &q->buf[q->offset]; + r->param.aead.iv_len = item->aead.iv_len; + q->offset += item->aead.iv_len; + } + //aad + if (((q->offset + item->aead.aad_len) <= BUF_SIZE) && + (item->aead.aad != NULL) && (item->aead.aad_len > 0)) { + memcpy(&q->buf[q->offset], item->aead.aad, item->aead.aad_len); + r->param.aead.aad = &q->buf[q->offset]; + r->param.aead.aad_len = item->aead.aad_len; + q->offset += item->aead.aad_len; + } + //packet + if (((q->offset + item->aead.ciphertext_len) <= BUF_SIZE) && + (item->aead.ciphertext != NULL) && (item->aead.ciphertext_len > 0)) { + memcpy(&q->buf[q->offset], item->aead.ciphertext, item->aead.ciphertext_len); + r->param.aead.ciphertext = &q->buf[q->offset]; + r->param.aead.ciphertext_len = item->aead.ciphertext_len; + q->offset += item->aead.ciphertext_len; + } + //tag + if (((q->offset + item->aead.tag_len) <= BUF_SIZE) && + (item->aead.tag != NULL) && (item->aead.tag_len > 0)) { + memcpy(&q->buf[q->offset], item->aead.tag, item->aead.tag_len); + r->param.aead.tag = &q->buf[q->offset]; + r->param.aead.tag_len = item->aead.tag_len; + q->offset += item->aead.tag_len; + } + //plaintext + if (((q->offset + item->aead.plaintext_len) <= BUF_SIZE) && + (item->aead.plaintext != NULL) && (item->aead.plaintext_len > 0)) { + memcpy(&q->buf[q->offset], item->aead.plaintext, item->aead.plaintext_len); + r->param.aead.plaintext = &q->buf[q->offset]; + r->param.aead.plaintext_len = item->aead.plaintext_len; + q->offset += item->aead.plaintext_len; + } + break; + default: + break; + } + r->node.prev = q->list.head.prev; + r->node.next = &(q->list.head); + r->node.next->prev = &r->node; + r->node.prev->next = &r->node; +} + +void queue_get(jqueue_t *q, resp_param_t *item) +{ + jresp_t *r = (jresp_t *)q->list.head.next; + if (queue_is_empty(q)) { + return; + } + switch (ALG_TYPE(r->param.alg)) { + case ALG_HMAC: + memcpy (item, &r->param, sizeof (resp_param_t)); + q->offset -= r->param.hmac.idata_len; + q->offset -= r->param.hmac.mac_len; + q->offset -= sizeof(jresp_t); + break; + case ALG_AES_GCM: + memcpy (item, &r->param, sizeof (resp_param_t)); + q->offset -= r->param.aead.iv_len; + q->offset -= r->param.aead.aad_len; + q->offset -= r->param.aead.ciphertext_len; + q->offset -= r->param.aead.tag_len; + q->offset -= sizeof(jresp_t); + break; + default: + break; + } + r->node.prev->next = r->node.next; + r->node.next->prev = r->node.prev; + r->node.prev = NULL; + r->node.next = NULL; +} + +/** + * \brief Verify secure Flash responses stored in queque. + * + * \param[in] crypto_wrapper Pointer of crypto wrapper + * \param[in] queue Queue of secure Flash responses + * + * \return true if crypto verification success, + * or false if crypto verification fail + */ +bool queue_verify(crypto_wrapper_t *crypto_wrapper, jqueue_t *queue) +{ + resp_param_t response; + crypto_indicator_t indicator = {}; + + while (!queue_is_empty(queue)) { + queue_get(queue, &response); + indicator.algorithm = response.alg; + indicator.property = response.property; + switch (ALG_TYPE(response.alg)) { + case ALG_HMAC: + if ((response.hmac.key_id != 0) && + (response.hmac.idata != NULL) && (response.hmac.idata_len > 0) || + (response.hmac.mac == NULL) && (response.hmac.mac_len > 0)) { + indicator.key_attr.flag = response.hmac.flag; + indicator.hmac.key_id = response.hmac.key_id; + indicator.hmac.key_len = response.hmac.key_len; + indicator.hmac.idata = response.hmac.idata; + indicator.hmac.idata_len = response.hmac.idata_len; + indicator.hmac.mac = response.hmac.mac; + indicator.hmac.mac_len = response.hmac.mac_len; + if (crypto_wrapper->crypto_func(&indicator) != JEDEC_ERROR_NONE) { + return false; + } + } + break; + case ALG_AES_GCM: + if ((response.aead.key_id != 0) && + (response.aead.iv != NULL) && (response.aead.iv_len > 0) && + (response.aead.aad != NULL) && (response.aead.aad_len > 0) && + (response.aead.ciphertext != NULL) && (response.aead.ciphertext_len > 0) && + (response.aead.tag != NULL) && (response.aead.tag_len > 0)) { + indicator.property = PROP_AUTHEN_TAG; + indicator.aead.key_id = response.aead.key_id; + indicator.aead.iv = response.aead.iv; + indicator.aead.iv_len = response.aead.iv_len; + indicator.aead.add = response.aead.aad; + indicator.aead.add_len = response.aead.aad_len; + indicator.aead.cipher_text = response.aead.ciphertext; + indicator.aead.text_len = response.aead.ciphertext_len; + indicator.aead.tag = response.aead.tag; + indicator.aead.tag_len = response.aead.tag_len; + if (crypto_wrapper->crypto_func(&indicator) != JEDEC_ERROR_NONE) { + return false; + } + } + break; + case ALG_NONE: + break; + default: + return false; + } + } + return true; +} diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/queue.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/queue.h new file mode 100644 index 00000000000..9914db6e870 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/queue.h @@ -0,0 +1,144 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _QUEUE_H_ +#define _QUEUE_H_ +#include +#include +#include "crypto_wrapper.h" + +#ifdef __cplusplus + extern "C" { +#endif + +#define BUF_SIZE (4*1024) + +typedef struct jnode { + struct jnode *next; + struct jnode *prev; +} jnode_t; + +typedef struct { + jnode_t head; +} jlist_t; + +typedef struct { + uint8_t buf[BUF_SIZE]; + uint16_t offset; + jlist_t list; +} jqueue_t; + +/** Response cipher type */ +enum cipher_type { + HMAC, + AEAD, + ENC_CIPHER, +}; + +typedef struct { + CryptoAlgorithm alg; + CryptoOpProperty property; + union { + struct { + uint32_t key_id; + uint8_t key_len; + uint8_t *idata; + uint16_t idata_len; + uint8_t *mac; + uint16_t mac_len; + uint8_t flag; + } hmac; + struct { + uint32_t key_id; + uint8_t *iv; + uint16_t iv_len; + uint8_t *aad; + uint16_t aad_len; + uint8_t *tag; + uint16_t tag_len; + uint8_t *ciphertext; + uint16_t ciphertext_len; + uint8_t *plaintext; + uint16_t plaintext_len; + uint8_t flag; + } aead; + struct { + uint32_t key_id; + uint8_t *iv; + uint16_t iv_len; + uint8_t *ciphertext; + uint16_t ciphertext_len; + uint8_t *plaintext; + uint16_t plaintext_len; + uint8_t flag; + } enc_cipher; + }; +} resp_param_t; + +typedef struct { + jnode_t node; + resp_param_t param; +} jresp_t; + +/** + * \brief Clear queue. + * + * \param[in] q Pointer to the queue + * + * \return NULL + */ +void queue_clear(jqueue_t *q); +/** + * \brief Check whether queue is empty. + * + * \param[in] q Pointer to the queue + * + * \return 1 if empty, otherwise 0 + */ +bool queue_is_empty(jqueue_t *q); +/** + * \brief Add a response item to the queue. + * + * \param[in] q Pointer to the queue + * \param[in] item Response item + * \return NULL + */ +void queue_add(jqueue_t *q, resp_param_t *item); +/** + * \brief Get a response item from the queue. + * + * \param[in] q Pointer to the queue + * \param[in] item Response item + * \return NULL + */ +void queue_get(jqueue_t *q, resp_param_t *item); + +/** + * \brief Verify secure Flash responses stored in queque. + * + * \param[in] crypto_wrapper Pointer of crypto wrapper + * \param[in] queue Queue of secure Flash responses + * + * \return true if crypto verification success, + * or false if crypto verification fail + */ +bool queue_verify(crypto_wrapper_t *crypto_wrapper, jqueue_t *queue); + +#ifdef __cplusplus +} +#endif + +#endif /* _QUEUE_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/vendor_security_impl.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/vendor_security_impl.h new file mode 100644 index 00000000000..8365cab97c0 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/JEDEC_security_HAL/vendor_security_impl.h @@ -0,0 +1,471 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _VENDOR_IMPL_H_ +#define _VENDOR_IMPL_H_ + +#include +#include +#include "include/jedec_defs.h" +#include "queue.h" +#include "crypto_wrapper.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * \brief vendor specific security operations + * + */ +typedef struct { + /** + * \brief Vendor id. + */ + uint8_t vendor_id; + /** + * \brief Pre create session. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] nonce Host input nonce + * \param[in] nonce_len Host nonce length + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*pre_create_session)(void *vendor_ctx, uint8_t *nonce, + uint32_t nonce_len, jqueue_t *resp_queue); + /** + * \brief Pack create session packet. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] root_key_id Associated root key id + * \param[in] packet Pointer to packet + * \param[in] packet_len Actual packet length + * \param[out] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*create_session_packet)(void *vendor_ctx, uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator); + /** + * \brief Post create session. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] root_key_id Associated root key id + * \param[in] session_key_id created session key id + * \param[in] nonce Nonce + * \param[in] nonce_len Nonce length + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*post_create_session)(void *vendor_ctx, uint32_t root_key_id, + uint32_t session_key_id, + uint8_t *nonce, uint32_t nonce_len, + jqueue_t *resp_queue); +#if defined(SESSION_CONFIRMATION) + int32_t (*pre_confirm_session)(); + int32_t (*confirm_session_packet)(); + int32_t (*post_confirm_session)(); +#endif + /** + * \brief Pre terminate session. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] nonce Host input nonce + * \param[in] nonce_len Host nonce length + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*pre_terminate_session)(void *vendor_ctx, uint8_t *nonce, + uint32_t nonce_len, jqueue_t *resp_queue); + /** + * \brief Pack terminate session packet. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] session_key_id Associated session key id + * \param[in] packet Pointer to packet + * \param[in] packet_len Actual packet length + * \param[out] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*terminate_session_packet)(void *vendor_ctx, + uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator); + /** + * \brief Post terminate session. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] session_key_id Associated session key id + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*post_terminate_session)(void *vendor_ctx, + uint32_t session_key_id, + jqueue_t *resp_queue); + /** + * \brief Pre secure init. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] nonce Host input nonce + * \param[in] nonce_len Host nonce length + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*pre_secure_init)(void *vendor_ctx, uint8_t *nonce, + uint32_t nonce_len, jqueue_t *resp_queue); + /** + * \brief Pack secure init packet. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] root_key_id Associated root key id + * \param[in] packet Pointer to packet + * \param[in] packet_len Actual packet length + * \param[out] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*secure_init_packet)(void *vendor_ctx, uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator); + /** + * \brief Post secure init. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*post_secure_init)(void *vendor_ctx, jqueue_t *resp_queue); + /** + * \brief Pre secure uninit. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] nonce Host input nonce + * \param[in] nonce_len Host nonce length + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*pre_secure_uninit)(void *vendor_ctx, uint8_t *nonce, + uint32_t nonce_len, jqueue_t *resp_queue); + /** + * \brief Pack secure uninit packet. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] root_key_id Associated root key id + * \param[in] packet Pointer to packet + * \param[in] packet_len Actual packet length + * \param[out] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*secure_uninit_packet)(void *vendor_ctx, uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator); + /** + * \brief Post secure uninit. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*post_secure_uninit)(void *vendor_ctx, jqueue_t *resp_queue); + /** + * \brief Pre secure program. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] addr Destination address + * \param[in] session_key_id Associated session key id + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*pre_secure_program)(void *vendor_ctx, uint32_t addr, + uint32_t session_key_id, jqueue_t *resp_queue); + /** + * \brief Pack secure program packet. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] addr Destination address + * \param[in] data Pointer to source data + * \param[in] len Data length + * \param[in] session_key_id Associated session key id + * \param[in] packet Pointer to packet + * \param[in] packet_len Actual packet length + * \param[out] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*secure_program_packet)(void *vendor_ctx, uint32_t addr, + const uint8_t *data, uint32_t len, + uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator); + /** + * \brief Post secure program. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] session_key_id Associated session key id + * \param[in] resp_queue Pointer to response queue holding responses from flash + * \param[in] resp_packet_buffer Buffer to hold secure program response from flash + * \param[out] resp_packet_buffer_size Actual secure program response size + * \param[out] indicator Indicator holding crypto algorithm parameters + * (if secure program response also needs crypto operations) + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*post_secure_program)(void *vendor_ctx, jqueue_t *resp_queue, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, + uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator); + /** + * \brief Parse secure program response. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] indicator Indicator holding crypto algorithm parameters + * \param[out] bytes_programmed Actual programmed bytes + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*parse_secure_program_response)(void *vendor_ctx, + crypto_indicator_t *indicator, + uint32_t *bytes_programmed); + /** + * \brief Pre secure erase. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] addr Destination address + * \param[in] len Erase size + * \param[in] session_key_id Associated session key id + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*pre_secure_erase)(void *vendor_ctx, uint32_t addr, uint32_t len, + uint32_t session_key_id, jqueue_t *resp_queue); + /** + * \brief Pack secure erase packet. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] addr Destination address + * \param[in] len Erase size + * \param[in] session_key_id Associated session key id + * \param[in] packet Pointer to packet + * \param[in] packet_len Actual packet length + * \param[out] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*secure_erase_packet)(void *vendor_ctx, uint32_t addr, uint32_t len, + uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator); + /** + * \brief Post secure erase. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] session_key_id Associated session key id + * \param[in] resp_queue Pointer to response queue holding responses from flash + * \param[in] resp_packet_buffer Buffer to hold secure erase response from flash + * \param[out] resp_packet_buffer_size Actual secure erase response size + * \param[out] indicator Indicator holding crypto algorithm parameters + * (if secure erase response also needs crypto operations) + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*post_secure_erase)(void *vendor_ctx, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, + uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator, + jqueue_t *resp_queue); + /** + * \brief Parse secure erase response. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*parse_secure_erase_response)(void *vendor_ctx, + crypto_indicator_t *indicator); +#if defined(SECURE_COPY) + int32_t (*pre_secure_copy)(); + int32_t (*secure_copy_packet)(); + int32_t (*post_secure_copy)(); + int32_t (*parse_secure_copy_response)(); +#endif +#if defined(SECURE_READ) + /** + * \brief Pre secure read. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] addr Target address + * \param[in] session_key_id Associated session key id + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*pre_secure_read)(void *vendor_ctx, uint32_t addr, + uint32_t session_key_id, jqueue_t *resp_queue); + /** + * \brief Pack secure read packet. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] addr Target address + * \param[in] len Data length + * \param[in] session_key_id Associated session key id + * \param[in] packet Pointer to packet + * \param[in] packet_len Actual packet length + * \param[out] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*secure_read_packet)(void *vendor_ctx, uint32_t addr, + uint32_t len, uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator); + /** + * \brief Post secure read. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] session_key_id Associated session key id + * \param[in] resp_queue Pointer to response queue holding responses from flash + * \param[in] resp_packet_buffer Buffer to hold secure read response from flash + * \param[out] resp_packet_buffer_size Actual secure read response size + * \param[out] indicator Indicator holding crypto algorithm parameters + * (if secure read response also needs crypto operations) + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*post_secure_read)(void *vendor_ctx, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, + uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator, + jqueue_t *resp_queue); + /** + * \brief Parse secure read response. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] indicator Indicator holding crypto algorithm parameters + * \param[out] data Buffer to hold read data + * \param[in] data_len Read data length + * \param[out] bytes_read Actual read bytes + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*parse_secure_read_response)(void *vendor_ctx, + crypto_indicator_t *indicator, + uint8_t *data, uint32_t data_len, + uint32_t *bytes_read); +#endif + /** + * \brief Pre get regions' information in security. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] session_key_id Associated session key id + * \param[out] session_key_valid_flag Session key valid flag + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*pre_secure_get_regions_info)(void *vendor_ctx, + uint32_t session_key_id, + bool *session_key_valid_flag, + jqueue_t *resp_queue); + /** + * \brief Pack get regions' information in security packet. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] session_key_id Associated session key id + * \param[in] region_index Region index + * \param[in] nonce Nonce + * \param[in] nonce_len Nonce length + * \param[in] packet Pointer to packet + * \param[in] packet_len Actual packet length + * \param[out] indicator Indicator holding crypto algorithm parameters + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*secure_get_regions_info_packet)(void *vendor_ctx, + uint32_t session_key_id, + int8_t region_index, + uint8_t *nonce, uint32_t nonce_len, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator); + /** + * \brief Post get regions' information in security. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] region_descr_p Pointer to the link list holding region description + * \param[out] resp_queue Pointer to response queue holding responses from flash + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*post_secure_get_regions_info)(void *vendor_ctx, + region_ll_node_t *region_descr_p, + jqueue_t *resp_queue); +#if defined(SECURE_REGION_MANAGE) + int32_t (*pre_secure_manage_region)(void *vendor_ctx); + int32_t (*secure_manage_region_packet)(void *vendor_ctx); + int32_t (*post_secure_manage_region)(void *vendor_ctx); +#endif + /** + * \brief Send security command packet to secure flash. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in/out] packet_out Pointer to command packet + * \param[in] packet_len Current packet length without cipher and mac + * \param[in] cipher_text Pointer to cipher text + * \param[in] cipher_text_len Cipher text length + * \param[in] mac Pointer to mac + * \param[in] mac_len Mac length + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*packet_send)(void *vendor_ctx, + uint8_t *packet_out, uint16_t packet_len, + uint8_t *cipher_text, uint16_t cipher_text_len, + uint8_t *mac, uint16_t mac_len); + /** + * \brief Receive security command response packet from secure flash. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] packet_in Pointer to response packet + * \param[in] packet_len Response packet length + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*packet_receive)(void *vendor_ctx, + uint8_t *packet_in, uint16_t packet_len); +} vendor_security_op_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _VENDOR_IMPL_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/README.md b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/README.md new file mode 100644 index 00000000000..a76ef05f86d --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/README.md @@ -0,0 +1,92 @@ +Getting started +================== + +JEDEC have published the [JESD261 standard](https://www.jedec.org/standards-documents/docs/jesd261), titled Serial NOR Security +Hardware Abstraction Layer(HAL). This standard defines a software layer to provide +a uniform interface for cryptographic operations on compliant Secure Flash devices. + +JESD261 HAL enables manufacturer-neutral communication with secure Flash devices. +Security features introduced in JESD261 include session-based communication, secure program +and erase operations, as well as partitioning of the Flash device into protected regions, +that are available through an application interface. JESD261 defines rules for implementing +this application interface to simplify the use and evaluation of these common security features +accross secure Flash vendors. + +************** +## Design concept + +JEDEC security HAL module could be compiled for use within different OSes and software platforms. +In this design, this module consits of JEDEC security HAL API layer and vendor specific implementation layer. + +JEDEC security HAL API layer implements the interfaces defined in JEDEC JESD261 standard. +And calls the pre-binding specific sub-steps implemented in vendor specific implementation layer. +Besides, the JEDEC security HAL API layer calls crypto services via vendor specific crypto wrapper functions. +Currently, mbedtls library and TF-M PSA crypto services are both supported. + +Vendor specific implementations layer provides specific sub-steps implementaion required by +JEDEC security HAL API layer. In addition, this layer also includes the specific implementation +of secure Flash provisioning which should be performed before deployment. + +Code structure +-------------- +```bash +TG424_3/ +├─JEDEC_security_HAL --------------------------------> JEDEC security HAL API layer +│ │ crypto_wrapper.h ------------------------------> Header file for crypto wrapper functions +│ │ jedec_security_hal.c ---------------------------> JEDEC security HAL API implementation +│ │ jedec_security_hal.h ---------------------------> Header file for JEDEC security HAL API layer +│ │ queue.c ----------------------------------------> Queue operations implementation +│ │ queue.h ----------------------------------------> Queue operations definition +│ │ vendor_security_impl.h --------------------------> Header file for vendor specific implementation of security HAL sub-steps +│ │ +│ └─include +│ +└─vendor_impl -----------------------------------------> Flash vendors specific implementation + │ vendor_provisioning_impl.h -------------------> Header file for vendor specific secure flash provisioning operations + │ vendor_secureflash.h --------------------------> Vendor secure flash definitions + │ vendor_secureflash_defs.h ----------------------> Vendor secure flash informations + │ + ├─macronix + │ ├─armorflash_mx75 -----------------------------> The specific implementations for Macronix MX75 ArmorFlash + │ │ + │ └─armorflash_mx78 -----------------------------> The specific implementations for Macronix MX78 ArmorFlash + │ │ libmx78_armor_lib.a --------------------> Binary library of MX78 ArmorFlash driver + │ │ mx78_armor.c ---------------------------> The specific implementations of JEDEC secuiry HAL sub-steps for Macronix MX78 ArmorFlash + │ │ mx78_armor.h + │ │ mx78_armor_lib.h + │ │ secureflash_layout.h -------------------> The specific parameters and secure region layout of MX78 ArmorFlash + │ │ + │ ├─crypto_wrapper ---------------------------> The specific crypto wrapper functions for Macronix MX78 ArmorFlash + │ │ crypto_wrapper_mbedtls.c -----------> The specific crypto wrapper functions based on mbedtls library + │ │ crypto_wrapper_psa.c --------------> The specific crypto wrapper functions based on Trusted Firmware-M crypto service + │ │ + │ └─provisioning ----------------------------> The specific provisioning implementation for Macronix MX78 ArmorFlash + │ libmx78_armor_provision_lib.a ------> Binary library of MX78 ArmorFlash provisioning driver + │ mx78_armor_provision.h + │ + └─vendor_template --------------------------------> Reserved vendor specific implementation for other secure Flash vendors reference +``` +******** +## Examples + +This JEDEC security HAL implementation has been integrated and tested with following OSes and software platforms. + +- [Arm Mbed OS](https://www.arm.com/products/development-tools/embedded-and-software/mbed-os) + +For Arm Mbed OS, JEDEC security HAL is integrated in the COMPONENT_SECUREF. The source code is available on [github](https://github.com/macronix/). + +``` +git clone -b macronix_secureflash_TG424_3 --recursive https://github.com/macronix/mbed-os +``` + + + +- [Arm Trusted Firmware-M](https://tf-m-user-guide.trustedfirmware.org/index.html) + +For Arm Trusted Firmware-M, JEDEC security HAL is integrated in the ETSS partition which provides solution for supporting secure Flash in Arm Trusted Firmware-M framework. The source code is available on [github](https://github.com/macronix/). + +``` +git clone -b macronix_secureflash_TG424_3 --recursive https://github.com/macronix/tf-m-extras +``` + + diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/CMakeLists.txt b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/CMakeLists.txt new file mode 100644 index 00000000000..b3af1ca5bca --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/CMakeLists.txt @@ -0,0 +1,12 @@ +#------------------------------------------------------------------------------- +# Copyright (c) 2020-2023 Macronix International Co. LTD. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +#------------------------------------------------------------------------------- + +cmake_minimum_required(VERSION 3.15) + +cmake_policy(SET CMP0079 NEW) + +add_subdirectory(${SECUREFLASH_TYPE}) \ No newline at end of file diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx75/CMakeLists.txt b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx75/CMakeLists.txt new file mode 100644 index 00000000000..efe2adcc990 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx75/CMakeLists.txt @@ -0,0 +1 @@ +Reserved for Macronix MX75 ArmorFlash. diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/CMakeLists.txt b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/CMakeLists.txt new file mode 100644 index 00000000000..0788c2963bb --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/CMakeLists.txt @@ -0,0 +1,85 @@ +#------------------------------------------------------------------------------- +# Copyright (c) 2020-2023 Macronix International Co. LTD. All rights reserved. +# +# SPDX-License-Identifier: Apache-2.0 +# +#------------------------------------------------------------------------------- +cmake_minimum_required(VERSION 3.15) + +cmake_policy(SET CMP0079 NEW) + +if (CRYPTO_MBEDTLS AND CRYPTO_PSA) + Message(FATAL_ERROR "CRYPTO_MBEDTLS and CRYPTO_PSA both enabled!") +endif() +if (NOT CRYPTO_MBEDTLS AND NOT CRYPTO_PSA) + Message(FATAL_ERROR "CRYPTO_MBEDTLS and CRYPTO_PSA both disabled!") +endif() +if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/libmx78_armor_lib.a) + Message(FATAL_ERROR "libmx78_armor_lib.a should be download and put under current directory") +else() + add_library(mx78_armor_lib STATIC IMPORTED) + set_target_properties(mx78_armor_lib PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/libmx78_armor_lib.a) + set_target_properties(mx78_armor_lib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}) +endif() + +add_library(mx78_armor STATIC) +target_include_directories(mx78_armor + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${CMAKE_CURRENT_SOURCE_DIR}/../../include +) + +target_compile_definitions(mx78_armor + PRIVATE + $<$:SECUREFLASH_PROVISION> + ETSS_PROV_DEVELOPER_MODE=${ETSS_PROV_DEVELOPER_MODE} + $<$:MULTI_CLIENT_ISOLATION> + $<$:SECUREFLASH_DEBUG> + $<$:MX78_ARMOR_TEST_MODE> + $<$:CRYPTO_MBEDTLS> + $<$:CRYPTO_PSA> +) + +target_sources(mx78_armor + PRIVATE + mx78_armor.c + $<$:${CMAKE_CURRENT_SOURCE_DIR}/crypto_wrapper/crypto_wrapper_psa.c> + $<$:${CMAKE_CURRENT_SOURCE_DIR}/crypto_wrapper/crypto_wrapper_mbedtls.c> +) + +target_link_libraries(mx78_armor + PRIVATE + psa_interface + platform_s + tfm_sprt + secureflash_platform + spi_nor_driver + mx78_armor_lib + INTERFACE + platform_common_interface +) + +target_link_libraries(jedec_security_hal + PRIVATE + mx78_armor +) +if(SECUREFLASH_PROVISION) +target_include_directories(mx78_armor + PRIVATE + provisioning +) + + +if(NOT EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/provisioning/libmx78_armor_provision_lib.a) + Message(FATAL_ERROR "libmx78_armor_provision_lib.a should be download and put under provisioning directory") +else() + add_library(mx78_armor_provision_lib STATIC IMPORTED) + set_target_properties(mx78_armor_provision_lib PROPERTIES IMPORTED_LOCATION ${CMAKE_CURRENT_SOURCE_DIR}/provisioning/libmx78_armor_provision_lib.a) + set_target_properties(mx78_armor_provision_lib PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/provisioning) +endif() + +target_link_libraries(mx78_armor + PRIVATE + mx78_armor_provision_lib +) +endif() diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/crypto_wrapper/crypto_wrapper_mbedtls.c b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/crypto_wrapper/crypto_wrapper_mbedtls.c new file mode 100644 index 00000000000..749f71d21aa --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/crypto_wrapper/crypto_wrapper_mbedtls.c @@ -0,0 +1,763 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifdef CRYPTO_MBEDTLS + +#include +#include +#include +#include +#include +#include "../../../JEDEC_security_HAL/crypto_wrapper.h" +#include "mbedtls/entropy.h" +#include "mbedtls/gcm.h" +#include "mbedtls/ccm.h" +#include "mbedtls/aes.h" +#include "mbedtls/md.h" +#include "mbedtls/hkdf.h" +#include "mbedtls/ecdh.h" +#include "mbedtls/ctr_drbg.h" +#include "../../../spi_nor_flash/spi_nor.h" + +#define KEY_SIZE ((256 / 8) * 2 + 1) +#define KEY_MAX_NUM 0x20 +#define KEY_UNUSED 0 +#define KEY_USED 1 + +#define KEY_HANDLE_NOT_LOADED 0 +#define MAC_SIZE 0x20 +#define TAG_LENGTH 0x10 + +#define MAX_ROOT_KEY_NUM 0x10 +#define MAX_SESSION_KEY_NUM 0x10 +#define MAC_KEY_SIZE 0x20 +#define ENC_KEY_SIZE 0x10 + +#define KEY_ID_LEN 4 + +typedef struct { + uint8_t value[KEY_SIZE]; + uint32_t id; + uint8_t used:1, + len:7; +} key_manager_t; + +typedef struct { + uint32_t enc_key_id; + uint32_t mac_key_id; +} session_key_id_t; + +static session_key_id_t session_key_id_slot[MAX_SESSION_KEY_NUM] = {}; +static key_manager_t key_manager[KEY_MAX_NUM] = {}; + +/** + * \brief Find an empty session key slot. + * + * \param[out] slot_index Returned empty slot index + * + * \return true if an empty session key slot was found, + * or false there's no empty session key slot + */ +static bool find_empty_session_key_id_slot(uint32_t *slot_index) +{ + for (uint32_t i = 0; i < MAX_SESSION_KEY_NUM; i++) { + if ((session_key_id_slot[i].enc_key_id == KEY_HANDLE_NOT_LOADED) && + (session_key_id_slot[i].mac_key_id == KEY_HANDLE_NOT_LOADED)) { + *slot_index = i; + return true; + } + } + return false; +} + +/** + * \brief Find the session key slot index corresponding to session key id. + * \param[in] session_key_id Input session key id + * \param[out] slot_index Returned empty slot index + * + * \return true if corresponding session key slot was found, + * or false there's no matching session key slot + */ +static bool find_session_key_id_slot(uint32_t session_key_id, uint32_t *slot_index) +{ + for (uint32_t i = 0; i < MAX_SESSION_KEY_NUM; i++) { + if ((uint32_t)&session_key_id_slot[i] == session_key_id) { + *slot_index = i; + return true; + } + } + return false; +} + +static int mbedtls_if_init() +{ + memset(key_manager, 0x00, sizeof(key_manager)); + memset(session_key_id_slot, 0x00, sizeof(session_key_id_slot)); + return JEDEC_ERROR_NONE; +} + +static int mbedtls_if_deinit() +{ + memset(key_manager, 0x00, sizeof(key_manager)); + memset(session_key_id_slot, 0x00, sizeof(session_key_id_slot)); + return JEDEC_ERROR_NONE; +} + +static int mbedtls_if_generate_random(uint8_t *odata, uint32_t odata_len) +{ + int32_t status = JEDEC_ERROR_NONE; + mbedtls_entropy_context entropy_ctx; + mbedtls_ctr_drbg_context ctr_drbg_ctx; + + mbedtls_entropy_init(&entropy_ctx); + mbedtls_ctr_drbg_init(&ctr_drbg_ctx); + + if (0 != mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx, NULL, 0)) { + status = JEDEC_ERROR_GENERATE_RANDOM; + goto generate_random_exit_point; + } + + if (0 != mbedtls_ctr_drbg_random(&ctr_drbg_ctx, odata, odata_len)) { + status = JEDEC_ERROR_GENERATE_RANDOM; + goto generate_random_exit_point; + } + +generate_random_exit_point: + mbedtls_entropy_free(&entropy_ctx); + mbedtls_ctr_drbg_free(&ctr_drbg_ctx); + + return status; +} + +static int simple_import_key(uint32_t *key_id, const uint8_t *key, uint8_t key_len) +{ + int32_t status = JEDEC_ERROR_NONE; + + if (0 == *key_id) { + uint8_t i = 0; + do { + status = mbedtls_if_generate_random((uint8_t *)key_id, KEY_ID_LEN); + if (JEDEC_ERROR_NONE != status) { + return status; + } + for (i = 0; i < KEY_MAX_NUM; i++) { + if (KEY_USED == key_manager[i].used && *key_id == key_manager[i].id) { + break; + } + } + }while (KEY_MAX_NUM != i); + } + for (uint8_t i = 0; i < KEY_MAX_NUM; i++) { + if (KEY_USED == key_manager[i].used && *key_id == key_manager[i].id) { + if (!memcmp(key, key_manager[i].value, key_manager[i].len)) { + return JEDEC_ERROR_NONE; + } + return JEDEC_ERROR_KEY_ID_CONFLICT; + } + if (KEY_UNUSED == key_manager[i].used) { + memcpy(key_manager[i].value, key, key_len); + key_manager[i].id = *key_id; + key_manager[i].len = key_len; + key_manager[i].used = KEY_USED; + return JEDEC_ERROR_NONE; + } + } + return JEDEC_ERROR_INSUFFICIENT_MEMORY; +} + +static int simple_export_key(uint32_t key_id, const uint8_t *key, uint8_t key_len, uint8_t *actual_key_len) +{ + for (uint8_t i = 0; i < KEY_MAX_NUM; i++) { + if (KEY_USED == key_manager[i].used && key_id == key_manager[i].id) { + if (key_len < key_manager[i].len) { + return JEDEC_ERROR_EXPORT_KEY; + } + memcpy(key, key_manager[i].value, key_manager[i].len); + *actual_key_len = key_manager[i].len; + return JEDEC_ERROR_NONE; + } + } + return JEDEC_ERROR_EXPORT_KEY; +} + +static int simple_destroy_key(uint32_t key_id) +{ + uint32_t index, target_key_id; + + if (true == find_session_key_id_slot(key_id, &index)) { + target_key_id = session_key_id_slot[index].mac_key_id; + + for (uint8_t i = 0; i < KEY_MAX_NUM; i++) { + if (target_key_id == key_manager[i].id) { + memset(&key_manager[i], 0x00, sizeof(key_manager_t)); + break; + } + } + target_key_id = session_key_id_slot[index].enc_key_id; + + for (uint8_t i = 0; i < KEY_MAX_NUM; i++) { + if (target_key_id == key_manager[i].id) { + memset(&key_manager[i], 0x00, sizeof(key_manager_t)); + break; + } + } + memset(&session_key_id_slot[index], 0x00, sizeof(session_key_id_t)); + return JEDEC_ERROR_NONE; + } + for (uint8_t i = 0; i < KEY_MAX_NUM; i++) { + if (key_id == key_manager[i].id) { + memset(&key_manager[i], 0x00, sizeof(key_manager_t)); + return JEDEC_ERROR_NONE; + } + } + return JEDEC_ERROR_KEY_ID_NOT_FOUND; +} + +static int simple_open_key(uint32_t key_id) +{ + (void)key_id; + return JEDEC_ERROR_NONE; +} + +static int simple_close_key(uint32_t key_id) +{ + (void)key_id; + return JEDEC_ERROR_NONE; +} + +static int mbedtls_if_aes_gcm(crypto_indicator_t *indicator) +{ + int32_t status = JEDEC_ERROR_NONE, mbedl_status, index; + mbedtls_gcm_context ctx = {}; + uint8_t key[16]; + uint8_t actual_key_len = 0; + + mbedtls_gcm_init(&ctx); + + /* export key from session key slot or key manager */ + bool ret = find_session_key_id_slot(indicator->aead.key_id, &index); + if (true == ret) { + status = simple_export_key(session_key_id_slot[index].enc_key_id, key, indicator->aead.key_len, &actual_key_len); + if (JEDEC_ERROR_NONE != status) { + goto aes_gcm_exit_point; + } + } else { + status = simple_export_key(indicator->aead.key_id, key, indicator->aead.key_len, &actual_key_len); + if (JEDEC_ERROR_NONE != status) { + goto aes_gcm_exit_point; + } + } + + if (0 != mbedtls_gcm_setkey(&ctx, MBEDTLS_CIPHER_ID_AES, key, indicator->aead.key_len * 8)) { + status = JEDEC_ERROR_AEAD; + goto aes_gcm_exit_point; + } + switch (indicator->property) { + case PROP_AUTHEN_TAG_DECRYPT_DATA: + case PROP_AUTHEN_TAG: + mbedl_status = mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_DECRYPT, + indicator->aead.text_len, + indicator->aead.iv, indicator->aead.iv_len, + indicator->aead.add, indicator->aead.add_len, + indicator->aead.cipher_text, indicator->aead.plain_text, + indicator->aead.tag_len, indicator->aead.tag); + if (0 != mbedl_status) { + status = JEDEC_ERROR_AEAD; + goto aes_gcm_exit_point; + } + break; + case PROP_ENCRYPT_TAG_DATA: + case PROP_ENCRYPT_TAG: + mbedl_status = mbedtls_gcm_crypt_and_tag(&ctx, MBEDTLS_GCM_ENCRYPT, + indicator->aead.text_len, + indicator->aead.iv, indicator->aead.iv_len, + indicator->aead.add, indicator->aead.add_len, + indicator->aead.plain_text, indicator->aead.cipher_text, + indicator->aead.tag_len, indicator->aead.tag); + if (0 != mbedl_status) { + status = JEDEC_ERROR_AEAD; + goto aes_gcm_exit_point; + } + break; + default: + status = JEDEC_ERROR_AEAD; + goto aes_gcm_exit_point; + } +aes_gcm_exit_point: + mbedtls_gcm_free(&ctx); + return status; +} + +static mbedtls_md_type_t sha_select_md_type(CryptoAlgorithm alg) +{ + switch (alg) { + case ALG_HKDF_SHA_1: + case ALG_HMAC_SHA_1: + return MBEDTLS_MD_SHA1; + case ALG_HKDF_SHA_224: + case ALG_HMAC_SHA_224: + return MBEDTLS_MD_SHA224; + case ALG_HKDF_SHA_256: + case ALG_HMAC_SHA_256: + return MBEDTLS_MD_SHA256; + case ALG_HKDF_SHA_384: + case ALG_HMAC_SHA_384: + return MBEDTLS_MD_SHA384; + case ALG_HKDF_SHA_512: + case ALG_HMAC_SHA_512: + return MBEDTLS_MD_SHA512; + default: + return MBEDTLS_MD_NONE; + } + return MBEDTLS_MD_NONE; +} + +static mbedtls_ecp_group_id ecdh_select_grp(CryptoAlgorithm alg) +{ + switch (alg) { + case ALG_ECDH_SECP192R1: + return MBEDTLS_ECP_DP_SECP192R1; + case ALG_ECDH_SECP224R1: + return MBEDTLS_ECP_DP_SECP224R1; + case ALG_ECDH_SECP256R1: + return MBEDTLS_ECP_DP_SECP256R1; + case ALG_ECDH_SECP384R1: + return MBEDTLS_ECP_DP_SECP384R1; + // case ALG_ECDH_SECP521R1: + // return MBEDTLS_ECP_DP_SECP521R1; + case ALG_ECDH_BP256R1: + return MBEDTLS_ECP_DP_BP256R1; + case ALG_ECDH_BP384R1: + return MBEDTLS_ECP_DP_BP384R1; + // case ALG_ECDH_BP512R1: + // return MBEDTLS_ECP_DP_BP512R1; + case ALG_ECDH_CURVE25519: + return MBEDTLS_ECP_DP_CURVE25519; + case ALG_ECDH_SECP192K1: + return MBEDTLS_ECP_DP_SECP192K1; + case ALG_ECDH_SECP224K1: + return MBEDTLS_ECP_DP_SECP224K1; + case ALG_ECDH_SECP256K1: + return MBEDTLS_ECP_DP_SECP256K1; + default: + return MBEDTLS_ECP_DP_NONE; + } + return MBEDTLS_ECP_DP_NONE; +} + +static int mbedtls_if_ecdh_gen_key_pair(crypto_indicator_t *indicator) +{ + /* The procedure refer to the Mbedtls's test suite (test_suite_ecdh.c) */ + int32_t status = JEDEC_ERROR_NONE; + size_t olen; + uint8_t pri_key[(256/8)+1] = {}, pub_key[(512/8)+1] ={}; + mbedtls_ecp_group_id ecp_grp_id; + mbedtls_mpi host_pri; + mbedtls_ecp_group grp; + mbedtls_ecp_point host_pub; + mbedtls_entropy_context entropy_ctx; + mbedtls_ctr_drbg_context ctr_drbg_ctx; + + /* Init. data struct */ + mbedtls_mpi_init(&host_pri); + mbedtls_ecp_group_init(&grp); + mbedtls_ecp_point_init(&host_pub); + mbedtls_entropy_init(&entropy_ctx); + mbedtls_ctr_drbg_init(&ctr_drbg_ctx); + /* Update random seed use custom string(pers) */ + mbedtls_ctr_drbg_seed(&ctr_drbg_ctx, mbedtls_entropy_func, &entropy_ctx, NULL, 0); + ecp_grp_id = ecdh_select_grp(indicator->algorithm); + if (ecp_grp_id == MBEDTLS_ECP_DP_NONE) { + status = JEDEC_ERROR_NOT_SUPPORT; + goto mbedtls_if_ecdh_gen_key_pair_exit_point; + } + mbedtls_ecp_group_load(&grp, ecp_grp_id); + /* Generate an ECDH key pair on an elliptic curve, private key in host_pri and public key in host_pub */ + mbedtls_ecdh_gen_public(&grp, &host_pri, &host_pub, mbedtls_ctr_drbg_random, &ctr_drbg_ctx); + /* Derived public key */ + switch (indicator->ecdh.pub_key_type) { + case RAW_PUB_KEY: + mbedtls_ecp_point_write_binary(&grp, &host_pub, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, + pub_key, indicator->ecdh.pub_key_len + 1); + memmove(pub_key, &pub_key[1], olen - 1); + if(indicator->ecdh.pub_key_len != (olen - 1)) { + status = JEDEC_ERROR_INV_ARGS; + goto mbedtls_if_ecdh_gen_key_pair_exit_point; + } + break; + case UNCOMPRESSED_PUB_KEY: + mbedtls_ecp_point_write_binary(&grp, &host_pub, MBEDTLS_ECP_PF_UNCOMPRESSED, &olen, + pub_key, indicator->ecdh.pub_key_len); + if(indicator->ecdh.pub_key_len != olen) { + status = JEDEC_ERROR_INV_ARGS; + goto mbedtls_if_ecdh_gen_key_pair_exit_point; + } + break; + case COMPRESSED_PUB_KEY: + mbedtls_ecp_point_write_binary(&grp, &host_pub, MBEDTLS_ECP_PF_COMPRESSED, &olen, + pub_key, indicator->ecdh.pub_key_len); + if(indicator->ecdh.pub_key_len != olen) { + status = JEDEC_ERROR_INV_ARGS; + goto mbedtls_if_ecdh_gen_key_pair_exit_point; + } + break; + default: + status = JEDEC_ERROR_NOT_SUPPORT; + goto mbedtls_if_ecdh_gen_key_pair_exit_point; + } + memcpy(indicator->ecdh.pub_key, pub_key, indicator->ecdh.pub_key_len); + indicator->ecdh.pri_key_len = mbedtls_mpi_size(&host_pri); + + /* Derived private key */ + mbedtls_mpi_write_binary(&host_pri, pri_key, mbedtls_mpi_size(&host_pri)); + status = simple_import_key(&indicator->ecdh.pub_key_id, indicator->ecdh.pub_key, indicator->ecdh.pub_key_len); + if (status != JEDEC_ERROR_NONE) { + goto mbedtls_if_ecdh_gen_key_pair_exit_point; + } + status = simple_import_key(&indicator->ecdh.pri_key_id, pri_key, indicator->ecdh.pri_key_len); + if (status != JEDEC_ERROR_NONE) { + goto mbedtls_if_ecdh_gen_key_pair_exit_point; + } + memset(pri_key, 0x00, sizeof(pri_key)); +mbedtls_if_ecdh_gen_key_pair_exit_point: + mbedtls_mpi_free(&host_pri); + mbedtls_ecp_group_free(&grp); + mbedtls_ecp_point_free(&host_pub); + mbedtls_entropy_free(&entropy_ctx); + mbedtls_ctr_drbg_free(&ctr_drbg_ctx); + return status; +} + +static int32_t mbedlts_if_ecdh_gen_shared_secret(crypto_indicator_t *indicator) +{ + int32_t status = JEDEC_ERROR_NONE; + const char *pers = "mbedtls_ecdh"; + mbedtls_mpi host_secret; + mbedtls_mpi host_pri; + mbedtls_ecp_group grp; + mbedtls_ecp_point dev_pub; + mbedtls_entropy_context entropy; + mbedtls_ctr_drbg_context ctr_drbg; + mbedtls_ecp_group_id ecp_grp_id; + uint8_t host_pri_key[256 / 8 +1] = {}; + uint8_t secret[256 / 8] = {}; + uint8_t actual_key_len; + uint8_t pub_key_half_len, ofs = 0; + + if (NULL == indicator->ecdh.secret && !indicator->key_attr.import_req) { + return JEDEC_ERROR_INVALID_PARAM; + } + /* Init. data struct */ + mbedtls_mpi_init(&host_secret); + mbedtls_mpi_init(&host_pri); + mbedtls_ecp_group_init(&grp); + mbedtls_ecp_point_init(&dev_pub); + mbedtls_entropy_init(&entropy); + mbedtls_ctr_drbg_init(&ctr_drbg); + + /* Update random seed use custom string(pers) */ + // mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, (const uint8_t *)pers, strlen(pers)); + mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy, NULL, 0); + ecp_grp_id = ecdh_select_grp(indicator->algorithm); + if (ecp_grp_id == MBEDTLS_ECP_DP_NONE) { + return JEDEC_ERROR_NOT_SUPPORT; + } + mbedtls_ecp_group_load(&grp, ecp_grp_id); + status = simple_export_key(indicator->ecdh.pri_key_id, host_pri_key, sizeof(host_pri_key), &actual_key_len); + if (JEDEC_ERROR_NONE != status) { + return status; + } + switch (indicator->ecdh.pub_key_type) { + case RAW_PUB_KEY: + pub_key_half_len = indicator->ecdh.pub_key_len / 2; + ofs = 0; + break; + case UNCOMPRESSED_PUB_KEY: + pub_key_half_len = (indicator->ecdh.pub_key_len - 1) / 2; + ofs = 1; + break; + default: + return JEDEC_ERROR_NOT_SUPPORT; + } + /* Import device public key to dev_pub */ + mbedtls_mpi_read_binary(&dev_pub.X, indicator->ecdh.pub_key + ofs, pub_key_half_len); + mbedtls_mpi_read_binary(&dev_pub.Y, indicator->ecdh.pub_key + ofs + pub_key_half_len, pub_key_half_len); + mbedtls_mpi_lset(&dev_pub.Z, 1); + + /* Import host private key to host_pri */ + mbedtls_mpi_read_binary(&host_pri, host_pri_key, indicator->ecdh.pri_key_len); + + /* Compute the shared secret */ + mbedtls_ecdh_compute_shared(&grp, &host_secret, &dev_pub, &host_pri, mbedtls_ctr_drbg_random, &ctr_drbg); + + /* Derived shared secret */ + mbedtls_mpi_write_binary(&host_secret, secret, mbedtls_mpi_size(&host_secret)); + indicator->ecdh.secret_len = mbedtls_mpi_size(&host_secret); + + if (indicator->key_attr.import_req) { + status = simple_import_key(&indicator->ecdh.secret_id, secret, indicator->ecdh.secret_len); + if (status != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_GENERATE_KEY; + } + memset(secret, 0x00, indicator->ecdh.secret_len); + } else { + memcpy(indicator->ecdh.secret, secret, indicator->ecdh.secret_len); + } + + mbedtls_mpi_free(&host_pri); + mbedtls_mpi_free(&host_secret); + mbedtls_ecp_group_free(&grp); + mbedtls_ecp_point_free(&dev_pub); + mbedtls_entropy_free(&entropy); + mbedtls_ctr_drbg_free(&ctr_drbg); + + return status; +} + +static int mbedtls_if_hmac_compute(crypto_indicator_t *indicator, mbedtls_md_info_t *md, uint8_t *key) +{ + int32_t status = JEDEC_ERROR_NONE; + uint8_t mac[256 / 8] = {}; + + if (0 != mbedtls_md_hmac(md, key, indicator->hmac.key_len, indicator->hmac.idata, indicator->hmac.idata_len, mac)) { + return JEDEC_ERROR_HMAC; + } + if (indicator->key_attr.import_req) { + status = simple_import_key(&indicator->hmac.mac_id, mac, indicator->hmac.key_len); + if (JEDEC_ERROR_NONE != status) { + return status; + } + memset (mac, 0x00, indicator->hmac.mac_len); + } else { + memcpy(indicator->hmac.mac, mac, indicator->hmac.mac_len); + } + return status; +} + +static int mbedtls_if_hmac_verify(crypto_indicator_t *indicator, mbedtls_md_info_t *md, uint8_t *key) +{ + uint8_t mac[256 / 8] = {}; + + if (0 != mbedtls_md_hmac(md, + key, + indicator->hmac.key_len, + indicator->hmac.idata, + indicator->hmac.idata_len, + mac)) { + return JEDEC_ERROR_HMAC; + } + if (memcmp(mac, indicator->hmac.mac, indicator->hmac.mac_len)) { + return JEDEC_ERROR_HMAC; + } + return JEDEC_ERROR_NONE; +} + +static int mbedtls_if_hmac(crypto_indicator_t *indicator) +{ + int32_t status = JEDEC_ERROR_NONE; + mbedtls_md_type_t md_type = MBEDTLS_MD_NONE; + mbedtls_md_info_t *md = NULL; + uint8_t key[256 / 8], actual_key_len = 0; + + if ((0 == indicator->hmac.key_id && NULL == indicator->hmac.key) || + NULL == indicator->hmac.idata || 0 == indicator->hmac.idata_len) { + return JEDEC_ERROR_INVALID_PARAM; + } + if (PROP_HMAC_VERIFY == indicator->property && NULL == indicator->hmac.mac) { + return JEDEC_ERROR_INVALID_PARAM; + } + if (NULL == indicator->hmac.mac && 0 == indicator->key_attr.import_req) { + return JEDEC_ERROR_INVALID_PARAM; + } + if (ALG_HMAC != ALG_TYPE(indicator->algorithm)) { + return JEDEC_ERROR_NOT_SUPPORT; + } + md_type = sha_select_md_type(indicator->algorithm); + if (MBEDTLS_MD_NONE == md_type) { + return JEDEC_ERROR_NOT_SUPPORT; + } + md = mbedtls_md_info_from_type(md_type); + if (NULL == md) { + return JEDEC_ERROR_NOT_SUPPORT; + } + if (indicator->hmac.mac_len != mbedtls_md_get_size(md)) { + return JEDEC_ERROR_INVALID_PARAM; + } + /* If hmac.key is NULL, then export key from session key slot or key manager */ + if (NULL == indicator->hmac.key) { + int index; + bool ret = find_session_key_id_slot(indicator->hmac.key_id, & index); + if (true == ret) { + status = simple_export_key(session_key_id_slot[index].mac_key_id, key, indicator->hmac.key_len, &actual_key_len); + if (JEDEC_ERROR_NONE != status) { + return status; + } + } else { + status = simple_export_key(indicator->hmac.key_id, key, indicator->hmac.key_len, &actual_key_len); + if (JEDEC_ERROR_NONE != status) { + return status; + } + } + } else { + memcpy(key , indicator->hmac.key, indicator->hmac.key_len); + } + + switch (indicator->property) { + case PROP_HMAC_COMPUTE: + status = mbedtls_if_hmac_compute(indicator, md, key); + break; + case PROP_HMAC_VERIFY: + status = mbedtls_if_hmac_verify(indicator, md, key); + break; + default: + return JEDEC_ERROR_INVALID_PARAM; + } + return status; +} + +static int mbedtls_if_mx78_kdf(crypto_indicator_t *indicator) +{ + int status = JEDEC_ERROR_NONE, index = 0; + uint8_t actual_key_len = 0, prk[256 / 8], okm[256 / 8]; + mbedtls_md_type_t md_type = MBEDTLS_MD_NONE; + mbedtls_md_info_t *md = NULL; + + if (true != find_empty_session_key_id_slot(&index)) { + return JEDEC_ERROR_INVALID_PARAM; + } + md_type = sha_select_md_type(indicator->algorithm); + if (MBEDTLS_MD_NONE == md_type) { + return JEDEC_ERROR_NOT_SUPPORT; + } + md = mbedtls_md_info_from_type(md_type); + if (NULL == md) { + return JEDEC_ERROR_NOT_SUPPORT; + } + if (indicator->hkdf.prk_len != mbedtls_md_get_size(md)) { + return JEDEC_ERROR_INVALID_PARAM; + } + if (NULL == indicator->hkdf.prk) { + status = simple_export_key(indicator->hkdf.prk_id, prk, indicator->hkdf.prk_len, &actual_key_len); + if (status != JEDEC_ERROR_NONE) { + return status; + } + } else { + memcpy(prk, indicator->hkdf.prk, indicator->hkdf.prk_len); + } + if (0 != mbedtls_md_hmac(md, prk, indicator->hkdf.prk_len, indicator->hkdf.info, indicator->hkdf.info_len, okm)) { + return JEDEC_ERROR_KDF; + } + /* import mac and enc key */ + status = simple_import_key(&session_key_id_slot[index].mac_key_id, okm, MAC_KEY_SIZE); + if (JEDEC_ERROR_NONE != status) { + return status; + } + status = simple_import_key(&session_key_id_slot[index].enc_key_id, okm, ENC_KEY_SIZE); + if (JEDEC_ERROR_NONE != status) { + return status; + } + memset(okm, 0x00, mbedtls_md_get_size(md)); + memset(prk, 0x00, mbedtls_md_get_size(md)); + indicator->hkdf.okm_id = (uint32_t)&session_key_id_slot[index]; + return status; + +} + +const static crypto_func_t CRYPTO_FUNC[] = { + 0, //ALG_NONE 0 + 0, //ALG_AES_CCM 1 + mbedtls_if_aes_gcm, //ALG_AES_GCM 2 + 0, //ALG_AES_ECB 3 + 0, //ALG_AES_CBC 4 + 0, //ALG_AES_OFB 5 + 0, //ALG_AES_CTR 6 + 0, //ALG_ECDSA 7 + 0, //ALG_ECDH 8 + mbedtls_if_hmac, //ALG_HMAC 9 + mbedtls_if_mx78_kdf, //ALG_HKDF 10 +}; + +static int mbedtls_if_algorithm_support(int alg, int index) +{ + if (ALG_NONE == alg) { + return JEDEC_ERROR_NOTHING; + } + if ((sizeof(CRYPTO_FUNC)/sizeof(crypto_func_t)) <= index || 0 == CRYPTO_FUNC[index]) { + return JEDEC_ERROR_NOT_SUPPORT; + } + return JEDEC_ERROR_NONE; +} + +static int mbedtls_if_crypto_func(crypto_indicator_t *indicator) +{ + int status = JEDEC_ERROR_NONE, index = ALG_TYPE(indicator->algorithm) >> 4; + + status = mbedtls_if_algorithm_support(indicator->algorithm, index); + if (JEDEC_ERROR_NOTHING == status) { + return JEDEC_ERROR_NONE; + } + if (JEDEC_ERROR_NONE != status) { + return status; + } + return CRYPTO_FUNC[index](indicator); +} + +static int mbedtls_if_key_derive(crypto_indicator_t *indicator, uint32_t *output_key_id) +{ + int32_t status = JEDEC_ERROR_NONE, index = ALG_TYPE(indicator->algorithm) >> 4; + + status = mbedtls_if_algorithm_support(indicator->algorithm, index); + if (JEDEC_ERROR_NOTHING == status) { + return JEDEC_ERROR_NONE; + } + if (JEDEC_ERROR_NONE != status) { + return status; + } + status = CRYPTO_FUNC[index](indicator); + if (JEDEC_ERROR_NONE != status) { + return JEDEC_ERROR_KDF; + } + switch (ALG_TYPE(indicator->algorithm)) { + case ALG_HKDF: + *output_key_id = indicator->hkdf.okm_id; + break; + case ALG_HMAC: + *output_key_id = indicator->hmac.mac_id; + break; + default: + return JEDEC_ERROR_NOT_SUPPORT; + } + return JEDEC_ERROR_NONE; + +} + +const crypto_wrapper_t mx78_armor_crypto_wrapper = { + .init = mbedtls_if_init, + .deinit = mbedtls_if_deinit, + .algorithm_support = mbedtls_if_algorithm_support, + .crypto_func = mbedtls_if_crypto_func, + .key_derive = mbedtls_if_key_derive, + .generate_random = mbedtls_if_generate_random, + .ecdh_gen_key_pair = mbedtls_if_ecdh_gen_key_pair, + .ecdh_gen_shared_secret = mbedlts_if_ecdh_gen_shared_secret, + .open_key = simple_open_key, + .close_key = simple_close_key, + .destroy_key = simple_destroy_key, + .export_key = simple_export_key, + .import_key = simple_import_key +}; +#endif /* CRYPTO_MBEDTLS */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/crypto_wrapper/crypto_wrapper_psa.c b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/crypto_wrapper/crypto_wrapper_psa.c new file mode 100644 index 00000000000..6d2bed4e041 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/crypto_wrapper/crypto_wrapper_psa.c @@ -0,0 +1,742 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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. + */ + +#ifdef CRYPTO_PSA + +#include +#include +#include +#include "../../../../JEDEC_security_HAL/crypto_wrapper.h" +#include "psa/crypto.h" + +#define KEY_HANDLE_NOT_LOADED (0) +#define MAC_SIZE (0x20) +#define TAG_LENGTH (0x10) + +#define MAX_ROOT_KEY_NUM (0x10) +#define MAX_SESSION_KEY_NUM (0x10) +#define MAC_KEY_SIZE (0x20) +#define ENC_KEY_SIZE (0x10) + + +typedef struct { + uint32_t session_enc_key_id; + uint32_t session_mac_key_id; +} session_key_id_t; + + +typedef struct { + uint32_t root_key_id; + uint32_t root_key_handle; +} root_key_handle_t; + +static session_key_id_t session_key_id_slot[MAX_SESSION_KEY_NUM] = {KEY_HANDLE_NOT_LOADED}; +static root_key_handle_t root_key_slot[MAX_ROOT_KEY_NUM] = {KEY_HANDLE_NOT_LOADED}; + +/** + * \brief Find an empty session key slot. + * + * \param[out] slot_index Returned empty slot index + * + * \return true if an empty session key slot was found, + * or false there's no empty session key slot + */ +static bool find_empty_session_key_id_slot(uint32_t *slot_index) +{ + for (uint32_t i = 0; i < MAX_SESSION_KEY_NUM; i++) { + if ((session_key_id_slot[i].session_enc_key_id == KEY_HANDLE_NOT_LOADED) && + (session_key_id_slot[i].session_mac_key_id == KEY_HANDLE_NOT_LOADED)) { + *slot_index = i; + return true; + } + } + return false; +} + +/** + * \brief Find the session key slot index corresponding to session key id. + * \param[in] session_key_id Input session key id + * \param[out] slot_index Returned empty slot index + * + * \return true if corresponding session key slot was found, + * or false there's no matching session key slot + */ +static bool find_session_key_id_slot(uint32_t session_key_id, uint32_t *slot_index) +{ + for (uint32_t i = 0; i < MAX_SESSION_KEY_NUM; i++) { + if ((uint32_t)&session_key_id_slot[i] == session_key_id) { + *slot_index = i; + return true; + } + } + return false; +} + +/** + * \brief Find an empty root key slot. + * + * \param[out] slot_index Returned empty slot index + * + * \return true if an empty root key slot was found, + * or false there's no empty root key slot + */ +static bool find_empty_root_key_id_slot(uint32_t *slot_index) +{ + for (uint32_t i = 0; i < MAX_ROOT_KEY_NUM; i++) { + if ((root_key_slot[i].root_key_handle == KEY_HANDLE_NOT_LOADED) && + (root_key_slot[i].root_key_id == KEY_HANDLE_NOT_LOADED)) { + *slot_index = i; + return true; + } + } + return false; +} + +/** + * \brief Find the root key slot index corresponding to root key id. + * \param[in] root_key_id Input root key id + * \param[out] slot_index Returned empty slot index + * + * \return true if corresponding session key slot was found, + * or false there's no matching session key slot + */ +static bool find_root_key_id_slot(uint32_t root_key_id, uint32_t *slot_index) +{ + for (uint32_t i = 0; i < MAX_ROOT_KEY_NUM; i++) { + if (root_key_slot[i].root_key_id == root_key_id) { + *slot_index = i; + return true; + } + } + return false; +} + +static int crypto_wrapper_init(void) +{ + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_deinit(void) +{ + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_mx78_kdf(crypto_indicator_t *indicator) +{ + psa_status_t status; + uint8_t mac[MAC_SIZE]; + size_t mac_length; + uint32_t vol_root_key_id; + uint32_t index; + /* session_key_id ==> { + * session_enc_key_id, + * session_mac_key_id + * } + */ + if (true == find_empty_session_key_id_slot(&index)) { + indicator->hkdf.okm_id = (uint32_t)&session_key_id_slot[index]; + } else { + return JEDEC_ERROR_INVALID_PARAM; + } + /* Open root key to tf-m crypto key slot */ + status = psa_open_key(indicator->hkdf.prk_id, &vol_root_key_id); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_OPEN_KEY; + } + /* Multi-part mac compute */ + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + status = psa_mac_sign_setup(&operation, + vol_root_key_id, + PSA_ALG_HMAC(PSA_ALG_SHA_256)); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_HMAC; + } + status = psa_mac_update(&operation, + indicator->hkdf.info, + indicator->hkdf.info_len); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_HMAC; + } + status = psa_mac_sign_finish(&operation, mac, MAC_SIZE, &mac_length); + if ((status != PSA_SUCCESS) || + (mac_length != MAC_SIZE)) { + return JEDEC_ERROR_HMAC; + } + /* import volatile hmac key */ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH); + psa_set_key_algorithm(&attributes, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(MAC_KEY_SIZE)); + status = psa_import_key(&attributes, + mac, MAC_KEY_SIZE, + &(session_key_id_slot[index].session_mac_key_id)); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_IMPORT_KEY; + } + /* import volatile aes-gcm key */ + memset(&attributes, 0x00, sizeof(attributes)); + psa_set_key_usage_flags(&attributes, + PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); + psa_set_key_algorithm(&attributes, + PSA_ALG_AEAD_WITH_SHORTENED_TAG(PSA_ALG_GCM, TAG_LENGTH)); + psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); + psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(ENC_KEY_SIZE)); + status = psa_import_key(&attributes, + mac, ENC_KEY_SIZE, + &(session_key_id_slot[index].session_enc_key_id)); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_IMPORT_KEY; + } + /* Close root key handle from tf-m crypto key slot */ + status = psa_close_key(vol_root_key_id); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_CLOSE_KEY; + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_ecdh_gen_key_pair(crypto_indicator_t *indicator) +{ + psa_status_t status; + uint16_t key_bits; + size_t exported_length = 0; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + /* Generate an ECC key pair */ + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE); + psa_set_key_algorithm(&attributes, PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, 0x00)); + switch (indicator->algorithm) { + case ALG_ECDH_SECP192R1: + case ALG_ECDH_SECP224R1: + case ALG_ECDH_SECP256R1: + case ALG_ECDH_SECP384R1: + psa_set_key_type(&attributes, + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_R1)); + break; + case ALG_ECDH_BP256R1: + case ALG_ECDH_BP384R1: + psa_set_key_type(&attributes, + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_BRAINPOOL_P_R1)); + break; + case ALG_ECDH_SECP192K1:; + case ALG_ECDH_SECP256K1: + psa_set_key_type(&attributes, + PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_SECP_K1)); + break; + default: + return JEDEC_ERROR_NOT_SUPPORT; + } + key_bits = 8 * indicator->ecdh.pri_key_len; + psa_set_key_bits(&attributes, key_bits); + status = psa_generate_key(&attributes, &indicator->ecdh.pri_key_id); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_GENERATE_KEY; + } + indicator->ecdh.pub_key_id = indicator->ecdh.pri_key_id; + status = psa_export_public_key(indicator->ecdh.pub_key_id, + indicator->ecdh.pub_key, + indicator->ecdh.pub_key_len, + &exported_length); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_EXPORT_KEY; + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_ecdh_gen_shared_secret(crypto_indicator_t *indicator) +{ + psa_status_t status; + uint32_t actual_size; + status = psa_raw_key_agreement(PSA_ALG_ECDH, + indicator->ecdh.pri_key_id, + indicator->ecdh.pub_key, + indicator->ecdh.pub_key_len, + indicator->ecdh.secret, + indicator->ecdh.secret_len, + &actual_size); + if (status) { + return JEDEC_ERROR_GENERATE_KEY; + } + return JEDEC_ERROR_NONE; +} + +/** + * \brief Open root key corresponding to root_key_id. + * \param[in] root_key_id Input root key id + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_open_key(uint32_t root_key_id) +{ + psa_status_t status; + uint32_t key_handle; + uint32_t index; + + status = psa_open_key(root_key_id, &key_handle); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_OPEN_KEY; + } + if (find_empty_root_key_id_slot(&index) == true) { + root_key_slot[index].root_key_id = root_key_id; + root_key_slot[index].root_key_handle = key_handle; + } else { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + return JEDEC_ERROR_NONE; +} + +/** + * \brief Close root key corresponding to root_key_id. + * \param[in] root_key_id Input root key id + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_close_key(uint32_t root_key_id) +{ + psa_status_t status; + uint32_t index; + + if (find_root_key_id_slot(root_key_id, &index) == true) { + status = psa_close_key(root_key_slot[index].root_key_handle); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_CLOSE_KEY; + } + root_key_slot[index].root_key_handle = KEY_HANDLE_NOT_LOADED; + root_key_slot[index].root_key_id = KEY_HANDLE_NOT_LOADED; + } else { + return JEDEC_ERROR_INVALID_PARAM; + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_export_public_key(uint32_t key_id, uint8_t *key_buf, + uint32_t buf_size, uint32_t *actual_size) +{ + psa_status_t status; + status = psa_export_public_key(key_id, key_buf, buf_size, + actual_size); + if (status != PSA_SUCCESS) { + return status; + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_export_key(uint32_t key_id, uint8_t *key_buf, + uint32_t buf_size, uint32_t *actual_size) +{ + psa_status_t status; + status = psa_export_key(key_id, key_buf, buf_size, + actual_size); + if (status != PSA_SUCCESS) { + return status; + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_import_key(uint32_t *key_id, uint8_t *key_buf, + uint32_t key_size, KeyLifeTime lifetime) +{ + psa_status_t status; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_DERIVE | PSA_KEY_USAGE_SIGN_HASH); + psa_set_key_algorithm(&attributes, PSA_ALG_HMAC(PSA_ALG_SHA_256)); + psa_set_key_type(&attributes, PSA_KEY_TYPE_HMAC); + psa_set_key_bits(&attributes, PSA_BYTES_TO_BITS(key_size)); + if (lifetime == KEY_LIFETIME_VOLATILE) { + *key_id = PSA_KEY_ID_NULL; + } else { + psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_PERSISTENT); + psa_set_key_id(&attributes, *key_id); + } + status = psa_import_key(&attributes, key_buf, key_size, key_id); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_IMPORT_KEY; + } + return JEDEC_ERROR_NONE; +} +/** + * \brief Destroy session key corresponding to session_key_id. + * \param[in] session_key_id Input session key id + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_destroy_key(uint32_t key_id) +{ + psa_status_t status; + uint32_t index; + + if (find_session_key_id_slot(key_id, &index) == true) { + /* Destroy session's encryption key */ + status = psa_destroy_key(session_key_id_slot[index].session_enc_key_id); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_DESTROY_KEY; + } + session_key_id_slot[index].session_enc_key_id = KEY_HANDLE_NOT_LOADED; + /* Destroy session's hmac key */ + status = psa_destroy_key(session_key_id_slot[index].session_mac_key_id); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_DESTROY_KEY; + } + session_key_id_slot[index].session_mac_key_id = KEY_HANDLE_NOT_LOADED; + } else { + status = psa_destroy_key(key_id); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_DESTROY_KEY; + } + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_aes_ccm(crypto_indicator_t *indicator) +{ + /* TODO */ + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_aes_ecb(crypto_indicator_t *indicator) +{ + /* TODO */ + return JEDEC_ERROR_NONE; +} + + +/** + * \brief Implement AEAD encryption with input session_key_id. + * \param[in] session_key_id Input session key id + * \param[in] indicator AEAD encryption parameters + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_aead_enc(uint32_t session_key_id, crypto_indicator_t *indicator) +{ + psa_status_t status; + size_t out_len; + + if (sizeof(indicator->buf) < + PSA_AEAD_ENCRYPT_OUTPUT_SIZE(PSA_KEY_TYPE_AES, + PSA_ALG_GCM, + indicator->aead.text_len)) { + return JEDEC_ERROR_INVALID_PARAM; + } + session_key_id_t *session_key_id_ptr = (session_key_id_t *)session_key_id; + status = psa_aead_encrypt((psa_key_id_t)session_key_id_ptr->session_enc_key_id, + PSA_ALG_GCM, + indicator->aead.iv, indicator->aead.iv_len, + indicator->aead.add, indicator->aead.add_len, + indicator->aead.plain_text, indicator->aead.text_len, + indicator->buf, sizeof(indicator->buf), &out_len); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_AEAD_ENC; + } + /* Authentication tag is appended to the encrypted data */ + /* NOTE:The indicator->aead.cipher_text has been set to point to send packet previously, + here just copy the actual mac and ciphertext to send packet */ + indicator->aead.text_len = out_len - + PSA_AEAD_TAG_LENGTH(PSA_KEY_TYPE_AES, 128, PSA_ALG_GCM); + memcpy(indicator->aead.cipher_text, indicator->buf, indicator->aead.text_len); + indicator->aead.tag_len = PSA_AEAD_TAG_LENGTH(PSA_KEY_TYPE_AES, 128, PSA_ALG_GCM); + memcpy(indicator->aead.tag, + indicator->buf + indicator->aead.text_len, + indicator->aead.tag_len); + return JEDEC_ERROR_NONE; +} + +/** + * \brief Implement AEAD decryption with input session_key_id. + * \param[in] session_key_id Input session key id + * \param[in] indicator AEAD decryption parameters + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_aead_dec(uint32_t session_key_id, crypto_indicator_t *indicator) +{ + psa_status_t status; + size_t out_len; + + session_key_id_t *session_key_id_ptr = (session_key_id_t *)session_key_id; + memcpy(indicator->buf, indicator->aead.cipher_text, indicator->aead.text_len); + memcpy(indicator->buf + indicator->aead.text_len, + indicator->aead.tag, indicator->aead.tag_len); + status = psa_aead_decrypt((psa_key_id_t)session_key_id_ptr->session_enc_key_id, + PSA_ALG_GCM, + indicator->aead.iv, indicator->aead.iv_len, + indicator->aead.add, indicator->aead.add_len, + indicator->buf, + indicator->aead.text_len + indicator->aead.tag_len, + indicator->aead.plain_text, + indicator->aead.text_len, &out_len); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_AEAD_DEC; + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_aes_gcm(crypto_indicator_t *indicator) +{ + psa_status_t status; + size_t out_len; + + switch (indicator->property) { + case PROP_AUTHEN_TAG_DECRYPT_DATA: + case PROP_AUTHEN_TAG: + return crypto_wrapper_aead_dec(indicator->aead.key_id, indicator); + break; + case PROP_ENCRYPT_TAG_DATA: + case PROP_ENCRYPT_TAG: + return crypto_wrapper_aead_enc(indicator->aead.key_id, indicator); + break; + default: + return JEDEC_ERROR_AEAD; + } +} + + +/** + * \brief Compute MAC with input session key. + * \param[in] session_key_id Input session key id + * \param[in] indicator MAC operation parameters + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_mac_compute(uint32_t key_id, crypto_indicator_t *indicator) +{ + psa_status_t status; + size_t actual_mac_size; + uint32_t index; + + /* No need for computing mac */ + if ((indicator->hmac.idata == NULL) && (indicator->hmac.idata_len == 0)) { + return JEDEC_ERROR_NONE; + } + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + + if (find_session_key_id_slot(key_id, &index) == true) { + session_key_id_t *session_key_id_ptr = key_id; + status = psa_mac_sign_setup(&operation, + session_key_id_ptr->session_mac_key_id, + PSA_ALG_HMAC(PSA_ALG_SHA_256)); + } else { + status = psa_mac_sign_setup(&operation, + key_id, + PSA_ALG_HMAC(PSA_ALG_SHA_256)); + } + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_HMAC; + } + status = psa_mac_update(&operation, + indicator->hmac.idata, + indicator->hmac.idata_len); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_HMAC; + } + status = psa_mac_sign_finish(&operation, + indicator->hmac.mac, + indicator->hmac.mac_len, + &actual_mac_size); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_HMAC; + } + return JEDEC_ERROR_NONE; +} + +/** + * \brief Implement MAC verify with input session key. + * \param[in] session_key_id Input session key id + * \param[in] indicator MAC verify parameters + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_mac_verify(uint32_t key_id, crypto_indicator_t *indicator) +{ + psa_status_t status; + uint32_t index; + + psa_mac_operation_t operation = PSA_MAC_OPERATION_INIT; + if (find_session_key_id_slot(key_id, &index) == true) { + session_key_id_t *session_key_id_ptr = key_id; + status = psa_mac_verify_setup(&operation, + session_key_id_ptr->session_mac_key_id, + PSA_ALG_HMAC(PSA_ALG_SHA_256)); + } else { + status = psa_mac_verify_setup(&operation, + key_id, + PSA_ALG_HMAC(PSA_ALG_SHA_256)); + } + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_HMAC; + } + status = psa_mac_update(&operation, + indicator->hmac.idata, + indicator->hmac.idata_len); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_HMAC; + } + status = psa_mac_verify_finish(&operation, + indicator->hmac.mac, + indicator->hmac.mac_len); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_HMAC; + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_hmac(crypto_indicator_t *indicator) +{ + if ((0 == indicator->hmac.key_id && NULL == indicator->hmac.key) || + NULL == indicator->hmac.idata || 0 == indicator->hmac.idata_len) { + return JEDEC_ERROR_INVALID_PARAM; + } + if (PROP_HMAC_VERIFY == indicator->property && NULL == indicator->hmac.mac) { + return JEDEC_ERROR_INVALID_PARAM; + } + if (NULL == indicator->hmac.mac && 0 == indicator->key_attr.import_req) { + return JEDEC_ERROR_INVALID_PARAM; + } + if (ALG_HMAC != ALG_TYPE(indicator->algorithm)) { + return JEDEC_ERROR_NOT_SUPPORT; + } + switch (indicator->property) { + case PROP_HMAC_COMPUTE: + return crypto_wrapper_mac_compute(indicator->hmac.key_id, indicator); + break; + case PROP_HMAC_VERIFY: + return crypto_wrapper_mac_verify(indicator->hmac.key_id, indicator); + break; + default: + return JEDEC_ERROR_INVALID_PARAM; + } +} +/** + * \brief Generate random number. + * \param[out] output Output buffer for generated random number + * \param[in] output_size Number of bytes to generate and output + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_generate_random(uint8_t *output, uint32_t output_size) +{ + psa_status_t status; + + status = psa_generate_random(output, output_size); + if (status != PSA_SUCCESS) { + return JEDEC_ERROR_GENERATE_RANDOM; + } + return JEDEC_ERROR_NONE; +} + +const static crypto_func_t CRYPTO_FUNC[] = { + 0, //ALG_NONE 0 + crypto_wrapper_aes_ccm, //ALG_AES_CCM 1 + crypto_wrapper_aes_gcm, //ALG_AES_GCM 2 + crypto_wrapper_aes_ecb, //ALG_AES_ECB 3 + 0, //ALG_AES_CBC 4 + 0, //ALG_AES_OFB 5 + 0, //ALG_AES_CTR 6 + 0, //ALG_ECDSA 7 + 0, //ALG_ECDH 8 + crypto_wrapper_hmac, //ALG_HMAC 9 + crypto_wrapper_mx78_kdf, //ALG_HKDF 10 +}; + +static int crypto_wrapper_algorithm_support(int alg, int index) +{ + if (ALG_NONE == alg) { + return JEDEC_ERROR_NOTHING; + } + if ((sizeof(CRYPTO_FUNC)/sizeof(crypto_func_t)) <= index || 0 == CRYPTO_FUNC[index]) { + return JEDEC_ERROR_NOT_SUPPORT; + } + return JEDEC_ERROR_NONE; +} + +static int crypto_wrapper_crypto_func(crypto_indicator_t *indicator) +{ + int32_t status = JEDEC_ERROR_NONE, index = ALG_TYPE(indicator->algorithm) >> 4; + + status = crypto_wrapper_algorithm_support(indicator->algorithm, index); + if (JEDEC_ERROR_NOTHING == status) { + return JEDEC_ERROR_NONE; + } + if (JEDEC_ERROR_NONE != status) { + return status; + } + return CRYPTO_FUNC[index](indicator); +} + +/** + * \brief Generate session key from root key. + * \param[in] crypto_indicator KDF parameters + * \param[out] session_key_id Returned session key id + * + * \return JEDEC_ERROR_NONE if success, + * or JEDEC_ERROR_XX if fail + */ +static int crypto_wrapper_kdf(crypto_indicator_t *indicator, + uint32_t *output_key_id) +{ + int32_t status = JEDEC_ERROR_NONE, index = ALG_TYPE(indicator->algorithm) >> 4; + + status = crypto_wrapper_algorithm_support(indicator->algorithm, index); + if (JEDEC_ERROR_NOTHING == status) { + return JEDEC_ERROR_NONE; + } + if (JEDEC_ERROR_NONE != status) { + return status; + } + status = CRYPTO_FUNC[index](indicator); + if (JEDEC_ERROR_NONE != status) { + return JEDEC_ERROR_KDF; + } + switch (ALG_TYPE(indicator->algorithm)) { + case ALG_HKDF: + *output_key_id = indicator->hkdf.okm_id; + break; + case ALG_HMAC: + *output_key_id = indicator->hmac.mac_id; + break; + default: + return JEDEC_ERROR_NOT_SUPPORT; + } + return JEDEC_ERROR_NONE; +} + +crypto_wrapper_t mx78_armor_crypto_wrapper = { + .init = crypto_wrapper_init, + .deinit = crypto_wrapper_deinit, + .algorithm_support = crypto_wrapper_algorithm_support, + .crypto_func = crypto_wrapper_crypto_func, + .key_derive = crypto_wrapper_kdf, + .generate_random = crypto_wrapper_generate_random, + .ecdh_gen_key_pair = crypto_wrapper_ecdh_gen_key_pair, + .ecdh_gen_shared_secret = crypto_wrapper_ecdh_gen_shared_secret, + .open_key = crypto_wrapper_open_key, + .close_key = crypto_wrapper_close_key, + .destroy_key = crypto_wrapper_destroy_key, + .export_public_key = crypto_wrapper_export_public_key, + .export_key = crypto_wrapper_export_key, + .import_key = crypto_wrapper_import_key, + +}; +#endif /* CRYPTO_PSA */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/libmx78_armor_lib.a b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/libmx78_armor_lib.a new file mode 100644 index 0000000000000000000000000000000000000000..a8fc70343181c8567a85b3ba4df7274474cba3c6 GIT binary patch literal 378188 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

#fBTF#S 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 +#include +#include "../../../JEDEC_security_HAL/include/error.h" +#include "../../vendor_provisioning_impl.h" +#include "mx78_armor.h" +#include "mx78_armor_lib.h" +#include "provisioning/mx78_armor_provision.h" +#include "spi_nor.h" +#include "../../vendor_secureflash_defs.h" +#include "plat_secure_flash.h" +#include "secureflash_layout.h" + +#define ARMOR_MC_SIZE 0x04 +#define DEFAULT_SECURITY_PROFILE_ID 0 + +#define CFG_READ_SIZE_DEFAULT 0x20 +#define HEALTH_TEST_TYPE_CNT 0x05 +#define TMP_BUFFER_SIZE 0x20 + +#ifdef SECUREFLASH_PROVISION +uint8_t PROVISIONING_BLOB[] = { + /* Major Header */ + 'S', 'F', 'P', 'I', // DWORD 0: [31:00] magic, "SFPI" + 0x01, 0x01, 0x3c, 0x00, // DWORD 1: [03:00] minor_version, [07:04] major_version, [15:08] sub header number, [31:16] total size + 0xc2, 0x29, 0x17, 0x00, // DWORD 2: [23:00] ID, [31:24] provision_disable_indicator + + /* Sub Header, ID: 0x03, APP_INFO */ + 0x03, 0x04, 0x30, 0x80, // DWORD 0: [07:00] ID, [15:08] num, [30:16] app_info size, [31] save:1, not save: 0 + /* APP_INFO, Table */ + 0xFE, 0xFF, 0xFF, 0xFF, // DWORD 1: [31:00] application id (-2/0xfffffffe) + 0x55, 0xAA, 0x55, 0x00, // DWORD 2: [31:00] key id (app0) + 0x00, 0xFF, 0xFF, 0xFF, // DWORD 3: [07:00] zone id, [31:08] reserved (app0) + 0xFD, 0xFF, 0xFF, 0xFF, // DWORD 1: [31:00] application id (-3/0xfffffffd) + 0x55, 0xAA, 0x55, 0x04, // DWORD 5: [31:00] key id (app1) + 0x01, 0xFF, 0xFF, 0xFF, // DWORD 6: [07:00] zone id, [31:08] reserved (app1) + 0xBA, 0x0B, 0x00, 0x00, // DWORD 1: [31:00] application id (3002/0x00000bba) + 0x55, 0xAA, 0x55, 0x0B, // DWORD 8: [31:00] key id (app2) + 0x02, 0xFF, 0xFF, 0xFF, // DWORD 9: [07:00] zone id, [31:08] reserved (app2) + 0xBE, 0x0B, 0x00, 0x00, // DWORD 10: [31:00] application id (3006/0x00000bbe) + 0x55, 0xAA, 0x55, 0x0F, // DWORD 11: [31:00] key id (app3) + 0x03, 0xFF, 0xFF, 0xFF, // DWORD 12: [07:00] zone id, [31:08] reserved (app3) + }; +#endif /* SECUREFLASH_PROVISION */ + +mx78_armor_context g_mx78_armor_ctx; +static mx78_armor_security_operation_t operation[CONC_OPER_NUM] = {}; + +/** + * \brief Allocate an operation context. + * + * \param[in] type ArmorFlash operation type + * \param[out] op_ctx Pointer to allocated operation context + * \return true or false + */ +static bool mx78_armor_operation_alloc(mx78_armor_operation_type_t type, + mx78_armor_security_operation_t **op_ctx) +{ + for (uint8_t i = 0; i < CONC_OPER_NUM; i++) { + if (operation[i].in_use == OPER_NOT_IN_USE) { + operation[i].in_use = OPER_IN_USE; + operation[i].type = type; + *op_ctx = &operation[i]; + return true; + } + } + *op_ctx = NULL; + return false; +} + +/** + * \brief Release an operation context. + * + * \param[in] op_ctx Pointer to the operation context + * \return NULL + */ +static void mx78_armor_operation_release(mx78_armor_security_operation_t *op_ctx) +{ + op_ctx->in_use = OPER_NOT_IN_USE; + memset(op_ctx, 0x00, sizeof(mx78_armor_security_operation_t)); +} + +/** + * \brief Look up an operation context. + * \param[in] type ArmorFlash operation type + * \param[out] op_ctx Pointer to the matched operation context + * \return NULL + */ +static void mx78_armor_operation_lookup(mx78_armor_operation_type_t type, + mx78_armor_security_operation_t **op_ctx) +{ + for (uint8_t i = 0; i < CONC_OPER_NUM; i++) { + if ((operation[i].in_use == OPER_IN_USE) && + (operation[i].type == type)) { + *op_ctx = &operation[i]; + return; + } + } + *op_ctx = NULL; +} + +static int32_t mx78_armor_packet_send(mx78_armor_context *mx78_armor_ctx, + uint8_t *outdata, uint32_t outdata_len, + uint8_t *cipher_text, uint32_t cipher_text_len, + uint8_t *mac, uint32_t mac_len) +{ + int32_t status; + uint32_t tx_packet_size; + uint32_t outdata_actual_len = outdata_len; + + if ((mac != NULL) && (mac_len > 0)) { + memcpy(outdata + outdata_len, mac, mac_len); + outdata_actual_len += mac_len; + } + if ((cipher_text != NULL) && (cipher_text_len > 0)) { + memcpy(outdata + outdata_actual_len, cipher_text, cipher_text_len); + outdata_actual_len += cipher_text_len; + } + status = __packet_assemble(mx78_armor_ctx, PACKET_OUT, + outdata, outdata_actual_len, + mx78_armor_ctx->scratchpad, &tx_packet_size); + if (status != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_CMD_PACKET; + } + if (spi_write(mx78_armor_ctx->scratchpad, tx_packet_size)) { + return JEDEC_ERROR_COMM_FAIL; + } + return JEDEC_ERROR_NONE; +} + +static int32_t mx78_armor_packet_receive(mx78_armor_context *mx78_armor_ctx, + uint8_t *indata, uint32_t indata_len) +{ + int32_t status; + uint8_t rx_buffer[PACKET_MAX_LEN]; + uint32_t tx_packet_size, rx_packet_size; + + rx_packet_size = indata_len; + status = __packet_assemble(mx78_armor_ctx, PACKET_IN, NULL, 0, + mx78_armor_ctx->scratchpad, &tx_packet_size); + if (status != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_CMD_PACKET; + } + if (spi_read(mx78_armor_ctx->scratchpad, tx_packet_size, rx_buffer, rx_packet_size)) { + return JEDEC_ERROR_COMM_FAIL; + } + memcpy(indata, rx_buffer, indata_len); + return JEDEC_ERROR_NONE; +} + +/** + * \brief Exchange nonce with ArmorFlash. + * + * \param[in] mx78_armor_ctx MX78 ArmorFlash context + * \param[in] nonce_in Input nonce from host + * \param[in] nonce_in_len Input nonce length + * \param[out] nonce_out Buffer for holding output nonce from ArmorFlash + * \param[in] buf_size Buffer size + * \param[out] actual_nonce_size Actual size of output nonce from ArmorFlash + * + * \return JEDEC_ERROR_NONE if successful, + * JEDEC_ERROR_XX if failed + */ +static int32_t _armor_generate_nonce(mx78_armor_context *mx78_armor_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + uint8_t *nonce_out, uint32_t buf_size, + uint32_t *actual_nonce_size) +{ + uint8_t rw_packet[PACKET_MAX_LEN]; + uint32_t wr_packet_len, rd_packet_len; + uint8_t nonce_len; + int32_t status; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(GENERATE_NONCE, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + nonce_len = __get_nonce_size(oper_ctx); + oper_ctx->out_size = nonce_len; + if (nonce_in_len > nonce_len) { + nonce_in_len = nonce_len; + } + status = __prepare_write_packet(oper_ctx, + nonce_in, nonce_in_len, + NULL, 0, + rw_packet, &wr_packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_generate_nonce_exit_point; + } + status = mx78_armor_packet_send(mx78_armor_ctx, + rw_packet, wr_packet_len, + NULL, 0, NULL, 0); + if (status != JEDEC_ERROR_NONE) { + goto _armor_generate_nonce_exit_point; + } + status = spi_nor_polling_for_out_ready(); + if (status) { + return JEDEC_ERROR_DEVICE_BUSY; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_generate_nonce_exit_point; + } + status = __parse_read_packet(oper_ctx, + nonce_out, buf_size, + NULL, 0, + rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_generate_nonce_exit_point; + } + *actual_nonce_size = nonce_len; +_armor_generate_nonce_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +/** + * \brief Read current ArmorFlash sessions status. + * + * \param[in] mx78_armor_ctx MX78 ArmorFlash context + * \param[out] session_status Buffer to store session status + * \param[in] status_size Size of status in byte + * \return JEDEC_ERROR_NONE if successful, + * JEDEC_ERROR_XX if failed + */ +static int32_t _armor_check_sessions_status(mx78_armor_context *mx78_armor_ctx, + uint8_t *session_status, + uint32_t status_size) +{ + uint8_t rw_packet[PACKET_MAX_LEN]; + uint32_t wr_packet_len, rd_packet_len; + int32_t status; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(READ_SESSION_STATUS, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->out_size = status_size; + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + rw_packet, &wr_packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_check_sessions_status_exit_point; + } + status = mx78_armor_packet_send(mx78_armor_ctx, + rw_packet, wr_packet_len, + NULL, 0, NULL, 0); + if (status != JEDEC_ERROR_NONE) { + goto _armor_check_sessions_status_exit_point; + } + status = spi_nor_polling_for_out_ready(); + if (status) { + return JEDEC_ERROR_DEVICE_BUSY; + } + memset(rw_packet, 0x00, sizeof(rw_packet)); + status = mx78_armor_packet_receive(mx78_armor_ctx, rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_check_sessions_status_exit_point; + } + status = __parse_read_packet(oper_ctx, + session_status, status_size, + NULL, 0, + rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_check_sessions_status_exit_point; + } +_armor_check_sessions_status_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +/** + * \brief Confirm session key. + * + * \param[in] mx78_armor_ctx MX78 ArmorFlash context + * \param[in] session_key_id Session key id + * \param[out] resp_queue Response queue + * + * \return JEDEC_ERROR_NONE if successful, + * JEDEC_ERROR_XX if failed + */ +static int32_t _armor_confirm_session(mx78_armor_context *mx78_armor_ctx, + uint32_t session_key_id, + jqueue_t *resp_queue) +{ + uint8_t rw_packet[PACKET_MAX_LEN]; + uint32_t wr_packet_len, rd_packet_len; + uint8_t mac[MAC_SIZE]; + uint8_t resp_buf[MAX_RESP_SIZE]; + uint32_t actual_resp_len; + int32_t status; + resp_param_t response = {}; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(CONFIRM_SESSION, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->crypto_key_id = session_key_id; + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + rw_packet, &wr_packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_confirm_session_exit_point; + } + status = mx78_armor_packet_send(mx78_armor_ctx, + rw_packet, wr_packet_len, + NULL, 0, + NULL, 0); + if (status != JEDEC_ERROR_NONE) { + goto _armor_confirm_session_exit_point; + } + status = spi_nor_polling_for_out_ready(); + if (status) { + return JEDEC_ERROR_DEVICE_BUSY; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_confirm_session_exit_point; + } + status = __parse_read_packet(oper_ctx, + NULL, 0, + mac, MAC_SIZE, + rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_confirm_session_exit_point; + } + /* Pack response packet */ + (void)__pack_response(oper_ctx, resp_buf, sizeof(resp_buf), &actual_resp_len); + /* Add response packet and mac to queue */ + response.alg = ALG_HMAC_SHA_256; + response.property = PROP_HMAC_VERIFY; + response.hmac.key_id = session_key_id; + response.hmac.key_len = 32; + response.hmac.idata = resp_buf; + response.hmac.idata_len = (uint16_t)actual_resp_len; + response.hmac.mac = mac; + response.hmac.mac_len = MAC_SIZE; + queue_add(resp_queue, &response); +_armor_confirm_session_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_health_test(mx78_armor_context *mx78_armor_ctx) +{ + uint8_t rw_packet[PACKET_MAX_LEN]; + uint32_t wr_packet_len, rd_packet_len; + int32_t status; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(HEALTH_TEST, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + for (uint8_t i = 0; i < HEALTH_TEST_TYPE_CNT; i++) { + oper_ctx->addr = i; + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + rw_packet, &wr_packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_health_test_exit_point; + } + status = mx78_armor_packet_send(mx78_armor_ctx, rw_packet, wr_packet_len, + NULL, 0, NULL, 0); + if (status != JEDEC_ERROR_NONE) { + goto armor_health_test_exit_point; + } + status = spi_nor_polling_for_out_ready(); + if (status) { + return JEDEC_ERROR_DEVICE_BUSY; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_health_test_exit_point; + } + status = __parse_read_packet(oper_ctx, + NULL, 0, + NULL, 0, + rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_health_test_exit_point; + } + } +armor_health_test_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_configure_security_profile(mx78_armor_context *mx78_armor_ctx) +{ + uint8_t rw_packet[PACKET_MAX_LEN]; + uint32_t wr_packet_len, rd_packet_len; + int32_t status; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(CFG_SECURITY_PROFILE, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->id.profile = DEFAULT_SECURITY_PROFILE_ID; + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + rw_packet, &wr_packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_configure_security_profile_exit_point; + } + status = mx78_armor_packet_send(mx78_armor_ctx, + rw_packet, wr_packet_len, + NULL, 0, + NULL, 0); + if (status != JEDEC_ERROR_NONE) { + goto armor_configure_security_profile_exit_point; + } + status = spi_nor_polling_for_out_ready(); + if (status) { + return JEDEC_ERROR_DEVICE_BUSY; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_configure_security_profile_exit_point; + } + status = __parse_read_packet(oper_ctx, + NULL, 0, + NULL, 0, + rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_configure_security_profile_exit_point; + } +armor_configure_security_profile_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_read_mc(mx78_armor_context *mx78_armor_ctx, + uint8_t mc_id, uint8_t *mc, + uint32_t mc_buf_size, uint32_t *mc_size) +{ + (void)mc_buf_size; + uint8_t rw_packet[PACKET_MAX_LEN]; + uint32_t wr_packet_len, rd_packet_len; + int32_t status = JEDEC_ERROR_NONE; + uint8_t mc_buf[ARMOR_MC_SIZE]; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(MC_READ, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->id.mc = mc_id; + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + rw_packet, &wr_packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_mc_rd_exit_point; + } + status = mx78_armor_packet_send(mx78_armor_ctx, + rw_packet, wr_packet_len, + NULL, 0, + NULL, 0); + if (status != JEDEC_ERROR_NONE) { + goto armor_mc_rd_exit_point; + } + status = spi_nor_polling_for_out_ready(); + if (status) { + return JEDEC_ERROR_DEVICE_BUSY; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_mc_rd_exit_point; + } + status = __parse_read_packet(oper_ctx, + mc_buf, ARMOR_MC_SIZE, + NULL, 0, + rw_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto armor_mc_rd_exit_point; + } + *mc_size = ARMOR_MC_SIZE; + memcpy(mc, mc_buf, ARMOR_MC_SIZE); +armor_mc_rd_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +/** + * \brief Pre-processing of creating session. + * + * \param[in] mx78_armor_ctx MX78 ArmorFlash context + * \param[in] nonce_in Input nonce + * \param[in] nonce_in_len Input nonce length + * \param[out] resp_queue Response queue + * + * \return JEDEC_ERROR_NONE if successful, + * JEDEC_ERROR_XX if failed + */ +static int32_t mx78_armor_pre_create_session(mx78_armor_context *mx78_armor_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + (void)resp_queue; + int32_t status; + uint8_t nonce_out[MAX_NONCE_SIZE]; + uint32_t actual_nonce_size; + uint8_t session_status = SESSION_STATUS_ABUNDANT; + + /* check whether session has been exhausted */ + status = _armor_check_sessions_status(mx78_armor_ctx, &session_status, sizeof(session_status)); + if (status != JEDEC_ERROR_NONE) { + return status; + } + if (session_status == SESSION_STATUS_EXHAUST) { + return JEDEC_ERROR_MAX_SESSIONS_REACHED; + } + /*exchange nonce with secure flash*/ + status = _armor_generate_nonce(mx78_armor_ctx, nonce_in, nonce_in_len, + nonce_out, MAX_NONCE_SIZE, &actual_nonce_size); + if (status != JEDEC_ERROR_NONE) { + return status; + } + return status; +} + +static int32_t mx78_armor_create_session_packet(mx78_armor_context *mx78_armor_ctx, + uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + uint32_t rd_packet_len; + int32_t status; + uint8_t mc[ARMOR_MC_SIZE]; + uint32_t actual_mc_size; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(CREATE_SESSION, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->crypto_key_id = root_key_id; + /* get linked monotonic counter id */ + status = __get_linked_mc_id(oper_ctx); + if (status != JEDEC_ERROR_NONE) { + return status; + } + status = mx78_armor_read_mc(mx78_armor_ctx, oper_ctx->id.mc, mc, + ARMOR_MC_SIZE, &actual_mc_size); + /* get kdf info */ + __get_kdf_msg(root_key_id, mc, ARMOR_MC_SIZE, indicator); + /* prepare create session cmd packet*/ + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + packet, packet_len, &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_create_session_exit_point; + } + return status; + +_armor_create_session_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_post_create_session(mx78_armor_context *mx78_armor_ctx, + uint32_t root_key_id, + uint32_t session_key_id, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + int32_t status; + uint8_t nonce_out[MAX_NONCE_SIZE]; + uint32_t actual_nonce_size; + uint8_t rd_packet[PACKET_MAX_LEN]; + uint32_t rd_packet_len; + mx78_armor_security_operation_t *oper_ctx; + + mx78_armor_operation_lookup(CREATE_SESSION, &oper_ctx); + if (oper_ctx == NULL) { + return JEDEC_ERROR_INV_ARGS; + } + __get_read_packet_size(oper_ctx, &rd_packet_len); + status = spi_nor_polling_for_out_ready(); + if (status) { + goto _armor_post_create_session_exit_point; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_create_session_exit_point; + } + status = __parse_read_packet(oper_ctx, + NULL, 0, + NULL, 0, + rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_create_session_exit_point; + } + /*exchange nonce with secure flash*/ + status = _armor_generate_nonce(mx78_armor_ctx, nonce_in, nonce_in_len, + nonce_out, MAX_NONCE_SIZE, &actual_nonce_size); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_create_session_exit_point; + } + status = _armor_confirm_session(mx78_armor_ctx, session_key_id, resp_queue); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_create_session_exit_point; + } +_armor_post_create_session_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_pre_terminate_session(mx78_armor_context *mx78_armor_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + (void)resp_queue; + int32_t status; + uint8_t nonce_out[MAX_NONCE_SIZE]; + uint32_t actual_nonce_size; + + /*exchange nonce with secure flash*/ + status = _armor_generate_nonce(mx78_armor_ctx, nonce_in, nonce_in_len, + nonce_out, MAX_NONCE_SIZE, &actual_nonce_size); + if (status != JEDEC_ERROR_NONE) { + return status; + } + return JEDEC_ERROR_NONE; +} + +static int32_t mx78_armor_terminate_session_packet(mx78_armor_context *mx78_armor_ctx, + uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + (void)mx78_armor_ctx; + (void)indicator; + uint32_t rd_packet_len; + int32_t status; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(TERMINATE_SESSION, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->crypto_key_id = session_key_id; + /* prepare create session cmd packet*/ + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + packet, packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_terminate_session_exit_point; + } + return status; + +_armor_terminate_session_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_post_terminate_session(mx78_armor_context *mx78_armor_ctx, + jqueue_t *resp_queue) +{ + (void)resp_queue; + int32_t status; + uint32_t rd_packet_len; + mx78_armor_security_operation_t *oper_ctx; + + mx78_armor_operation_lookup(TERMINATE_SESSION, &oper_ctx); + if (oper_ctx == NULL) { + return JEDEC_ERROR_INV_ARGS; + } + __get_read_packet_size(oper_ctx, &rd_packet_len); + status = spi_nor_polling_for_out_ready(); + if (status) { + goto _armor_post_terminate_session_exit_point; + } + + status = mx78_armor_packet_receive(mx78_armor_ctx, mx78_armor_ctx->scratchpad, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_terminate_session_exit_point; + } + status = __parse_read_packet(oper_ctx, + NULL, 0, + NULL, 0, + mx78_armor_ctx->scratchpad, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_terminate_session_exit_point; + } +_armor_post_terminate_session_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + + +static int32_t mx78_armor_pre_secure_init(mx78_armor_context *mx78_armor_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + (void)resp_queue; + int32_t status; + uint8_t nonce_out[MAX_NONCE_SIZE]; + uint32_t actual_nonce_size; + + status = mx78_armor_health_test(mx78_armor_ctx); + if (status) { + return JEDEC_ERROR_INIT_FAIL; + } + status = mx78_armor_configure_security_profile(mx78_armor_ctx); + if (status != JEDEC_ERROR_NONE) { + return status; + } + /*exchange nonce with secure flash*/ + return _armor_generate_nonce(mx78_armor_ctx, nonce_in, nonce_in_len, + nonce_out, MAX_NONCE_SIZE, &actual_nonce_size); +} + +static int32_t mx78_armor_secure_init_packet(mx78_armor_context *mx78_armor_ctx, + uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + (void)mx78_armor_ctx; + (void)root_key_id; + (void)indicator; + uint32_t rd_packet_len; + int32_t status; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(SECURE_INIT, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->crypto_key_id = root_key_id; + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + packet, packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_init_packet_exit_point; + } + return status; + +_armor_secure_init_packet_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_post_secure_init(mx78_armor_context *mx78_armor_ctx, + jqueue_t *resp_queue) +{ + (void)resp_queue; + int32_t status; + uint32_t rd_packet_len; + uint8_t init_resp_buf[TMP_BUFFER_SIZE]; + mx78_armor_security_operation_t *oper_ctx; + + mx78_armor_operation_lookup(SECURE_INIT, &oper_ctx); + if (oper_ctx == NULL) { + return JEDEC_ERROR_INV_ARGS; + } + __get_read_packet_size(oper_ctx, &rd_packet_len); + status = spi_nor_polling_for_out_ready(); + if (status) { + goto _armor_post_secure_init_exit_point; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, mx78_armor_ctx->scratchpad, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_init_exit_point; + } + status = __parse_read_packet(oper_ctx, + init_resp_buf, oper_ctx->out_size, + NULL, 0, + mx78_armor_ctx->scratchpad, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_init_exit_point; + } +_armor_post_secure_init_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_pre_secure_uninit(mx78_armor_context *mx78_armor_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + (void)resp_queue; + uint8_t nonce_out[MAX_NONCE_SIZE]; + uint32_t actual_nonce_size; + + /*exchange nonce with secure flash*/ + return _armor_generate_nonce(mx78_armor_ctx, nonce_in, nonce_in_len, + nonce_out, MAX_NONCE_SIZE, &actual_nonce_size); +} + +static int32_t mx78_armor_secure_uninit_packet(mx78_armor_context *mx78_armor_ctx, + uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + (void)mx78_armor_ctx; + (void)root_key_id; + (void)packet; + (void)packet_len; + (void)indicator; + *packet_len = 0; + return JEDEC_ERROR_NONE; +} + +static int32_t mx78_armor_post_secure_uninit(mx78_armor_context *mx78_armor_ctx, + jqueue_t *resp_queue) +{ + (void)mx78_armor_ctx; + (void)resp_queue; + return JEDEC_ERROR_NONE; +} + +static int32_t mx78_armor_pre_secure_program(mx78_armor_context *mx78_armor_ctx, + uint32_t addr, uint32_t session_key_id, + jqueue_t *resp_queue) +{ + (void)mx78_armor_ctx; + (void)addr; + (void)session_key_id; + (void)resp_queue; + return JEDEC_ERROR_NONE; +} + +static int32_t mx78_armor_secure_program_packet(mx78_armor_context *mx78_armor_ctx, + uint32_t addr, const uint8_t *data, + uint32_t len, uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + int32_t status; + uint32_t rd_packet_len; + uint8_t mc[ARMOR_MC_SIZE]; + uint32_t actual_mc_size; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(SECURITY_WRITE, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->addr = addr; + oper_ctx->in_data = data; + oper_ctx->in_size = len; + oper_ctx->crypto_key_id = session_key_id; + + /* get linked monotonic counter id */ + status = __get_linked_mc_id(oper_ctx); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_program_exit_point; + } + status = mx78_armor_read_mc(mx78_armor_ctx, oper_ctx->id.mc, mc, + ARMOR_MC_SIZE, &actual_mc_size); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_program_exit_point; + } + /* get aead info */ + indicator->algorithm = ALG_AES_GCM_256; + indicator->property = PROP_ENCRYPT_TAG_DATA; + if (__get_aead_msg(oper_ctx, indicator) != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_INV_ARGS; + } + /* Prepare secure program packet without tag and ciphertext*/ + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + packet, packet_len, + &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_program_exit_point; + } + /* Make the pointers point to the packet buffer in advance */ + indicator->aead.key_id = session_key_id; + indicator->aead.key_len = 16; + indicator->aead.tag = &packet[*packet_len]; + indicator->aead.cipher_text = &packet[*packet_len + indicator->aead.tag_len]; + return status; + +_armor_secure_program_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + + +static int32_t mx78_armor_post_secure_program(mx78_armor_context *mx78_armor_ctx, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, + uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator, + jqueue_t *resp_queue) +{ + (void)session_key_id; + (void)resp_queue; + (void)resp_packet_buffer_size; + uint8_t rd_packet[PACKET_MAX_LEN]; + uint32_t rd_packet_len; + int32_t status; + uint8_t mc[ARMOR_MC_SIZE]; + uint32_t actual_mc_size; + mx78_armor_security_operation_t *oper_ctx; + + mx78_armor_operation_lookup(SECURITY_WRITE, &oper_ctx); + if (oper_ctx == NULL) { + return JEDEC_ERROR_INV_ARGS; + } + indicator->property = PROP_AUTHEN_TAG_DECRYPT_DATA; + __get_read_packet_size(oper_ctx, &rd_packet_len); + status = spi_nor_polling_for_out_ready(); + if (status) { + goto _armor_post_secure_program_exit_point; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_program_exit_point; + } + /* get linked monotonic counter id */ + status = __get_linked_mc_id(oper_ctx); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_program_exit_point; + } + status = mx78_armor_read_mc(mx78_armor_ctx, oper_ctx->id.mc, mc, + ARMOR_MC_SIZE, &actual_mc_size); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_program_exit_point; + } + /* get aead info */ + if (__get_aead_msg(oper_ctx, indicator) != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_INV_ARGS; + } + /* Allocate buffer for AEAD information */ + indicator->aead.cipher_text = resp_packet_buffer; + indicator->aead.tag = resp_packet_buffer + indicator->aead.text_len; + indicator->aead.plain_text = resp_packet_buffer + indicator->aead.text_len + indicator->aead.tag_len; + status = __parse_read_packet(oper_ctx, + indicator->aead.cipher_text, indicator->aead.text_len, + indicator->aead.tag, (uint8_t)indicator->aead.tag_len, + rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_program_exit_point; + } +_armor_post_secure_program_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_parse_secure_program_response(mx78_armor_context *mx78_armor_ctx, + crypto_indicator_t *indicator, + uint32_t *bytes_programmed) +{ + (void)mx78_armor_ctx; + + if (__parse_secure_program_response(indicator->aead.plain_text, + (uint16_t)indicator->aead.text_len, + bytes_programmed) == JEDEC_ERROR_NONE) { + return JEDEC_ERROR_NONE; + } else { + return JEDEC_ERROR_COMM_FAIL; + } +} + + +static int32_t mx78_armor_pre_secure_read(mx78_armor_context *mx78_armor_ctx, + uint32_t addr, uint32_t session_key_id, + jqueue_t *resp_queue) +{ + (void)mx78_armor_ctx; + (void)addr; + (void)session_key_id; + (void)resp_queue; + return JEDEC_ERROR_NONE; +} + +static int32_t mx78_armor_secure_read_packet(mx78_armor_context *mx78_armor_ctx, + uint32_t addr, uint32_t len, + uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + int32_t status; + uint32_t rd_packet_len; + uint8_t mc[ARMOR_MC_SIZE]; + uint32_t actual_mc_size; + mx78_armor_security_operation_t *oper_ctx; + + if (!mx78_armor_operation_alloc(SECURITY_READ, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->addr = addr; + oper_ctx->out_size = len; + oper_ctx->crypto_key_id = session_key_id; + /* get linked monotonic counter id */ + status = __get_linked_mc_id(oper_ctx); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_read_exit_point; + } + status = mx78_armor_read_mc(mx78_armor_ctx, oper_ctx->id.mc, mc, + ARMOR_MC_SIZE, &actual_mc_size); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_read_exit_point; + } + /* get aead info */ + indicator->algorithm = ALG_AES_GCM_256; + indicator->property = PROP_ENCRYPT_TAG_DATA; + if (__get_aead_msg(oper_ctx, indicator) != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_INV_ARGS; + } + /* Prepare secure read packet without tag and ciphertext*/ + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + packet, packet_len, &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_read_exit_point; + } + /* Make the pointers point to the packet buffer in advance */ + indicator->aead.key_id = session_key_id; + indicator->aead.key_len = 16; + indicator->aead.tag = &packet[*packet_len]; + indicator->aead.cipher_text = &packet[*packet_len + indicator->aead.tag_len]; + return status; + +_armor_secure_read_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_post_secure_read(mx78_armor_context *mx78_armor_ctx, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, + uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator, + jqueue_t *resp_queue) +{ + (void)resp_queue; + (void)resp_packet_buffer_size; + uint8_t rd_packet[PACKET_MAX_LEN]; + uint32_t rd_packet_len; + int32_t status; + uint8_t mc[ARMOR_MC_SIZE]; + uint32_t actual_mc_size; + mx78_armor_security_operation_t *oper_ctx = NULL; + + mx78_armor_operation_lookup(SECURITY_READ, &oper_ctx); + if (oper_ctx == NULL) { + return JEDEC_ERROR_INV_ARGS; + } + __get_read_packet_size(oper_ctx, &rd_packet_len); + status = spi_nor_polling_for_out_ready(); + if (status) { + return JEDEC_ERROR_DEVICE_BUSY; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_read_exit_point; + } + /* get linked monotonic counter id */ + status = __get_linked_mc_id(oper_ctx); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_read_exit_point; + } + status = mx78_armor_read_mc(mx78_armor_ctx, oper_ctx->id.mc, mc, + ARMOR_MC_SIZE, &actual_mc_size); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_read_exit_point; + } + indicator->algorithm = ALG_AES_GCM_256; + indicator->property = PROP_AUTHEN_TAG_DECRYPT_DATA; + /* get aead info */ + if (__get_aead_msg(oper_ctx, indicator) != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_INV_ARGS; + } + /* Allocate buffer for AEAD information */ + indicator->aead.key_id = session_key_id; + indicator->aead.key_len = 16; + indicator->aead.cipher_text = resp_packet_buffer; + indicator->aead.tag = resp_packet_buffer + indicator->aead.text_len; + indicator->aead.plain_text = resp_packet_buffer + indicator->aead.text_len + indicator->aead.tag_len; + + status = __parse_read_packet(oper_ctx, + indicator->aead.cipher_text, indicator->aead.text_len, + indicator->aead.tag, (uint8_t)indicator->aead.tag_len, + rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_read_exit_point; + } +_armor_post_secure_read_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_parse_secure_read_response(mx78_armor_context *mx78_armor_ctx, + crypto_indicator_t *indicator, + uint8_t *data, uint32_t data_len, + uint32_t *bytes_read) +{ + (void)mx78_armor_ctx; + + if (__parse_secure_read_response(indicator->aead.plain_text, + (uint16_t)indicator->aead.text_len, + data, data_len, bytes_read) == JEDEC_ERROR_NONE) { + return JEDEC_ERROR_NONE; + } else { + return JEDEC_ERROR_COMM_FAIL; + } +} + + +static int32_t mx78_armor_pre_secure_erase(mx78_armor_context *mx78_armor_ctx, + uint32_t addr, uint32_t len, + uint32_t session_key_id, + jqueue_t *resp_queue) +{ + (void)resp_queue; + (void)mx78_armor_ctx; + (void)session_key_id; + uint32_t secure_erase_size; + + secure_erase_size = __secure_erase_min_size(); + if (secure_erase_size == 0) { + return JEDEC_ERROR_INV_ARGS; + } + if (((addr % secure_erase_size) != 0) || (((addr + len) % secure_erase_size) != 0)) { + return JEDEC_ERROR_INV_ARGS; + } + return JEDEC_ERROR_NONE; +} + +static int32_t mx78_armor_secure_erase_packet(mx78_armor_context *mx78_armor_ctx, + uint32_t addr, uint32_t len, + uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + int32_t status; + uint32_t rd_packet_len; + uint8_t mc[ARMOR_MC_SIZE]; + uint32_t actual_mc_size; + mx78_armor_security_operation_t *oper_ctx = NULL; + + if (!mx78_armor_operation_alloc(SECURITY_ERASE, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + oper_ctx->addr = addr; + oper_ctx->in_size = len; + oper_ctx->crypto_key_id = session_key_id; + /* get linked monotonic counter id */ + status = __get_linked_mc_id(oper_ctx); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_erase_exit_point; + } + status = mx78_armor_read_mc(mx78_armor_ctx, oper_ctx->id.mc, mc, + ARMOR_MC_SIZE, &actual_mc_size); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_erase_exit_point; + } + indicator->algorithm = ALG_AES_GCM_256; + indicator->property = PROP_ENCRYPT_TAG_DATA; + /* get aead info */ + if (__get_aead_msg(oper_ctx, indicator) != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_INV_ARGS; + } + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + packet, packet_len, &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_secure_erase_exit_point; + } + indicator->aead.key_id = session_key_id; + indicator->aead.key_len = 16; + indicator->aead.tag = &packet[*packet_len]; + indicator->aead.cipher_text = &packet[*packet_len + indicator->aead.tag_len]; + return status; + +_armor_secure_erase_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_post_secure_erase(mx78_armor_context *mx78_armor_ctx, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, + uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator, + jqueue_t *resp_queue) +{ + (void)session_key_id; + (void)resp_queue; + (void)resp_packet_buffer_size; + uint8_t rd_packet[PACKET_MAX_LEN]; + uint32_t rd_packet_len; + int32_t status; + uint8_t mc[ARMOR_MC_SIZE]; + uint32_t actual_mc_size; + mx78_armor_security_operation_t *oper_ctx = NULL; + + mx78_armor_operation_lookup(SECURITY_ERASE, &oper_ctx); + if (oper_ctx == NULL) { + return JEDEC_ERROR_INV_ARGS; + } + indicator->property = PROP_AUTHEN_TAG_DECRYPT_DATA; + __get_read_packet_size(oper_ctx, &rd_packet_len); + status = spi_nor_polling_for_out_ready(); + if (status) { + goto _armor_post_secure_erase_exit_point; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_erase_exit_point; + } + /* get linked monotonic counter id */ + status = __get_linked_mc_id(oper_ctx); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_erase_exit_point; + } + status = mx78_armor_read_mc(mx78_armor_ctx, oper_ctx->id.mc, mc, + ARMOR_MC_SIZE, &actual_mc_size); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_erase_exit_point; + } + /* get aead info */ + if (__get_aead_msg(oper_ctx, indicator) != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_INV_ARGS; + } + /* Allocate buffer for AEAD information */ + indicator->aead.cipher_text = resp_packet_buffer; + indicator->aead.tag = resp_packet_buffer + indicator->aead.text_len; + indicator->aead.plain_text = resp_packet_buffer + indicator->aead.text_len + indicator->aead.tag_len; + status = __parse_read_packet(oper_ctx, + indicator->aead.cipher_text, indicator->aead.text_len, + indicator->aead.tag, (uint8_t)indicator->aead.tag_len, + rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_secure_erase_exit_point; + } +_armor_post_secure_erase_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t mx78_armor_parse_secure_erase_response(mx78_armor_context *mx78_armor_ctx, + crypto_indicator_t *indicator) +{ + (void)mx78_armor_ctx; + + if (__parse_secure_erase_response(indicator->aead.plain_text, + indicator->aead.text_len) == JEDEC_ERROR_NONE) { + return JEDEC_ERROR_NONE; + } else { + return JEDEC_ERROR_COMM_FAIL; + } +} + +static int32_t mx78_armor_pre_get_region_info(mx78_armor_context *mx78_armor_ctx, + uint32_t session_key_id, + bool *session_key_valid_flag, + jqueue_t *resp_queue) +{ + (void)mx78_armor_ctx; + (void)resp_queue; + + __mx78_armor_check_session_key_id_validity(session_key_id, session_key_valid_flag); + return JEDEC_ERROR_NONE; +} + +static int32_t mx78_armor_get_region_info_packet(mx78_armor_context *mx78_armor_ctx, + uint32_t session_key_id, int8_t region_index, + uint8_t *nonce_in, uint32_t nonce_in_len, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + (void)indicator; + int32_t status; + uint32_t rd_packet_len; + uint8_t nonce_out[MAX_NONCE_SIZE]; + uint32_t actual_nonce_size; + mx78_armor_security_operation_t *oper_ctx = NULL; + + if (!mx78_armor_operation_alloc(SECURITY_GET_REGION_CONFIG, &oper_ctx)) { + return JEDEC_ERROR_INSUFFICIENT_MEMORY; + } + if (session_key_id == INVALID_SESSION_KEY_ID) { + oper_ctx->crypto_key_id = 0; + } else { + oper_ctx->crypto_key_id = session_key_id; + /*exchange nonce with secure flash*/ + status = _armor_generate_nonce(mx78_armor_ctx, nonce_in, nonce_in_len, + nonce_out, MAX_NONCE_SIZE, &actual_nonce_size); + if (status != JEDEC_ERROR_NONE) { + return status; + } + } + oper_ctx->id.region = region_index; + oper_ctx->out_size = CFG_SIZE_PER_REGION; + status = __prepare_write_packet(oper_ctx, + NULL, 0, + NULL, 0, + packet, packet_len, &rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_get_region_config_exit_point; + } +_armor_get_region_config_exit_point: + return status; +} + + +static int32_t mx78_armor_post_get_region_info(mx78_armor_context *mx78_armor_ctx, + region_ll_node_t *region_descr_p, + jqueue_t *resp_queue) +{ + uint8_t rd_packet[PACKET_MAX_LEN]; + uint32_t rd_packet_len; + uint8_t cfg[CFG_READ_SIZE_DEFAULT]; + uint8_t mac[MAC_SIZE]; + uint8_t resp_buf[MAX_RESP_SIZE]; + uint32_t actual_resp_len; + resp_param_t response; + int32_t status; + mx78_armor_security_operation_t *oper_ctx = NULL; + + mx78_armor_operation_lookup(SECURITY_GET_REGION_CONFIG, &oper_ctx); + if (oper_ctx == NULL) { + return JEDEC_ERROR_INV_ARGS; + } + __get_read_packet_size(oper_ctx, &rd_packet_len); + status = spi_nor_polling_for_out_ready(); + if (status) { + goto _armor_post_get_region_config_exit_point; + } + status = mx78_armor_packet_receive(mx78_armor_ctx, rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_get_region_config_exit_point; + } + oper_ctx->out_data = cfg; + if (oper_ctx->crypto_key_id != 0) { + status = __parse_read_packet(oper_ctx, + cfg, CFG_SIZE_PER_REGION, + mac, MAC_SIZE, + rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_get_region_config_exit_point; + } + /* Pack response packet */ + (void)__pack_response(oper_ctx, resp_buf, sizeof(resp_buf), &actual_resp_len); + /* Add response packet and mac to queue */ + response.alg = ALG_HMAC_SHA_256; + response.hmac.idata = resp_buf; + response.hmac.idata_len = (uint16_t)actual_resp_len; + response.hmac.mac = mac; + response.hmac.mac_len = MAC_SIZE; + queue_add(resp_queue, &response); + } else { + status = __parse_read_packet(oper_ctx, + cfg, CFG_SIZE_PER_REGION, + NULL, 0, + rd_packet, rd_packet_len); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_get_region_config_exit_point; + } + } + /* Return region's attributes */ + status = __parse_region_config(oper_ctx, region_descr_p); + if (status != JEDEC_ERROR_NONE) { + goto _armor_post_get_region_config_exit_point; + } +_armor_post_get_region_config_exit_point: + mx78_armor_operation_release(oper_ctx); + return status; +} + +static int32_t _check_major_header(uint8_t *provision_data_blob, + provision_major_header_t **major_header) +{ + *major_header = (provision_major_header_t *)provision_data_blob; + + SF_DBG_PR("major_header-magic: %c%c%c%c\r\n", + (*major_header)->magic[0],(*major_header)->magic[1], + (*major_header)->magic[2],(*major_header)->magic[3]); + SF_DBG_PR("major_header-major-version: %X\r\n", + (*major_header)->major_version); + SF_DBG_PR("major_header-minor-version: %X\r\n", + (*major_header)->minor_version); + SF_DBG_PR("major_header-total size: %X\r\n", + (*major_header)->total_size); + SF_DBG_PR("major_header-sub_header num: %X\r\n", + (*major_header)->sub_header_num); + if (memcmp((*major_header)->magic, "SFPI", 4) != 0) { + SF_ERR_PR("Check magic \"SFPI\" failed\r\n"); + return -1; + } + if (memcmp((*major_header)->id, macronix_mx78_info.id, macronix_mx78_info.id_len) != 0) { + return -1; + } + return 0; +} +#ifdef SECUREFLASH_PROVISION +static int32_t mx78_armor_provision_perform_and_verify(mx78_armor_context *mx78_armor_ctx, + uint8_t *provision_data, + uint32_t data_length) +{ + int32_t status; + provision_major_header_t *major_header = NULL; + uint8_t provision_data_buf[PROVISION_INFO_SIZE] = {}; + uint8_t vfy_provision_data_buf[PROVISION_INFO_SIZE] = {}; + uint32_t data_store_size = 0; + + if (provision_data == NULL) { + provision_data = PROVISIONING_BLOB; + data_length = sizeof(PROVISIONING_BLOB); + } + memcpy(provision_data_buf, provision_data, SFPI_MAJOR_HEADER_SIZE); + /* check major header */ + if (_check_major_header(provision_data_buf, &major_header) != 0) { + return JEDEC_ERROR_PROVISION; + } + status = mx78_armor_configure_security_profile(mx78_armor_ctx); + if (status != JEDEC_ERROR_NONE) { + return status; + } + status = mx78_armor_provision_perform(mx78_armor_ctx, provision_data, data_length, + provision_data_buf, &data_store_size); + if (status != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_PROVISION; + } + status = plat_store_secure_flash_provision_info(provision_data_buf, data_store_size); + if (status != 0) { + return JEDEC_ERROR_PROVISION; + } + status = plat_get_secure_flash_provision_info(vfy_provision_data_buf, data_store_size); + if (status != 0) { + return JEDEC_ERROR_PROVISION; + } + /* Read back provision data stored by above code line, and check whether + provision data has been stored successfully */ + if (memcmp(vfy_provision_data_buf, provision_data_buf, data_store_size) != 0) { + return JEDEC_ERROR_PROVISION; + } + return status; +} +#endif /* SECUREFLASH_PROVISION */ +static int32_t mx78_armor_provision_item_get_data(mx78_armor_context *mx78_armor_ctx, + provision_item_type_e item_type, + uint8_t *item_data_buf, + uint32_t item_data_buf_size, + uint8_t *item_data_num, + uint32_t *item_data_act_size) +{ + (void)mx78_armor_ctx; + int32_t status; + uint8_t provision_data_buf[PROVISION_INFO_SIZE] = {}; + uint32_t act_size = 0; + uint8_t i; + provision_major_header_t *major_header = NULL; + provision_sub_item_header_t *sub_header = NULL; + /* Get the major header of provisioning info*/ + status = plat_get_secure_flash_provision_info(provision_data_buf, SFPI_MAJOR_HEADER_SIZE); + if (0 != status) { + return JEDEC_ERROR_PROVISION; + } + major_header = provision_data_buf; + /* Check major header magic */ + if (memcmp(major_header->magic, "SFPI", 4) != 0) { + SF_ERR_PR("Provision info header magic check failed\r\n"); + return JEDEC_ERROR_GET_PROVISION_INFO; + } + act_size = major_header->total_size + sizeof(major_header->magic); + if (act_size > PROVISION_INFO_SIZE) { + return JEDEC_ERROR_INV_ARGS; + } + status = plat_get_secure_flash_provision_info(provision_data_buf, act_size); + if (JEDEC_ERROR_NONE != status) { + return JEDEC_ERROR_GET_PROVISION_INFO; + } + sub_header = provision_data_buf + SFPI_MAJOR_HEADER_SIZE; + switch (item_type) { + case ITEM_APP_INFO: + for (i = 0; i < major_header->sub_header_num; i++) { + if (sub_header->id == SUB_ID_APP_INFO) { + if (item_data_buf_size < sub_header->size) { + return JEDEC_ERROR_PROVISION; + } + *item_data_num = sub_header->num; + memcpy(item_data_buf, (uint8_t *)sub_header + SFPI_SUB_HEADER_SIZE, sub_header->size); + if (__parse_app_info(item_data_buf, sub_header->size, sub_header->num) != JEDEC_ERROR_NONE) { + return JEDEC_ERROR_GET_PROVISION_INFO; + } + *item_data_act_size = sub_header->size; + return JEDEC_ERROR_NONE; + } + /* Move to next sub-item header */ + sub_header = (uint8_t *)sub_header + SFPI_SUB_HEADER_SIZE + sub_header->size; + } + break; + /*TODO*/ + default: + break; + } + return JEDEC_ERROR_GET_PROVISION_INFO; +} + +flash_profile_t macronix_armor_mx78_profile = { + .security_feature.security_storage = SECURE_FLASH_SECURITY_STORAGE_CAP, + .architecture.regions_min_secure_erase_size = SECURE_FLASH_SECTOR_SIZE, + .architecture.secure_program_size = SECURE_FLASH_PROGRAM_SIZE, + .architecture.secure_read_size = SECURE_FLASH_READ_SIZE, + .architecture.secure_zone_number = SECURE_FLASH_ZONE_NUM, + .architecture.secure_zone_size = SECURE_FLASH_ZONE_SIZE, + .architecture.secure_zone_total_size = SECURE_FLASH_SIZE +}; + +vendor_security_op_t macronix_armor_mx78 = { + .vendor_id = 0x03, + .pre_create_session = mx78_armor_pre_create_session, + .create_session_packet = mx78_armor_create_session_packet, + .post_create_session = mx78_armor_post_create_session, +#if defined(SESSION_CONFIRMATION) + .pre_confirm_session = null, + .confirm_session_packet = null, + .post_confirm_session = null, +#endif + .pre_terminate_session = mx78_armor_pre_terminate_session, + .terminate_session_packet = mx78_armor_terminate_session_packet, + .post_terminate_session = mx78_armor_post_terminate_session, + .pre_secure_init = mx78_armor_pre_secure_init, + .secure_init_packet = mx78_armor_secure_init_packet, + .post_secure_init = mx78_armor_post_secure_init, + .pre_secure_uninit = mx78_armor_pre_secure_uninit, + .secure_uninit_packet = mx78_armor_secure_uninit_packet, + .post_secure_uninit = mx78_armor_post_secure_uninit, + .pre_secure_program = mx78_armor_pre_secure_program, + .secure_program_packet = mx78_armor_secure_program_packet, + .post_secure_program = mx78_armor_post_secure_program, + .parse_secure_program_response = mx78_armor_parse_secure_program_response, + .pre_secure_erase = mx78_armor_pre_secure_erase, + .secure_erase_packet = mx78_armor_secure_erase_packet, + .post_secure_erase = mx78_armor_post_secure_erase, + .parse_secure_erase_response = mx78_armor_parse_secure_erase_response, +#if defined(SECURE_COPY) + .pre_secure_copy = null, + .secure_copy_packet = null,//TODO:return not supported + .post_secure_copy = null, +#endif +#if defined(SECURE_READ) + .pre_secure_read = mx78_armor_pre_secure_read, + .secure_read_packet = mx78_armor_secure_read_packet, + .post_secure_read = mx78_armor_post_secure_read, + .parse_secure_read_response = mx78_armor_parse_secure_read_response, +#endif + .pre_secure_get_regions_info = mx78_armor_pre_get_region_info, + .secure_get_regions_info_packet = mx78_armor_get_region_info_packet, + .post_secure_get_regions_info = mx78_armor_post_get_region_info, +#if defined(SECURE_REGION_MANAGE) + .pre_secure_manage_region = null, + .secure_manage_region_packet = null, + .post_secure_manage_region = null, +#endif + .packet_send = mx78_armor_packet_send, + .packet_receive = mx78_armor_packet_receive, +}; + +vendor_provisioning_op_t macronix_armor_mx78_provisioning = { +#ifdef SECUREFLASH_PROVISION + .perform_and_verify = mx78_armor_provision_perform_and_verify, +#endif /* SECUREFLASH_PROVISION */ + .provision_item_get_data = mx78_armor_provision_item_get_data +}; + +extern crypto_wrapper_t mx78_armor_crypto_wrapper; + +const secure_flash_info_t macronix_mx78_info = { + "mx78u64xx", + {0xc2, 0x29, 0x17}, + 3, + .flash_profile = ¯onix_armor_mx78_profile, + .vendor_security_op = ¯onix_armor_mx78, + .vendor_provisioning_op = ¯onix_armor_mx78_provisioning, + .crypto_wrapper = &mx78_armor_crypto_wrapper, + &g_mx78_armor_ctx +}; diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor.h new file mode 100644 index 00000000000..52a2caf428e --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor.h @@ -0,0 +1,139 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _MX78_ARMOR_H_ +#define _MX78_ARMOR_H_ + +#include +#include "../../../JEDEC_security_HAL/vendor_security_impl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SF_WARN_PR +#define SF_DBG_PR +#define SF_DBG0_PR +#define SF_INFO_PR +#define SF_ERR_PR +#define SF_TMP_PR + +#define SESSION_STATUS_EXHAUST 0x00 +#define SESSION_STATUS_ABUNDANT 0x01 +#define SESSION_STATUS_EMPTY 0x02 +#define MAX_NONCE_SIZE 0x10 +#define MAC_SIZE 0x20 +#define MAX_RESP_SIZE 0x150 +#define SCRATCHPAD_SIZE 0x150 +/* Extended configure registers number */ +#define EX_CR_NUM 0x06 +/* The max number of concurrent operations */ +#define CONC_OPER_NUM 0x04 +/* Data region configure size */ +#define CFG_SIZE_PER_REGION 0x04 +#define INVALID_SESSION_KEY_ID (0) +/** + * MX78 ArmorFlash supported security operations definition + * + */ +typedef enum { + SECURITY_READ, /*!< Security read */ + SECURITY_WRITE, /*!< Security program */ + SECURITY_ERASE, /*!< Security erase */ + MC_INCREASEMENT, /*!< Increase Monotonic counter */ + MC_READ, /*!< Read Monotonic counter */ + GENERATE_NONCE, /*!< Generate nonce for cryptographic operations */ + READ_SESSION_STATUS, /*!< Read sessions status */ + CREATE_SESSION, /*!< Create new sessions */ + CONFIRM_SESSION, /*!< Session confirmation */ + TERMINATE_SESSION, /*!< Session termination */ + GEN_PRIV_KEY, /*!< Generate private key */ + GEN_PUB_KEY, /*!< Generate public key */ + EXPORT_PUB_KEY, /*!< Export public key */ + SYNC_HOST_PUB_KEY, /*!< Synchronize host public key */ + SYNC_SALT, /*!< Synchronize salt */ + GEN_ROOT_KEY, /*!< Generate root key */ + SECURITY_GET_REGION_CONFIG, + GET_CFG, /*!< Read configuration information */ + SET_CFG, + CFG_SECURITY_PROFILE,/*!< Configure security profile */ + SECURE_INIT, + SECURE_UNINIT, + HEALTH_TEST, + LOCK_DOWN /*!< Lock down */ +} mx78_armor_operation_type_t; + +enum { + OPER_NOT_IN_USE = 0, + OPER_IN_USE = 1, +}; + +#define PACKET_OUT 0 +#define PACKET_IN 1 + +typedef enum { + ITEM_DATA_CFG, + ITEM_MC_CFG, + ITEM_KEY_CFG, + ITEM_LKD_CFG, + ITEM_TOTAL_CFG, + ITEM_MC, + ITEM_KEY, + ITEM_EXTRA, +} mx78_armor_security_item_t; + +/** + * \struct mx78_armor_security_operation_t + * + * \brief The structure holding MX78 ArmorFlash security operations' context. + */ +typedef struct { + uint32_t in_use; + mx78_armor_operation_type_t type; /*!< Security operation type */ + uint8_t *in_data; /*!< Pointer to current security operation input data */ + uint32_t in_size; /*!< Input data size in bytes */ + uint8_t *out_data; /*!< Pointer to current security operation output data */ + uint32_t out_size; /*!< Output data size in bytes */ + uint32_t addr; /*!< The access address of current security operation */ + union { + uint8_t mc; /*!< Current security operation linked monotonic counter id */ + uint8_t profile; /*!< Current security operation linked security profile id */ + uint8_t region; /*!< Current security operation linked region id */ + uint32_t root_key; /*!< Current security operation linked root key id */ + } id; + uint32_t crypto_key_id; /*!< Current security operation linked crypto service key id */ +} mx78_armor_security_operation_t; + + +typedef struct { + uint8_t status_register; + uint8_t configure_register; + uint8_t configure_register_extend[EX_CR_NUM];//cr2 + uint8_t secure_register; +} registers_t; + + +typedef struct { + const char *name; + registers_t regs; + uint8_t scratchpad[SCRATCHPAD_SIZE]; +} mx78_armor_context; + +#ifdef __cplusplus +} +#endif + +#endif /* _MX78_ARMOR_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor_lib.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor_lib.h new file mode 100644 index 00000000000..53955fa448d --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/mx78_armor_lib.h @@ -0,0 +1,251 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _MX78_ARMOR_LIB_H_ +#define _MX78_ARMOR_LIB_H_ + +#include +#include +#include "../../../JEDEC_security_HAL/crypto_wrapper.h" +#include "../../../JEDEC_security_HAL/include/jedec_defs.h" +#include "mx78_armor.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Check whether input session_key_id is valid. + * + * \param[in] session_key_id Input session_key_id + * \param[out] session_key_valid_flag Session key validity flag + * \return NULL + */ +void __mx78_armor_check_session_key_id_validity(uint32_t session_key_id, + bool *session_key_valid_flag); +/** + * \brief Get security item's access address and size. + * + * \param[in] item Security item type + * \param[out] addr Pointer to address + * \param[out] size Pointer to size + * \return JEDEC_ERROR_NONE on success, otherwise JEDEC_ERROR_XX + */ +int32_t __get_target_addr_and_size(mx78_armor_security_item_t item, + uint32_t *addr, uint32_t *size); +/** + * \brief Get the minimum size of secure erase operation. + * + * \return The minimum size of secure erase operation. + */ +uint32_t __secure_erase_min_size(void); +/** + * \brief Get security operation's Nonce size. + * + * \param[in] params Security operation parameters + * + * \return Size of the nonce + */ +uint8_t __get_nonce_size(mx78_armor_security_operation_t *oper_ctx); +/** + * \brief Get security operation's linked monotonic counter id. + * + * \param[in] params Security operation parameters + * + * \return Monotonic counter id + */ +int32_t __get_linked_mc_id(mx78_armor_security_operation_t *oper_ctx); + +/** + * \brief Get security operation's to-be-received packet size. + * + * \param[in] params Security operation parameters + * \param[out] rd_packet_len The size of to-be-received packet from secure Flash + * \return NULL + */ +void __get_read_packet_size(mx78_armor_security_operation_t *oper_ctx, + uint32_t *rd_packet_len); + +/** + * \brief Get the messages needed by AEAD cryptographic algorithm. + * + * \param[in] params Security operation parameters + * \param[out] indicator The cryptographic indicator of AEAD cryptographic algorithm + * + * \return JEDEC_ERROR_XX + */ +int32_t __get_aead_msg(mx78_armor_security_operation_t *oper_ctx, + crypto_indicator_t *indicator); +/** + * \brief Get the messages needed by key derivation function. + * + * \param[in] root_key_id The id of kdf's input root key + * \param[in] mc Pointer to the monotonic counter involved in key derivation + * \param[in] mc_size The size of monotonic counter + * \param[out] indicator The cryptographic indicator of key derivation operation + * \return JEDEC_ERROR_XX + */ +int32_t __get_kdf_msg(uint32_t root_key_id, + uint8_t *mc, uint32_t mc_size, + crypto_indicator_t *indicator); +/** + * \brief Prepare write packet to be sent to secure Flash. + * + * \param[in] params Structure containing security operation's parameters + * \param[in] buf Buffer containing ciphertext data + * \param[in] buf_size The size of ciphertext data i bytes + * \param[in] mac Buffer containing authentication code + * \param[in] mac_size The size of authentication code + * \param[out] wr_packet Pointer to write packet + * \param[out] wr_packet_len The size of write packet in bytes + * \param[out] rd_packet_len The size of read packet in bytes + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __prepare_write_packet(mx78_armor_security_operation_t *oper_ctx, + uint8_t *buf, uint32_t buf_size, + uint8_t *mac, uint8_t mac_size, + uint8_t *wr_packet, uint32_t *wr_packet_len, + uint32_t *rd_packet_len); + +/** + * \brief Pack the command packet to secure Flash. + * + * \param[in] ctx Secure flash context + * \param[in] type Command packet type + * \param[in] data Buffer holding command data + * \param[in] data_size The buffer size + * \param[out] tx_buffer Buffer to hold command packet + * \param[out] tx_buffer_len The actual command packet size in byte + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + +int32_t __packet_assemble(mx78_armor_context *ctx, uint8_t type, + uint8_t *data, uint32_t data_size, + uint8_t *tx_buffer, uint32_t *tx_buffer_len); +/** + * \brief Pack the to-be-authenticated response from secure Flash. + * + * \param[in] params Security operation's parameters + * \param[in] resp_buf Buffer to hold the response + * \param[in] resp_buf_size The size of resp_buf + * \param[out] actual_resp_len The actual packed response size in byte + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __pack_response(mx78_armor_security_operation_t *oper_ctx, + uint8_t *resp_buf, uint32_t resp_buf_size, + uint32_t *actual_resp_len); +/** + * \brief Parse secure Flash security configuration. + * + * \param[in] data_buf Buffer holding secure Flash security configuration + * \param[in] data_size The size of secure Flash security configuration + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __parse_security_configuration(uint8_t *data_buf, uint32_t data_size); +/** + * \brief Parse read packet received from secure Flash. + * + * \param[in] params Structure containing security operation's parameters + * \param[out] buf Buffer to hold data parsed from read packet + * \param[in] buf_size The size of data buf + * \param[out] mac_buf Buffer to hold mac + * \param[in] mac_size The size of mac + * \param[in] rd_packet Pointer to read packet + * \param[in] rd_packet_len The size of read packet in bytes + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __parse_read_packet(mx78_armor_security_operation_t *oper_ctx, + uint8_t *buf, uint32_t buf_size, + uint8_t *mac_buf, uint8_t mac_size, + uint8_t *rd_packet, uint32_t rd_packet_len); +/** + * \brief Parse region security description based on region configure. + * + * \param[in] oper_ctx Security operation's context + * \param[out] region_descr_p Pointer to region description link list node + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __parse_region_config(mx78_armor_security_operation_t *oper_ctx, + region_ll_node_t *region_descr_p); +/** + * \brief Parse secure program response from secure Flash. + * + * \param[in] response_buffer Buffer containing response + * \param[in] response_buffer_size The size of response buffer + * \param[out] bytes_programmed The size of programmed data in byte + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __parse_secure_program_response(uint8_t *response_buffer, + uint16_t response_buffer_size, + uint32_t *bytes_programmed); +/** + * \brief Parse secure erase response from secure Flash. + * + * \param[in] response_buffer Buffer containing response + * \param[in] response_buffer_size The size of response buffer + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __parse_secure_erase_response(uint8_t *response_buffer, + uint32_t response_buffer_size); +/** + * \brief Parse secure read response from secure Flash. + * + * \param[in] response_buffer Buffer holding response + * \param[in] response_buffer_size The size of response buffer + * \param[out] data Buffer to hold data + * \param[out] data_len Buffer length + * \param[out] bytes_read The actual size of read data in byte + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __parse_secure_read_response(uint8_t *response_buffer, + uint16_t response_buffer_size, + uint8_t *data, uint32_t data_len, + uint32_t *bytes_read); +/** + * \brief Parse app info. + * + * \param[in] app_data Buffer holding app data + * \param[in] info_size The size of app data + * \param[in] app_data_num App data item number + * + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ +int32_t __parse_app_info(uint8_t *app_data, uint32_t info_size, uint8_t app_data_num); + +#ifdef __cplusplus +} +#endif + +#endif /* _MX78_ARMOR_LIB_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/provisioning/libmx78_armor_provision_lib.a b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/provisioning/libmx78_armor_provision_lib.a new file mode 100644 index 0000000000000000000000000000000000000000..b7bb5f02f1160ad6a1987ecd1e6d2404290a0ef9 GIT binary patch literal 365928 zcmeFa33wdEwKm*6Gp!kowy`DK@;;Vi<4vnK*?5y=jV-)LmSlqoFqWmUC9DlC-XPc* zY?iRt*z68=SaJ=)EMWA~cAPI!^2AdcVkih@GRi|fqWRZKn``zdH@BN>@ zAGO}9Q>RXyI#qS5s=KFq)Xch$`o`J*{9yc1IJJ0cVZqemDFwkG{%|<|2ZNKR6>CCp zrQa}&hYchBe#3b7lmC^c`PP2pf5Pc%X*B--^S@>T_w`4O|L)TozQ>3=SFAKFeYR|v zRvfPDXld&Rw|BIyYwB!jYYn$YI##!Jv=|FY7KIm-)GP=u?rg7UZR&~<;$ujj7l|kJ zj#spnwKsQ_)ipP-s;gfc6Ets4XII}T`BbBwac11RH)zqzPYF%>-xjNF>)z%T0QnNl3i8O?(I@;~G~%Nngn zeBL;+BXg9F9#_60jFh6H$xglF=)L+yEJMuPQ42}pl&O771HEr)(S*1@9V<$=m!sFk zl0=Jtm4P2J0&m3Up>d6UU@E+(nFT zS1j7$dhthOX6l##-lT+Bs%6$QhG(p%jVUTERS?F ztqxaoF0X5DYN%;i(^}Wn-J$0}W~pjxYV9g(jMT3MPi1RacgMO|V#%sDnUcs_U9uQ+ zU_{SRBvfJ*P}ha!z%kP^Q;(gUNsXI`g`g)vi7akwh$-Nh%9&i%*4&H{UAa11vO%&^ zKTb$#Q&&yXrbxKESTUf`%J!~fiBZa=BAc(IqoZ!4Gw>I| zp=kMrc2)>h>28jhrMjl1th%=EG}R<*g+B6?mz6E$=`81X47gaKu`+ z8Y^CBd0+s^$xn_be|(pV&ls2HkPt_=MU_-n#Fbmp5Gxd6ve_NIrhAn$>`cN4izacj zIdYY?v`3Q=b|#^Y&f;{m7lX4JWX$s zbJwMKq7#IQ#qYWl$uUK!cprNgMMq)w-eghOreMt>TPQ{8W5=fG<*oG{8{50=>MPp3 z$gwo~%@*zK6;EaNu^T-t@R*IBB0D!+nok$3T2T?C)EL25&0e)QxuR{g^< zF;!AtGrw#xJZCGXzDP-TS7U9-{HT1*0YCn)ILce=Ry8wg+rr4kd6=HpsY%o+WL{He z-#pbz=IccO1Jaf)t0E0u&7I-Kx{ikRbsdp#JElp)$nSljEB>dc`~loO`TEl|L$V^_Uf3y%5Z53NcsU&cGuh z?@bS>@@_>rrT&mHiYW_FmiropjIo)&ggnCoUPw2-en0oa!zBkkzn~>GGzi=|S)P!0 z|8OO*fA%Fk!Ggfg%w0xy%9SZcGJFZQj}J``xvzY_$G64U<=yYUf-(<6`k@{p7*M)j z{QznEKr3q0l&S|NHM&BsU4frLC$|}LqXhD#pwve`IDAgq;egTe{~FB7i|ZE zRc~Is{i9E;)pNp&rd*g*^<4g2<`>T&F(%*q_=NioBpu3o@xGCFoU~)sJ0IoMXRJ!; zdHu&fAMxdrKm4JAnp7I24}9^&sou*yp;=iMgl6v_7xG;(>xw0Ju)J}gRqmg)Qiq3S zCGSsC`i~3E{cK)o$`@w^m@;Ew`T_TmVDk7-X5)%R-z4|1>dOa&zI@^SBSxBW`1q}J zzc{zPB9wrsqv>G_@=<0$Rw`G4%Wc)A(- zW6yclB<t}=Gz+@jMHLdMuO}G=^2H zF#gyJl)d0C*gN6D_l;jlS2ji=kn-7CCoc^JL*pCCL+)E%7~kj)ndqvC?n0w1mm0#EJ)7zoO}eAcY(9EB zG#GlVQ-hnM8vI#l7jLC2Z;x4tW7~DPv#)GEy4_KJT9)^^!qDVhy|pMpYiT=aD>NUY zLw!_-eSPRq3oG>9bF{p9x6is2V{=?dmS*i3l;Jw0Z>`|kbk{%=kLx&N;Y_zU{l%EN}=eIz+CIok@Q zg${;pymR8U=8n@uJmE^JV!i9{v)RU+4pl7x^8zLnVVeX@Yw_5FZ! z@v)%+6+Hb82$7G4}Am z--!P$fi1Tj{?6Ek^D&(7;53eWXIzzM7#`)IJ?69h3?upOfrfEsGME)n|8sdPTyrtp z9`A!#i+EZChT$4FXbuwHX2M?vgY;GgNE0%v70GvK5K>&$kaVQHn1Gb&gR#5=$vbig zNRwS4EzaCblE*3~S8^sum4h!=BySKpxP8{8P{Zx>rb2=z%|bY%|0FPY?n^>A6YCGd zupM5d`on&I&?R_y+Og^q;+ znV#8!%US4;P)E<4AQdpYr?H&5s+@rLXA>e=5YZ(3fX#)(SzyCte)BPPt4Ei5M>L~w9pk(=zAtKw~1yu_CYhcns ze*mpjNwdRsOp`S?X_tglc~cB-Hp{NHvU= z{#Q|Ao&Qr%R{8gWv)-Q!$?g7yFwzh@Pbw{`w`ps)Au zhs+KBL7;E+-wK{h{^QX?XZr7lCTIEgK>iQ?Q&77t{sXARIsW$%-s=A=?0&BQ_b}dh z{>_lE&Hq_{!`SXW6Mf`-e+4wZ!2b&5U+CWg3tZ$s74m=NuR=Rs?7sstclg~%-Rb`n zdS32NMQ_~YzY4i_`)SlG{O4jU?DL-t%1`|FL!TS`#gK5LzXsB7@~?x0n+;DpBGzX2 zO(@@Pnr*4KvuVDCaSZ=wsJ`i6g`mqn5kfE1vY-7S#h;7+ss8t2WOS~*sI9ffJqLx^MPHx#28%ul-3QGPX-`wvbmNe12JMpLB@ZN&vwC)+G zN60LwX5FhIp5&%=hIQXSGWopkK!58ODj_+!4wpBq`&CN7dlmGx9#AP6-an!=>p_*0 z>3s&Kw;ocFVcw@x5P4X!1(Un#))>|!DkaBzw+|_gs+6%96L-@t1=f>^)ij)siBG9S z!&?uE^|Xq3yyevU870H#y@NG-R;46+7s4pkZ&W0Z+}^R)uzsu9GQ8ud-g7D?)4K%? zW<4+J&0sBlr&5C6^JvcBt4L0A+u9bxI;hyjdV}aw)(a|SqLGwo9ZI}~S}Gg9rpSge z+v|$oqs{h)O7Lmpys1)>wQ>HaB5(^d&RdEt!$``p-b);de&k)sZuHk-Y<0ssjg^02 z-+C2Wtca_JbzP> zppg``K2N+B#V4PgA!9SQB&)eNvY^^sc!PWm~GnnSVmgFGE6HQ{WLI|N|`-#m|XTVg9j^rr!gz(3e7mg%K(1?j80R~uG>nL>uS86VL%_{{^!0-&j07ytrds8v|*=9IG2L6L!mhWjo^%FAE z+$i4XNyL98XE*|iZ3sh1;rRE=hH(-Gq~$B#DK>8fdFcj*C@0~mKno-cR0VsEpd%&> z(%~JW2bXs|II_;k99gE?gFsA>Phy=X36mZ<2Y;b3I7gMFNgK4Tv?+rA{ zxDm?`8Rnfs(Gvo7hy=Y*)<~Gxk2cBiUJQj2a?>wGWUSYR?w*i0_*z6JdV_4U{Qk5+ zfp;6tTG0PhL?#=a^HFv}k((7vcn*Ed=j|W(n)Tt*!keDXu;E>X6mN!#c+j{ng6LTd z<-G&k3@7=#gA%i$jQ3)g-kX_XP%*=^5yHKL-N}gcOAdI4Ce|aA%zp13mcpY_M?Hj6AQHJ+z z3~k?-v@#@kydOZGZ&F$_B0ldm@JhbYv}+Mb_NGB=-!jD(@ZJy0`&KEo4DX*I+t-%% z0N66U4?#QMS&D6#_a|_8z6%vw(0c)#q;GfHOJK|KUX5|)+pE~ddUIIXZHjH8_W(w` z?-z=#!239L`&HVPV3_P}q1G>^O#wr(_c<8K_ikD}A~U@Q(Llb>(zYTpH!uv%;4{p$ zpCL9c;KjV;OEc5nLTo|cZi)z+Y01!KQD7swj4#hj8-rL?UTP>+7`qb0=u!?_3bj#?nkUKa0iWcgPHb6#F_&W zF>HOmFw+81w>|J3P4I-7mXBDMG58=F@_5v9i24^D>_cXOW(@QAJZx5#+XvhD)4HS&+|_^H`_W^w(@AlR{MA`Df_T9Xl~_@#mP3=$?P2tRbpC5y)LjB z*>U=0R88bm_bxjNdzT|iJfm(!C~oHW@ljUbZGc*(H_)yLu|M6{UMr9g`|wDIpREz+Dz=;TDYqD6H(#u zA!PZBorPndR~94>1a;8)bTv4SG+4|73b&e>aWbpAFBE=zkVtA6oz@kmOGTJ=wnmBR9o=D`ckn_aIlA ze?9zl!2cco_w&~wSAYLQC^p?+3zwbY9}W2f{NKW14)pIqV-7Mrzk@4CEOnQHE5(!8 zV2wf6;Vtz>VnnTkQWG#OC9c-tKOke`8XZ0X#U(cOr}44)Ogts=CS>&PqO?xx>bs>THl2cP*WXtah?iVcaz%>nsgL@B=uIr{OxF3 z!~YLRH~mH6a`}ITO1S-hM)Q08@4yjT{&v_W!9M~f^%_YY|N6wgBj-pM%D+L`#^cL0 zMz(gh82*hC4;VrEb^j(EPcx0+Ip`(+Gj$w;!tfkK$MB!!rWQ%b{%wh?&^5gisnhm; z3>(2FXyv~^wY|qP%oCi1ZsosF;+dY{pI}t~MLO;ac!KHB(0{Rx2XHlq8eXboiOoA8 zR@oePgt{;){Fk}E)5Tw#_#%t{DRsI|DXDiA{ykdbU>VtetRucmUoZz9$bY@W1HRyy zu$_Oejt4NwdiHY^?Q@?Ev1tu{%S^P8Bk&jtNH9mxf=1v@+Qe(7FiDo>0}xayhK)hn zSUJV{ABo;#`um|dvF9EHQqnM29+H!MNm+@T z5%7M6agsDK?P`P#FIV?T$EQ7oh)26r(jAUQjW{gRK~)}?oI06>Iy`RFheNyTk>nW- zACOey{sF>CxTKa?is2kIpjh%?Z69-_5qu09CJ&ML2xDY78Y_9I#D^OrJMbiN@-T@H zGe)*!otHdZ;zJE(@8l6Wj&8w=R1i4QeMpx&Ch=;PItjxi`8dsmP8$3XS}A#~#L-Dx z&5~08$;^3I@z%fu61gVC2fPiOJIjq#gYt|C6~i?mZHm06O#N$3U1^Hs_)@5>nM z$qQ5@=)HnERH#Uf_hlG0`2-ai>%AGxo4im(CK?0SO`e4;dszL0sf*k*KuTJWT$7j% zt-KGDSewa?V0b6vfAX>nwvWf#!I~@&(5yahGs`?NgW@s`PlQ%G$^8l?1(R1M?w}<4 z#N@gphH)_iU1pVXupaMC7#+#=s`h?wE<0Dlz$pVr!I|YoEzG^H#Dh?g$4YoRbI++LID58En!yN)Q%CkXNC$rn$I%#&vT$Y>qVncG zS3b?V0OvdsKctu5=PDy-8wgD#-UY7@VjUMgdqMay&YVW-bAxMSJqmgNgu6+!U~%(C zS5^~JFd&6`|mS{w^avvB6@LWcu!1PfXQLJf&KspriuW#U#48gY(pG0dN8 z(zS?kwKKYvqyS628_@$eb0)Cle{*HDq2#}U@D|Q7A#mj39EaX{+*&k+IX-bT;{6dH zYm8gYWTcNbt6UrLf2perlnFWG4C7J+Orw$i?{SS@1fdg4KsF{W=KtI&_@Bqr{1phz zgv|?hMi6oDJal%!87gq~=7Jxpz{_VBz6$0C5!sKTSzb5GvyyqIV!T<+OykWOmoI1( zek7bP#&hz1hT`mra(*qG-^6qBt_L_>>+oNT^urh|d^GCeD4e%Ol*s!x3+K2Y!s+1R z%@f7-G7C0~Q0TZ~uysd;u>*50fQ^irMmY0e(~WS^c5s`MTmieMet_mS_oSl{bWfe+ zn#-PgIjpxGXEyIjfPV!$ICCyyUzy@cq2x*s#*z35=00<( zOL-3NAt&O@IgD-#;z-I|4#E-Q;&`HA(q|a2=5$vkuQR!K?p{BMD@k*PD@AkcZkxo5wB}5k@NUE8 zTRCd3ahW$kmnn@DxYo2F&$t=AbF4ApHwZt4s6Nb-T&aHx8phNwK;8khFU2`EFvc+c z4S|~(aU=2=0uM9tA=qf)cNuvTk^UCe(>OT@jQru?Jpr)-p5qaL(rXH)=|Ee-JQaAX zp;TS!wTLJUHt0ZG!8R3mxS`;36@V&xRRF5orUK7J@*hCp zJ1=|=&S_5}@;drr;U|zj1GW4oS`mHMz4N&U#+O;Zap21_3VCa&9O-k1In&)c_k#@_ zLH6u|@va{l>c59-A?9orNsaMtTKB&mAwZYb-u+{r@~$I<$%SFChpbpDI8D47jo`cX94yU1dy|Jy+_ ze`}_GG6KCNh#vA7M*5>Tr=}zF1_G}zl7I-W$@y{S=OR*wz(|}0GZ0Z-Xub}#71XK# zx=@`8pbNFC!2U@7nF!o~Tzha%y8w~X;amz2LmT#-Y0!aQ4=s*~fWA}roJ=s$#GCp^ z*B&;m-J%vgGuCq^LIf>*g@|zUoMI&Do`V1_{6H_BHe(#s6ccOhbnmW)N$IwG7msOs z`hhXHFNtiX`VS{$mO+WpjKF(1^It)P+r%L_^Z$&i3AdICf(z^O`&AP~l>O1%<+-Hfo* z0|@*KXWj8ZKg-T#4fPeW?~5#`nH(9T0y%H%<^;}>rv7{2w^9Fx%U?+IA7Y{lC{cLocsq*_CXXt-iw@sKd8fzuwG1c16Y;dJkWWn^+AU^@){tD;R zazx%h;5D3i3y>>63Y{5D0 zK}0ez2Nm7`4cInUjEZ)g_|f$Q7--tp`(pSP!^^!BHn2_mDumFqpKBpb)BYJr%Ctuj zplJug3LNJ47=&r5Y1Fi_dE*!tCZ?T!Ia~+t)S2r423X8I&6RpjHhVUb9)|^fiF4{5 zh`fowtBl-;NJ~a@pcvs`)+4+aoiv)9#Q5cf+Vmp2J`e?e_KhI;#xEvf6u~cKmZ6K`B1Z*3O z89zsSt&V$k-r0~~%&7L@(GEwJ!qY&Y1KpvCTu&U|3K~rKh$bcOG}hx0mJd>fIU0s@ z5goiyxE0J?3B0Yj(O;CBv|P1T^FeSNj-?tiUj#u}3ag>p$j$!C=2lY8Lb-^?wW``# z&(#<8=r~k-q%mu;kG)*4-pwbFFk5Xe4cAOA+Gjt{J(uCC<+^;1T93Q*dVJ0~*pZlS zm%3Fv=K~}oGM4v^=lqqML62T{&v_iv4kE+fq<|a9mf#xAtFLq17&E5VHHL-G9Sd({ zj&YgTR4{SYw^$UI51VRJVg49XhF0rgvrMVAP$!?lt>x^mQ7CxZSj6lQHqz!JX0b}y z!X4-%<_W5BrTt^3+D&NfAJ;d!wf2vj4()$ss{Mu5{#T~f{@Njs_G>eR>gcLGVLGbv zq^YY?o(i_3rdi_Hjbf0i%c-2SmF4FcrET1e+-#k;hoW)2sZ_yE>bR3g*Lj+gnTEuQMD<)#MS)@epmaBQC)_h)*VP2)H*Gt1$ zKf>rWr;5?vrbyKnrkW)*x^Dhrrvaxs4LIFtz!^>h&JY7~RGG8P^iLsL8*rY}faPMq zLs+hv7n)|dbMr(C{u23_SNg6+6=zk`jFu=(zpa(Fw9>QrObL=tR{dcLD`=U`vHpO| zrrcfX{@`;~$LFk0qO&@QQXSQs{mvRBNe$L<)J(uHA3}djKI)vHPtICZlnAjXmmeyJ4b7DQBHMKlfTW`s}J%vP+7UPHKm za`$kOS*Q$F8KS{XfJPyl3wf)>tPE4NcEov|YT*L*nyTYz%mawuLYk^V)gto!9`WDe zT=XPIP_;6sd*=u17b!#5C_}n;KD>UBGG>i3rhDgO>ldrWs8v<}2KoP;GL(^*DI>dg zKCynWGW2p~=u^`0cp=UupR%0w=2nzl2*;u3xckS;4D|$^wOZY&t!pXLId^{@m#(~c zwes(A`z)%Bg^|y-K`%Wyc~n6H=j{P7nPctq!Fr>3Ig-@8y$S)Ebowzof}a~Y zjWr6fw=&kjl#>xvhUlcF3o$7=7JFRQa08ACs^!I2WLRcrQ1dNKjCmQ8__!I6XkS9) zWQ<9ciEJ2j$Kjmcf_gY{`qu+Ic)S|mB^+y`3e*6F*AEM>DnQT)(%0LWsDy9gJDZk-u^rpfe4>s{XHXl5a9zOCeHjrh)hFZ63&8m5pnJ8%0CSJ0SL=*PJ9j#W8!1# z|2^vee*Q0nDODp6M`^J}=9M7#-_50HI6yU8n2Rsf&^x<~aY1Cd)Xso#4d-W>qiZ?U zM6hlN7&YAD@Jwk=tYM{ire2U(vmSnrXUZ*!%gAoePBoQKm1oM9#G0?rYCO}%CD!yu zhvk_zG7%LvN2jJIxl@OxE*sR(44|*Oa*U*;*-3nA%`L%H1JAq}?$jAc$0tossz4#u z7=w_IZn{&Gk~G}}!JFb>D@Gx{G)N8Z{_P!Gv%O;4f<0ZB!)BAvPWA>E3V+CK%Vlb$ZcL>V*Ua-#naFwKf|${aWfdGbLS zBxX=~uooL(95*_O_r6n72!MJZZ3M}#Vxd&am6x8k`^vk*+kzT1H_yTRxWojNc@485GK&jF>1_pu&()XbWcfMS19OTD@bcI4Q0= z$cd*VZ03HLg{MSySC!McPo)A<sQxrTtOhf*R@CWu9o$u#$=J+j z42!^cZG)=rvz;x0C7x4j>^NdN!*5147HeaQaf;Le%OEh?WVo5;Y(dW7j&4zr&Ylty z84yY%YAN$YE$NpFL@kNJ>)YKBlW%T>V}LlL&$<1zQb)!raEjuD!NY=BbJ0aWOa3T*giY z)!{Ntk%XxLDFu6p-Jz*)&c%J<5NQ&5sH}jTuT67N`!? z97q9b6>gkNxEQC~*kta#6aii8$q1>#>#{?(84LF2LB?bycPU{S&SYbq;rnh#r!nG8 zBWo`yEI3J1XDVV$PAv2)f`@RT-@FC*vkra)II4qR190Jh47>^2Uk8T(f;x!wK93Ag zk%_QR1#1Zp<75Y^+RM;t4|!?{Z{pMm45_=Ej`jmDkAXPRK?s_|VQZ#yg+Y#L0y*OQ zud*bxW{=ZWb^a&aJ}yUqy^?qnPE>^Oa}``p)t}H*!m}#4ly~N5;&dB{pYG+=<`FoN zkuXXHml8@UzxSSVNt8hXBfmg&#V>#huP37gUvvrV{t`6!T zFIv%p$YvXo6@iNSfP#Av(luGZnrLoE*Vl~K1NPy>FelezI!IuB-Nw$F)-&*;j@;P* zREMcu#q*Ftscs9Q7<;97@szE7~kgn z575Q?%^50d6@mATVI0C}O(pQQF{lJxVilWvo}yOqR`N<6B=Dz9@Eru+N=7z9y9!nj zI#qBffp>+$K;YvtXe+`#O(pzHQwhAq3tqxKn))E%DIFwe11u-#;D6Ar|AA$WN&W;B zAB3YNo{Q{dh|3G;OjsFBU`X4biohD{9I?7rvF;Z~6BxP-Cqxlg7oCHl*K`75weD*u zoS`P1urq-}MCV{=y-KJeY>g%`bfHREO5kHGItN31z72&Fu8t-!be&Eh+#gL~NRNP} z1iD=SzHBIXdAD(FD6CJWg58k{w0F$fk>TnmOac zx8BoW3yG1P&55$&4i{Y?Pqpyi{ovo}j!590U9=40Pnt^LO6jZNDtVh1m@8R!OE^_i38!f)VYQ~V1LzuHq?m_TN)Mq+REs(t?89EM z@-h05=YN08O4JE4*;On;SDiT!>2=@%Fy<%FWZ1HH?qy&J zPQ3v_AnwpDvXnq3*g7s5k%5X_P0$>1=Hf;BEX7epdo9pG{Er^}5&G9M$T|uqG$)L~ z8LzAf3Kp(7-G+Yn}PsN`>@y9Ot%-8Z#v}I(}FF-o?`6$mm*^zg`L&_k#ra(w-JM;LhSg0S zEqu9OZcBdG>fDxGyr;gSt~0+K?~HHaD|kEeL5Xy<;+ylku+SN%`yW`g*+Pzp16YIUjd|I@(&BHsq^U|0}*QUkjKY z{RjYGg>P$J+t$|HnZKsKJ{Rn{t!=H5+(_N3rrhbdMFm9#xn~sRcDFa+g^83FOU`d< zt#9sbh_JB6rq;-Rm$=T2ol#N$tL&Mdmlyjxp?qF>nQq0Vu8sK2!Xn#V|DUYf|Jq9Z zuT@-kwf{ZpI|oN+dsDa-zDhZ!|5vNfj_+ykTPV@NY4rK_)OvdPa6@Eu9o`il?%LQM z>BNtFFd4!@H{g9>h+yJ6J<<@VCl?bj@u`>QwjE1Y-%;0&x37Z<_Z!dXj&w(KP&~MD z?sOV-c}xPTw~tA|~!t-A&D1xlOIJIi(sFp2kt@1NThRveMJjGpy8vbjx&a3R-z(R%)&eS_zXu zH-kaTN?2t162O~~Z6$(Y`A#A+dxd2pU%R^QPz-zT&o`?nF(3Qz0l=izJQ%?B$5WDLuP_|Bg(N7+=;<~1Fbw1lbRbm0fd5H zn#{BUqbzd{wAhrFhx-GN7OmbvJO#%3p>7GOCs?D4f|fbV8l5Bmt%UpxD?`<57HW#{ z3=}Ym#+Ydh>Xa~*Es7ROgMe_dlYn@>#B-FzKv?Kx*r7z234?$YtlJr;mF!OIt?u0( ztuC}pvr-E!b9kPWQbc7_a)MUsNaP*`lj7AtK`SL_W#?HlR)e<2%2aje&QOU~!sAkQ zWx6#up`R5jvIY+WUx76We8H@IYt(Q=^R4WJ0oHN()-WdK!@#gWaD*LZwP#raV0d4? zH9(OOBXyJ#AIL(`{gjbsjUY9cFl(0T4Ini#OPEg6vaTo4Fg51)CBi~n@!6HBU0bRFvWvn3S}Sa2m@uJ2{Ll6)GWJu>ekMe z0`Fukm_MqL-8XxyG?zKZxk4(g7~c zkU@D8SRI3W1EO%KR=RtmuA4Ow-pq7&!c!^_I?xS|8PIMFkKQPC(CJaqCcc zzeV8i9D9`AThX9n|N*KWZNKz3D9b%WHjj{#`d6Gi+5At-J|0y;HUGDia zp)9&wy6rRF8Cw~Iy6*n3s!(cymCpLAHU%oT5JaO~%xNaU{pFc6?bfy%wvYsN;Mvmr zia>+LaYP9-tz7r#nCS;YH8TPAREU`$wn3Z20R7UfaS(~#A9SUo1*@tntHKKw&I=j! z&262LFkj5q+|=1+lq{Mb)*swjP!gU}SY+UBhhaV+Z?9$ex_#`0!&n4{+q%2L9g(_* zjVhb^E=jnws%Xm8>Ow=6P_nFcLHUv}KN>*6_?|!*9~3Z9TDX4o8s`FvBkYaA{= zsj6hjJV;;>U0Qe1EDYU|fXfK_Y63oc&>7KL=c67A%U6V}N-CuWRbS=LMR&@Tml5YKY)#Z(UB~ zM-3nS(jgVr?MV&sAt;m_4kI@|4CKsO-?hPr)HgJSQDn3S)_2TJ-9VVO(v%M=ol^^Ga$<40b8iBkRzc4L0+l zicooN#p3dCRe5zyMNMt_l3Jr3A30%*>i$M$x#?IP(M5K#@4i(FMwDYAPz1 z7%ffQx2=nG@I#U?p{_p%jomRFyNF|&Ejnt-O3T89Ma7Q(pxR~1)P;q{>8<>TRGdkb zh8>YLo$TfE5ldA^zId-@S!uXpp6bkHl}kbu_{PSPP^Hn;)`iPyDyMKPE;%W@WZB}< z@@iYUQ*Zn7S%a}L+}43j9mlB3zPJn$X@{Ef#k8BK236MZLrgLtm8YQxaff(VNx-NhjWb6jWn%oQDa{ZH%PY`%F9ZPrdHJ) zbgYf6mo3=gd%9cIFpVFHa2{y!CglR_o7x*A9m>yX4~-#)z82SlYzefjU36J(weml@ z2{7{NBTe`MT60_LnxOg@=Uw{R8n?CEb}B~`a{$4)>p_GbN#|Fw1Ya4V>R^zq9%i4s=KNzaIN?- zEWW!I?(A--n|Ioqvw-cO4c+WeX?0U`1TvJjsa_oQjZ!J;7j3IocVeE#4By_Zd}${> z@uOya-4b}iv#OJ0h^PS~eT>bBTL&FRxD;3CjZRh*9SZ}5?>N(gOlD+MSoPJ4+7;mw zt1B?fDHD0o1(XxR=WXe&aXE>zSV!axJ>pdHN?VRw)h!{)HmGroVNm60Ta}hM+P07o`^DB%(QfTrN=2I&i&}=cqUL92!_dKSbZ?`Kp9pI!v4>97 zm8b=vIQ7|*(GE_wim!;3DP)kRnEMth~vu|cLwZ9#H|Ib%|q zTI<>FXmvN7X4L)adB~n=#Wj${P=3{JTm$nxJD6JbgllTC60V6ViYZSiiuF%N1ao{u zo7xr{n}1M2{7!R8xVo~kR?Xb(W#~5cowxt^{uXZ|<}NSHn`Ylj%yryMtZ%7X8wv9& zY(rSzA+(oAa)&UydcE^5BUJrQ-f#>T{&(GSG!|8sEey}AJaLJ^r7tS)bnvn&(7waq z9!C#9yB=`LtHNsTh`JZ&0)ygc(z>oL8HTFMmY-BxT~Ze37^IBiBwS#ETKeF|iH6e$cVX zRZAeXQIO@Jnyr^qa_}vK|6dWV#45j}mg{v)Kg!kAVY`CmFsrG!z^tJ}_QbY?Ly zXGFDw?rS3?D{$2aXw~KAMN_5}PUobh#L-v9Hg7DRt20?UsBOOOjGSFk%@i$lYntkf z=;|`+E|u@b));NgeI2hdgzGvxBQ5yep|LQsaSebmvLUx+(xeHkZL8Zs?P|mho2f0G5y0x^wz{rdJj5`ovu$-32;8;L!oF!; zac)aq3ofWkXy`<~HNo7<&R{O?;?1f@vAL@`T5_SddMpER&I}AYX{~L!>dseFD;PQ( zp-XNAt1`#|x4xW_s}=^>IdU42>9VrD%DzV9Tv4d@bWD@d-D&2q?lo!>tw-Z=M=Fas zX*O;+8rL?gK6cw-DD{Gzhj6U{-ja={$7~C}@QW!Eb;W8;@0rLJ-yhKJA)A+} zl$tFOm>0~E)-_#?juoKtF{_%}R$(K+b%IoKg{e%9J8i<>XZe^Tq&#?!z zJF{As#`?SpscysF3D+j?%dC71-Mbt_yLsLp%)UvFef& zqdPq1X;`8>FJ&w$sj0;$Y*Fo6eEL=QZhLI1(SWT9=95lC`^t57&E5KPNCU2U)vQ=j z7G6*Z3HAk-`qnOWJq-gyT``nNjB9;y_}aAgbW8oj`}JP++BN*oF9H7tJu;T>=&;eH zOJ2QNPQh~8Ni?-`$E!!bbRDeK<Ujrr<+}QM*q@U+mTu}wD6f0O<}|hQiZ+>Y zr_z)3WoGU5u;}OG8(}P5cvZ4#^+p*js+oFqJw1gw_keLdm;>^dYrisA#}-3sf|T}G zOIx)Zi*`-JG0nw_$3j7_i>tm>gYg<2q})u{MaN&OU0%5et2iu&q$}AAA>{+vfny60 zHO=U4w0(6Cnn#;RFQIADa3uaZ26*&!eRyU)6+|cIC6!CcogOMW$Q+w8qbq(F#wx2J z3(vz!sntik*NXXAT18JVY=7;gqU%CilG+r)hskXM)e-u>lE>=u(u^t~x)h`qdJ>Fw zCQftGf@Mpva&lZXcJyyJWOdJ_9Jd3ix>sQZRLhG2@O|~|8ohxno*LZexq&k zjX|I!)ps5^N>Ue8rQ%7cxO6@fU6Pu~^jd$a54rK&Gfw;9v(lwVPuON{_nhy(K$UdJ zccwozvy{Aic-ubsJauVm&UuO3eA~U}CtP4%=())KBjhMc{a~o;M1N{dX_AwFB_0Xu zlfNSMPP0<68zqw<9e0XW;niN_5VVSO7w0W5saS$9ZFv_by8C-F%mL;gccwYm9Ab{} zWSRVVcKqQac=$eO$AL$<95Fsat)C*diEchS=Q!|P1iX6Pd3X%G-$_Yfo8Z#|&P3!+ z=*6F4Oc8#zjf+prIS%{^rx(A^Sd4UP$+mLwSvSYw#uM`K`uL4@q?4a*=TcA5$1=E8 zuX4sydgV_sE=Mx+vpro#ig7!_1yM6;em?iE51r0;HrsJKM0De8h|a@}ABo578&LA+ zMdk7LDY3(C^p8ala`Ss|j>D~fY7isfQ*E&bLT-aUV~HJZ)mqLte+v>j+{VaQ1R=M< zA8^DDH-3n5S1hW2^8V{y@#lKQU+NYAW3TwTz2e{WiVu$3M*qZ~Uz)TJT5CSyB(221 z{?y3fAEUD@7D733w-0*lhaERx^57afyO6UzuOLpA-gol?_4l!SkB4!iFkLF z$_IWWA2^wGV5#6!f*%O_(Dsy@Nrc=iBII)1y6^%gzNx~MhaZ+oJj8UA$3DaI_&a3E z=g*EQXAco__7frJJ|g&EC4&DGBKR}lF3CTZ2>!`L@P~-t=kI69&)<#m1=yPew-Zr0 z{?eGp-*htldO`l8o^qcfLhd0VuArfQ{^c5l*Bc3){ekJFg7XEJ z37#TYC)g&~CAd}ae8DRO_Xyr7xL@!d!6yY@5Tq}o9v=#_{*1d>KVU#`xZrp}z9WU{ z${xUKiJvC8R&ayhHo$mR`3PEKM4L&@GpWN3i34*l=r#dH-e^1 z)A=G8rl$y|3l0_>DL7hif?%N_f7H$VvjocoD+DVAmkItruugD|V2farIozvdvlpWq0=34-NxT&O?IF z3jRUxJ;A>VnwX%;pC))#s*X>^_J;JOf+q@|D##BWGQCN#O|V<=Ou=&m&lkKzaF^gU zf_nvT7UZ`GDgQ3PUkE-T_-nyu1%EI2ir^c9e-?CM%|dypftFp9J~AOv)L6g%B}IFki4-aHZfT!7YN@1uqu7T<~ha>jiHT zd`a*xf=2}tv4Cg!0|mzjP8AFZb_!k~c!l5%fc)Q@Eg1-}dOYm=k{B$Yhr(&T> z93hx5I8Sh?;Aw(u1vdz86TDn-ui$NhzY%;}@DstW1QW2aV)+?@;{;0uD+NyxY!X~2 zc&^}$g7*kMCitA-AtDyz?@RpeL~QoH6#R$KeYpHUda__YBJ>+B@lk?fgkB`^se;D~ zeZIsO3RVgI6p61Cj0pV$(65vDUcsA$ey7Cm5&X5_(}J%GzApGS z5%v36;-3m0CBj}N_Bu>YCnBCFIGGs4x?k`IMA$7XxS0t4TLfPc{DKtg3k#a6nu>cd2b1R zD_DXHBBY-p7!lkoc#q&Kf`hTACjVr?S%N1DUL?3f@Ma?Fd5hrB1@9C5rQojxpA$SN zn1H;hy#>>WC_huMSny22D+TuoJ}7uZ@JqpDT#lsNfZ$-k;eulX#|uspoF+Jv2tAex zt`Tez+)9M}?ShvH?h@Q1xKHpF!2^OnCqn+yg0Bf46Z!1Ut4!x&^Nkd_(XH!S4izU~H0qiD0c@6A|`m z5nLy@N$_03^96SZ?iT!w;O_+gKtwrj3jR~DEL+o06I>&>Rq$THR|UTm9F(K;O&2^t z@MOVe!5<3l6ns?h9l;}llSe}ybP5U92(A=t5!@trzTj@bp9tPA_>kal1^+1ck>Hnt zo-w-Ieu5(eCkPe`mJ3!3h6S4iHwtbSyiD+R!G{H375t0f5y9^S2Op>PDHNP5SS=V9 zY!=)oxLt6U;6A|vf)5HlEqGAyb3yM|U2cZpC_#RZlI?Q5-~z#Af~y4E1kVz@P;i&v zO@em{J}mf*;LC#V2!0~iBj_2Y^#}+K6C5o#Rj^cWiQvhCs|7m*&lY@0@F^npTCYg_ z9U{ixyMiAI{iwuy1pg^?-+0ZREZC0-yA6|gP;j)+rwEn{E)!fyM7a%uO+w!&c%INN z5WGa_yNS^M8o>tyzY!cbL8p%toFQ0Cgq~rdWf-jz-$;ZWKNQ?5^h+eZQ}9Zm|3u<9 z3*IX9dnNvW;G;tSo#0!7A4>Ya3w|ba53ZF{uPK7d1-ppQdz;`+!5<6WB6zRh^MbDl zz9)D@(1mO8l-FNy4AF-Vy%1rCS%Q^V5<0>m|I6l@S&E7&HuPH>ap zIfB~+FBaS(c$MIFf;R~MRPcb{U4r)uJ}mfa!KVd(C-|Zuzhch%d?RQj=y;-FKrlma zgkYB7IKf=OX@bWKmI{UhD+Ox=PZg{aY!qx3>=xW8$PcMg-(7;&3SKYB&#E*1HNkfT z-xXB%bCCY2#J>>yTF~_B{FY#{U_fw?;84LV!7+llf`x*`f-?on1m_Db5v&$mA$Y1_ zgJ7dzn_#ElCc(1>w+UV-xI=Ka;B|s~1%E1dt02EAPy6wcTf`K>eu9~T!vx0&ju$Kx zoFX_!uuM?h--6sF5*xJB?H!Ak|N5WGh4M!|m*yj}3;f)5Km zCit|Vx~~R3Uy=B0g6{~vEBH6T&jh~`{HLHdQQI*|u)p9y!JuG{-~_>Z!Rdmt1l9dJ z=(RxN)q=|fPZg{aRQL73-zM>Of|~@-6TCq1GQnMfKNkFn;C?}M9}sdMllT*Y{8~Nx z&D(+>3aa~opdXcZk03v2PyT=)ziH3-6v3H-a|P9XL!>X1INuUX{^f$F3f2iW3N{ON z3a%GCTX3u3g@Ts|-XQo>!2^Qoek97hU*Zo7@|*J1=Vif<1wR$!hwqsFPeBjX{EYhq z`5`jKgM#WlCF0X0&acNaeTg7HAkTP{V5i`E!LtRo3i2EC-M>WqE{XH2=uCe` zkRL{8{B1!#V8pmbFh#JRV5ZmW9lVFQrm*57$&4T9&UL<&_AitVUeSRVMxZsn5&j}tBd|mJ@ z!S@6|6#Tp3mxA94@>&JUNfrzU4iX$HI9hO=V1eLdL4MDcvX%%g6I>z4Z`(4xOYls= z&4SwnFB04(c$FZ(gUkGP3EnUGu;A|nUlM#>@GU`p@0R($6HMU!Z-o7V{R9UH;>(LF zJt%mb-~_=ULG?TU+Q>MtO|VmtA7mx{M}qvuDzB@k=LUecN&HU12LvAxdRTC^;OT;A2zCo%*=o3OuL|lYyWzH` zs(+9OIb(@v_W~luV=)o?ZytaU~b=i4wQvPnNhU z2kA2K>xb^`*GI*8zVCtk7{{?vj(K#yPv-A=h(E8Rjk-FO>-{i4D_NLV*mq5O zGlT0GfyX>(|1TvT!>tQ$h^*>fqu+Uvr^9utR`Gptsby|f;DdP6QG@f4z+Ufr ze1X$h?#eBO`UD=!#ZoO=?l9!1T)W(hA$Tj|_Q5osi-gZEm)AvBGC>ZzT>dV>SuTYw z01wLz;I!@6De@${(GKHYmuJUxnT)mdlD7czPJ*+gy#6?Cd7FF5OPXdF-`Yxn56SA- z*^4i6CO+nlIn_RFzDp2r)??qbupjtXE*>3=mb%|7h3#}RPqclCi- zKc!ExeUid+}x91`nzdJ#0Syeu829XaJwbL9m5B_>(`~0jv z(yj%^uU5VR4tHJq{K5Z%BmD&O)PS?BZWsQa3;vXU!B6glh18(5j-_m^V=2EzF0FsS zn_QjyVNaPSlp5L@8Xo%Y%WuDXyXTZG-p~U_jI`jvLp?bKQ$KRMgDOW-U9(lq0&gsL_qBP^s;s?xZcTm|7mYhgy6ly4NH?Z)1{ z12%Ub{?0Y@fh|3ggDk-ay0XntsV7jj%FRUEdrMF5Tq9=-q&-pRS+%8S+T3}o zSkAVdDRZ~=Oq}~k{ltb~sQ8iphrKtCud2G<#?QIuo_jM~LP9POLP%~V2t!7~q#z-I zAVVM|fZ))~kc3PoVQ|J4$EpRj)+#ktt#z*8RKMR zsr6G^&+vE-nc0C#>cp}=PtlL;sh=IRre1$+R$%hrp^rStZuCCiFap_)hwE=`O5HQ1 zwfr-qW=j1%t=5p`$r{RVl^=cHbx(a@Q{vKR8^`R)9^wFzU`~l2oYI~(WVxm^#$;_jHnRviz?RdB zw!E9!{_EDV$*GlTZ&-gQGi#5z!pQK+TudJ&x_5alN*_ z3G1`Ojw5bZJ-_JQ*7w@7hcYbJkY!dJ{eg9F{q1d8jlZrpvzr>HwpR?9usli|Jx{p{ zF8Zwfdyy^V1zWJPi;vh+VY}%J6ZGMk;{C=sqN6f zwcwa<+D)x4_~U|OG1JDk#x2brx~L-ivvaNN&+00&hrHH;WA3^3GFtn(a-`!wAH?i{ zuN|~~HOGK)ts_`l<0_OtHofB5(6m*@+|w$K9h+8*vwXH3!YW?5C_v<6F;Y^-n$ahGPL*F=mey7&?+!)!sg8$mKe!?C)6L$Qv3A zY@(Er_p8CFt!9>hL64A;_x9kX_M5#wIh2k1Pj5A*{ODjd{Mj0r-Tv)ODbHBb z^INY-y*Xyw!i$fN&W!b@JmdPq7bS;VZ%#FT`xWcg^)cDk8d(^h8AbPtZTD~LS$yn~ z^h!tj*Y}zSWAe@$H1|w=+{}e!bzbE$#E8-&b862)Q&yeQ>U+x<54tAJtb#;OUaR-! zz-PAx0z(Ikl!~$;w~<$W7e)oqb|}LrJNg{F1isUw`(PvX;OZjpXM;LE%{!R0(p9;y z)s;yf-B%x|^xUkiaMIvOt?A2B%Zm=4Br`N0DQAWjDW7#BLw?b?cU@CQOPv#*8Ck{l z?`q9zPk$z)#Y)Wlm}d^R(gKfhC9GyFDJ{C{h(Gh^n9qlfxNs%$zX2s;9+R1ZKN&q8|h8jGrE^r*Ef$~_M(>9!I%|At&w&ws&}b5_2v;X z@WK&S;D@>&+0@xkpBw3?)i*#xbJuFou?9L$!J5Z-nNn#zSpU?{W2K4aeO~@o-uaRvv8;~z8n$liAWFp$VcVs7GidiszaB^$rXSZf%4jnMZRV)~C zo0ID+8`auXw5k0KuW_jT@lEY-dX3kvlooY8&mOU|D;kd;;Vwq+eX#Gj?t>$)M)T(9 z>YvA2u?G?1d&BzEht(1K8RjlC?*M&w+J%F$dt7_6VRNjD26s7MS5~K;|8mfrNbc8R z!CA{6EWhMfX?<7Y=)pj%=N8A3MaqK%^>@;Pjfq1Up30;9vDv(}{)Zn~$x|9#nU@^P zPc$;?+xL6TL)Mgp*H=As&*M`YZf{LgQTFzF)EYo6hp%NfK7GXeru(GW)`oguF|^El z??^pkmzg+Z8Y_?fz#MgON~3wx(fZq3xvs`l+*UueAz_b|y{XYa4@wQzWLZh?hVspu zMzuCV>h@O77}cJVVtbcj)Zx7*d!BPTrHXhaR>l-ORV% z(4PAsJ-6D-uRsKFP2%nmzFQ5i?J@Jyt7Z(Ed2xezQNEa`=6rM45W<jT-R%qZoV4fAv6%f&Vu$KF*wkt<=Bh|xSDx{%juqmy zjKqi+`G^#y)pwPr3^r7~bL^^%^IFTM8b@OgV-t+apDFundr{fYLPTFHaNr|r3?qHM z@m|A$WB&cOH==Z4Fx@rTgE6I5)m1>zi$|;}D^{!-+}mow%k<9fIqM-r;oI69+Grge zuWx)@M{5haU&gNck357GivIUdeN5)bl{#*v?6Gq5Dxdmr=wiKFcqp`6a8(?AR@Ix; zez-MMuaW0Oxt|?8UX4cHYlC{9aC>VL{>SI-jpn=VDU=QF8iF-PYHZ{^F7fj_kanrL z`B1$psHfS$2#7f-CCA!VEoJQ)n~{DX4mI7zSof5u&>n~vTW|JDvtipwz3a=WHht6u%#n3} zy=tg&&@8MNbOolbWZ!4EzH{u*{;~oC{j4}@SeHKo@0(sRw9q=a{=i3`Q4<^YwR#Xk zbYGvfS{gp6-&b#CPp&LMZ}N)*o3fT=u6V973vp-qapKMd6?dkqYnSQRDboT*-UAYM z?m6Nv95cw(bw&}clZvu%Wn@e}H0a~C6|Uc=4%_iOkKH@#xEkvgOEGRe32R<5EN-3r z&R@R>k6jN=Mm*C|&CEfepR|mfc0X zv1E9DiVvF~-bRSo+kN9+1QBx-&3a}gF(dZ7_!xl>^DQlSsg(T>C4k$Gp4nr`R|0(b zmlE(Ab~@5Azk-x!&RFte-$8n%BY+y_Q{ZYS?m1~J`SCH{Av2an^)0EbF}$CeNxwu9 zV+3E);r*kg+(yE8pq2MC)2e3~zxU5(EbnglDk@Osm{Vpk>h*puWk#GG3+k_$iWxD3 z75q(8z7fw+^6#2TG!poPi}xQU{(&HBcY7^YY&@FRwcD;_rIC@q@8P{3m&NCTp?8cc zcD|CL-f<%3tEdDOuPNyLSMRkV9o1DCb_KTl#1~tyJEjd zskbmL-q9j8;>)nRcTBM6GTf2$j&)hRkc_2syF7xp{x0DYvf{Zxr|bR8uj&Q$f7qN?OQ5z9m(l4(hCckKG*9L=OJpCR+cQD`^P? z^L!P|d)1ZH2I`pBhBYW{+=hoL#Qqd2u6LNzwGic%&3mEEGwEIQ#Fcan+yn2KyA3vuEw)|=D>BO*jp?4mhZ2k=KB9%1(~uu~ zlJ!?Wav-iS_Bu%T`FKoRv5);U{9{pHT&W`5{$-Gho2`f#e;K?e?qo&y{7<3rxKk97 z=;!l$aSIgT_w(VJxP^*H@qY$=8vs%7KgdRRE3sx{ zjGF;vtUKlJ=+QWSfb|P>a*X>AAjY^EqsF)y7sj|b@5i_~lgGF@Q{8-BRos9}%TGk@ z$!YODvEM>MnJ~ULl_oS)*W&w9shQh92_qEWuZS4`Tj*!}W<~h?d?Ge}iy{*JRj^w8 zRz>*zAEDFn+Z2&vxM!fF@#nZ^qaE+JVVn4ytO;y1+2<>aJ;K)cJ&Dh8%6y3X9H%@R z{qfE8Q7Q)APz%V%=&5hE^(c7aS-w-uX;3EV5Og(?x?w&usTDf#{bul`q>ZT3o791H zjNzWcDi^u<{ud8RIdv>E+gV2ijASM~30YSXpUJV3_$Cf_(hc~u4flhn+_%~_FRb9{ z<}McGgOEnjKcO7v-scoM%f%_2ywKMldkYMwoYuEB{bRIj_^$)UcTNU-=JVsbD#Lf4 zA`<<4vd(vbBK-cDsKIxUB2xSw)_QR|2PVybki=Kg>3{)0-*V%-L=iduze82uWs1o2 zXOXxfoe^c4|F>-CtLf}$zQ2TqxH_W*MT?XJ_`a4-2Pjo@$hRw{*QK+M zGyQye)c5sthOt?GzS7P24b{$U`G%@sqy$QU-2c=7Ch#{0Osz0piATV^e?hzlHeTR zF@6sQ0?bN|5x+*wBJ*)J|BCN)4#HhHcaxcl4uIK4rVq99OWL%z=?a5B6%hGtF25;E zyB$3-f2Nu5f$=(-7qj@QzR^@H2k9)FX&aGa9`cQ&kDd(1ESwV#pyI10kh*RDEssT|mjDP?&3|R(6ALJeB2}v$o(gQvXI#)1wgHi&Gms-B-@`_};xkWGaKF`_{6(e2h&tPOVwGuZ5C)179zhBiWU7-?@}L z1O|U9qSa0JT}7q`BZ5gR>ISsBv%x6A8F&UWo1F2wPiMrb0eB2dK8mjUbY`eeO4GBnrbi+0E>0td(a-!e zR{Q8I%sB0%pT^EoKB|J&r?HEbkE+1%Y3vznJ69R^)7W+rlkm@j4fC^DwQZQh2d>RO z#VX@XQbG04u}U=~=S9l3OKY6|y0!LL6_#>UYwfWr9ObIk+GAB1(sQLfR)rruSK4D$ z*wK2l$Et9n_2>xKqcdEOF3}_JeyaFfOe*8Q`MaPV&&8agdaWkob1^H}HzV(6nAki$ zMy+pV8RoxM`t+ESDLhH}&Y3Z~yKf~x_ADXzCNT3$nwZT0o?_N%!pNJ(hF|e&1sGFw zov(P+yf&u1z&3a3d@6x(hnd2-qG!r3?@~5ij5skB=lBlRx67;MdJ`CDk;$D1n47qH zxDJf3k*WH&%X>ci_5>IY<4k)VzF=PGRej*|`M<%L4XuoCPDY}@Nt8>%m^w%UF81Qn zFtl?s$Yxa1?-zS>=`LnRaQ@JnG2tVfOC@#yGin?;K6KwfxqSLT59N56o4grY92q`<&zJJ)y0$T&AF8fSZ^kW- z+>MaoMT3^Z(?!^+IC9`ys=Z^=Y}wDaQ|0U#~ag_l_JN+}|IRyM}W7P;&j= zj4^(($uA)DT2StK%JDa=191Y6dL^*yaxozHz4UU|NiT6cN?pDh2L&=@% z&A7&q8v{8%C#jl4cOjvlfd-oAdovz&>=MhI3WjxCg(a^IRw%z+>Wf*HL~aC?hD2^HLmv* zv&Lso@S$LhXXqOJm_>{TdLMPBM}^VJU`!(OO@`*PG_wqhxn!z+)R#PJAH~W4C7fA# zXtNFq@y&R$$DMFKc+uOaRgAJ_o;Vtfha0E4`vK~zI$Gu_Vn=tN&h0pj)C*YPB|Et9 z`o1b~8SVn;0x#K%S>S#Me+OsUG7#p=w(7?RU>qj18QnL3qM0`4h=p_F3V4V4Q_VD2 zWlcoIT+Qa9>d+23xFfgXKD3OiauMPS^df|6Rettz|wNYp$0C#G!G9{Lu;`Du|T}rVu^d<%8_)D0-(_Tn7 z<*Oh08pyN&nb+!Wo(IO+WbR|F*J&n$%hNcE@Xs->*DMS3%EW0D?m<;%t8Sj1Gk4z? zY`)blR?R~&1*dMl)n1^Q2V)*i-F&+=560PKs^&LI^I$xUlg)QX^Oy`KP9t?C7x%TH z(WkY{-Mm?V2M4JTVx)(K*Bi3WJ_!{vll>ko^F8?CpEf-tAhnn z?fLBF1_(Fe)S)2NUdc{=6^zTsRG}c%ZlYH|2nOFfSn*@4TyX(v zaLHvBp@RqM-ez?3Be0>+?rqv?TgUJ)$d=SL)3jHx`M-kkXEIgurrpTqGdPuS{@dnV zG27Ap<81$BcW`>aFJR!?gYCcUR_!lDjb%9Xoq(6!s(t59z{_scGN*nEPJKc1viof6 zcnug=;+%j@p<&@8WDVEZARJ@BCbj9V3k+Ty8>uyP&wbHa%^8o%_55(FQG5s$e-EeD zYM)!hXGg1jZWWpN%FRPKwO0GwJ=E$SV0@1AKhr7`CCzI?S|PH)R=*2sb*)?NsHwpaOm#VZt-FhoyTG^_XNKDCUhDn}C0)CW2^<>U zq6t|r(x1Xi2ur$^dp*i1vOQYmThq{{GURr*nm>=Cy}eXKxz~2Lx|n+tj9-wcRNn4Z z7jyiH)8ENdDsOjpQsrDQGjVE_x4Xam|50V-k~x=h6+BJrZ2l6e>g$Ws+;ce*_2txQ z?&YdsWuw#dNE@lVDPk@T^%kmOfYwiNdb`-I=JHagT8UHlcClN{xkdxh3}w)O^n893WrPeU20zu_`FIa=>g z>5kr$^(1^8nmj@k)hJEYlkjaYeo3a%d$OL27Q)vTVE$XZmBkBrF>Zch(Ol|Za+tf< z;c0s9A)i&iw`ijl^U) zobh8|5Z!*@Cq=N;#9uC`&H-?Ld6Nzqdj!ReWB(d zk16y-u=#zKS`YeAVK-Y+-P&iV_25P@t|wD9zt2*u!c$;8PNoVN`z*C8ya&eHI92@R zt-%xN6c*Dk!N$Vm=6#}Ereopztn!eB@3U$`7QWBg5VG)n)@E(tHSFRop)Tquu^shk zkH1BC(Tf>uF?6Ud-lDrW4U9Z8RTpp3U0el570!QaVZG!gF}__EYG0dpLYCA1WtLjp zwxGsdHl^CX%uMGRUI<%I;%Nk;&oP^%282yopm{LjMU4y&t4MhueSFm!Tv6>RA7(8wB`G6 zb$^#wD!>C1jZwKM)VyA`Z_7rdY6&c|)T(_B zD%{MLl(~y6wQAo2#!Y0Z=8G(~YCi|YGh`}r7g=i6{t%4!aq1TxuioJQ&ZCsYcyd*c`!k+3Kv1 zO?5SA%%HEDubU&j9?W{(R9A4u%rtE5%ooiO=AK~Qi>A7>Y@~jbdArP@?Mk45x%*QH zYTNBH)eY~&0KQMe$w*?bb+{b9+`$a|2Un(rp$g6f)7;HFCAxxzruyz;CJGneoWP}> zIRLE=ghY-ukN50Ib^ZzFM#1huQe44jWs z>Ae?;2XIbjwWVzP5U7JVOR2-3k@ytn9O{zQ1sUF3z7V|@TV~w=9(Nyi1bLBP#96zr z96 zcoSzS8~6-~Arfq84BTN9&N*yw!cx>tn>fB@%;HzPJhtOBcb7~?g}f0q7ez{OX7Z&# z{YdnXSOsD?64&C)XUQTKRW`x5m3)8jNfc3qK7h<&7GiZ?*xSN6VKobcnRdc7&>RtJ z#P34&*P><-F3}O3YhZd_FY$2WG-eEcc#7DOybVieqC3@hQJ_><%3#PffC*b@zcXNCfAsm`>SIe*+-?A2q0rQ|g$ z)-;zSn<#~srY}k6L!R-;W_&yKO)g2EjrY0wBSkt|M@da|5)s9`n;Lsyy; ztz|&jCpWx)+*OLu49!gnXCPh|z&P6A?5VC}GGv0ohRGf&6Jd?1;dv1`(}MjXTlb7z z$PeymogQzl!HAWXhAccIQjY4( zjAUuE%qqZ0Md*NMJTOkM>Ks;wfI=E`wZPy6=}K@+%ZMNcCZS;()r7ewlPbulOcQj} zQ3~2LRuN|6bQ`!WIKb46I)iWvPM6W2LVO#i7Gf%(8dych!KpKt(k-uoh(5pyKXj`# zZ0Ay(rcpz<9w*vKBeE@4S2MtNvQQpEp_&m1xd2huR6~N>Os4t9aYJX zJ`%wQ;DiYY6IHUB3Kr>PsI@gzulus1QeCLR$FacZ`lc4Kf8?I`ts#}l5 zT7pw|ZCKrFor+PB9IS&Dr;b}B?I;s56452xVI|yQCAbd?J81S2oEQNXI7KHN`LKDw z`me6{IP#Icq)YQ^EU07c0PEVIDyStm zI_jCHRSC{Knyv@uHldA#XbDr5usXVzqTz4=?U!+d(IBD#55y>pPie)DRxTY2h*7 z0VaN=`Gi9_VaNdO`#a5cnu%~}WT_(ypKw*eM4TL)>H($>=nQ=7BdC+Z2p|C;B{(Hl zsghH424Nvij70}AGGsidg$QhkLI8a@O*yEz0ep&cJI&Ro<|=gtfo{Ma)C@3nzRn=r z6wF}iR-CAc;3!oyz?ANDXgYv(q9Y9%@LhtVPHmVvwc+a2Ds{NXpaj8Dr*?p;-8zHd zs8g%dc|vCpp2Vpq4pUF-3<6cw8A#}Z9%RJf4B$j`qJAgoWT=zDh$Kb^_#hKday2YM zGr&rx1MbjC!UH&!-!XAQd*Ln!%2pG)RB|baPu_{zzZFq@Q!Rh4`)ABV0 zr`nofLj3uoGJ2@mHR09PMwA!>*)%0zP0)%57hxA(NTuFMrIBWHQjvVkMFqHi#tAbJ za&THkk6XnOP7M$-15A`?!Z*N#9w)V9DARoR026d4Rf37-n(z%Uv04*}15BKW6Acm? zHQznJM5`uZ2AE*SP?v9j3A!VQ!~rJG*Mxt7i5;3q8DQcnoT!*^t>(K2n7C0BF#}B8 zq6wec8{eUc!~rJm(}aJ3iM^UgQ9I=Wnn(*9iBz!Ca7M(d02n$})gUDY-82tpgyb|Z z3KTQ6XsC_j>}bikV4M=oEC*v@H1kw2mPRvIfKjcOVGXSVgHt9#r7wX|AI)qAV?#8v z9gL1>W)B#2azqb&%CQZ2F3t$%gcs92^6)Av-{PBOc@B43HdB~s23OH$g>f;TntE6jscRcfcgkU znt%QP6JU60}8OS@Fl6i0^$x(Ya7sEXhanQDWhm5!M?%sop~E!BkO zI8W4njv4eKP*n9Jyv&LEA672GDMuw4yHOv3C+a(YfC=hwg8s8Pr~gdPRHC&6T?P6d zsv_8b?9L&fl|D}YwSv_I4({>$|Gy<>Ysvd3-x*A^yvkpwMu|wNP!iP~=7qrPb?9|BKDx0SxwzFbe1Kd#8m~g$oTI zJ1tZbE;QT(T8RHjBiP(<6KJ9TyL9?5JKcY)Ik|`mUASXlf=3Qg@C^bNT`Zb}g_=v) zp}B;+G?(yz<`SONT*9ZCOVD=_stEyJjToy5(^RsW@V4d>^c~x3lu`$8a-e`dH7kP7 zQCZbddUBhBR?Y_uSNVUS=jmF{F2IdCc?;l4o!k$Qj)WA$Tcm+e(5xTpCYSp(g$5b;l1QbFgy%z*mW)F46pQ#M< zE+lw^$uS4hv zfI{sA{dGs^Qq5UsDIdI?cP`yl@d_>rCwE_~2AFEaiK_tu1GCOxit!m41jbOE!PMV% z2Em6&s56+7Z_)|WN@qYyAK>6{X+mE~)`S$OVUus;#0`N^BQ>FFX_$y31{4+pg0{ih zU^bfyYlvTUuq7IaX*NaenRXgrI(Q5(?+z>P4lC~tEANgduQcEQ>8`4;56VGe*jWB8 zoeXug+NlWw$O|1VZZSl4H&Nd^s}0v8ByEI?NAxsIMs2d#9=>65MhnSs36)mJ<;KK#_%1xcGpmI|6`wYx)$Y#(3Oqo{;zg~ zupI(Z9UT5QMLUZdA!&E0H_lfXZA&psQ(> znbJP9ia_)045oB-!P^+nNR9vyny~OfoeYVGW>I*YfRtebaAH0YSd zkX4ndXl7{S5II=E5>>l$(_zId)hR8Z=FM?cK?OLb;>4^c^r++-Lcit`wyWeY)A1%3 zoRXj=hRhCUvnGz5_|gNIX}gN~jK?VwW+R~H@* zzO2b%^=ucuV+p4p(R%C`Ij24c=boV76sI5X?8WKW6UpE~+fr}ZBnKxa-!`1*s;A7m$m!B<29{wP;Lt7X*hZPEI{Yh<_!Q?coNtBI z!~E_1z;QrBus{FLKeR#ii(MSQaPVLG$LHAoSjN{dNnPgO5=QWnn^wnZ7PfYGwiGrL zZtd>f)YsF{)KXlyx-!42u)lY6Uw=#UhPK|$tqr{``JIZLzqYtwmXntsmf6$M(7&O( zx3duc?69!Ep?70Te_>yLXCeQgUUwIh(@Q#LY};1Y+R#y0+uLOPpKiB6wcOO%*VY%T zdv$krM_=K_rl$Oc-p>54?yi>nmWIZ*{5kn0MI}Y~XP4w}?!iAmMEAq93){MyIyN`A z6n5e*iW(JY4+c;_QIAeZA~qOOLUKdJ^61n&%s99_z{LP{&isC>}xG( zqWh`ws>28sv~mHcYi`-lu(_kZuK%2#mOk*{v2}g@&3OMc2sh-rdt2(7I;T(9@-S(C zOD816yTHYTzmPLawrz`G3?r2?XcAJ0j=@?qyR+eV>fD1#|3#DRvA#)4|^m{ws#t4A@>zi5(A-xPTptvKdGw=hzujK$X}&P%|l4 z0GVPt2jt|efSs8RGRw{c8SAk|1?({@bqcsCXwPabu#>=yiPG@t)~+ zio!y(fPJ&@UT-vIEh$i%x!edvAsf7)Y~Z)O53*gi3z}N5^%dCGXgkhh2Hd8dVcq33 z(VxuZki@Hffe49vhDi)YN!&L~;*}_g2OvSidX)vVCND>6vS(N=FGbbz&@hP~MM*p| zOyc<{iN}UX9Eg&591_qqi{t8cd8{8q=sCf9Qs*Z-`Oid@pI|*5mH$*!{{E=^eNp+( zM&&=Q@;O)@>v%4Oek`go6kun9%03d6|0A6r?CjpC&i**6{DV>D ze-f4dKve!KQTg{p+9jA{8>mW6B5Y5iMDGSbSX&uJ-|# zWF*=)!(OR1nt=muPyl5aV;JwO=?Flh?ZgQdf|!+SCt7C%kulbjY>&x65`0SUHND z+4h)XG_EQ}CE=}Sd;E6FG&X4Y%<0E%Ypk^C^H^&T_pE7wK!hzgx_S)lv6-kTGdbC> z0yJW|?I}5(!(+$L(AEJ1!wf1u$4*HDqofEuv8FLNTDM*lpspNnv^1)~9tk4WvmAX+ z1{N}w>!lzU21%O$UZ%}>9gB#cE68;r9_xs0-T58rWvS zHx7FP7^{J81-3_}eF58}4KV`sc?&$*Rmz*Zo_U~?tQ%0uLcijy-=ZYj9Kkg>*G}`K zSm$yQPDGNL;gy>*adTKZ@|;$K|jz7&neP^gVT#u1=c^dVl8Ju z>oXLBg2_umbxzlFGhk!fVm(z=u*_&XgTq~{j1-HRjN;ZusKhLy=iCJ6O0u3aXn^G8 z8i!BwLvwfS^K6uq$D{a=8H3fA%yz(@x7kvxT56 zF7h60hW0nBJfI}NIYk-3V=WEXsVI|{X{TnOszNoEI?UMc&y(R!_JpOLY{*Pd<6wG9 zl}kc*FOuVt5j)8qZ=FLrOBpT}ir7|yo-&q)n;i7^EIm31nACXDNLUe34sJShA&Oc% z&46ctZI4zCYI#anug7}-Li~42Zohnb!^Dmz^uq?(%aBU4UW73@`nF#?VvaqA8?rJx z-g+ozu55?q?ZW;g*-p=Zfisb`k~rD)B*c{PEJZ~RN=5L)cr@X$9^v4ijd(aqbgSQo zFlN<{VN%K1>`z&S=MQ^$#^ z$IWH8{fZZ!wy=SnV2}H<&Yc3gd8{6I4x$4F9UIFDDg{l5)6{O)%98v-@{;XhLV`Vo zAcC-iIWej~J7Fj6p{ZG%XNGOT13Z}wpw0N|fv51|%&6cEz6yq7+Ha2snk}QDC}GvY8ptZNTs0@H5cb zrhvV+zz$5bo4B4`G{asVus7*hGcnm-4vF&vTtm1j=($;jW=wk-#*9;IKBmD~J7c_^ z%7${)q_7bMuyRB#rQAZoQgm7~Gs;p&N9jR>p>Q=b4hu3C=t=gtG%#}1IOSnfuwKy` zG!p$qUDW8Qji}2|eH1YZmbvMo0!Fm1rI_xh7_-v0r+NxTLtPW)x#)YdZT|$&#h!rY zI?r6snFw>_qx6&lUUSX0li6CK42c9628m+Kv25Fmtq(t80JW{c@`zF3vV{B5&DFsYBP0uStS48^?@`v4b#^RwH~uzztkp}i7mma7@73CQ5sH-d z*mR+96HRmOXtn5ht=_;=tWwD)_9blvQ9gRirgdSUBs(tCHa8YT1b_#j^NDjmMU|XR z92prHtX$amaoglUQ4=I5k{TfT=)W4E1A2hU0vw+g8WiiCLwx0ld5vI~a`w>tWh0qGD0Jom6J%bJx5JT*;(;?yYbk0Nm56HEC4XCz=}rn0ztxJIiF zJ5_No7z(8jToF^|+gys8`Y~cEer97xtXLE3Zl&=N?=4)z!FbejI%Sur%ePt4gMtbW zmKh6K3b#gJ?Hsj%k3%zw7~?o=8;9Z)vtV?U8Z->VMz{j!N%;=SxyZm$k>j!>rrl+Fq@9Sh3}1hT?^*W!>+Da)=$J*9NWiSX3Nq6kM{8JE zp^oni85k*aVk8GF$L1aYU*F?{j!`PpMXfP|+A$u!D4-l;DTUNWXo{-2i7?14#y$-sBMuAB=?G3-M;YX}(}gjx4dO87(v(Fu@1!weaEqJCxR7#GY#h zKw?vZUI#o|WlO=0MliZz*-1r-jsh-v*fu9vo1z!JU!Xe(3W(^D%iS$###X_iz#fkg z&BwJZH>aGO!F7xVMsP)gve8g|3)m`F6xgSu0~Y?#I3ZxS1Khy?qT@NHQJxyqGlRi= zX=Df~mgsS=vXwTQL=P2W=rEiq=~DU!r9k;Q1jLTHip)QkAc^c;}FM(I# znp-zEz2M(h&oF3DK#BP}I<7Jp)23Q=HP9TEG)d!}puW$yby2MpNbu^od0mK@gsBQ{du4j9l582DEXhz-S0bfkI=kxKRx zAs1A;iBe|)8}5QCc46kKh0K|`nCmigu}2H@&2rgRWCBw%ZEv>PzosMLPKI0N_>)IF z8#HSSrbG4w?8U3p;(_h(2J8i3>o{y}y;ul&k&BxP=^lJlptrh($`O55LLll&Z(qpF z@vvj`BJuLS&VFT_3Tv`M;LZuR7qN?Tqid%Bc%dL2okt*?M;qV{jfOJ;hyhg^xO}*C z><+>dyQqW{wJ3b`_*{?4RnF=m+d6nEinyloLqb8&)@rD*(ld{f6}t%)8?<$*Fb@8c zbc`s&b@v!MbqWMBu{*#uOuDs&7bYASwbYD3zCRoH0n}}gOk5-Tu^wYOPA_oQp6pyZ zcRW~G|8@mhK5PX-)O+e8T(zpk)T+tFz8F=Hv(v0|xRcCOK?+kp+4lNXn=y=Y>u)fz zWx$POy%$j%R2r}JILnzYVmZ55-H8~!bH6Eqf9iteb*C<Q*nQTC{S_ zYGYwTf5U2g&1iJu8^oUeUW0P0+nP32_O_hOjD;;5jGj~Rb>GU({pFiCY^a9JlCHLX zqq3u6V{Ly!|K>hpVby{<@*Mf7wrbbZRxetyaM41er>dosm9+F4b#~(mM~hL7Zy{TZj_$6F0shNi)L*q`U0p;2b;DGxS~#O_K~qyFzII*I)y!5~ zn>!oYxqVcru-sZjq9lb3L&F55a?rUvn zHkMRY)EMYs9lkzq?yGCinRPunS6Mx1`o6Y}T`kRlrq+gDe6g&2CFC14oVK_@^PJky zv4JM4Xz1u@Y-rk~2T)bDxl1bIaIykeG~^vDTvZ%Vbj^aA)nN{yMjxe0lvf+- zC|%2l=uKa1cW=LP(;k%SYc)74if3Y41nXAiBj&>DB^Ar+Dr*+4sw*x!^>m|A&8WV% zb6Y}%sE{s0uN2>3($(Cu?Km<l3Plpth7R?Ui^|o}h zU`kC6UE-sa@S0dcf^(-Q3y8+|Irhz=n?QhW>m!V=}L=dqY1M z4ZTgR^YD1dmf8881)Nn=oBL31V<3NJUmzckHq2{6v-unF7)nEaUrQ6sjf@Q&Hg~CX zIIpWazj1S0M}J!v1eE#mTe>!PQrxj@zH&I{ftCN^V;)VN4Vzl(*kp5EOMhQqT~BZK zmbN} z(2p_1@bTkEWF3o*)oqba%ns`fD4uv~^)x zVUjFaUD3T6OubsEV4xPYnnnDt)V1nym@ov1UW{x}v)|D(py1$5PMD#exp30iBF4HH-Ow+k0dmfx%PTiX~l+ zZK(&B&&_?II4o)}+}PJ25#TAyabM7gSY$Z5wes;JW5KE6n}MG0jt=+@o{nn4nyg!E z=~XMVQ;FUY)ht@Da6L?hfoaC9UfH#{Tlt%`q=m}Y)UH=<-rK@3#)?nm=oQtNI%*=G zhB0WNEtIL@F7!v8Rf#&FvUVw~d`(<Llx<897 zUs^9p7J)mqphtsz<|Bh{62#?~oYF>0*EzJZ7xXJ3CqR}(#rvJGwh zE4#SLuIcJ;>u_QjHv-xxoh^h82qAx0(S!b`Dh=wfq-K>iNG}3uzlub`P@tBQpd<#5 zE*j?X!oeI5`;D{)X(wOw<-^Qtf6P;eL1yECHWa zaj;9pLCmaLx|*}WkKB=l_&V4)RR#G$*J4;va}qmOXHs$#tc?!6up5WA*t)Y5kuJn- zi!xlW+OP>j-lCeCmC;Iy_bWT0N*zVT5}~=O_tZue1C3rQBKJqsU&kD^y;{*&jkUFJ zb@!rPUIu9M$_B`wL)5I5sS)z<;v(*&>aa-ZPQU=lLn@ck-6U8a*KpDWN(W;~*tqLn z>rGzJrO?XpMoTZ^+A}%jrCA0cz2l4wr>YfrU)XY_qEkeNwKG*U^fBIuf5>`TgT)-H z18PT!unVNHX^Ot97f1{13L|0@9Y~fmXB62g>W{kWtg2m7)6liC zMPIKu7lJ_zmvlu0`=-8Lb!l2rRjfj)^aH_40!X8pb4{`*(ANXQE+@2?Em^#%c6C@6 zm)EUXA<s=9B`40BuDv0=Pin0TULEOS zj#fvFGz}18K*kEVvP_v8lUS%u*0@G`oZwYC%hog zzoz;E2fhk8KLPJy56E-*gzY#NB&6(cUt(SAy2d;wE+KJ2Ja|5w&T#;VfI|TJyN&XM zz*k~+crWo>YG3BQ+`0l~Bg*hm;gqm4XCl2IA@M8j9o8lPPPXJfkmYY~P_Lu^Fdf4C zL7n_Konsmj3ld^>P@iyJ`FocW)#*W*@H(UErCH)&f2?H z#U=RQUEz8iuY3SyoJ0ETjBmB$pW%Js?@FZKi~e1|{}b7lrC#(~+4|q<9j?>%P+88a zh(2>H!t072XZl|`=A`Em`%?F1*5&_hoWt9V9@p@(*spZ*hj~w=Pej`$yx!=%@Ot@s zo@l=e@89vpc)@fi)4<=j;D6X5Y0u8DDyUkpWQE~L@>a!KquhRTv^f@Ew~aGX&2)E` zS%6j|k8pq0)u^S5*QpjAVM zghlNfmk}n-`ZA;BGo$jeqw=$(@^ho|bEEPnMdjlk?}QzeF*z!KveNhZpbVo{ z=&&%`Bm2kSOotB3$dAg;4`m`{8HG{#g{r;%L8-!EiU|v^(2H!3zhDg=7Jii+nO_ph zM9MNsqw-5td;DJ+`Y3f03N2$sl>CgS{8>@?v!e3nMCH$k%0D?O|KzCrc~SZEl)j;O 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 literal 0 HcmV?d00001 diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/provisioning/mx78_armor_provision.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/provisioning/mx78_armor_provision.h new file mode 100644 index 00000000000..6c55efd3812 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/provisioning/mx78_armor_provision.h @@ -0,0 +1,288 @@ +/* + * Copyright (c) 2020-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _MX78_ARMOR_PROVISION_H +#define _MX78_ARMOR_PROVISION_H + +#include +#include +#include "../mx78_armor.h" + +#ifdef __cplusplus + extern "C" { +#endif + +/** + * \file mx78_armor_provision.h + * + * \brief This file describes the structures of mx78 series secure Flash + * provisioning data template + */ + +#define PROVISION_INFO_SIZE 0x80 +/**The template structure of provisioning data blob +----------------------------------------------------------- +| Magic | +----------------------------------------------------------- +|Sub header num |Sub header size | Version | +----------------------------------------------------------- +|Provision_disable | ID2 |ID1 |ID0 | +| Reserved | Total size | +----------------------------------------------------------- +| Sub header 1:application info | /// app_info id; app_info num; each app_info size; app_info storage flag +----------------------------------------------------------- +| Detailed application info | /// app_info id; +| description | [app_id0, zone_id0, root_key_id0];[app_id1, zone_id1, root_key_id1] +----------------------------------------------------------- +| Sub header 2:key derivation info | /// key_info id; key_info num; each key_info size; key_info storage flag +----------------------------------------------------------- +| Detailed key derivation info | /// key_info id; key exchange type: 00:send key directly to the other party +| description | 01:DH +----------------------------------------------------------- +| Sub header 3:counter info | +----------------------------------------------------------- +| Detailed counter info | /// counter_info id; +| description | [counter_id0, counter_init_value0];[counter_id1, counter_init_value1] +----------------------------------------------------------- +| Sub header 4:configure info | /// configure_info id; configure_info num; each configure_info size; configure_info storage flag +----------------------------------------------------------- +| Detailed configure info | /// configure_info id; +| description | +----------------------------------------------------------- +| Sub header 5:lock info | /// lock_info id; lock_info num; each lock_info size; lock_info storage flag +----------------------------------------------------------- +| Detailed lock info | /// lock_info id; +| description | +----------------------------------------------------------- +As it doesn't need to store the whole provisioning data, the size of +provisioning data to be stored in memory is less than received provisioning data +blob's size. +The template structure of provisioning data in memory is similar to above +provisioning data blob's structure. + +The template structure of provisioning data stored in memory +----------------------------------------------------------- +| Magic | +----------------------------------------------------------- +|Sub header stored num |Sub header size | Version | +----------------------------------------------------------- +|Provision_disable | ID2 |ID1 |ID0 | +| Reserved | Total stored size | +----------------------------------------------------------- +| Sub header 1:application info | /// app_info id; app_info num; each app_info size; app_info storage flag +----------------------------------------------------------- +| Detailed application info | /// app_info id; +| description | [app_id0, zone_id0, root_key_id0];[app_id1, zone_id1, root_key_id1] +----------------------------------------------------------- +| Sub header 2:key derivation info | /// key_info id; key_info num; each key_info size; key_info storage flag +----------------------------------------------------------- +| Detailed key derivation info | /// key_info id; key exchange type: 00:send key directly to the other party +| description | 01:DH +----------------------------------------------------------- +| Sub header 3:lock info | /// lock_info id; lock_info num; each lock_info size; lock_info storage flag +----------------------------------------------------------- +| Detailed lock info | /// lock_info id; +| description | lock_type:counter/datazone/key/... +----------------------------------------------------------- +*/ + +#define ARMOR_APP_INFO_MAX_NUM 16 +#define ARMOR_LKD_INFO_MAX_NUM 8 +#define KEY_INFO_MAX_NUM 16 +#define MC_INFO_MAX_NUM 16 +#define MC_MAX_SIZE 4 +#define CFG_INFO_MAX_NUM 0x60 + +/** + * Sub items' header id of ArmorFlash provisioning information. + */ +typedef enum { + SUB_ID_DATA_CONFIG_INFO = 0, + SUB_ID_KEY_CONFIG_INFO = 1, + SUB_ID_CNT_CONFIG_INFO = 2, + SUB_ID_APP_INFO = 3, + SUB_ID_KEY_INFO = 4, + SUB_ID_MC_INFO = 5, + SUB_ID_LOCK_INFO = 6, + SUB_ID_MAX_NUMBER, +} SubHeaderId; + + +/** + * \struct provision_sub_item_header_t + * + * \brief Structure holding each sub item's header of ArmorFlash provisioning + * information. + */ +typedef struct { + uint32_t id: 8, /*!< Sub item's header id */ + num: 8, /*!< Sub item's number */ + size: 15, /*!< Sub item's size */ + store: 1; /*!< Sub item's storage flag */ +} provision_sub_item_header_t; + + +/** + * \struct provision_major_header_t + * + * \brief Structure holding the major header of ArmorFlash provisioning + * information. + */ +typedef struct { + uint8_t magic[4]; /*!< The magic bytes of this + provisioning information */ + uint32_t minor_version: 4, /*!< The version of this provisioning information */ + major_version: 4, + sub_header_num: 8, /*!< The number of this provisioning + information's sub headers */ + total_size: 16; /*!< The total size of this + provisioning information */ + uint8_t id[4]; /*!< The secure Flash id */ +} provision_major_header_t; + +#define SFPI_MAJOR_HEADER_SIZE (sizeof(provision_major_header_t)) +#define SFPI_SUB_HEADER_SIZE (sizeof(provision_sub_item_header_t)) + +/** + * \struct mx_app_data_t + * + * \brief Structure to store a certain application item's + * provisioning information. + */ +typedef struct { + uint32_t app_id; /*!< Application id */ + uint32_t key_id; /*!< Application binded crypto key id */ + uint32_t zone_id:8, /*!< Application binded security zone id */ + mc_id:8, /*!< Application binded monotonic counter id */ + reserved:16; +} mx_app_data_t; +/** + * \struct mx_app_info_t + * + * \brief Structure holding provisioning information for applications. + */ +typedef struct { + provision_sub_item_header_t header; + mx_app_data_t app_data[ARMOR_APP_INFO_MAX_NUM]; /*!< Buffer holding + application items' + provisioning + information */ +} mx_app_info_t; + +/** provision for lock_info + * + * DWORD 0: [07:00] ID, [31:08] reserved + * DWORD 1: [07:00] config_regs, [15:08] ind_key_lock, + * [23:16] ind_key_dis, [31:24] reserved + * DWORD 2: [15:00] datazone lock(bitwise), [31:16] provision (bitwise) + **/ + +/** + * \struct lock_info_t + * + * \brief Structure holding provisioning information for + * ArmorFlash's lock down configuration. + */ +typedef struct { + provision_sub_item_header_t header; + uint8_t lock_data[ARMOR_LKD_INFO_MAX_NUM]; /*!< Buffer holding lock + down information */ +} lock_info_t; + +/** provision for key_info + * + * DWORD 0: [07:00] ID, [15:08] key number, [31:16] Reserved + * key 0 + * DWORD 1: [31:00] key id + * DWORD 2: [31:00] key derive message + * DWORD 3: [07:00] key derive params suite, [23:08] key length (in bytes), + * [31:24] key inject type + * key 1 ~ (key number -1) + **/ +/** + * \struct key_data_t + * + * \brief Structure holding provisioning information used to derive a + * certain root key of ArmorFlash. + */ +typedef struct { + uint32_t key_id; /*!< Root key id */ + uint32_t derive_message; /*!< Specific information for root key + derivation */ + uint32_t key_derive_suite: 8, /*!< Key derivation cipher algorithm */ + key_exchange_type: 4, /*!< Key exchange type: DH or directly share to the other party */ + key_len: 8, /*!< Derived root key length in bytes */ + zone_id: 8, /*!< Corresponding */ + inject_type: 4; /*!< The mode of synchronizing ArmorFlash's + root key */ +} key_data_t; +/** + * \struct key_info_t + * + * \brief Structure holding provisioning information for ArmorFlash root keys + * derivation. + */ +typedef struct { + provision_sub_item_header_t header; + key_data_t key_data[KEY_INFO_MAX_NUM]; /*!< Buffer holding root keys' + provisioning information */ +} key_info_t; + +/** provision for mc_info + * + * DWORD 0: [07:00] ID, [15:08] mc number, [31:16] reserved + * mc 0 + * DWORD 1: [31:00] mc value + * mc 1 ~ (mc number -1) + **/ +typedef struct { + uint8_t value[MC_MAX_SIZE]; // power of two, '0' is not supported +} mc_data_t; +/** + * \struct mc_info_t + * + * \brief Structure holding ArmorFlash monotonic counters' provisioning + * information. + */ +typedef struct { + provision_sub_item_header_t header; + mc_data_t mc_data[MC_INFO_MAX_NUM]; /*!< Buffer holding monotonic counters' + provisioning information */ +} mc_info_t; + + +/** + * \struct config_info_t + * + * \brief Structure holding provisioning information used to configure ArmorFlash. + */ +typedef struct { + provision_sub_item_header_t header; + uint8_t config_data[CFG_INFO_MAX_NUM]; /*!< Buffer holding ArmorFlash + configuration' provisioning + information */ +} config_info_t; + +int32_t mx78_armor_provision_perform(mx78_armor_context *mx78_armor_ctx, + uint8_t *provision_data_blob, + uint32_t data_size, + uint8_t *provision_data_buf, + uint32_t *data_store_size); +#ifdef __cplusplus +} +#endif + +#endif /* _MX78_ARMOR_PROVISION_H */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/secureflash_layout.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/secureflash_layout.h new file mode 100644 index 00000000000..b74b3f50ebb --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/macronix/armorflash_mx78/secureflash_layout.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _SECUREFLASH_LAYOUT_H_ +#define _SECUREFLASH_LAYOUT_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +/** \brief Secure Flash name string. */ +#define SECURE_FLASH_NAME "mx78x64" +#define SECURE_FLASH_SECURITY_STORAGE_CAP (1) +#define SECURE_FLASH_SECTOR_SIZE (0x1000) /*!< The size of secure Flash's sector */ +#define SECURE_FLASH_ERASED_VALUE (0xFF) /*!< The erase value of secure Flash */ +#define SECURE_FLASH_PROGRAM_UNIT (0x01) /*!< The program unit of secure Flash */ +#define SECURE_FLASH_READ_UNIT (0x01) /*!< The read unit of secure Flash */ +#define SECURE_FLASH_PROGRAM_SIZE (0x100) /*!< The max data size of each secure program packet */ +#define SECURE_FLASH_READ_SIZE (0x100) /*!< The max data size of each secure read packet */ +#define SECURE_FLASH_SIZE (0x800000) +#define SECURE_FLASH_ZONE_SIZE (0x80000) +#define SECURE_FLASH_ZONE_NUM (SECURE_FLASH_SIZE / SECURE_FLASH_ZONE_SIZE) + +#define SECURE_FLASH_MAX_PUF_SIZE (0x20) +#define SECURE_FLASH_MAX_TRNG_SIZE (0x20) +#define SECURE_FLASH_MAX_MC_SIZE (4) +/** \brief If multi-client(or multi-application) isolation is defined, + * the number of clients, + * and the layout of secure Flash memory region should be defined. + * Here takes four applications for example. + */ +#if defined(MULTI_CLIENT_ISOLATION) /*!< Enable multi-client isolation */ +/* multi-client secure flash layout*/ +/* Negative client id stands for clients from nspe; + * Positive client id stands for clients from spe */ +#define SECURE_FLASH_CLIENT_NUM (3) +#define SECURE_FLASH_CLIENT0_ID (-2) /*!< Client id 0:0xfffffffe */ +#define SECURE_FLASH_CLIENT0_AREA_START_ADDR (0) /*!< Start address of security memory region allocated for Client id 0 */ +#define SECURE_FLASH_CLIENT0_AREA_SIZE (SECURE_FLASH_ZONE_SIZE) /*!< The size of security memory region allocated for Client id 0 */ +#define SECURE_FLASH_CLIENT0_SECTORS_PER_BLOCK (4) +#if (SECURE_FLASH_CLIENT_NUM > 1) +#define SECURE_FLASH_CLIENT1_ID (-3) /*!< Client id 1:0xfffffffd */ +#define SECURE_FLASH_CLIENT1_AREA_START_ADDR (SECURE_FLASH_CLIENT0_AREA_START_ADDR + SECURE_FLASH_CLIENT0_AREA_SIZE) /*!< Start address of security memory region allocated for Client id 1 */ +#define SECURE_FLASH_CLIENT1_AREA_SIZE (SECURE_FLASH_ZONE_SIZE) /*!< The size of security memory region allocated for Client id 1 */ +#define SECURE_FLASH_CLIENT1_SECTORS_PER_BLOCK (4) +#endif +#if (SECURE_FLASH_CLIENT_NUM > 2) +#define SECURE_FLASH_CLIENT2_ID (3002) /*!< Client id 2:0xbba */ +#define SECURE_FLASH_CLIENT2_AREA_START_ADDR (SECURE_FLASH_CLIENT1_AREA_START_ADDR + SECURE_FLASH_CLIENT1_AREA_SIZE) /*!< Start address of security memory region allocated for Client id 2 */ +#define SECURE_FLASH_CLIENT2_AREA_SIZE (SECURE_FLASH_ZONE_SIZE) /*!< The size of security memory region allocated for Client id 2 */ +#define SECURE_FLASH_CLIENT2_SECTORS_PER_BLOCK (4) +#endif +#if (SECURE_FLASH_CLIENT_NUM > 3) +#define SECURE_FLASH_CLIENT3_ID (3006) /*!< Client id 3:0xbbe */ +#define SECURE_FLASH_CLIENT3_AREA_START_ADDR (SECURE_FLASH_CLIENT2_AREA_START_ADDR + SECURE_FLASH_CLIENT2_AREA_SIZE) /*!< Start address of security memory region allocated for Client id 3 */ +#define SECURE_FLASH_CLIENT3_AREA_SIZE (SECURE_FLASH_ZONE_SIZE) /*!< The size of security memory region allocated for Client id 3 */ +#define SECURE_FLASH_CLIENT3_SECTORS_PER_BLOCK (4) +#endif +#endif +/* Multi client isolation disabled */ +#define SECURE_FLASH_START_ADDR (0) /*!< Start address of secure Flash's security memory region */ +#define SECURE_FLASH_DEFAULT_CLIENT_AREA_SIZE (SECURE_FLASH_ZONE_SIZE *2) /*!< Default client area size equals secure zone size */ +#define SECURE_FLASH_SECTORS_PER_BLOCK (4) /*!< The number of sectors of per secure Flash block */ + +#ifdef __cplusplus +} +#endif + +#endif /* _SECUREFLASH_LAYOUT_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_provisioning_impl.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_provisioning_impl.h new file mode 100644 index 00000000000..e10b25645b2 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_provisioning_impl.h @@ -0,0 +1,82 @@ +/* + * Copyright (c) 2020-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _VENDOR_PROVISIONING_IMPL_H_ +#define _VENDOR_PROVISIONING_IMPL_H_ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +/* secure Flash provisioning error definition */ +#define JEDEC_ERROR_PROVISION -0x2001 +#define JEDEC_ERROR_GET_PROVISION_INFO -0x2002 +#define JEDEC_ERROR_PROVISION_EXCHANGE_KEY -0x2003 +#define JEDEC_ERROR_PROVISION_SYNC_SALT -0x2004 +#define JEDEC_ERROR_PROVISION_GEN_ROOT_KEY -0x2005 + +typedef enum { + ITEM_APP_INFO, + ITEM_LKD_INFO, + LAST_ITEM_TYPE +}provision_item_type_e; + +/** + * \brief vendor specific provisioning operations + * + */ +typedef struct { + /** + * \brief Perform secure Flash provisioning and verify. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] provision_data Input provisioning data blob + * \param[in] data_length The size of input provisioning data blob in bytes + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*perform_and_verify)(void *vendor_ctx, + uint8_t *provision_data, + uint32_t data_length); + /** + * \brief Get secure Flash provisioning item data. + * + * \param[in] vendor_ctx The vendor specific secure Flash context + * \param[in] item_type Provisioning item type + * \param[in] provision_data_buf Buffer to store provisioning item data + * \param[in] provision_data_size Buffer size + * \param[out] item_data_num The number of item entries + * \param[out] provision_data_act_size Actual size of provisioning item data + * \return JEDEC_ERROR_NONE if successful, + * or a specific JEDEC_ERROR_XX error code + */ + int32_t (*provision_item_get_data)(void *vendor_ctx, + provision_item_type_e item_type, + uint8_t *provision_data_buf, + uint32_t provision_data_size, + uint8_t *item_data_num, + uint32_t *provision_data_act_size); +} vendor_provisioning_op_t; + +#ifdef __cplusplus +} +#endif + +#endif /* _VENDOR_PROVISIONING_IMPL_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_secureflash.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_secureflash.h new file mode 100644 index 00000000000..ae053d35800 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_secureflash.h @@ -0,0 +1,37 @@ +/* + * Copyright (c) 2020-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _VENDOR_SECUREFLASH_H_ +#define _VENDOR_SECUREFLASH_H_ + +#include "vendor_secureflash_defs.h" + +#ifdef __cplusplus +extern "C" { +#endif + +extern const secure_flash_info_t macronix_mx78_info; + +const secure_flash_info_t *flash_info[] = { + ¯onix_mx78_info, + /* Add secure flash info here */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* _VENDOR_SECUREFLASH_H_ */ \ No newline at end of file diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_secureflash_defs.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_secureflash_defs.h new file mode 100644 index 00000000000..dde3e784358 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_secureflash_defs.h @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2020-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _VENDOR_SECUREFLASH_DEFS_H +#define _VENDOR_SECUREFLASH_DEFS_H + +#include "../JEDEC_security_HAL/jedec_security_hal.h" +#include "vendor_provisioning_impl.h" +#include "secureflash_layout.h" + +#ifdef __cplusplus +extern "C" { +#endif + +#define SECURE_FLASH_MAX_ID_LEN 6 + +/*! + * \struct security_feature_t + * + * \brief Structure to store the security features of secure Flash. + */ +typedef struct { + uint32_t security_storage: 1, /*!< Support security storage of data */ + rpmc: 1, /*!< Support replay protection monotonic counter */ + uid: 1, /*!< Support unique id */ + rng: 1, /*!< Support random number generator */ + puf: 1, /*!< Support physical unclonable function */ + reserved: 27; /*!< Reserved */ +} security_feature_t; + +/*! + * \struct cipher_suite_t + * + * \brief Structure to store the cipher suites of secure Flash. + */ +typedef struct { + uint32_t key_exchange_alg: 8, /*!< Key exchange algorithm */ + key_derive_alg: 8, /*!< Key derivation algorithm */ + encryption_alg: 8, /*!< Encryption algorithm */ + signature_alg:8; /*!< Signature algorithm */ +} cipher_suite_t; + +/*! + * \struct key_size_t + * + * \brief Structure to store secure Flash various keys' size. + */ +typedef struct { + uint32_t session_key_size: 16, + private_key_size: 16; + uint32_t public_key_size: 16, + preshare_key_size: 16; + uint32_t salt_key_size: 16, + root_key_size: 16; + uint32_t rpmc_root_key_size: 16, + rpmc_hmac_key_size: 16; +} key_size_t; + +/*! + * \struct architecture_t + * + * \brief Structure to store secure Flash architecture. + */ +typedef struct { + uint8_t erase_value; + uint32_t secure_read_size; /*!< Security read size in bytes */ + uint32_t secure_program_size; /*!< Security program size in bytes */ + uint32_t secure_erase_size[4]; /*!< Security erase size in bytes */ + uint32_t regions_min_secure_erase_size; /*!< Minimum security erase size in bytes */ + uint32_t sector_size; + uint32_t secure_zone_number; /*!< Secure data zone number */ + uint32_t secure_zone_size; /*!< Individual data zone size */ + uint32_t secure_zone_total_size; /*!< Whole data zones size */ +} architecture_t; + +/*! + * \struct flash_profile_t + * + * \brief Structure to store secure Flash profile. + */ +typedef struct { + security_feature_t security_feature; /*!< Secure Flash security features */ + cipher_suite_t cipher_suite; /*!< Secure Flash cipher suites */ + key_size_t key_size; /*!< Secure Flash keys size */ + architecture_t architecture; /*!< Secure Flash architecture */ +} flash_profile_t; + + + /*! + * \struct secure_flash_info_t + * + * \brief Structure to store secure Flash specific information. + */ +typedef struct { + char *name; /*! Secure Flash name */ + uint8_t id[SECURE_FLASH_MAX_ID_LEN]; /*! Secure Flash ID */ + uint8_t id_len; /*! Secure Flash ID length */ + flash_profile_t *flash_profile; + vendor_security_op_t *vendor_security_op; /*! Vendor specific security operations */ + vendor_provisioning_op_t *vendor_provisioning_op; /*! Vendor specific secure Flash provisioning operations */ + crypto_wrapper_t *crypto_wrapper; /*! Vendor specific crypto wrapper functions */ + void *vendor_ctx; /*! Vendor specific context */ +} secure_flash_info_t; + +extern const secure_flash_info_t macronix_mx78_info; + +#ifdef __cplusplus +} +#endif + +#endif /* _VENDOR_SECUREFLASH_DEFS_H */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor.c b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor.c new file mode 100644 index 00000000000..202ae8742d1 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor.c @@ -0,0 +1,384 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "vendor.h" +#include "../../vendor_secureflash_defs.h" + +extern crypto_wrapper_t vendor_crypto_wrapper; + +static int32_t vendor_secureflash_pre_create_session(vendor_secureflash_context *secureflash_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_create_session_packet(vendor_secureflash_context *secureflash_ctx, + uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_create_session(vendor_secureflash_context *secureflash_ctx, + uint32_t root_key_id, + uint32_t *session_key_id, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +#if defined(SESSION_CONFIRMATION) +static int32_t vendor_secureflash_pre_confirm_session() +{ + //TODO + return 0; +} +static int32_t vendor_secureflash_confirm_session_packet() +{ + //TODO + return 0; +} +static int32_t vendor_secureflash_post_confirm_session() +{ + //TODO + return 0; +} +#endif + +static int32_t vendor_secureflash_pre_terminate_session(vendor_secureflash_context *secureflash_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_terminate_session_packet(vendor_secureflash_context *secureflash_ctx, + uint32_t session_key_id, + uint8_t *packet,uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_terminate_session(vendor_secureflash_context *secureflash_ctx, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_pre_secure_init(vendor_secureflash_context *secureflash_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_secure_init_packet(vendor_secureflash_context *secureflash_ctx, + uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_secure_init(vendor_secureflash_context *secureflash_ctx, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_pre_secure_uninit(vendor_secureflash_context *secureflash_ctx, + uint8_t *nonce_in, uint32_t nonce_in_len, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_secure_uninit_packet(vendor_secureflash_context *secureflash_ctx, + uint32_t root_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_secure_uninit(vendor_secureflash_context *secureflash_ctx, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_pre_secure_program(vendor_secureflash_context *secureflash_ctx, uint32_t addr, + uint32_t session_key_id, jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_secure_program_packet(vendor_secureflash_context *secureflash_ctx, uint32_t addr, + const uint8_t *data, uint32_t len, uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_secure_program(vendor_secureflash_context *secureflash_ctx, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_parse_secure_program_response(vendor_secureflash_context *secureflash_ctx, + crypto_indicator_t *indicator, + uint32_t *bytes_programmed) +{ + //TODO + return 0; +} + +#if defined(SECURE_READ) +static int32_t vendor_secureflash_pre_secure_read(vendor_secureflash_context *secureflash_ctx, uint32_t addr, + uint32_t session_key_id, jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_secure_read_packet(vendor_secureflash_context *secureflash_ctx, + uint32_t addr, uint32_t len, uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_secure_read(vendor_secureflash_context *secureflash_ctx, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, + uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_parse_secure_read_response(vendor_secureflash_context *secureflash_ctx, + crypto_indicator_t *indicator, + uint8_t *data, uint32_t data_len, + uint32_t *bytes_read) +{ + //TODO + return 0; +} +#endif + +static int32_t vendor_secureflash_pre_secure_erase(vendor_secureflash_context *secureflash_ctx, + uint32_t addr, uint32_t len, uint32_t session_key_id, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_secure_erase_packet(vendor_secureflash_context *secureflash_ctx, + uint32_t addr, uint32_t len, uint32_t session_key_id, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_secure_erase(vendor_secureflash_context *secureflash_ctx, + uint32_t session_key_id, + uint8_t *resp_packet_buffer, + uint32_t *resp_packet_buffer_size, + crypto_indicator_t *indicator, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_parse_secure_erase_response(vendor_secureflash_context *secureflash_ctx, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +#if defined(SECURE_COPY) +static int32_t vendor_secureflash_pre_secure_copy() +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_secure_copy_packet() +{ + //TODO + return 0; +} +static int32_t vendor_secureflash_post_secure_copy() +{ + //TODO + return 0; +} +#endif + +static int32_t vendor_secureflash_pre_get_region_info(vendor_secureflash_context *secureflash_ctx, + uint32_t session_key_id, bool *session_key_valid_flag, + jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_get_region_info_packet(vendor_secureflash_context *secureflash_ctx, + uint32_t session_key_id, int8_t region_index, + uint8_t *nonce_in, uint32_t nonce_in_len, + uint8_t *packet, uint32_t *packet_len, + crypto_indicator_t *indicator) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_get_region_info(vendor_secureflash_context *secureflash_ctx, + region_ll_node_t *region_descr_p, jqueue_t *resp_queue) +{ + //TODO + return 0; +} + +#if defined(SECURE_REGION_MANAGE) +static int32_t vendor_secureflash_pre_secure_manage_region() +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_secure_manage_region_packet() +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_post_secure_manage_region() +{ + //TODO + return 0; +} +#endif + +static int32_t vendor_secureflash_packet_send(vendor_secureflash_context *secureflash_ctx, + uint8_t *outdata, uint32_t outdata_len, + uint8_t *cipher_text, uint32_t cipher_text_len, + uint8_t *mac, uint32_t mac_len) +{ + //TODO + return 0; +} + +static int32_t vendor_secureflash_packet_receive(vendor_secureflash_context *secureflash_ctx, + uint8_t *indata, uint32_t indata_len) +{ + //TODO + return 0; +} + + +vendor_security_op_t vendor_secureflash = { + .vendor_id = VENOR_ID, + .pre_create_session = vendor_secureflash_pre_create_session, + .create_session_packet = vendor_secureflash_create_session_packet, + .post_create_session = vendor_secureflash_post_create_session, +#if defined(SESSION_CONFIRMATION) + .pre_confirm_session = vendor_secureflash_pre_confirm_session, + .confirm_session_packet = vendor_secureflash_confirm_session_packet, + .post_confirm_session = vendor_secureflash_post_confirm_session, +#endif + .pre_terminate_session = vendor_secureflash_pre_terminate_session, + .terminate_session_packet = vendor_secureflash_terminate_session_packet, + .post_terminate_session = vendor_secureflash_post_terminate_session, + .pre_secure_init = vendor_secureflash_pre_secure_init, + .secure_init_packet = vendor_secureflash_secure_init_packet, + .post_secure_init = vendor_secureflash_post_secure_init, + .pre_secure_uninit = vendor_secureflash_pre_secure_uninit, + .secure_uninit_packet = vendor_secureflash_secure_uninit_packet, + .post_secure_uninit = vendor_secureflash_post_secure_uninit, + + .pre_secure_program = vendor_secureflash_pre_secure_program, + .secure_program_packet = vendor_secureflash_secure_program_packet, + .post_secure_program = vendor_secureflash_post_secure_program, + .parse_secure_program_response = vendor_secureflash_parse_secure_program_response, + + .pre_secure_erase = vendor_secureflash_pre_secure_erase, + .secure_erase_packet = vendor_secureflash_secure_erase_packet, + .post_secure_erase = vendor_secureflash_post_secure_erase, + .parse_secure_erase_response = vendor_secureflash_parse_secure_erase_response, +#if defined(SECURE_COPY) + .pre_secure_copy = vendor_secureflash_pre_secure_copy, + .secure_copy_packet = vendor_secureflash_secure_copy_packet, + .post_secure_copy = vendor_secureflash_post_secure_copy, +#endif +#if defined(SECURE_READ) + .pre_secure_read = vendor_secureflash_pre_secure_read, + .secure_read_packet = vendor_secureflash_secure_read_packet, + .post_secure_read = vendor_secureflash_post_secure_read, + .parse_secure_read_response = vendor_secureflash_parse_secure_read_response, +#endif + .pre_secure_get_regions_info = vendor_secureflash_pre_get_region_info, + .secure_get_regions_info_packet = vendor_secureflash_get_region_info_packet, + .post_secure_get_regions_info = vendor_secureflash_post_get_region_info, +#if defined(SECURE_REGION_MANAGE) + .pre_secure_manage_region = vendor_secureflash_pre_secure_manage_region, + .secure_manage_region_packet = vendor_secureflash_secure_manage_region_packet, + .post_secure_manage_region = vendor_secureflash_post_secure_manage_region, +#endif + .packet_send = vendor_secureflash_packet_send, + .packet_receive = vendor_secureflash_packet_receive, +}; + +const secure_flash_info_t vendor_secure_flash_info = { + .id = {0xff, 0xff, 0xff}, + .id_len = 3, + .vendor_security_op = &vendor_secureflash, + .crypto_wrapper = &vendor_crypto_wrapper, +}; diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor.h b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor.h new file mode 100644 index 00000000000..370a1afd98a --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor.h @@ -0,0 +1,88 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _VENDOR_H_ +#define _VENDOR_H_ + +#include "../../../JEDEC_security_HAL/vendor_security_impl.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * Define your macros here + * + */ +#define VENOR_ID 0xFF + +/** + * Secure Flash supported security operations definition + * + */ +typedef enum { + SECURITY_READ, /*!< Security read */ + SECURITY_WRITE, /*!< Security program */ + SECURITY_ERASE, /*!< Security erase */ + MC_INCREASEMENT, /*!< Increase monotonic counter */ + MC_READ, /*!< Read monotonic counter */ + GENERATE_NONCE, /*!< Generate random number */ + CREATE_SESSION, /*!< Session creation */ + CONFIRM_SESSION, /*!< Session confirmation */ + TERMINATE_SESSION, /*!< Session termination */ + GEN_ROOT_KEY, /*!< Generate root key */ + SECURITY_GET_REGION_CONFIG, /*!< Get region configure in security */ + CFG_SECURITY_PROFILE, /*!< Set secure Flash security profile */ + SECURE_INIT, /*!< Initialize in security */ + SECURE_UNINIT, /*!< Unitialize in security */ + LOCK_DOWN /*!< Lock down */ + /* Add secure Flash operations here */ + //TODO +} vendor_secureflash_operation_type_t; + +/** + * \struct vendor_secureflash_security_operation_t + * + * \brief The structure holding secure Flash security operations' context. + */ +typedef struct { +//TODO +} vendor_secureflash_security_operation_t; + + +/** + * \struct secureflash_registers_t + * + * \brief The structure holding secure Flash registers' value. + */ +typedef struct { +//TODO +} secureflash_registers_t; + + +typedef struct { + const char *name; + //TODO + /* Add vendor context members here */ +} vendor_secureflash_context; + +extern vendor_security_op_t vendor_secureflash; + +#ifdef __cplusplus +} +#endif + +#endif /* _VENDOR_H_ */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor_crypto_wrapper/vendor_crypto_wrapper.c b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor_crypto_wrapper/vendor_crypto_wrapper.c new file mode 100644 index 00000000000..2c5c46b896b --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/TG424_3/vendor_impl/vendor_template/vendor_crypto_wrapper/vendor_crypto_wrapper.c @@ -0,0 +1,123 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 +#include +#include "../../../JEDEC_security_HAL/crypto_wrapper.h" + +#define KEY_HANDLE_NOT_LOADED (0) +#define MAC_SIZE +#define TAG_LENGTH +#define MAX_ROOT_KEY_NUM +#define MAX_SESSION_KEY_NUM +#define MAC_KEY_SIZE +#define ENC_KEY_SIZE + + +typedef struct { + //TODO +}session_key_id_t; + + +static int crypto_wrapper_init(void) +{ + //TODO +} + +static int crypto_wrapper_deinit(void) +{ + //TODO +} + +static int crypto_wrapper_algorithm_support(int alg, int index) +{ + //TODO +} + +static int crypto_wrapper_crypto_func(crypto_indicator_t *indicator) +{ + //TODO +} +static int crypto_wrapper_kdf(crypto_indicator_t *indicator, + uint32_t *output_key_id) +{ + //TODO +} + +static int crypto_wrapper_generate_random(uint8_t *output, uint32_t output_size) +{ + //TODO +} + +static int crypto_wrapper_ecdh_gen_key_pair(crypto_indicator_t *indicator) +{ + //TODO +} + +static int crypto_wrapper_ecdh_gen_shared_secret(crypto_indicator_t *indicator) +{ + //TODO +} + +static int crypto_wrapper_open_key(uint32_t root_key_id) +{ + //TODO +} + +static int crypto_wrapper_close_key(uint32_t root_key_id) +{ + //TODO +} +static int crypto_wrapper_destroy_key(uint32_t key_id) +{ + //TODO +} + +static int crypto_wrapper_export_public_key(uint32_t key_id, uint8_t *key_buf, + uint32_t buf_size, uint32_t *actual_size) +{ + //TODO +} + +static int crypto_wrapper_export_key(uint32_t key_id, uint8_t *key_buf, + uint32_t buf_size, uint32_t *actual_size) +{ + //TODO +} + +static int crypto_wrapper_import_key(uint32_t *key_id, uint8_t *key_buf, + uint32_t key_size, KeyLifeTime lifetime) +{ + //TODO +} + +crypto_wrapper_t vendor_crypto_wrapper = { + .init = crypto_wrapper_init, + .deinit = crypto_wrapper_deinit, + .algorithm_support = crypto_wrapper_algorithm_support, + .crypto_func = crypto_wrapper_crypto_func, + .key_derive = crypto_wrapper_kdf, + .generate_random = crypto_wrapper_generate_random, + .ecdh_gen_key_pair = crypto_wrapper_ecdh_gen_key_pair, + .ecdh_gen_shared_secret = crypto_wrapper_ecdh_gen_shared_secret, + .open_key = crypto_wrapper_open_key, + .close_key = crypto_wrapper_close_key, + .destroy_key = crypto_wrapper_destroy_key, + .export_public_key = crypto_wrapper_export_public_key, + .export_key = crypto_wrapper_export_key, + .import_key = crypto_wrapper_import_key, +}; diff --git a/storage/blockdevice/COMPONENT_SECUREF/include/SECUREF/SecureFBlockDevice.h b/storage/blockdevice/COMPONENT_SECUREF/include/SECUREF/SecureFBlockDevice.h new file mode 100644 index 00000000000..c476cfa3605 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/include/SECUREF/SecureFBlockDevice.h @@ -0,0 +1,312 @@ +/* mbed Microcontroller Library + * Copyright (c) 2023 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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 MBED_SECUREFLASH_BLOCK_DEVICE_H +#define MBED_SECUREFLASH_BLOCK_DEVICE_H + +#include "platform/SingletonPtr.h" +#include "drivers/SPI.h" +#include "drivers/DigitalOut.h" +#include "blockdevice/internal/SFDP.h" +#include "blockdevice/BlockDevice.h" + +#include "../../TG424_3/vendor_impl/vendor_secureflash_defs.h" + +#ifndef MBED_CONF_SECUREF_DRIVER_SPI_MOSI +#define MBED_CONF_SECUREF_DRIVER_SPI_MOSI NC +#endif +#ifndef MBED_CONF_SECUREF_DRIVER_SPI_MISO +#define MBED_CONF_SECUREF_DRIVER_SPI_MISO NC +#endif +#ifndef MBED_CONF_SECUREF_DRIVER_SPI_CLK +#define MBED_CONF_SECUREF_DRIVER_SPI_CLK NC +#endif +#ifndef MBED_CONF_SECUREF_DRIVER_SPI_CS +#define MBED_CONF_SECUREF_DRIVER_SPI_CS NC +#endif +#ifndef MBED_CONF_SECUREF_DRIVER_SPI_FREQ +#define MBED_CONF_SECUREF_DRIVER_SPI_FREQ 40000000 +#endif + +#define APP_INFO_MAX_NUM (0x10) + +/** Enum spif standard error codes + * + * @enum spif_bd_error + */ +enum securef_bd_error { + SECUREF_BD_ERROR_OK = 0, /*!< no error */ + SECUREF_BD_ERROR_DEVICE_ERROR = mbed::BD_ERROR_DEVICE_ERROR, /*!< device specific error -4001 */ + SECUREF_BD_ERROR_PARSING_FAILED = -4002, /* SFDP Parsing failed */ + SECUREF_BD_ERROR_READY_FAILED = -4003, /* Wait for Memory Ready failed */ + SECUREF_BD_ERROR_WREN_FAILED = -4004, /* Write Enable Failed */ + SECUREF_BD_ERROR_INVALID_ERASE_PARAMS = -4005, /* Erase command not on sector aligned addresses or exceeds device size */ + SECUREF_BD_ERROR_DEVICE_UNSUPPORT = -4006, /* Device id match failed */ + SECUREF_BD_ERROR_PROVISION_FLASH = -4007, /* Provision secure flash failed */ + SECUREF_BD_ERROR_GET_PROVISION = -4007, /* Get the information of provision failed */ + SECUREF_BD_ERROR_ILLEGAL_ACCESS = -4008, /* Operation illegal */ + SECUREF_BD_ERROR_CREATE_SESSION = -4009, /* Create sessopm failed */ + SECUREF_BD_ERROR_TERMINATE_SESSION = -4010, /* Terminate sessopm failed */ + SECUREF_BD_ERROR_SECURE_READ = -4011, /* Secure read failed */ + SECUREF_BD_ERROR_SECURE_PROGRAM = -4012, /* Secure program failed */ + SECUREF_BD_ERROR_SECURE_ERASE = -4013, /* Secure erase failed */ + SECUREF_BD_ERROR_INVALID_PGM_PARAMS = -4014, /* Program command invalid arguments */ + SECUREF_BD_ERROR_INVALID_READ_PARAMS = -4015, /* Read command invalid arguments */ +}; + +/** + * app_data_t + * + * Structure to store the pre-provisioned application & secure zone binding information. + */ +typedef struct { + int32_t app_id; /*!< The id of applications */ + uint32_t key_id; /*!< The id of crypto root keys */ + uint32_t zone_id:8, /*!< The id of security zone id */ + mc_id:8, /*!< The id of monotonic counter */ + reserved:16; +} app_data_t; + +/** + * app_info_t + * + * Structure to store the pre-provisioned application information. + */ +typedef struct { + uint8_t id; /*!< The id of app info */ + uint8_t num; /*!< The number of app_data items */ + app_data_t app_data[APP_INFO_MAX_NUM]; /*!< The detailed app_data */ +} app_info_t; + +/** + * session_info_t + * + * Structure to store secure Flash session information. + */ +typedef struct{ + uint32_t key_id; /*!< Root key id */ + uint32_t session_key_id; /*!< Session key id */ + uint32_t session_id; /*!< Session id */ +} session_info_t; + +/** + * secureflash_t + * + * Structure indicating secure Flash API layer informations + */ +typedef struct { + uint32_t _init_ref_count; /*!< The initialization count of secure Flash */ + bool _is_initialized; /*!< Secure Flash initialization status */ + app_info_t app_info; /*!< The pre-provisioned application information of secure Flash */ + secure_flash_info_t flash_info; /*!< The specific secure Flash information */ +} secureflash_t; + +class SecureFBlockDevice : public mbed::BlockDevice { +public: + /** Creates a SecureFBlockDevice on a SPI bus specified by pins + * + * @param mosi SPI master out, slave in pin + * @param miso SPI master in, slave out pin + * @param sclk SPI clock pin + * @param csel SPI chip select pin + * @param freq Clock speed of the SPI bus (defaults to 40MHz) + * + * + */ + SecureFBlockDevice(PinName mosi = MBED_CONF_SECUREF_DRIVER_SPI_MOSI, + PinName miso = MBED_CONF_SECUREF_DRIVER_SPI_MISO, + PinName sclk = MBED_CONF_SECUREF_DRIVER_SPI_CLK, + PinName csel = MBED_CONF_SECUREF_DRIVER_SPI_CS, + int freq = MBED_CONF_SECUREF_DRIVER_SPI_FREQ); + + /** Initialize a block device + * + * @return SPIF_BD_ERROR_OK(0) - success + * SPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * SPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * SPIF_BD_ERROR_PARSING_FAILED - unexpected format or values in one of the SFDP tables + */ + virtual int init(); + + /** Deinitialize a block device + * + * @return SPIF_BD_ERROR_OK(0) - success + */ + virtual int deinit(); + + /** Desctruct SPIFBlockDevice + */ + ~SecureFBlockDevice() + { + deinit(); + } + + /** Read blocks from a block device + * + * @param buffer Buffer to write blocks to + * @param addr Address of block to begin reading from + * @param size Size to read in bytes, must be a multiple of read block size + * @return SPIF_BD_ERROR_OK(0) - success + * SPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + */ + virtual int read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size); + + /** Program blocks to a block device + * + * @note The blocks must have been erased prior to being programmed + * + * @param buffer Buffer of data to write to blocks + * @param addr Address of block to begin writing to + * @param size Size to write in bytes, must be a multiple of program block size + * @return SPIF_BD_ERROR_OK(0) - success + * SPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * SPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * SPIF_BD_ERROR_WREN_FAILED - Write Enable failed + */ + virtual int program(const void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size); + + /** Erase blocks on a block device + * + * @note The state of an erased block is undefined until it has been programmed + * + * @param addr Address of block to begin erasing + * @param size Size to erase in bytes, must be a multiple of erase block size + * @return SPIF_BD_ERROR_OK(0) - success + * SPIF_BD_ERROR_DEVICE_ERROR - device driver transaction failed + * SPIF_BD_ERROR_READY_FAILED - Waiting for Memory ready failed or timed out + * SPIF_BD_ERROR_INVALID_ERASE_PARAMS - Trying to erase unaligned address or size + */ + virtual int erase(mbed::bd_addr_t addr, mbed::bd_size_t size); + + /** Get the size of a readable block + * + * @return Size of a readable block in bytes + */ + + virtual int secure_read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id); + virtual int secure_program(const void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id); + virtual int secure_erase(mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id); + virtual bd_size_t secure_zone_number() const; + virtual bd_size_t secure_zone_size() const; + + virtual mbed::bd_size_t get_read_size() const; + + /** Get the size of a programable block + * + * @return Size of a programable block in bytes + * @note Must be a multiple of the read size + */ + virtual mbed::bd_size_t get_program_size() const; + + /** Get the size of an erasable block + * + * @return Size of an erasable block in bytes + * @note Must be a multiple of the program size + */ + virtual mbed::bd_size_t get_erase_size() const; + + /** Get the size of minimal erasable sector size of given address + * + * @param addr Any address within block queried for erase sector size (can be any address within flash size offset) + * @return Size of minimal erase sector size, in given address region, in bytes + * @note Must be a multiple of the program size + */ + virtual mbed::bd_size_t get_erase_size(mbed::bd_addr_t addr) const; + + /** Get the value of storage byte after it was erased + * + * If get_erase_value returns a non-negative byte value, the underlying + * storage is set to that value when erased, and storage containing + * that value can be programmed without another erase. + * + * @return The value of storage when erased, or -1 if you can't + * rely on the value of erased storage + */ + virtual int get_erase_value() const; + + /** Get the total size of the underlying device + * + * @return Size of the underlying device in bytes + */ + virtual mbed::bd_size_t size() const; + + /** Get the BlockDevice class type. + * + * @return A string representation of the BlockDevice class type. + */ + virtual const char *get_type() const; + +private: + /********************************/ + /* Calls to SPI Driver APIs */ + /********************************/ + + // Send set_frequency command to Driver + securef_bd_error _spi_set_frequency(int freq); + /********************************/ + + // Soft Reset Flash Memory + int _reset_flash_mem(); + + // Configure Write Enable in Status Register + int _set_write_enable(); + + // Wait on status register until write not-in-progress + bool _is_mem_ready(); + + // Query vendor ID and handle special behavior that isn't covered by SFDP data + int _handle_vendor_quirks(); + + // Probe secure flash + int _probe(); + + // Find the flash_info + int _find_flash_info(uint8_t *id); + + // Get the information of provision + int _secureflash_get_app_info(); + + // SPI protocol read + static int _spi_read(uint8_t *tx_buf, uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len); + + // SPI protocol program + static int _spi_write(uint8_t *tx_buf, uint32_t tx_len); + + app_data_t *_query_app_info(mbed::bd_addr_t addr, int app_id); + +private: + // Master side hardware + static mbed::SPI *_spi; + + PinName _mosi; + PinName _miso; + PinName _sclk; + PinName _csel; + int _freq; + + // Mutex is used to protect Flash device for some SPI Driver commands that must be done sequentially with no other commands in between + // e.g. (1)Set Write Enable, (2)Program, (3)Wait Memory Ready + static SingletonPtr _mutex; + + // Bus configuration + uint32_t _init_ref_count; + bool _is_initialized; + + // Struct for secure flash information + secureflash_t _secureflash; +}; + +#endif /* MBED_SECUREFLASH_BLOCK_DEVICE_H */ diff --git a/storage/blockdevice/COMPONENT_SECUREF/mbed_lib.json b/storage/blockdevice/COMPONENT_SECUREF/mbed_lib.json new file mode 100644 index 00000000000..a71093dbd20 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/mbed_lib.json @@ -0,0 +1,93 @@ +{ + "name": "securef-driver", + "config": { + "param1": { + "help": "Enable mbedtls based crypto wrapper", + "macro_name": "CRYPTO_MBEDTLS", + "value": 1 + }, + "param2": { + "help": "Enable provisioning flow", + "macro_name": "SECUREFLASH_PROVISION", + "value": 1 + }, + "param3": { + "help": "Enable test mode", + "macro_name": "MX78_ARMOR_TEST_MODE", + "value": 1 + }, + "SPI_MOSI": "SPI_MOSI", + "SPI_MISO": "SPI_MISO", + "SPI_CLK": "SPI_SCK", + "SPI_CS": "SPI_CS", + "SPI_FREQ": "40000000", + "debug": { + "help": "Enable debug logs. [0/1]", + "options" : [0, 1], + "value": 1 + } + }, + "target_overrides": { + "HANI_IOT": { + "SPI_MOSI": "P0_26", + "SPI_MISO": "P1_3", + "SPI_CLK": "P1_2", + "SPI_CS": "P0_20" + }, + "LPC54114": { + "SPI_MOSI": "P0_20", + "SPI_MISO": "P0_18", + "SPI_CLK": "P0_19", + "SPI_CS": "P1_2" + }, + "NRF52840_DK": { + "SPI_MOSI": "p20", + "SPI_MISO": "p21", + "SPI_CLK": "p19", + "SPI_CS": "p17" + }, + "HEXIWEAR": { + "SPI_MOSI": "PTD6", + "SPI_MISO": "PTD7", + "SPI_CLK": "PTD5", + "SPI_CS": "PTD4" + }, + "WIO_EMW3166": { + "SPI_MOSI": "PB_15", + "SPI_MISO": "PB_14", + "SPI_CLK": "PB_13", + "SPI_CS": "PA_10" + }, + "ADV_WISE_1570": { + "SPI_MOSI": "PA_7", + "SPI_MISO": "PA_6", + "SPI_CLK": "PA_5", + "SPI_CS": "PB_12", + "SPI_FREQ": "20000000" + }, + "UHURU_RAVEN": { + "SPI_MOSI": "PE_14", + "SPI_MISO": "PE_13", + "SPI_CLK": "PE_12", + "SPI_CS": "PE_11" + }, + "MTS_DRAGONFLY_F411RE": { + "SPI_MOSI": "SPI3_MOSI", + "SPI_MISO": "SPI3_MISO", + "SPI_CLK": "SPI3_SCK", + "SPI_CS": "SPI_CS1" + }, + "MTS_DRAGONFLY_F413RH": { + "SPI_MOSI": "SPI3_MOSI", + "SPI_MISO": "SPI3_MISO", + "SPI_CLK": "SPI3_SCK", + "SPI_CS": "SPI_CS1" + }, + "NUCLEO_L4R5ZI_P": { + "SPI_MOSI": "PA_7", + "SPI_MISO": "PA_6", + "SPI_CLK": "PA_1", + "SPI_CS": "PA_5" + } + } +} diff --git a/storage/blockdevice/COMPONENT_SECUREF/platform/include/plat_secure_flash.h b/storage/blockdevice/COMPONENT_SECUREF/platform/include/plat_secure_flash.h new file mode 100644 index 00000000000..7bd6d4c6a15 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/platform/include/plat_secure_flash.h @@ -0,0 +1,31 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _PLATFORM_IMPL_H_ +#define _PLATFORM_IMPL_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +int32_t plat_store_secure_flash_provision_info(uint8_t *buffer, int size); +int32_t plat_get_secure_flash_provision_info(uint8_t *buffer, uint32_t size); + +#ifdef __cplusplus +} +#endif + +#endif /* _PLATFORM_IMPL_H_ */ \ No newline at end of file diff --git a/storage/blockdevice/COMPONENT_SECUREF/platform/plat_secure_flash.cpp b/storage/blockdevice/COMPONENT_SECUREF/platform/plat_secure_flash.cpp new file mode 100644 index 00000000000..40ef6b61cdd --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/platform/plat_secure_flash.cpp @@ -0,0 +1,158 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 +#include +#include "../include/plat_secure_flash.h" +#include "FlashIAPBlockDevice.h" + +#define PROVISION_INFO_SIZE 0x1000 + +static inline uint32_t align_up(uint32_t val, uint32_t size) +{ + return (((val - 1) / size) + 1) * size; +} + +int32_t plat_store_secure_flash_provision_info(uint8_t *buffer, int size) +{ +#if COMPONENT_FLASHIAP +#if (MBED_CONF_FLASHIAP_BLOCK_DEVICE_SIZE == 0) && (MBED_CONF_FLASHIAP_BLOCK_DEVICE_BASE_ADDRESS == 0xFFFFFFFF) + int status; + uint32_t actual_size = 0; + uint32_t bottom_addr, provision_start_addr, last_addr, erase_addr, program_addr; + + mbed::FlashIAP flash; + status = flash.init(); + if (0 != status) { + return -1; + } + //Find the start of first sector after text area + bottom_addr = align_up(FLASHIAP_APP_ROM_END_ADDR, flash.get_sector_size(FLASHIAP_APP_ROM_END_ADDR)); + last_addr = flash.get_flash_start() + flash.get_flash_size(); + status = flash.deinit(); + if (0 != status) { + return -1; + } + FlashIAPBlockDevice iap_bd(bottom_addr, last_addr - bottom_addr); +#else + FlashIAPBlockDevice iap_bd; +#endif + provision_start_addr = last_addr - 1; + while (actual_size < PROVISION_INFO_SIZE) { + provision_start_addr -= flash.get_sector_size(provision_start_addr); + actual_size += flash.get_sector_size(provision_start_addr); + } + if ((provision_start_addr + 1) < bottom_addr) { + return -1; + } + provision_start_addr = provision_start_addr - bottom_addr + 1; + + status = iap_bd.init(); + if (0 != status) { + printf("Error : iap init failed.\r\n"); + return -1; + } + erase_addr = provision_start_addr; + while (erase_addr < (last_addr - bottom_addr)) { + status = iap_bd.erase(erase_addr, iap_bd.get_erase_size(erase_addr)); + if (0 != status) { + printf("Error : iap erase failed.\r\n"); + iap_bd.deinit(); + return -1; + } + erase_addr += iap_bd.get_erase_size(erase_addr); + } + program_addr = provision_start_addr; + while (size > 0) { + status = iap_bd.program(&buffer[program_addr - provision_start_addr], program_addr, iap_bd.get_program_size()); + if (0 != status) { + printf("Error : iap program failed.\r\n"); + iap_bd.deinit(); + return -1; + } + program_addr += iap_bd.get_program_size(); + size -= iap_bd.get_program_size(); + } + status = iap_bd.deinit(); + if (0 != status) { + printf("Error : iap deinit failed.\r\n"); + } + return 0; +#endif +} + +int32_t plat_get_secure_flash_provision_info(uint8_t *buffer, uint32_t size) +{ +#if COMPONENT_FLASHIAP +#if (MBED_CONF_FLASHIAP_BLOCK_DEVICE_SIZE == 0) && (MBED_CONF_FLASHIAP_BLOCK_DEVICE_BASE_ADDRESS == 0xFFFFFFFF) + int status; + uint32_t actual_size = 0; + uint32_t bottom_addr, provision_start_addr, last_addr, read_addr; + + mbed::FlashIAP flash; + status = flash.init(); + if (0 != status) { + return -1; + } + + //Find the start of first sector after text area + bottom_addr = align_up(FLASHIAP_APP_ROM_END_ADDR, flash.get_sector_size(FLASHIAP_APP_ROM_END_ADDR)); + last_addr = flash.get_flash_start() + flash.get_flash_size(); + status = flash.deinit(); + if (0 != status) { + return -1; + } + FlashIAPBlockDevice iap_bd(bottom_addr, last_addr - bottom_addr); +#else + FlashIAPBlockDevice iap_bd; +#endif + + provision_start_addr = last_addr - 1; + while (actual_size < PROVISION_INFO_SIZE) { + provision_start_addr -= flash.get_sector_size(provision_start_addr); + actual_size += flash.get_sector_size(provision_start_addr); + } + if ((provision_start_addr + 1) < bottom_addr) { + return -1; + } + provision_start_addr = provision_start_addr - bottom_addr + 1; + + status = iap_bd.init(); + if (0 != status) { + printf("Error : iap init failed.\r\n"); + return -1; + } + + read_addr = provision_start_addr; + while (size > 0) { + status = iap_bd.read(&buffer[read_addr - provision_start_addr], read_addr, iap_bd.get_read_size()); + if (0 != status) { + printf("Error : iap read failed.\r\n"); + iap_bd.deinit(); + return -1; + } + read_addr += iap_bd.get_read_size(); + size -= iap_bd.get_read_size(); + } + + status = iap_bd.deinit(); + if (0 != status) { + printf("Error : iap deinit failed.\r\n"); + return -1; + } + return 0; +#endif +} \ No newline at end of file diff --git a/storage/blockdevice/COMPONENT_SECUREF/source/SecureFBlockDevice.cpp b/storage/blockdevice/COMPONENT_SECUREF/source/SecureFBlockDevice.cpp new file mode 100644 index 00000000000..89311e5f4e8 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/source/SecureFBlockDevice.cpp @@ -0,0 +1,540 @@ +/* mbed Microcontroller Library + * Copyright (c) 2023 ARM Limited + * SPDX-License-Identifier: Apache-2.0 + * + * 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 "SecureFBlockDevice.h" +#include "rtos/ThisThread.h" +#include "mbed_critical.h" + +#include +#include + +#include "mbed_trace.h" + +#include "../spi_nor_flash/spi_nor.h" +#include "../TG424_3/JEDEC_security_HAL/include/error.h" +#include "../TG424_3/JEDEC_security_HAL/include/jedec_defs.h" +#include "../TG424_3/JEDEC_security_HAL/jedec_security_hal.h" +#include "../TG424_3/vendor_impl/vendor_secureflash.h" + +#define TRACE_GROUP "SECUREF" +using namespace std::chrono; +using namespace mbed; + +// Status Register Bits +#define SPIF_STATUS_BIT_WIP 0x1 //Write In Progress +#define SPIF_STATUS_BIT_WEL 0x2 // Write Enable Latch + +mbed::SPI *SecureFBlockDevice::_spi; +// Mutex is used for some SPI Driver commands that must be done sequentially with no other commands in between +// e.g. (1)Set Write Enable, (2)Program, (3)Wait Memory Ready +SingletonPtr SecureFBlockDevice::_mutex; + +//************************** +// SECUREF Block Device APIs +//************************** +SecureFBlockDevice::SecureFBlockDevice(PinName mosi, PinName miso, PinName sclk, PinName csel, int freq) + : + _mosi(mosi), _miso(miso), _sclk(sclk), _csel(csel), _freq(freq), + _is_initialized(false) +{} + +int SecureFBlockDevice::init() +{ + int status = SECUREF_BD_ERROR_OK; + + _mutex->lock(); + + if (!_is_initialized) { + _init_ref_count = 0; + } + + _init_ref_count++; + + if (_init_ref_count != 1) { + goto exit_point; + } + memset(&_secureflash, 0x00, sizeof (secureflash_t)); + + _spi = new (std::nothrow) mbed::SPI(_mosi, _miso, _sclk, _csel, use_gpio_ssel); + if (0 == _spi) { + tr_error("Allocation of mbed::SPI failed"); + goto exit_point; + } + if (SECUREF_BD_ERROR_OK != _spi_set_frequency(_freq)) { + tr_error("SPI Set Frequency Failed"); + goto exit_point; + } + if (0 != spi_nor_init(_spi_read, _spi_write)) { + status = SECUREF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + // Soft Reset + if (0 != _reset_flash_mem()) { + tr_error("init - Unable to initialize flash memory, tests failed"); + status = SECUREF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } else { + tr_debug("Initialize flash memory OK"); + } + // Synchronize Device + if (0 != spi_nor_polling_for_wr_ready()) { + tr_error("init - spi_nor_polling_for_wr_ready Failed"); + status = SECUREF_BD_ERROR_READY_FAILED; + goto exit_point; + } + tr_debug("Probe secure flash"); + status = _probe(); + if (SECUREF_BD_ERROR_OK != status) { + goto exit_point; + } +#ifdef SECUREFLASH_PROVISION + tr_debug("Provision the secure flash"); + status = _secureflash.flash_info.vendor_provisioning_op->perform_and_verify(_secureflash.flash_info.vendor_ctx, + NULL, 0); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_PROVISION_FLASH; + goto exit_point; + } +#endif /* SECUREFLASH_PROVISION */ + tr_debug("Get the applicaition information"); + status = _secureflash_get_app_info(); + if (SECUREF_BD_ERROR_OK != status) { + goto exit_point; + } + tr_debug("Jedect secure init"); + status = jedec_secure_init(SECUREFLASH_AUTHEN_KEY_ID); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_DEVICE_ERROR; + goto exit_point; + } + _is_initialized = true; + tr_debug("Init successful"); +exit_point: + if (false == _is_initialized && 0 != _spi) { + delete _spi; + } + _mutex->unlock(); + return status; +} + +int SecureFBlockDevice::_probe() +{ + uint8_t vendor_device_ids[4]; + uint32_t data_length = 3; + + // Read Manufacturer ID (1byte), and Device ID (2bytes) + if (0 != spi_nor_read_id(vendor_device_ids, data_length)) { + tr_error("Read Vendor ID Failed"); + return SECUREF_BD_ERROR_DEVICE_ERROR; + } + + tr_debug("Vendor device ID = 0x%x 0x%x 0x%x", vendor_device_ids[0], vendor_device_ids[1], vendor_device_ids[2]); + + if (0 != _find_flash_info(vendor_device_ids)) { + return SECUREF_BD_ERROR_DEVICE_UNSUPPORT; + } + return SECUREF_BD_ERROR_OK; +} + +int SecureFBlockDevice::_find_flash_info(uint8_t *id) +{ + for (uint8_t i = 0; i < sizeof(flash_info); i++) { + if (!memcmp(id, flash_info[i]->id, flash_info[i]->id_len)) { + jedec_set_vendor(flash_info[i]->vendor_security_op, + flash_info[i]->crypto_wrapper, + flash_info[i]->vendor_ctx); + flash_info[i]->crypto_wrapper->init(); + memcpy(&_secureflash.flash_info, flash_info[i], sizeof(secure_flash_info_t)); + return 0; + } + } + return -1; +} + +int SecureFBlockDevice::_secureflash_get_app_info() +{ + int status = JEDEC_ERROR_NONE; + uint32_t actual_size; + vendor_provisioning_op_t *provision_op = _secureflash.flash_info.vendor_provisioning_op; + + status = provision_op->provision_item_get_data(_secureflash.flash_info.vendor_ctx, + ITEM_APP_INFO, + (uint8_t *)_secureflash.app_info.app_data, + sizeof(_secureflash.app_info.app_data), + &_secureflash.app_info.num, + &actual_size); + if (JEDEC_ERROR_NONE != status) { + return SECUREF_BD_ERROR_GET_PROVISION; + } + return SECUREF_BD_ERROR_OK; +} + +int SecureFBlockDevice::deinit() +{ + securef_bd_error status = SECUREF_BD_ERROR_OK; + + _mutex->lock(); + if (!_is_initialized) { + _init_ref_count = 0; + goto exit_point; + } + _init_ref_count--; + + if (_init_ref_count) { + goto exit_point; + } + _is_initialized = false; + +exit_point: + if (false == _is_initialized && 0 != _spi) { + delete _spi; + } + _mutex->unlock(); + return status; +} + +int SecureFBlockDevice::_spi_read(uint8_t *tx_buf, uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len) +{ + _spi->select(); + + // Write/Read Data + for (uint32_t i = 0; i < tx_len; i++) { + _spi->write(tx_buf[i]); + } + for (uint32_t i = 0; i < rx_len; i++) { + rx_buf[i] = _spi->write(0); + } + + _spi->deselect(); + return 0; +} + +int SecureFBlockDevice::_spi_write(uint8_t *tx_buf, uint32_t tx_len) +{ + _spi->select(); + + // Write Data + for (uint32_t i = 0; i < tx_len; i++) { + _spi->write(tx_buf[i]); + } + + _spi->deselect(); + return 0; +} + +int SecureFBlockDevice::read(void *buffer, bd_addr_t addr, bd_size_t size) +{ + return 0; +} + +int SecureFBlockDevice::program(const void *buffer, bd_addr_t addr, bd_size_t size) +{ + return 0; +} + +int SecureFBlockDevice::erase(bd_addr_t addr, bd_size_t size) +{ + return 0; +} + +app_data_t *SecureFBlockDevice::_query_app_info(mbed::bd_addr_t addr, int app_id) +{ + uint8_t zone_id = (uint8_t)(addr / SecureFBlockDevice::secure_zone_size()); + + for (int i = 0; i < _secureflash.app_info.num; i++) { + if ((_secureflash.app_info.app_data[i].app_id == app_id) && + (_secureflash.app_info.app_data[i].zone_id == zone_id)) { + return &(_secureflash.app_info.app_data[i]); + } + } + return NULL; +} + +int SecureFBlockDevice::secure_read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id) +{ + int status = SECUREF_BD_ERROR_OK; + app_data_t *app_data; + uint32_t session_key_id, actual_read_size, offset, chunk, read_size = get_read_size(); + + if (!_is_initialized) { + return BD_ERROR_DEVICE_ERROR; + } + if (((uint32_t)(addr + size)) > SecureFBlockDevice::size()) { + return SECUREF_BD_ERROR_INVALID_PGM_PARAMS; + } + _mutex->lock(); + app_data = _query_app_info(addr, app_id); + if (NULL == app_data) { + status = SECUREF_BD_ERROR_ILLEGAL_ACCESS; + goto secure_read_exit_point; + } + status = jedec_create_session(app_data->key_id, 0, &session_key_id); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_CREATE_SESSION; + goto secure_read_exit_point; + } + while (size > 0) { + offset = addr % read_size; + chunk = (offset + size < read_size) ? size : (read_size - offset); + status = jedec_secure_read(addr, (uint8_t *)buffer, chunk, session_key_id, &actual_read_size); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_SECURE_READ; + goto secure_read_exit_point; + } + size -= chunk; + addr += chunk; + buffer += chunk; + } + status = jedec_terminate_session(session_key_id); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_TERMINATE_SESSION; + goto secure_read_exit_point; + } +secure_read_exit_point: + _mutex->unlock(); + return status; +} +int SecureFBlockDevice::secure_program(const void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id) +{ + int status = SECUREF_BD_ERROR_OK; + uint32_t session_key_id; + app_data_t *app_data; + uint32_t actual_program_size, chunk, offset, pgm_size = get_program_size(); + + if (!_is_initialized) { + return BD_ERROR_DEVICE_ERROR; + } + + if (((uint32_t)(addr + size)) > SecureFBlockDevice::size()) { + return SECUREF_BD_ERROR_INVALID_PGM_PARAMS; + } + _mutex->lock(); + app_data = _query_app_info(addr, app_id); + if (NULL == app_data) { + status = SECUREF_BD_ERROR_ILLEGAL_ACCESS; + goto secure_program_exit_point; + } + status = jedec_create_session(app_data->key_id, 0, &session_key_id); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_CREATE_SESSION; + goto secure_program_exit_point; + } + while (size > 0) { + offset = addr % pgm_size; + chunk = (offset + size < pgm_size) ? size : (pgm_size - offset); + status = jedec_secure_program(addr, (uint8_t *)buffer, chunk, session_key_id, &actual_program_size); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_SECURE_PROGRAM; + goto secure_program_exit_point; + } + size -= chunk; + addr += chunk; + buffer += chunk; + } + status = jedec_terminate_session(session_key_id); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_TERMINATE_SESSION; + goto secure_program_exit_point; + } +secure_program_exit_point: + _mutex->unlock(); + return status; +} + +int SecureFBlockDevice::secure_erase(mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id) +{ + int status = SECUREF_BD_ERROR_OK; + app_data_t *app_data; + uint32_t session_key_id, ers_size = get_erase_size(); + + if (!_is_initialized) { + return BD_ERROR_DEVICE_ERROR; + } + if (((uint32_t)(addr + size)) > SecureFBlockDevice::size()) { + return SECUREF_BD_ERROR_INVALID_ERASE_PARAMS; + } + if ((addr % ers_size) || (size % ers_size)) { + return SECUREF_BD_ERROR_INVALID_ERASE_PARAMS; + } + _mutex->lock(); + app_data = _query_app_info(addr, app_id); + if (NULL == app_data) { + status = SECUREF_BD_ERROR_ILLEGAL_ACCESS; + goto secure_erase_exit_point; + } + status = jedec_create_session(app_data->key_id, 0, &session_key_id); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_CREATE_SESSION; + goto secure_erase_exit_point; + } + while (size > 0) { + status = jedec_secure_erase((uint32_t)addr, ers_size, session_key_id); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_SECURE_ERASE; + goto secure_erase_exit_point; + } + size -= ers_size; + addr += ers_size; + } + status = jedec_terminate_session(session_key_id); + if (JEDEC_ERROR_NONE != status) { + status = SECUREF_BD_ERROR_TERMINATE_SESSION; + goto secure_erase_exit_point; + } +secure_erase_exit_point: + _mutex->unlock(); + return status; +} + +bd_size_t SecureFBlockDevice::get_read_size() const +{ + if (!_is_initialized) { + return 0; + } + return _secureflash.flash_info.flash_profile->architecture.secure_read_size; +} + +bd_size_t SecureFBlockDevice::get_program_size() const +{ + if (!_is_initialized) { + return 0; + } + return _secureflash.flash_info.flash_profile->architecture.secure_program_size; +} + +bd_size_t SecureFBlockDevice::get_erase_size() const +{ + if (!_is_initialized) { + return 0; + } + return _secureflash.flash_info.flash_profile->architecture.regions_min_secure_erase_size; +} + +// Find minimal erase size supported by the region to which the address belongs to +bd_size_t SecureFBlockDevice::get_erase_size(bd_addr_t addr) const +{ + if (!_is_initialized) { + return 0; + } + return _secureflash.flash_info.flash_profile->architecture.regions_min_secure_erase_size; +} +bd_size_t SecureFBlockDevice::secure_zone_number() const +{ + if (!_is_initialized) { + return 0; + } + return _secureflash.flash_info.flash_profile->architecture.secure_zone_number; +} + +bd_size_t SecureFBlockDevice::secure_zone_size() const +{ + if (!_is_initialized) { + return 0; + } + return _secureflash.flash_info.flash_profile->architecture.secure_zone_size; +} + +bd_size_t SecureFBlockDevice::size() const +{ + if (!_is_initialized) { + return 0; + } + + return _secureflash.flash_info.flash_profile->architecture.secure_zone_total_size; +} + +int SecureFBlockDevice::get_erase_value() const +{ + return 0xFF; +} + +const char *SecureFBlockDevice::get_type() const +{ + return "SECUREF"; +} + +/***************************************************/ +/*********** SPI Driver API Functions **************/ +/***************************************************/ +securef_bd_error SecureFBlockDevice::_spi_set_frequency(int freq) +{ + _spi->frequency(freq); + return SECUREF_BD_ERROR_OK; +} + +/*********************************************************/ +/********** SFDP Parsing and Detection Functions *********/ +/*********************************************************/ +int SecureFBlockDevice::_reset_flash_mem() +{ + // Perform Soft Reset of the Device prior to initialization + int status = 0; + uint8_t status_value[2] = {0}; + tr_info("_reset_flash_mem:"); + // Read the Status Register from device + if (0 == spi_nor_read_sr(status_value, 1)) { + // store received values in status_value + tr_debug("Reading Status Register Success: value = 0x%x", (int)status_value[0]); + } else { + tr_error("Reading Status Register failed"); + status = -1; + } + + if (0 == status) { + // Send Reset Enable + if (0 == spi_nor_reset_enable()) { + // store received values in status_value + tr_debug("Sending RSTEN Success"); + } else { + tr_error("Sending RSTEN failed"); + status = -1; + } + + if (0 == status) { + // Send Reset + if (0 == spi_nor_reset()) { + // store received values in status_value + tr_debug("Sending RST Success"); + } else { + tr_error("Sending RST failed"); + status = -1; + } + if (0 != spi_nor_polling_for_wr_ready()) { + tr_error("Device not ready, write failed"); + status = -1; + } + } + } + + return status; +} + +int SecureFBlockDevice::_handle_vendor_quirks() +{ + uint8_t vendor_device_ids[4]; + size_t data_length = 3; + + // Read Manufacturer ID (1byte), and Device ID (2bytes) + if (0 != spi_nor_read_id(vendor_device_ids, data_length)) { + tr_error("Read Vendor ID Failed"); + return -1; + } + + tr_debug("Vendor device ID = 0x%x 0x%x 0x%x", vendor_device_ids[0], vendor_device_ids[1], vendor_device_ids[2]); + + return 0; +} \ No newline at end of file diff --git a/storage/blockdevice/COMPONENT_SECUREF/spi_nor_flash/spi_nor.c b/storage/blockdevice/COMPONENT_SECUREF/spi_nor_flash/spi_nor.c new file mode 100644 index 00000000000..6e5cf63c9a5 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/spi_nor_flash/spi_nor.c @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 +#include +#include "spi_nor.h" + +#define STD_INST_PP 0x02 +#define STD_INST_READ 0x03 +#define STD_INST_ERASE_SECTOR 0x20 +#define STD_INST_RDSCUR 0x2B +#define STD_INST_RDCR 0x15 +#define STD_INST_RDCR2 0x71 +#define STD_INST_RDSR 0x05 +#define STD_INST_RDID 0x9F + +#define STD_INST_RSTEN 0x66 +#define STD_INST_RST 0x99 +#define STD_INST_RSTQPI 0xF5 + +#define STD_INST_WREN 0x06 +#define STD_INST_FREAD 0x0B +#define STD_INST_ENSO 0xB1 +#define STD_INST_EXSO 0xC1 + +/* status register definition */ +#define SR_WIP 0x01 +#define SR_WEL 0x02 +#define SR_QE 0x40 /* Quad-IO enable bit */ +#define SR_BP0 0x04 /* Block protect 0 */ +#define SR_BP1 0x08 /* Block protect 1 */ +#define SR_BP2 0x10 /* Block protect 2 */ +#define SR_BP3 0x20 /* Block protect 3 */ +#define SR_BP_BIT_OFFSET 2 /* Offset to Block protect 0 */ +#define SR_BP_BIT_MASK (SR_BP3 | SR_BP2 | SR_BP1 | SR_BP0) +#define SR_SRWD 0x80 /* SR write protect */ + +#define SCUR_ORDY 0x10 /* Security packet output ready bit */ + +#define SUCCESS 0 +#define FAILURE -1 +#define BUFFER_SIZE 0x132 +#define MAX_POLLING_TIMES 1000 + +static spi_nor_t spi_nor = {}; + +static void wait_for_us(uint32_t microsec) +{ + //TODO +} + +static void wait_for_ms(uint32_t millisec) +{ + //TODO +} + +static void cmd_packing(nor_cmd_packet_t *cmd_packet) +{ + uint32_t send_packet_size = 0; + + cmd_packet->cmd_buffer.tx_buf[0] = cmd_packet->cmd; + send_packet_size++; + if (cmd_packet->addr_len > 0) { + memcpy(&cmd_packet->cmd_buffer.tx_buf[send_packet_size], cmd_packet->addr, cmd_packet->addr_len); + send_packet_size += cmd_packet->addr_len; + } + if (cmd_packet->dummy_len > 0) { + memset(&cmd_packet->cmd_buffer.tx_buf[send_packet_size], 0xFF, cmd_packet->dummy_len); + send_packet_size += cmd_packet->dummy_len; + } + if (cmd_packet->data != NULL) { + memcpy(&cmd_packet->cmd_buffer.tx_buf[send_packet_size], cmd_packet->data, cmd_packet->data_len); + send_packet_size += cmd_packet->data_len; + } + cmd_packet->cmd_buffer.tx_len = send_packet_size; +} + +int32_t spi_read(uint8_t *tx_buf, uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len) +{ + spi_nor.read(tx_buf, tx_len, rx_buf, rx_len); + return 0; +} + +int32_t spi_write(uint8_t *tx_buf, uint32_t tx_len) +{ + spi_nor.write(tx_buf, tx_len); + return 0; +} + +int32_t spi_nor_reset_enable(void) +{ + spi_nor.cmd_packet.cmd = STD_INST_RSTEN; + spi_nor.cmd_packet.addr_len = 0; + spi_nor.cmd_packet.dummy_len = 0; + spi_nor.cmd_packet.data = NULL; + cmd_packing(&spi_nor.cmd_packet); + return spi_nor.write(spi_nor.cmd_packet.cmd_buffer.tx_buf, spi_nor.cmd_packet.cmd_buffer.tx_len); +} + +int32_t spi_nor_reset(void) +{ + spi_nor.cmd_packet.cmd = STD_INST_RST; + spi_nor.cmd_packet.addr_len = 0; + spi_nor.cmd_packet.dummy_len = 0; + spi_nor.cmd_packet.data = NULL; + cmd_packing(&spi_nor.cmd_packet); + return spi_nor.write(spi_nor.cmd_packet.cmd_buffer.tx_buf, spi_nor.cmd_packet.cmd_buffer.tx_len); + +} + +int32_t spi_nor_write_enable(void) +{ + spi_nor.cmd_packet.cmd = STD_INST_WREN; + spi_nor.cmd_packet.addr_len = 0; + spi_nor.cmd_packet.dummy_len = 0; + spi_nor.cmd_packet.data = NULL; + cmd_packing(&spi_nor.cmd_packet); + return spi_nor.write(spi_nor.cmd_packet.cmd_buffer.tx_buf, spi_nor.cmd_packet.cmd_buffer.tx_len); +} + +int32_t spi_nor_write_disable(void) +{ + //TODO + return 0; +} + +int32_t spi_nor_read(uint8_t *buffer, uint32_t addr, uint32_t size) +{ + //TODO + return 0; +} + +int32_t spi_nor_program(uint8_t *buffer, uint32_t addr, uint32_t size) +{ + //TODO + return 0; +} + +int32_t spi_nor_erase(uint32_t addr, uint32_t size) +{ + //TODO + return 0; +} + +int32_t spi_nor_read_id(uint8_t *id, uint32_t size) +{ + spi_nor.cmd_packet.cmd = STD_INST_RDID; + spi_nor.cmd_packet.addr_len = 0; + spi_nor.cmd_packet.dummy_len = 0; + spi_nor.cmd_packet.data = NULL; + cmd_packing(&spi_nor.cmd_packet); + return spi_nor.read(spi_nor.cmd_packet.cmd_buffer.tx_buf, + spi_nor.cmd_packet.cmd_buffer.tx_len, + id, size); +} + +int32_t spi_nor_read_sr(uint8_t *sr, uint32_t size) +{ + spi_nor.cmd_packet.cmd = STD_INST_RDSR; + spi_nor.cmd_packet.addr_len = 0; + spi_nor.cmd_packet.dummy_len = 0; + spi_nor.cmd_packet.data = NULL; + cmd_packing(&spi_nor.cmd_packet); + return spi_nor.read(spi_nor.cmd_packet.cmd_buffer.tx_buf, + spi_nor.cmd_packet.cmd_buffer.tx_len, + sr, size); +} + +int32_t spi_nor_read_cr(uint8_t *cr, uint32_t size) +{ + spi_nor.cmd_packet.cmd = STD_INST_RDCR; + spi_nor.cmd_packet.addr_len = 0; + spi_nor.cmd_packet.dummy_len = 0; + spi_nor.cmd_packet.data = NULL; + cmd_packing(&spi_nor.cmd_packet); + return spi_nor.read(spi_nor.cmd_packet.cmd_buffer.tx_buf, + spi_nor.cmd_packet.cmd_buffer.tx_len, + cr, size); +} + +int32_t spi_nor_read_cr2(uint8_t *cr, uint32_t size) +{ + //TODO + return 0; +} + +int32_t spi_nor_read_scur(uint8_t *scur, uint32_t size) +{ + spi_nor.cmd_packet.cmd = STD_INST_RDSCUR; + spi_nor.cmd_packet.addr_len = 0; + spi_nor.cmd_packet.dummy_len = 0; + spi_nor.cmd_packet.data = NULL; + cmd_packing(&spi_nor.cmd_packet); + return spi_nor.read(spi_nor.cmd_packet.cmd_buffer.tx_buf, + spi_nor.cmd_packet.cmd_buffer.tx_len, + scur, size); +} + +int32_t spi_nor_enter_secured_OTP(void) +{ + //TODO + return 0; +} + +int32_t spi_nor_exit_secured_OTP(void) +{ + //TODO + return 0; +} + +int32_t spi_nor_software_reset(void) +{ + if (spi_nor_reset_enable() != SUCCESS) { + return -1; + } + if (spi_nor_reset() != SUCCESS) { + return -1; + } + wait_for_ms(1);//stm32l562 hal driver not support us delay + return 0; +} + +int32_t spi_nor_polling_for_wr_ready(void) +{ + uint32_t cnt = 0; + uint8_t status_reg; + /* Polling for secure Flash ready for program */ + do { + cnt++; + if (spi_nor_read_sr(&status_reg, 1) != SUCCESS) { + return -1; + } + wait_for_ms(1); + } while ((status_reg & SR_WIP) && (cnt < MAX_POLLING_TIMES)); + + if (cnt >= MAX_POLLING_TIMES) { + return -1; + } + return 0; +} +int32_t spi_nor_polling_for_out_ready(void) +{ + uint32_t cnt = 0; + uint8_t status_reg, scur_reg; + + /* Polling for secure Flash ready for program */ + do { + cnt++; + if (spi_nor_read_scur(&scur_reg, 1) != SUCCESS) { + return -1; + } + if (spi_nor_read_sr(&status_reg, 1) != SUCCESS) { + return -1; + } + wait_for_ms(1); + } while (((status_reg & SR_WIP) || !(scur_reg & SCUR_ORDY)) && (cnt < MAX_POLLING_TIMES)); + + if (cnt >= MAX_POLLING_TIMES) { + return -1; + } + return 0; +} + +int32_t spi_nor_init(spi_read_t spi_read, spi_write_t spi_write) +{ + if (spi_read == NULL || spi_write == NULL) { + printf("ERROR: spi_ctx == NULL(%08X, %08X)\r\n", spi_read, spi_write); + } + spi_nor.read = spi_read; + spi_nor.write = spi_write; + + return 0; +} diff --git a/storage/blockdevice/COMPONENT_SECUREF/spi_nor_flash/spi_nor.h b/storage/blockdevice/COMPONENT_SECUREF/spi_nor_flash/spi_nor.h new file mode 100644 index 00000000000..64fe54147a5 --- /dev/null +++ b/storage/blockdevice/COMPONENT_SECUREF/spi_nor_flash/spi_nor.h @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2022-2023 Macronix International Co. LTD. All rights reserved. + * SPDX-License-Identifier: Apache-2.0 + * + * 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 _SPI_NOR_H_ +#define _SPI_NOR_H_ + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#define MAX_ADDR_LEN 4 +#define MAX_ID_LEN 6 +#define BUFFER_SIZE 0x132 + +typedef int (*spi_read_t)(uint8_t *tx_buf, uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len); +typedef int (*spi_write_t)(uint8_t *tx_buf, uint32_t tx_len); + +struct nor_cmd_packet { + uint8_t cmd; + uint8_t addr[MAX_ADDR_LEN]; + uint8_t addr_len; + uint8_t dummy_len; + uint8_t *data; + uint32_t data_len; + struct { + uint8_t tx_buf[BUFFER_SIZE]; + uint32_t tx_len; + uint8_t rx_buf[BUFFER_SIZE]; + uint32_t rx_len; + } cmd_buffer; +}; + +struct spi_nor { + uint8_t id[MAX_ID_LEN]; + uint8_t read_dummy_cycle; + struct nor_cmd_packet cmd_packet; + spi_read_t read; + spi_write_t write; +}; + +typedef struct nor_cmd_packet nor_cmd_packet_t; +typedef struct spi_nor spi_nor_t; + +int32_t spi_read(uint8_t *tx_buf, uint32_t tx_len, uint8_t *rx_buf, uint32_t rx_len); +int32_t spi_write(uint8_t *tx_buf, uint32_t tx_len); + +int32_t spi_nor_init(spi_read_t spi_read, spi_write_t spi_write); +/** + * \brief Enable software reset. + * + * \return: 0 on success, -1 if not + */ +int32_t spi_nor_reset_enable(void); +/** + * \brief Perform a software reset. + * + * \return: 0 on success, -1 if not + */ +int32_t spi_nor_reset(void); +/** + * \brief Set write enable latch. + * + * \return: 0 on success, -1 if not + */ +int32_t spi_nor_write_enable(void); +/** + * \brief Send write disable instruction to the chip. + * + * \return: 0 on success, -1 if not + */ +int32_t spi_nor_write_disable(void); +/** + * \brief Read data from flash. + * \param[in] buffer pointer to destination buffer + * \param[in] addr Address to read from + * \param[in] size Number of bytes to read + * \return: number of bytes read successfully, -1 otherwise + */ +int32_t spi_nor_read(uint8_t *buffer, uint32_t addr, uint32_t size); +/** + * \brief Write data to flash. + * \param[in] buffer Pointer to source buffer + * \param[in] addr Address to write to + * \param[in] size Number of bytes to write + * \return: number of bytes written successfully, -1 otherwise + */ +int32_t spi_nor_program(uint8_t *buffer, uint32_t addr, uint32_t size); +/** + * \brief Erase an address range on the nor chip. + * \param[in] addr Address to erase + * \param[in] size Erase size + * \return: 0 on success, -1 otherwise + */ +int32_t spi_nor_erase(uint32_t addr, uint32_t size); +/** + * \brief Read the JEDEC ID. + * \param[in] id Pointer to buffer where the value of JEDEC ID will be written + * \param[in] size ID size + * \return: 0 on success, -1 otherwise + */ +int32_t spi_nor_read_id(uint8_t *id, uint32_t size); +/** + * \brief Read the status register. + * \param[in] sr Pointer to buffer where the value of status register will be written + * \param[in] size SR size + * \return: 0 on success, -1 otherwise + */ +int32_t spi_nor_read_sr(uint8_t *sr, uint32_t size); +/** + * \brief Read the configuration register. + * \param[in] cr Pointer to buffer where the value of configuration register will be written + * \param[in] size CR size + * \return: 0 on success, -1 otherwise + */ +int32_t spi_nor_read_cr(uint8_t *cr, uint32_t size); +/** + * \brief Read the security register. + * \param[in] scur Pointer to buffer where the value of security register will be written + * \param[in] size SCUR size + * \return: 0 on success, -1 otherwise + */ +int32_t spi_nor_read_scur(uint8_t *scur, uint32_t size); +/** + * \brief Enter secured OTP. + * \return: 0 on success, -1 otherwise + */ +int32_t spi_nor_enter_secured_OTP(void); +/** + * \brief Exit secured OTP. + * \return: 0 on success, -1 otherwise + */ +int32_t spi_nor_exit_secured_OTP(void); +/** + * \brief Polling for nor flash ready for program. + * + * \return: 0 if flash is ready for program, -1 otherwise + */ +int32_t spi_nor_polling_for_wr_ready(void); +/** + * \brief Polling for nor flash ready for read. + * + * \return: 0 if flash is ready for read, -1 otherwise + */ +int32_t spi_nor_polling_for_out_ready(void); + +#ifdef __cplusplus +} +#endif + +#endif /* _SPI_NOR_H_ */ diff --git a/storage/blockdevice/include/blockdevice/BlockDevice.h b/storage/blockdevice/include/blockdevice/BlockDevice.h index 688728e1a44..b026ea05129 100644 --- a/storage/blockdevice/include/blockdevice/BlockDevice.h +++ b/storage/blockdevice/include/blockdevice/BlockDevice.h @@ -139,6 +139,27 @@ class BlockDevice { return 0; } + virtual int secure_read(void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id) + { + return -1; + } + virtual int secure_program(const void *buffer, mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id) + { + return -1; + } + virtual int secure_erase(mbed::bd_addr_t addr, mbed::bd_size_t size, int app_id) + { + return -1; + } + virtual bd_size_t secure_zone_number() const + { + return 0; + } + virtual bd_size_t secure_zone_size() const + { + return 0; + } + /** Get the size of a readable block * * @return Size of a readable block in bytes diff --git a/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/CMakeLists.txt b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/CMakeLists.txt index 29a242abb95..10301137edd 100644 --- a/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/CMakeLists.txt +++ b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/CMakeLists.txt @@ -38,6 +38,10 @@ if("SPIF" IN_LIST MBED_TARGET_LABELS) list(APPEND mbed_blockdevice_libs mbed-storage-spif) endif() +if("SECUREF" IN_LIST MBED_TARGET_LABELS) + list(APPEND mbed_blockdevice_libs mbed-storage-securef) +endif() + mbed_greentea_add_test( TEST_NAME ${TEST_TARGET} diff --git a/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp index 11b3e11d447..b15c527a743 100644 --- a/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp +++ b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/main.cpp @@ -28,6 +28,7 @@ #include "BufferedBlockDevice.h" #include "BlockDevice.h" #include +#include "mx78_armor2_provision_example.h" #if COMPONENT_SPIF #include "SPIFBlockDevice.h" @@ -57,6 +58,10 @@ #include "SPINANDBlockDevice.h" #endif +#if COMPONENT_SECUREF +#include "SecureFBlockDevice.h" +#endif + // Debug available #ifndef MODE_DEBUG #define MODE_DEBUG 0 @@ -97,10 +102,11 @@ enum bd_type { flashiap, ospif, spinand, + securef, default_bd }; -uint8_t bd_arr[6] = {0}; +uint8_t bd_arr[default_bd] = {0}; static uint8_t test_iteration = 0; @@ -238,6 +244,19 @@ static BlockDevice *get_bd_instance(uint8_t bd_type) #endif return &default_bd; +#endif + break; + } + case securef: { +#if COMPONENT_SECUREF + static SecureFBlockDevice default_bd( + MBED_CONF_SECUREF_DRIVER_SPI_MOSI, + MBED_CONF_SECUREF_DRIVER_SPI_MISO, + MBED_CONF_SECUREF_DRIVER_SPI_CLK, + MBED_CONF_SECUREF_DRIVER_SPI_CS, + MBED_CONF_SECUREF_DRIVER_SPI_FREQ + ); + return &default_bd; #endif break; } @@ -273,16 +292,43 @@ void basic_erase_program_read_test(BlockDevice *block_device, bd_size_t block_si write_block[i_ind] = 0xff & rand(); } // Write, sync, and read the block - DEBUG_PRINTF("test %0*llx:%llu...\n", addrwidth, block, curr_block_size); + DEBUG_PRINTF("test %llx:%llu...\n", addrwidth, block); + if (!strcmp(block_device->get_type(), "SECUREF")) { + uint32_t valid_app_id, valid_zone_id; + bd_size_t access_zone_id = block / block_device->secure_zone_size(); + int n; + + for (n = 0; n < AVAILABLE_PAIR_NUM; n++) { + if (access_zone_id == app_zone_available_pair[n].secure_zone_id) { + valid_app_id = app_zone_available_pair[n].app_id; + valid_zone_id = app_zone_available_pair[n].secure_zone_id; + break; + } + } + if (AVAILABLE_PAIR_NUM == n) { + utest_printf("Address %llx is not in the avaliable zone %llx , SKIP!!!\n", block, access_zone_id); + _mutex->unlock(); + return; + } + err = block_device->secure_erase(block, curr_block_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); - err = block_device->erase(block, curr_block_size); - TEST_ASSERT_EQUAL(0, err); + err = block_device->secure_program(write_block, block, block_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); - err = block_device->program(write_block, block, block_size); - TEST_ASSERT_EQUAL(0, err); + err = block_device->secure_read(read_block, block, block_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); + } else { - err = block_device->read(read_block, block, block_size); - TEST_ASSERT_EQUAL(0, err); + err = block_device->erase(block, curr_block_size); + TEST_ASSERT_EQUAL(0, err); + + err = block_device->program(write_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + + err = block_device->read(read_block, block, block_size); + TEST_ASSERT_EQUAL(0, err); + } // Check that the data was unmodified srand(seed); @@ -406,6 +452,7 @@ void test_multi_threads() utest_printf("\nTest Multi Threaded Erase/Program/Read Starts..\n"); TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found."); + TEST_SKIP_UNLESS_MESSAGE(strcmp(block_device->get_type(), "SECUREF"), "Secure Flash currently does not support this type of test."); for (unsigned atr = 0; atr < sizeof(ATTRS) / sizeof(ATTRS[0]); atr++) { static const char *prefixes[] = {"", "k", "M", "G"}; @@ -575,44 +622,94 @@ void test_erase_functionality() uint8_t *out_data_buf = new (std::nothrow) uint8_t[data_buf_size]; TEST_SKIP_UNLESS_MESSAGE(out_data_buf != NULL, "Not enough memory for test"); - // First must Erase given memory region - utest_printf("erasing given memory region\n"); - int err = block_device->erase(start_address, data_buf_size); - TEST_ASSERT_EQUAL(0, err); + if (!strcmp(block_device->get_type(), "SECUREF")) { + uint32_t valid_app_id, valid_zone_id; + bd_size_t access_zone_id = start_address / block_device->secure_zone_size(); + int n; - // Write random data to selected region to make sure data is not accidentally set to "erased" value. - // With this pre-write, the test case will fail even if block_device->erase() is broken. - for (bd_size_t i = 0; i < data_buf_size; i++) { - data_buf[i] = (uint8_t) rand(); - } + for (n = 0; n < AVAILABLE_PAIR_NUM; n++) { + if (access_zone_id == app_zone_available_pair[n].secure_zone_id) { + valid_app_id = app_zone_available_pair[n].app_id; + valid_zone_id = app_zone_available_pair[n].secure_zone_id; + break; + } + } + // First must Erase given memory region + utest_printf("erasing given memory region\n"); + int err = block_device->secure_erase(start_address, data_buf_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); - utest_printf("writing given memory region\n"); - err = block_device->program((const void *)data_buf, start_address, data_buf_size); - TEST_ASSERT_EQUAL(0, err); + // Write random data to selected region to make sure data is not accidentally set to "erased" value. + // With this pre-write, the test case will fail even if block_device->erase() is broken. + for (bd_size_t i = 0; i < data_buf_size; i++) { + data_buf[i] = (uint8_t) rand(); + } - // Read written memory region to verify it contains information - memset(out_data_buf, 0, data_buf_size); - utest_printf("reading written memory region\n"); - err = block_device->read((void *)out_data_buf, start_address, data_buf_size); - TEST_ASSERT_EQUAL(0, err); + utest_printf("writing given memory region\n"); + err = block_device->secure_program((const void *)data_buf, start_address, data_buf_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); - // Verify erased memory region - utest_printf("verifying written memory region\n"); - for (bd_size_t i = 0; i < data_buf_size; i++) { - TEST_ASSERT_EQUAL(out_data_buf[i], data_buf[i]); - } + // Read written memory region to verify it contains information + memset(out_data_buf, 0, data_buf_size); + utest_printf("reading written memory region\n"); + err = block_device->secure_read((void *)out_data_buf, start_address, data_buf_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); - // Erase given memory region - utest_printf("erasing written memory region\n"); - err = block_device->erase(start_address, data_buf_size); - TEST_ASSERT_EQUAL(0, err); + // Verify erased memory region + utest_printf("verifying written memory region\n"); + for (bd_size_t i = 0; i < data_buf_size; i++) { + TEST_ASSERT_EQUAL(out_data_buf[i], data_buf[i]); + } - // Read erased memory region - utest_printf("reading erased memory region\n"); - memset(out_data_buf, 0, data_buf_size); - err = block_device->read((void *)out_data_buf, start_address, data_buf_size); - TEST_ASSERT_EQUAL(0, err); + // Erase given memory region + utest_printf("erasing written memory region\n"); + err = block_device->secure_erase(start_address, data_buf_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); + // Read erased memory region + utest_printf("reading erased memory region\n"); + memset(out_data_buf, 0, data_buf_size); + err = block_device->secure_read((void *)out_data_buf, start_address, data_buf_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); + } else { + // First must Erase given memory region + utest_printf("erasing given memory region\n"); + int err = block_device->erase(start_address, data_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Write random data to selected region to make sure data is not accidentally set to "erased" value. + // With this pre-write, the test case will fail even if block_device->erase() is broken. + for (bd_size_t i = 0; i < data_buf_size; i++) { + data_buf[i] = (uint8_t) rand(); + } + + utest_printf("writing given memory region\n"); + err = block_device->program((const void *)data_buf, start_address, data_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Read written memory region to verify it contains information + memset(out_data_buf, 0, data_buf_size); + utest_printf("reading written memory region\n"); + err = block_device->read((void *)out_data_buf, start_address, data_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Verify erased memory region + utest_printf("verifying written memory region\n"); + for (bd_size_t i = 0; i < data_buf_size; i++) { + TEST_ASSERT_EQUAL(out_data_buf[i], data_buf[i]); + } + + // Erase given memory region + utest_printf("erasing written memory region\n"); + err = block_device->erase(start_address, data_buf_size); + TEST_ASSERT_EQUAL(0, err); + + // Read erased memory region + utest_printf("reading erased memory region\n"); + memset(out_data_buf, 0, data_buf_size); + err = block_device->read((void *)out_data_buf, start_address, data_buf_size); + TEST_ASSERT_EQUAL(0, err); + } // Verify erased memory region utest_printf("verifying erased memory region\n"); for (bd_size_t i = 0; i < data_buf_size; i++) { @@ -637,6 +734,7 @@ void test_contiguous_erase_write_read() // 3. Return step 2 for whole erase region // Test parameters + int err = 0; bd_size_t program_size = block_device->get_program_size(); TEST_ASSERT(program_size > 0); utest_printf("program_size=%" PRId64 "\n", program_size); @@ -679,8 +777,27 @@ void test_contiguous_erase_write_read() // Must Erase the whole region first utest_printf("erasing memory, from 0x%" PRIx64 " of size 0x%" PRIx64 "\n", start_address, contiguous_erase_size); - int err = block_device->erase(start_address, contiguous_erase_size); - TEST_ASSERT_EQUAL(0, err); + if (!strcmp(block_device->get_type(), "SECUREF")) { + uint32_t valid_app_id, valid_zone_id; + bd_size_t access_zone_id = start_address / block_device->secure_zone_size(); + int n; + + for (n = 0; n < AVAILABLE_PAIR_NUM; n++) { + if (access_zone_id == app_zone_available_pair[n].secure_zone_id) { + valid_app_id = app_zone_available_pair[n].app_id; + valid_zone_id = app_zone_available_pair[n].secure_zone_id; + break; + } + } + if (AVAILABLE_PAIR_NUM == n) { + utest_printf("Address %llx is not in the avaliable zone(%llu), SKIP!!!\n", start_address, access_zone_id); + } + err = block_device->secure_erase(start_address, contiguous_erase_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); + } else { + int err = block_device->erase(start_address, contiguous_erase_size); + TEST_ASSERT_EQUAL(0, err); + } // Pre-fill the to-be-erased region. By pre-filling the region, // we can be sure the test will not pass if the erase doesn't work. @@ -694,14 +811,54 @@ void test_contiguous_erase_write_read() } DEBUG_PRINTF("pre-filling memory, from 0x%" PRIx64 " of size 0x%" PRIx64 "", start_address + offset, write_read_buf_size); - err = block_device->program((const void *)write_read_buf, start_address + offset, write_read_buf_size); - TEST_ASSERT_EQUAL(0, err); + if (!strcmp(block_device->get_type(), "SECUREF")) { + uint32_t valid_app_id, valid_zone_id; + bd_size_t access_zone_id = start_address / block_device->secure_zone_size(); + int n; + + for (n = 0; n < AVAILABLE_PAIR_NUM; n++) { + if (access_zone_id == app_zone_available_pair[n].secure_zone_id) { + valid_app_id = app_zone_available_pair[n].app_id; + valid_zone_id = app_zone_available_pair[n].secure_zone_id; + break; + } + } + if (AVAILABLE_PAIR_NUM == n) { + utest_printf("Address %llx is not in the avaliable zone(%llu), SKIP!!!\n", start_address, access_zone_id); + continue; + } + DEBUG_PRINTF("Test secure_program in zone %d\n", valid_zone_id); + err = block_device->secure_program((const void *)write_read_buf, start_address + offset, write_read_buf_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); + } else { + err = block_device->program((const void *)write_read_buf, start_address + offset, write_read_buf_size); + TEST_ASSERT_EQUAL(0, err); + } } // Erase the whole region again utest_printf("erasing memory, from 0x%" PRIx64 " of size 0x%" PRIx64 "\n", start_address, contiguous_erase_size); - err = block_device->erase(start_address, contiguous_erase_size); - TEST_ASSERT_EQUAL(0, err); + if (!strcmp(block_device->get_type(), "SECUREF")) { + uint32_t valid_app_id, valid_zone_id; + bd_size_t access_zone_id = start_address / block_device->secure_zone_size(); + int n; + + for (n = 0; n < AVAILABLE_PAIR_NUM; n++) { + if (access_zone_id == app_zone_available_pair[n].secure_zone_id) { + valid_app_id = app_zone_available_pair[n].app_id; + valid_zone_id = app_zone_available_pair[n].secure_zone_id; + break; + } + } + if (AVAILABLE_PAIR_NUM == n) { + utest_printf("Address %llx is not in the avaliable zone(%llu), SKIP!!!\n", start_address, access_zone_id); + } + err = block_device->secure_erase(start_address, contiguous_erase_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); + } else { + err = block_device->erase(start_address, contiguous_erase_size); + TEST_ASSERT_EQUAL(0, err); + } // Loop through all write/read regions int region = 0; @@ -718,13 +875,55 @@ void test_contiguous_erase_write_read() } // Write test data - err = block_device->program((const void *)write_read_buf, start_address, write_read_buf_size); - TEST_ASSERT_EQUAL(0, err); + if (!strcmp(block_device->get_type(), "SECUREF")) { + uint32_t valid_app_id, valid_zone_id; + bd_size_t access_zone_id = start_address / block_device->secure_zone_size(); + int n; + + for (n = 0; n < AVAILABLE_PAIR_NUM; n++) { + if (access_zone_id == app_zone_available_pair[n].secure_zone_id) { + valid_app_id = app_zone_available_pair[n].app_id; + valid_zone_id = app_zone_available_pair[n].secure_zone_id; + break; + } + } + if (AVAILABLE_PAIR_NUM == n) { + utest_printf("Address %llx is not in the avaliable zone(%llu), SKIP!!!\n", start_address, access_zone_id); + continue; + } + DEBUG_PRINTF("Test secure_program in zone %d\n", valid_zone_id); + err = block_device->secure_program((const void *)write_read_buf, start_address, write_read_buf_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); + } else { + err = block_device->program((const void *)write_read_buf, start_address, write_read_buf_size); + TEST_ASSERT_EQUAL(0, err); + } // Read test data memset(write_read_buf, 0, (size_t)write_read_buf_size); - err = block_device->read(write_read_buf, start_address, write_read_buf_size); - TEST_ASSERT_EQUAL(0, err); + if (!strcmp(block_device->get_type(), "SECUREF")) { + uint32_t valid_app_id, valid_zone_id; + bd_size_t access_zone_id = start_address / block_device->secure_zone_size(); + int n; + + for (n = 0; n < AVAILABLE_PAIR_NUM; n++) { + if (access_zone_id == app_zone_available_pair[n].secure_zone_id) { + valid_app_id = app_zone_available_pair[n].app_id; + valid_zone_id = app_zone_available_pair[n].secure_zone_id; + break; + } + } + if (AVAILABLE_PAIR_NUM == n) { + utest_printf("Address %llx is not in the avaliable zone(%llu), SKIP!!!\n", start_address, access_zone_id); + continue; + } + DEBUG_PRINTF("Test secure_read in zone %d\n", valid_zone_id); + err = block_device->secure_read(write_read_buf, start_address, write_read_buf_size, valid_app_id); + TEST_ASSERT_EQUAL(0, err); + } else { + err = block_device->read(write_read_buf, start_address, write_read_buf_size); + TEST_ASSERT_EQUAL(0, err); + } // Verify read data srand(seed); @@ -746,6 +945,7 @@ void test_program_read_small_data_sizes() utest_printf("\nTest program-read small data sizes, from 1 to 7 bytes..\n"); TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found."); + TEST_SKIP_UNLESS_MESSAGE(strcmp(block_device->get_type(), "SECUREF"), "Secure Flash currently does not support this type of test."); bd_size_t program_size = block_device->get_program_size(); bd_size_t read_size = block_device->get_read_size(); @@ -803,6 +1003,7 @@ void test_unaligned_erase_blocks() TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found."); TEST_SKIP_UNLESS_MESSAGE(block_device->get_erase_value() != -1, "block device has no erase functionality."); + TEST_SKIP_UNLESS_MESSAGE(strcmp(block_device->get_type(), "SECUREF"), "Secure Flash currently does not support this type of test."); bd_addr_t addr = 0; bd_size_t sector_erase_size = block_device->get_erase_size(addr); @@ -856,6 +1057,7 @@ void test_deinit_bd() void test_write_deinit_init() { TEST_SKIP_UNLESS_MESSAGE(block_device != NULL, "no block device found."); + const char *bd_type = block_device->get_type(); // Determine start_address & stop_address bd_addr_t addr = sectors_addr[rand() % num_of_sectors]; bd_size_t erase_size = block_device->get_erase_size(addr); @@ -871,17 +1073,46 @@ void test_write_deinit_init() prog[j] = (uint8_t)'0' + i + j; } - int err; - err = block_device->erase(addr, erase_size); - TEST_ASSERT_EQUAL(err, 0); - err = block_device->program(prog, addr, prog_size); - TEST_ASSERT_EQUAL(err, 0); - err = block_device->deinit(); - TEST_ASSERT_EQUAL(0, err); - err = block_device->init(); - TEST_ASSERT_EQUAL(0, err); - err = block_device->read(buf, addr, prog_size); - TEST_ASSERT_EQUAL(0, memcmp(prog, buf, prog_size)); + int err, n; + if (!strcmp(bd_type, "SECUREF")) { + uint32_t valid_app_id, valid_zone_id; + bd_size_t access_zone_id = addr / block_device->secure_zone_size(); + + for (n = 0; n < AVAILABLE_PAIR_NUM; n++) { + if (access_zone_id == app_zone_available_pair[n].secure_zone_id) { + valid_app_id = app_zone_available_pair[n].app_id; + valid_zone_id = app_zone_available_pair[n].secure_zone_id; + break; + } + } + if (AVAILABLE_PAIR_NUM == n) { + utest_printf("Address %llx is not in the avaliable zone(%llu), SKIP!!!\n", addr, access_zone_id); + continue; + } + err = block_device->secure_erase(addr, erase_size, valid_app_id); + TEST_ASSERT_EQUAL(err, 0); + err = block_device->secure_program(prog, addr, prog_size, valid_app_id); + TEST_ASSERT_EQUAL(err, 0); + err = block_device->deinit(); + TEST_ASSERT_EQUAL(0, err); + err = block_device->init(); + TEST_ASSERT_EQUAL(0, err); + // err = block_device->secure_read(buf, addr, prog_size, app_id); + err = block_device->secure_read(buf, addr, prog_size, valid_app_id); + TEST_ASSERT_EQUAL(0, memcmp(prog, buf, prog_size)); + + } else { + err = block_device->erase(addr, erase_size); + TEST_ASSERT_EQUAL(err, 0); + err = block_device->program(prog, addr, prog_size); + TEST_ASSERT_EQUAL(err, 0); + err = block_device->deinit(); + TEST_ASSERT_EQUAL(0, err); + err = block_device->init(); + TEST_ASSERT_EQUAL(0, err); + err = block_device->read(buf, addr, prog_size); + TEST_ASSERT_EQUAL(0, memcmp(prog, buf, prog_size)); + } } free(prog); free(buf); @@ -911,6 +1142,8 @@ void test_get_type_functionality() TEST_ASSERT_EQUAL(0, strcmp(bd_type, "FLASHIAP")); #elif COMPONENT_SPINAND TEST_ASSERT_EQUAL(0, strcmp(bd_type, "SPINAND")); +#elif COMPONENT_SECUREF + TEST_ASSERT_EQUAL(0, strcmp(bd_type, "SECUREF")); #endif } @@ -975,11 +1208,14 @@ int get_bd_count() #if COMPONENT_SPINAND bd_arr[count++] = spinand; //6 #endif +#if COMPONENT_SECUREF + bd_arr[count++] = securef; //7 +#endif return count; } -static const char *prefix[] = {"SPIF ", "QSPIF ", "DATAFLASH ", "SD ", "FLASHIAP ", "OSPIF ", "SPINAND ", "DEFAULT "}; +static const char *prefix[] = {"SPIF ", "QSPIF ", "DATAFLASH ", "SD ", "FLASHIAP ", "OSPIF ", "SPINAND ", "SECUREF ", "DEFAULT "}; int main() { diff --git a/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/mx78_armor2_provision_example.h b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/mx78_armor2_provision_example.h new file mode 100644 index 00000000000..afc8e72b6a0 --- /dev/null +++ b/storage/blockdevice/tests/TESTS/blockdevice/general_block_device/mx78_armor2_provision_example.h @@ -0,0 +1,18 @@ +#ifndef MBED_MX78_ARMOR2_PROVISION_EXAMPLE_H +#define MBED_MX78_ARMOR2_PROVISION_EXAMPLE_H +#include + +#define AVAILABLE_PAIR_NUM 4 +typedef struct { + uint32_t app_id; + uint32_t secure_zone_id; +} app_zone_pair_t; + +const app_zone_pair_t app_zone_available_pair[AVAILABLE_PAIR_NUM] = { + {0xFFFFFFFE, 0x00}, + {0xFFFFFFFD, 0x01}, + {0x00000BBA, 0x02}, + {0x00000BBE, 0x03}, +}; + +#endif /* MBED_MX78_ARMOR2_PROVISION_EXAMPLE_H */ \ No newline at end of file diff --git a/targets/targets.json b/targets/targets.json index 49de7cd93f3..041dbcd885b 100644 --- a/targets/targets.json +++ b/targets/targets.json @@ -4571,6 +4571,9 @@ ], "detect_code": [ "0781" + ], + "components_add": [ + "SECUREF" ] }, "MCU_STM32L4R9xI": {