From 260a33574b0330a6723f2566d9e8b5c0c0e85345 Mon Sep 17 00:00:00 2001 From: Vikas Katariya Date: Fri, 15 Jan 2021 15:53:31 +0000 Subject: [PATCH] core: Upgrade TFM v1.2 related files These files have been imported/copied from: * ARMmbed/trusted-firmware-m * ARMmbed/tf-m-tests These are generic files, which are required for TF-M v1.2 integration with Mbed OS for PSA_V8M and PSA_DUAL_CORE targets. --- .../TARGET_TFM_LATEST/include/psa/client.h | 4 +- .../TARGET_TFM_LATEST/include/psa/crypto.h | 23 +- .../include/psa/crypto_client_struct.h | 7 +- .../include/psa/crypto_compat.h | 137 ++++- .../include/psa/crypto_extra.h | 42 +- .../include/psa/crypto_sizes.h | 52 +- .../include/psa/crypto_types.h | 183 +++--- .../include/psa/crypto_values.h | 336 ++++++---- .../include/psa/initial_attestation.h | 8 +- .../TARGET_TFM_LATEST/include/psa_audit_api.h | 127 ++++ .../include/psa_audit_defs.h | 34 ++ .../include/psa_manifest/sid.h | 6 - .../TARGET_TFM_LATEST/include/tfm_api.h | 4 +- .../include/tfm_crypto_defs.h | 1 - .../src/tfm_crypto_ipc_api.c | 99 ++- .../src/tfm_initial_attestation_ipc_api.c | 8 +- .../src/tfm_platform_ipc_api.c | 1 + tools/psa/tfm/bin_utils/assemble.py | 0 tools/psa/tfm/bin_utils/imgtool.py | 238 +------- tools/psa/tfm/bin_utils/imgtool/__init__.py | 15 + .../psa/tfm/bin_utils/imgtool/boot_record.py | 47 ++ tools/psa/tfm/bin_utils/imgtool/image.py | 574 ++++++++++++++++++ .../tfm/bin_utils/imgtool/keys/__init__.py | 94 +++ tools/psa/tfm/bin_utils/imgtool/keys/ecdsa.py | 157 +++++ .../tfm/bin_utils/imgtool/keys/ecdsa_test.py | 99 +++ .../psa/tfm/bin_utils/imgtool/keys/ed25519.py | 105 ++++ .../bin_utils/imgtool/keys/ed25519_test.py | 103 ++++ .../psa/tfm/bin_utils/imgtool/keys/general.py | 45 ++ tools/psa/tfm/bin_utils/imgtool/keys/rsa.py | 163 +++++ .../tfm/bin_utils/imgtool/keys/rsa_test.py | 115 ++++ .../psa/tfm/bin_utils/imgtool/keys/x25519.py | 107 ++++ tools/psa/tfm/bin_utils/imgtool/main.py | 386 ++++++++++++ tools/psa/tfm/bin_utils/imgtool/version.py | 53 ++ tools/psa/tfm/bin_utils/macro_parser.py | 5 +- tools/psa/tfm/bin_utils/wrapper.py | 126 ++++ 35 files changed, 2899 insertions(+), 605 deletions(-) create mode 100644 platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_audit_api.h create mode 100644 platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_audit_defs.h mode change 100644 => 100755 tools/psa/tfm/bin_utils/assemble.py mode change 100644 => 100755 tools/psa/tfm/bin_utils/imgtool.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/__init__.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/boot_record.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/image.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/__init__.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/ecdsa.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/ecdsa_test.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/ed25519.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/ed25519_test.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/general.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/rsa.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/rsa_test.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/keys/x25519.py create mode 100755 tools/psa/tfm/bin_utils/imgtool/main.py create mode 100644 tools/psa/tfm/bin_utils/imgtool/version.py create mode 100755 tools/psa/tfm/bin_utils/wrapper.py diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/client.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/client.h index 4115f930e9..a5e851803f 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/client.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/client.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2018-2019, Arm Limited. All rights reserved. + * Copyright (c) 2018-2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -126,7 +126,7 @@ psa_handle_t psa_connect(uint32_t sid, uint32_t version); * \brief Call an RoT Service on an established connection. * * \param[in] handle A handle to an established connection. - * \param[in] type The reuqest type. + * \param[in] type The request type. * Must be zero( \ref PSA_IPC_CALL) or positive. * \param[in] in_vec Array of input \ref psa_invec structures. * \param[in] in_len Number of input \ref psa_invec structures. diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto.h index 14591957b8..c58abda240 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto.h @@ -723,11 +723,12 @@ psa_status_t psa_import_key(const psa_key_attributes_t *attributes, * where `m` is the bit size associated with the curve, i.e. the bit size * of the order of the curve's coordinate field. This byte string is * in little-endian order for Montgomery curves (curve types - * `PSA_ECC_CURVE_CURVEXXX`), and in big-endian order for Weierstrass - * curves (curve types `PSA_ECC_CURVE_SECTXXX`, `PSA_ECC_CURVE_SECPXXX` - * and `PSA_ECC_CURVE_BRAINPOOL_PXXX`). - * This is the content of the `privateKey` field of the `ECPrivateKey` - * format defined by RFC 5915. + * `PSA_ECC_FAMILY_CURVEXXX`), and in big-endian order for Weierstrass + * curves (curve types `PSA_ECC_FAMILY_SECTXXX`, `PSA_ECC_FAMILY_SECPXXX` + * and `PSA_ECC_FAMILY_BRAINPOOL_PXXX`). + * For Weierstrass curves, this is the content of the `privateKey` field of + * the `ECPrivateKey` format defined by RFC 5915. For Montgomery curves, + * the format is defined by RFC 7748, and output is masked according to ยง5. * - For Diffie-Hellman key exchange key pairs (key types for which * #PSA_KEY_TYPE_IS_DH_KEY_PAIR is true), the * format is the representation of the private key `x` as a big-endian byte @@ -920,7 +921,7 @@ psa_status_t psa_hash_compare(psa_algorithm_t alg, const uint8_t *input, size_t input_length, const uint8_t *hash, - const size_t hash_length); + size_t hash_length); /** The type of the state data structure for multipart hash operations. * @@ -1288,7 +1289,7 @@ psa_status_t psa_mac_verify(psa_key_handle_t handle, const uint8_t *input, size_t input_length, const uint8_t *mac, - const size_t mac_length); + size_t mac_length); /** The type of the state data structure for multipart MAC operations. * @@ -3490,10 +3491,10 @@ psa_status_t psa_key_derivation_output_bytes( * length is determined by the curve, and sets the mandatory bits * accordingly. That is: * - * - #PSA_ECC_CURVE_CURVE25519: draw a 32-byte string - * and process it as specified in RFC 7748 §5. - * - #PSA_ECC_CURVE_CURVE448: draw a 56-byte string - * and process it as specified in RFC 7748 §5. + * - Curve25519 (#PSA_ECC_FAMILY_MONTGOMERY, 255 bits): draw a 32-byte + * string and process it as specified in RFC 7748 §5. + * - Curve448 (#PSA_ECC_FAMILY_MONTGOMERY, 448 bits): draw a 56-byte + * string and process it as specified in RFC 7748 §5. * * - For key types for which the key is represented by a single sequence of * \p bits bits with constraints as to which bit sequences are acceptable, diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_client_struct.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_client_struct.h index 1d919b051f..959f573b7b 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_client_struct.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_client_struct.h @@ -31,16 +31,15 @@ extern "C" { * data structure internally. */ struct psa_client_key_attributes_s { - uint32_t type; uint32_t lifetime; uint32_t id; uint32_t alg; - uint32_t alg2; uint32_t usage; - uint16_t bits; + size_t bits; + uint16_t type; }; -#define PSA_CLIENT_KEY_ATTRIBUTES_INIT {0, 0, 0, 0, 0, 0, 0} +#define PSA_CLIENT_KEY_ATTRIBUTES_INIT {0, 0, 0, 0, 0, 0} #ifdef __cplusplus } diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_compat.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_compat.h index 518008b8a7..26c205ac9b 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_compat.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_compat.h @@ -1,3 +1,9 @@ +/* + * Copyright (c) 2019-2020, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ /** * \file psa/crypto_compat.h * @@ -10,24 +16,6 @@ * \note This file may not be included directly. Applications must * include psa/crypto.h. */ -/* - * Copyright (C) 2019-2020, ARM Limited, 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. - * - * This file is part of mbed TLS (https://tls.mbed.org) - */ #ifndef PSA_CRYPTO_COMPAT_H #define PSA_CRYPTO_COMPAT_H @@ -50,6 +38,13 @@ extern "C" { typedef MBEDTLS_PSA_DEPRECATED size_t mbedtls_deprecated_size_t; typedef MBEDTLS_PSA_DEPRECATED psa_status_t mbedtls_deprecated_psa_status_t; typedef MBEDTLS_PSA_DEPRECATED psa_key_usage_t mbedtls_deprecated_psa_key_usage_t; +typedef MBEDTLS_PSA_DEPRECATED psa_ecc_family_t mbedtls_deprecated_psa_ecc_family_t; +typedef MBEDTLS_PSA_DEPRECATED psa_dh_family_t mbedtls_deprecated_psa_dh_family_t; +typedef MBEDTLS_PSA_DEPRECATED psa_ecc_family_t psa_ecc_curve_t; +typedef MBEDTLS_PSA_DEPRECATED psa_dh_family_t psa_dh_group_t; + +#define PSA_KEY_TYPE_GET_CURVE PSA_KEY_TYPE_ECC_GET_FAMILY +#define PSA_KEY_TYPE_GET_GROUP PSA_KEY_TYPE_DH_GET_FAMILY #define MBEDTLS_DEPRECATED_CONSTANT( type, value ) \ ( (mbedtls_deprecated_##type) ( value ) ) @@ -104,6 +99,112 @@ MBEDTLS_PSA_DEPRECATED psa_status_t psa_asymmetric_verify( psa_key_handle_t key, #endif /* MBEDTLS_DEPRECATED_REMOVED */ +/* + * Size-specific elliptic curve families. + */ +#define PSA_ECC_CURVE_SECP160K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_K1 ) +#define PSA_ECC_CURVE_SECP192K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_K1 ) +#define PSA_ECC_CURVE_SECP224K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_K1 ) +#define PSA_ECC_CURVE_SECP256K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_K1 ) +#define PSA_ECC_CURVE_SECP160R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R1 ) +#define PSA_ECC_CURVE_SECP192R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R1 ) +#define PSA_ECC_CURVE_SECP224R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R1 ) +#define PSA_ECC_CURVE_SECP256R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R1 ) +#define PSA_ECC_CURVE_SECP384R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R1 ) +#define PSA_ECC_CURVE_SECP521R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R1 ) +#define PSA_ECC_CURVE_SECP160R2 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R2 ) +#define PSA_ECC_CURVE_SECT163K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_K1 ) +#define PSA_ECC_CURVE_SECT233K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_K1 ) +#define PSA_ECC_CURVE_SECT239K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_K1 ) +#define PSA_ECC_CURVE_SECT283K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_K1 ) +#define PSA_ECC_CURVE_SECT409K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_K1 ) +#define PSA_ECC_CURVE_SECT571K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_K1 ) +#define PSA_ECC_CURVE_SECT163R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R1 ) +#define PSA_ECC_CURVE_SECT193R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R1 ) +#define PSA_ECC_CURVE_SECT233R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R1 ) +#define PSA_ECC_CURVE_SECT283R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R1 ) +#define PSA_ECC_CURVE_SECT409R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R1 ) +#define PSA_ECC_CURVE_SECT571R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R1 ) +#define PSA_ECC_CURVE_SECT163R2 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R2 ) +#define PSA_ECC_CURVE_SECT193R2 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R2 ) +#define PSA_ECC_CURVE_BRAINPOOL_P256R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_BRAINPOOL_P_R1 ) +#define PSA_ECC_CURVE_BRAINPOOL_P384R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_BRAINPOOL_P_R1 ) +#define PSA_ECC_CURVE_BRAINPOOL_P512R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_BRAINPOOL_P_R1 ) +#define PSA_ECC_CURVE_CURVE25519 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_MONTGOMERY ) +#define PSA_ECC_CURVE_CURVE448 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_MONTGOMERY ) + +/* + * Curves that changed name due to PSA specification. + */ +#define PSA_ECC_CURVE_SECP_K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_K1 ) +#define PSA_ECC_CURVE_SECP_R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R1 ) +#define PSA_ECC_CURVE_SECP_R2 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECP_R2 ) +#define PSA_ECC_CURVE_SECT_K1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_K1 ) +#define PSA_ECC_CURVE_SECT_R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R1 ) +#define PSA_ECC_CURVE_SECT_R2 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_SECT_R2 ) +#define PSA_ECC_CURVE_BRAINPOOL_P_R1 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_BRAINPOOL_P_R1 ) +#define PSA_ECC_CURVE_MONTGOMERY \ + MBEDTLS_DEPRECATED_CONSTANT( psa_ecc_family_t, PSA_ECC_FAMILY_MONTGOMERY ) + +/* + * Finite-field Diffie-Hellman families. + */ +#define PSA_DH_GROUP_FFDHE2048 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_dh_family_t, PSA_DH_FAMILY_RFC7919 ) +#define PSA_DH_GROUP_FFDHE3072 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_dh_family_t, PSA_DH_FAMILY_RFC7919 ) +#define PSA_DH_GROUP_FFDHE4096 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_dh_family_t, PSA_DH_FAMILY_RFC7919 ) +#define PSA_DH_GROUP_FFDHE6144 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_dh_family_t, PSA_DH_FAMILY_RFC7919 ) +#define PSA_DH_GROUP_FFDHE8192 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_dh_family_t, PSA_DH_FAMILY_RFC7919 ) + +/* + * Diffie-Hellman families that changed name due to PSA specification. + */ +#define PSA_DH_GROUP_RFC7919 \ + MBEDTLS_DEPRECATED_CONSTANT( psa_dh_family_t, PSA_DH_FAMILY_RFC7919 ) +#define PSA_DH_GROUP_CUSTOM \ + MBEDTLS_DEPRECATED_CONSTANT( psa_dh_family_t, PSA_DH_FAMILY_CUSTOM ) + #ifdef __cplusplus } #endif diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_extra.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_extra.h index d658b7664c..b8a4d7e36c 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_extra.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_extra.h @@ -35,7 +35,7 @@ extern "C" { * string. The length of the byte string is the length of the base prime `p` * in bytes. */ -#define PSA_KEY_TYPE_DSA_PUBLIC_KEY ((psa_key_type_t)0x60020000) +#define PSA_KEY_TYPE_DSA_PUBLIC_KEY ((psa_key_type_t)0x4002) /** DSA key pair (private and public key). * @@ -53,48 +53,10 @@ extern "C" { * Add 1 to the resulting integer and use this as the private key *x*. * */ -#define PSA_KEY_TYPE_DSA_KEY_PAIR ((psa_key_type_t)0x70020000) +#define PSA_KEY_TYPE_DSA_KEY_PAIR ((psa_key_type_t)0x7002) /**@}*/ -/** \brief Declare the enrollment algorithm for a key. - * - * An operation on a key may indifferently use the algorithm set with - * psa_set_key_algorithm() or with this function. - * - * \param[out] attributes The attribute structure to write to. - * \param alg2 A second algorithm that the key may be used - * for, in addition to the algorithm set with - * psa_set_key_algorithm(). - * - * \warning Setting an enrollment algorithm is not recommended, because - * using the same key with different algorithms can allow some - * attacks based on arithmetic relations between different - * computations made with the same key, or can escalate harmless - * side channels into exploitable ones. Use this function only - * if it is necessary to support a protocol for which it has been - * verified that the usage of the key with multiple algorithms - * is safe. - */ -static inline void psa_set_key_enrollment_algorithm( - psa_key_attributes_t *attributes, - psa_algorithm_t alg2) -{ - attributes->alg2 = alg2; -} - -/** Retrieve the enrollment algorithm policy from key attributes. - * - * \param[in] attributes The key attribute structure to query. - * - * \return The enrollment algorithm stored in the attribute structure. - */ -static inline psa_algorithm_t psa_get_key_enrollment_algorithm( - const psa_key_attributes_t *attributes) -{ - return attributes->alg2; -} - #ifdef __cplusplus } #endif diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_sizes.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_sizes.h index 4f67501b56..cbdf59716a 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_sizes.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_sizes.h @@ -133,51 +133,9 @@ * operations, and does not need to accept all key sizes up to the limit. */ #define PSA_VENDOR_RSA_MAX_KEY_BITS 4096 -/* The maximum size of an ECC key on this implementation, in bits. - * This is a vendor-specific macro. */ +/* The maximum size of an ECC key on this implementation, in bits */ #define PSA_VENDOR_ECC_MAX_CURVE_BITS 521 -/** Bit size associated with an elliptic curve. - * - * \param curve An elliptic curve (value of type #psa_ecc_curve_t). - * - * \return The size associated with \p curve, in bits. - * This may be 0 if the implementation does not support - * the specified curve. - */ -#define PSA_ECC_CURVE_BITS(curve) \ - ((curve) == PSA_ECC_CURVE_SECT163K1 ? 163 : \ - (curve) == PSA_ECC_CURVE_SECT163R1 ? 163 : \ - (curve) == PSA_ECC_CURVE_SECT163R2 ? 163 : \ - (curve) == PSA_ECC_CURVE_SECT193R1 ? 193 : \ - (curve) == PSA_ECC_CURVE_SECT193R2 ? 193 : \ - (curve) == PSA_ECC_CURVE_SECT233K1 ? 233 : \ - (curve) == PSA_ECC_CURVE_SECT233R1 ? 233 : \ - (curve) == PSA_ECC_CURVE_SECT239K1 ? 239 : \ - (curve) == PSA_ECC_CURVE_SECT283K1 ? 283 : \ - (curve) == PSA_ECC_CURVE_SECT283R1 ? 283 : \ - (curve) == PSA_ECC_CURVE_SECT409K1 ? 409 : \ - (curve) == PSA_ECC_CURVE_SECT409R1 ? 409 : \ - (curve) == PSA_ECC_CURVE_SECT571K1 ? 571 : \ - (curve) == PSA_ECC_CURVE_SECT571R1 ? 571 : \ - (curve) == PSA_ECC_CURVE_SECP160K1 ? 160 : \ - (curve) == PSA_ECC_CURVE_SECP160R1 ? 160 : \ - (curve) == PSA_ECC_CURVE_SECP160R2 ? 160 : \ - (curve) == PSA_ECC_CURVE_SECP192K1 ? 192 : \ - (curve) == PSA_ECC_CURVE_SECP192R1 ? 192 : \ - (curve) == PSA_ECC_CURVE_SECP224K1 ? 224 : \ - (curve) == PSA_ECC_CURVE_SECP224R1 ? 224 : \ - (curve) == PSA_ECC_CURVE_SECP256K1 ? 256 : \ - (curve) == PSA_ECC_CURVE_SECP256R1 ? 256 : \ - (curve) == PSA_ECC_CURVE_SECP384R1 ? 384 : \ - (curve) == PSA_ECC_CURVE_SECP521R1 ? 521 : \ - (curve) == PSA_ECC_CURVE_BRAINPOOL_P256R1 ? 256 : \ - (curve) == PSA_ECC_CURVE_BRAINPOOL_P384R1 ? 384 : \ - (curve) == PSA_ECC_CURVE_BRAINPOOL_P512R1 ? 512 : \ - (curve) == PSA_ECC_CURVE_CURVE25519 ? 255 : \ - (curve) == PSA_ECC_CURVE_CURVE448 ? 448 : \ - 0) - /** \def PSA_ALG_TLS12_PSK_TO_MS_MAX_PSK_LEN * * This macro returns the maximum length of the PSK supported @@ -419,7 +377,7 @@ * \param key_type An asymmetric key type (this may indifferently be a * key pair type or a public key type). * \param key_bits The size of the key in bits. - * \param alg The signature algorithm. + * \param alg The asymmetric encryption algorithm. * * \return If the parameters are valid and supported, return * a buffer size in bytes that guarantees that @@ -438,9 +396,9 @@ /** Sufficient output buffer size for psa_asymmetric_decrypt(). * - * This macro returns a sufficient buffer size for a ciphertext produced using + * This macro returns a sufficient buffer size for a plaintext produced using * a key of the specified type and size, with the specified algorithm. - * Note that the actual size of the ciphertext may be smaller, depending + * Note that the actual size of the plaintext may be smaller, depending * on the algorithm. * * \warning This function may call its arguments multiple times or @@ -450,7 +408,7 @@ * \param key_type An asymmetric key type (this may indifferently be a * key pair type or a public key type). * \param key_bits The size of the key in bits. - * \param alg The signature algorithm. + * \param alg The asymmetric encryption algorithm. * * \return If the parameters are valid and supported, return * a buffer size in bytes that guarantees that diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_types.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_types.h index 6ac95a8d9f..540e49aead 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_types.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_types.h @@ -55,85 +55,29 @@ typedef uint16_t psa_key_handle_t; /** \brief Encoding of a key type. */ -typedef uint32_t psa_key_type_t; +typedef uint16_t psa_key_type_t; -/** The type of PSA elliptic curve identifiers. +/** The type of PSA elliptic curve family identifiers. * * The curve identifier is required to create an ECC key using the * PSA_KEY_TYPE_ECC_KEY_PAIR() or PSA_KEY_TYPE_ECC_PUBLIC_KEY() * macros. * - * The encoding of curve identifiers is taken from the - * TLS Supported Groups Registry (formerly known as the - * TLS EC Named Curve Registry) - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 - * - * This specification defines identifiers for some of the curves in the IANA - * registry. Implementations that support other curves that are in the IANA - * registry should use the IANA value and a implementation-specific identifier. - * Implemenations that support non-IANA curves should use one of the following - * approaches for allocating a key type: - * - * 1. Select a ::psa_ecc_curve_t value in the range #PSA_ECC_CURVE_VENDOR_MIN to - * #PSA_ECC_CURVE_VENDOR_MAX, which is a subset of the IANA private use - * range. - * 2. Use a ::psa_key_type_t value that is vendor-defined. - * - * The first option is recommended. + * Values defined by this standard will never be in the range 0x80-0xff. + * Vendors who define additional families must use an encoding in this range. */ -typedef uint16_t psa_ecc_curve_t; +typedef uint8_t psa_ecc_family_t; -/** The type of PSA Diffie-Hellman group identifiers. +/** The type of PSA Diffie-Hellman group family identifiers. * * The group identifier is required to create an Diffie-Hellman key using the * PSA_KEY_TYPE_DH_KEY_PAIR() or PSA_KEY_TYPE_DH_PUBLIC_KEY() * macros. * - * The encoding of group identifiers is taken from the - * TLS Supported Groups Registry (formerly known as the - * TLS EC Named Curve Registry) - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 - * - * This specification defines identifiers for some of the groups in the IANA - * registry. Implementations that support other groups that are in the IANA - * registry should use the IANA value and a implementation-specific identifier. - * Implemenations that support non-IANA groups should use one of the following - * approaches for allocating a key type: - * - * 1. Select a ::psa_dh_group_t value in the range #PSA_DH_GROUP_VENDOR_MIN to - * #PSA_DH_GROUP_VENDOR_MAX, which is a subset of the IANA private use - * range. - * 2. Select a ::psa_dh_group_t value from the named groups allocated for - * GREASE in the IETF draft specification. The GREASE specification and - * values are listed below. - * 3. Use a ::psa_key_type_t value that is vendor-defined. - * - * Option 1 or 2 are recommended. - * - * The current draft of the GREASE specification is - * https://datatracker.ietf.org/doc/draft-ietf-tls-grease - * - * The following GREASE values are allocated for named groups: - * \code - * 0x0A0A - * 0x1A1A - * 0x2A2A - * 0x3A3A - * 0x4A4A - * 0x5A5A - * 0x6A6A - * 0x7A7A - * 0x8A8A - * 0x9A9A - * 0xAAAA - * 0xBABA - * 0xCACA - * 0xDADA - * 0xEAEA - * 0xFAFA - * \endcode + * Values defined by this standard will never be in the range 0x80-0xff. + * Vendors who define additional families must use an encoding in this range. */ -typedef uint16_t psa_dh_group_t; +typedef uint8_t psa_dh_family_t; /** \brief Encoding of a cryptographic algorithm. * @@ -156,18 +100,117 @@ typedef uint32_t psa_algorithm_t; * The lifetime of a key indicates where it is stored and what system actions * may create and destroy it. * - * Keys with the lifetime #PSA_KEY_LIFETIME_VOLATILE are automatically - * destroyed when the application terminates or on a power reset. + * Lifetime values have the following structure: + * - Bits 0-7 (#PSA_KEY_LIFETIME_GET_PERSISTENCE(\c lifetime)): + * persistence level. This value indicates what device management + * actions can cause it to be destroyed. In particular, it indicates + * whether the key is _volatile_ or _persistent_. + * See ::psa_key_persistence_t for more information. + * - Bits 8-31 (#PSA_KEY_LIFETIME_GET_LOCATION(\c lifetime)): + * location indicator. This value indicates where the key is stored + * and where operations on the key are performed. + * See ::psa_key_location_t for more information. + * + * Volatile keys are automatically destroyed when the application instance + * terminates or on a power reset of the device. Persistent keys are + * preserved until the application explicitly destroys them or until an + * implementation-specific device management event occurs (for example, + * a factory reset). * - * Keys with a lifetime other than #PSA_KEY_LIFETIME_VOLATILE are said - * to be _persistent_. - * Persistent keys are preserved if the application or the system restarts. * Persistent keys have a key identifier of type #psa_key_id_t. + * This identifier remains valid throughout the lifetime of the key, + * even if the application instance that created the key terminates. * The application can call psa_open_key() to open a persistent key that * it created previously. + * + * This specification defines two basic lifetime values: + * - Keys with the lifetime #PSA_KEY_LIFETIME_VOLATILE are volatile. + * All implementations should support this lifetime. + * - Keys with the lifetime #PSA_KEY_LIFETIME_PERSISTENT are persistent. + * All implementations that have access to persistent storage with + * appropriate security guarantees should support this lifetime. */ typedef uint32_t psa_key_lifetime_t; +/** Encoding of key persistence levels. + * + * What distinguishes different persistence levels is what device management + * events may cause keys to be destroyed. _Volatile_ keys are destroyed + * by a power reset. Persistent keys may be destroyed by events such as + * a transfer of ownership or a factory reset. What management events + * actually affect persistent keys at different levels is outside the + * scope of the PSA Cryptography specification. + * + * This specification defines the following values of persistence levels: + * - \c 0 = #PSA_KEY_PERSISTENCE_VOLATILE: volatile key. + * A volatile key is automatically destroyed by the implementation when + * the application instance terminates. In particular, a volatile key + * is automatically destroyed on a power reset of the device. + * - \c 1 = #PSA_KEY_PERSISTENCE_DEFAULT: + * persistent key with a default lifetime. + * Implementations should support this value if they support persistent + * keys at all. + * Applications should use this value if they have no specific needs that + * are only met by implementation-specific features. + * - \c 2-127: persistent key with a PSA-specified lifetime. + * The PSA Cryptography specification does not define the meaning of these + * values, but other PSA specifications may do so. + * - \c 128-254: persistent key with a vendor-specified lifetime. + * No PSA specification will define the meaning of these values, so + * implementations may choose the meaning freely. + * As a guideline, higher persistence levels should cause a key to survive + * more management events than lower levels. + * - \c 255 = #PSA_KEY_PERSISTENCE_READ_ONLY: + * read-only or write-once key. + * A key with this persistence level cannot be destroyed. + * Implementations that support such keys may either allow their creation + * through the PSA Cryptography API, preferably only to applications with + * the appropriate privilege, or only expose keys created through + * implementation-specific means such as a factory ROM engraving process. + * Note that keys that are read-only due to policy restrictions + * rather than due to physical limitations should not have this + * persistence levels. + * + * \note Key persistence levels are 8-bit values. Key management + * interfaces operate on lifetimes (type ::psa_key_lifetime_t) which + * encode the persistence as the lower 8 bits of a 32-bit value. + */ +typedef uint8_t psa_key_persistence_t; + +/** Encoding of key location indicators. + * + * If an implementation of this API can make calls to external + * cryptoprocessors such as secure elements, the location of a key + * indicates which secure element performs the operations on the key. + * If an implementation offers multiple physical locations for persistent + * storage, the location indicator reflects at which physical location + * the key is stored. + * + * This specification defines the following values of location indicators: + * - \c 0: primary local storage. + * All implementations should support this value. + * The primary local storage is typically the same storage area that + * contains the key metadata. + * - \c 1: primary secure element. + * Implementations should support this value if there is a secure element + * attached to the operating environment. + * As a guideline, secure elements may provide higher resistance against + * side channel and physical attacks than the primary local storage, but may + * have restrictions on supported key types, sizes, policies and operations + * and may have different performance characteristics. + * - \c 2-0x7fffff: other locations defined by a PSA specification. + * The PSA Cryptography API does not currently assign any meaning to these + * locations, but future versions of this specification or other PSA + * specifications may do so. + * - \c 0x800000-0xffffff: vendor-defined locations. + * No PSA specification will assign a meaning to locations in this range. + * + * \note Key location indicators are 24-bit values. Key management + * interfaces operate on lifetimes (type ::psa_key_lifetime_t) which + * encode the location as the upper 24 bits of a 32-bit value. + */ +typedef uint32_t psa_key_location_t; + /** Encoding of identifiers of persistent keys. * * - Applications may freely choose key identifiers in the range diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_values.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_values.h index e21ef273e9..75e30505d4 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_values.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/crypto_values.h @@ -272,7 +272,7 @@ * * Zero is not the encoding of any key type. */ -#define PSA_KEY_TYPE_NONE ((psa_key_type_t)0x00000000) +#define PSA_KEY_TYPE_NONE ((psa_key_type_t)0x0000) /** Vendor-defined key type flag. * @@ -281,15 +281,15 @@ * must use an encoding with the #PSA_KEY_TYPE_VENDOR_FLAG bit set and should * respect the bitwise structure used by standard encodings whenever practical. */ -#define PSA_KEY_TYPE_VENDOR_FLAG ((psa_key_type_t)0x80000000) +#define PSA_KEY_TYPE_VENDOR_FLAG ((psa_key_type_t)0x8000) -#define PSA_KEY_TYPE_CATEGORY_MASK ((psa_key_type_t)0x70000000) -#define PSA_KEY_TYPE_CATEGORY_SYMMETRIC ((psa_key_type_t)0x40000000) -#define PSA_KEY_TYPE_CATEGORY_RAW ((psa_key_type_t)0x50000000) -#define PSA_KEY_TYPE_CATEGORY_PUBLIC_KEY ((psa_key_type_t)0x60000000) -#define PSA_KEY_TYPE_CATEGORY_KEY_PAIR ((psa_key_type_t)0x70000000) +#define PSA_KEY_TYPE_CATEGORY_MASK ((psa_key_type_t)0x7000) +#define PSA_KEY_TYPE_CATEGORY_RAW ((psa_key_type_t)0x1000) +#define PSA_KEY_TYPE_CATEGORY_SYMMETRIC ((psa_key_type_t)0x2000) +#define PSA_KEY_TYPE_CATEGORY_PUBLIC_KEY ((psa_key_type_t)0x4000) +#define PSA_KEY_TYPE_CATEGORY_KEY_PAIR ((psa_key_type_t)0x7000) -#define PSA_KEY_TYPE_CATEGORY_FLAG_PAIR ((psa_key_type_t)0x10000000) +#define PSA_KEY_TYPE_CATEGORY_FLAG_PAIR ((psa_key_type_t)0x3000) /** Whether a key type is vendor-defined. * @@ -303,8 +303,8 @@ * This encompasses both symmetric keys and non-key data. */ #define PSA_KEY_TYPE_IS_UNSTRUCTURED(type) \ - (((type) & PSA_KEY_TYPE_CATEGORY_MASK & ~(psa_key_type_t)0x10000000) == \ - PSA_KEY_TYPE_CATEGORY_SYMMETRIC) + (((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_RAW || \ + ((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_SYMMETRIC) /** Whether a key type is asymmetric: either a key pair or a public key. */ #define PSA_KEY_TYPE_IS_ASYMMETRIC(type) \ @@ -347,7 +347,7 @@ * * A "key" of this type cannot be used for any cryptographic operation. * Applications may use this type to store arbitrary data in the keystore. */ -#define PSA_KEY_TYPE_RAW_DATA ((psa_key_type_t)0x50000001) +#define PSA_KEY_TYPE_RAW_DATA ((psa_key_type_t)0x1001) /** HMAC key. * @@ -357,21 +357,21 @@ * HMAC keys should generally have the same size as the underlying hash. * This size can be calculated with #PSA_HASH_SIZE(\c alg) where * \c alg is the HMAC algorithm or the underlying hash algorithm. */ -#define PSA_KEY_TYPE_HMAC ((psa_key_type_t)0x51000000) +#define PSA_KEY_TYPE_HMAC ((psa_key_type_t)0x1100) /** A secret for key derivation. * * The key policy determines which key derivation algorithm the key * can be used for. */ -#define PSA_KEY_TYPE_DERIVE ((psa_key_type_t)0x52000000) +#define PSA_KEY_TYPE_DERIVE ((psa_key_type_t)0x1200) /** Key for a cipher, AEAD or MAC algorithm based on the AES block cipher. * * The size of the key can be 16 bytes (AES-128), 24 bytes (AES-192) or * 32 bytes (AES-256). */ -#define PSA_KEY_TYPE_AES ((psa_key_type_t)0x40000001) +#define PSA_KEY_TYPE_AES ((psa_key_type_t)0x2400) /** Key for a cipher or MAC algorithm based on DES or 3DES (Triple-DES). * @@ -382,17 +382,17 @@ * deprecated and should only be used to decrypt legacy data. 3-key 3DES * is weak and deprecated and should only be used in legacy protocols. */ -#define PSA_KEY_TYPE_DES ((psa_key_type_t)0x40000002) +#define PSA_KEY_TYPE_DES ((psa_key_type_t)0x2301) /** Key for a cipher, AEAD or MAC algorithm based on the * Camellia block cipher. */ -#define PSA_KEY_TYPE_CAMELLIA ((psa_key_type_t)0x40000003) +#define PSA_KEY_TYPE_CAMELLIA ((psa_key_type_t)0x2403) /** Key for the RC4 stream cipher. * * Note that RC4 is weak and deprecated and should only be used in * legacy protocols. */ -#define PSA_KEY_TYPE_ARC4 ((psa_key_type_t)0x40000004) +#define PSA_KEY_TYPE_ARC4 ((psa_key_type_t)0x2002) /** Key for the ChaCha20 stream cipher or the Chacha20-Poly1305 AEAD algorithm. * @@ -401,30 +401,30 @@ * Implementations must support 12-byte nonces, may support 8-byte nonces, * and should reject other sizes. */ -#define PSA_KEY_TYPE_CHACHA20 ((psa_key_type_t)0x40000005) +#define PSA_KEY_TYPE_CHACHA20 ((psa_key_type_t)0x2004) /** RSA public key. */ -#define PSA_KEY_TYPE_RSA_PUBLIC_KEY ((psa_key_type_t)0x60010000) +#define PSA_KEY_TYPE_RSA_PUBLIC_KEY ((psa_key_type_t)0x4001) /** RSA key pair (private and public key). */ -#define PSA_KEY_TYPE_RSA_KEY_PAIR ((psa_key_type_t)0x70010000) +#define PSA_KEY_TYPE_RSA_KEY_PAIR ((psa_key_type_t)0x7001) /** Whether a key type is an RSA key (pair or public-only). */ #define PSA_KEY_TYPE_IS_RSA(type) \ (PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type) == PSA_KEY_TYPE_RSA_PUBLIC_KEY) -#define PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE ((psa_key_type_t)0x60030000) -#define PSA_KEY_TYPE_ECC_KEY_PAIR_BASE ((psa_key_type_t)0x70030000) -#define PSA_KEY_TYPE_ECC_CURVE_MASK ((psa_key_type_t)0x0000ffff) +#define PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE ((psa_key_type_t)0x4100) +#define PSA_KEY_TYPE_ECC_KEY_PAIR_BASE ((psa_key_type_t)0x7100) +#define PSA_KEY_TYPE_ECC_CURVE_MASK ((psa_key_type_t)0x00ff) /** Elliptic curve key pair. * - * \param curve A value of type ::psa_ecc_curve_t that identifies the - * ECC curve to be used. + * \param curve A value of type ::psa_ecc_family_t that + * identifies the ECC curve to be used. */ #define PSA_KEY_TYPE_ECC_KEY_PAIR(curve) \ (PSA_KEY_TYPE_ECC_KEY_PAIR_BASE | (curve)) /** Elliptic curve public key. * - * \param curve A value of type ::psa_ecc_curve_t that identifies the - * ECC curve to be used. + * \param curve A value of type ::psa_ecc_family_t that + * identifies the ECC curve to be used. */ #define PSA_KEY_TYPE_ECC_PUBLIC_KEY(curve) \ (PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE | (curve)) @@ -443,85 +443,97 @@ PSA_KEY_TYPE_ECC_PUBLIC_KEY_BASE) /** Extract the curve from an elliptic curve key type. */ -#define PSA_KEY_TYPE_GET_CURVE(type) \ - ((psa_ecc_curve_t) (PSA_KEY_TYPE_IS_ECC(type) ? \ +#define PSA_KEY_TYPE_ECC_GET_FAMILY(type) \ + ((psa_ecc_family_t) (PSA_KEY_TYPE_IS_ECC(type) ? \ ((type) & PSA_KEY_TYPE_ECC_CURVE_MASK) : \ 0)) -/* The encoding of curve identifiers is currently aligned with the - * TLS Supported Groups Registry (formerly known as the - * TLS EC Named Curve Registry) - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 - * The values are defined by RFC 8422 and RFC 7027. */ -#define PSA_ECC_CURVE_SECT163K1 ((psa_ecc_curve_t) 0x0001) -#define PSA_ECC_CURVE_SECT163R1 ((psa_ecc_curve_t) 0x0002) -#define PSA_ECC_CURVE_SECT163R2 ((psa_ecc_curve_t) 0x0003) -#define PSA_ECC_CURVE_SECT193R1 ((psa_ecc_curve_t) 0x0004) -#define PSA_ECC_CURVE_SECT193R2 ((psa_ecc_curve_t) 0x0005) -#define PSA_ECC_CURVE_SECT233K1 ((psa_ecc_curve_t) 0x0006) -#define PSA_ECC_CURVE_SECT233R1 ((psa_ecc_curve_t) 0x0007) -#define PSA_ECC_CURVE_SECT239K1 ((psa_ecc_curve_t) 0x0008) -#define PSA_ECC_CURVE_SECT283K1 ((psa_ecc_curve_t) 0x0009) -#define PSA_ECC_CURVE_SECT283R1 ((psa_ecc_curve_t) 0x000a) -#define PSA_ECC_CURVE_SECT409K1 ((psa_ecc_curve_t) 0x000b) -#define PSA_ECC_CURVE_SECT409R1 ((psa_ecc_curve_t) 0x000c) -#define PSA_ECC_CURVE_SECT571K1 ((psa_ecc_curve_t) 0x000d) -#define PSA_ECC_CURVE_SECT571R1 ((psa_ecc_curve_t) 0x000e) -#define PSA_ECC_CURVE_SECP160K1 ((psa_ecc_curve_t) 0x000f) -#define PSA_ECC_CURVE_SECP160R1 ((psa_ecc_curve_t) 0x0010) -#define PSA_ECC_CURVE_SECP160R2 ((psa_ecc_curve_t) 0x0011) -#define PSA_ECC_CURVE_SECP192K1 ((psa_ecc_curve_t) 0x0012) -#define PSA_ECC_CURVE_SECP192R1 ((psa_ecc_curve_t) 0x0013) -#define PSA_ECC_CURVE_SECP224K1 ((psa_ecc_curve_t) 0x0014) -#define PSA_ECC_CURVE_SECP224R1 ((psa_ecc_curve_t) 0x0015) -#define PSA_ECC_CURVE_SECP256K1 ((psa_ecc_curve_t) 0x0016) -#define PSA_ECC_CURVE_SECP256R1 ((psa_ecc_curve_t) 0x0017) -#define PSA_ECC_CURVE_SECP384R1 ((psa_ecc_curve_t) 0x0018) -#define PSA_ECC_CURVE_SECP521R1 ((psa_ecc_curve_t) 0x0019) -#define PSA_ECC_CURVE_BRAINPOOL_P256R1 ((psa_ecc_curve_t) 0x001a) -#define PSA_ECC_CURVE_BRAINPOOL_P384R1 ((psa_ecc_curve_t) 0x001b) -#define PSA_ECC_CURVE_BRAINPOOL_P512R1 ((psa_ecc_curve_t) 0x001c) -/** Curve25519. +/** SEC Koblitz curves over prime fields. * - * This is the curve defined in Bernstein et al., - * _Curve25519: new Diffie-Hellman speed records_, LNCS 3958, 2006. - * The algorithm #PSA_ALG_ECDH performs X25519 when used with this curve. + * This family comprises the following curves: + * secp192k1, secp224k1, secp256k1. + * They are defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf */ -#define PSA_ECC_CURVE_CURVE25519 ((psa_ecc_curve_t) 0x001d) -/** Curve448 - * - * This is the curve defined in Hamburg, - * _Ed448-Goldilocks, a new elliptic curve_, NIST ECC Workshop, 2015. - * The algorithm #PSA_ALG_ECDH performs X448 when used with this curve. - */ -#define PSA_ECC_CURVE_CURVE448 ((psa_ecc_curve_t) 0x001e) +#define PSA_ECC_FAMILY_SECP_K1 ((psa_ecc_family_t) 0x17) -/** Minimum value for a vendor-defined ECC curve identifier +/** SEC random curves over prime fields. * - * The range for vendor-defined curve identifiers is a subset of the IANA - * registry private use range, `0xfe00` - `0xfeff`. + * This family comprises the following curves: + * secp192k1, secp224r1, secp256r1, secp384r1, secp521r1. + * They are defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf */ -#define PSA_ECC_CURVE_VENDOR_MIN ((psa_ecc_curve_t) 0xfe00) -/** Maximum value for a vendor-defined ECC curve identifier - * - * The range for vendor-defined curve identifiers is a subset of the IANA - * registry private use range, `0xfe00` - `0xfeff`. - */ -#define PSA_ECC_CURVE_VENDOR_MAX ((psa_ecc_curve_t) 0xfe7f) +#define PSA_ECC_FAMILY_SECP_R1 ((psa_ecc_family_t) 0x12) +/* SECP160R2 (SEC2 v1, obsolete) */ +#define PSA_ECC_FAMILY_SECP_R2 ((psa_ecc_family_t) 0x1b) -#define PSA_KEY_TYPE_DH_PUBLIC_KEY_BASE ((psa_key_type_t)0x60040000) -#define PSA_KEY_TYPE_DH_KEY_PAIR_BASE ((psa_key_type_t)0x70040000) -#define PSA_KEY_TYPE_DH_GROUP_MASK ((psa_key_type_t)0x0000ffff) +/** SEC Koblitz curves over binary fields. + * + * This family comprises the following curves: + * sect163k1, sect233k1, sect239k1, sect283k1, sect409k1, sect571k1. + * They are defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf + */ +#define PSA_ECC_FAMILY_SECT_K1 ((psa_ecc_family_t) 0x27) + +/** SEC random curves over binary fields. + * + * This family comprises the following curves: + * sect163r1, sect233r1, sect283r1, sect409r1, sect571r1. + * They are defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf + */ +#define PSA_ECC_FAMILY_SECT_R1 ((psa_ecc_family_t) 0x22) + +/** SEC additional random curves over binary fields. + * + * This family comprises the following curve: + * sect163r2. + * It is defined in _Standards for Efficient Cryptography_, + * _SEC 2: Recommended Elliptic Curve Domain Parameters_. + * https://www.secg.org/sec2-v2.pdf + */ +#define PSA_ECC_FAMILY_SECT_R2 ((psa_ecc_family_t) 0x2b) + +/** Brainpool P random curves. + * + * This family comprises the following curves: + * brainpoolP160r1, brainpoolP192r1, brainpoolP224r1, brainpoolP256r1, + * brainpoolP320r1, brainpoolP384r1, brainpoolP512r1. + * It is defined in RFC 5639. + */ +#define PSA_ECC_FAMILY_BRAINPOOL_P_R1 ((psa_ecc_family_t) 0x30) + +/** Curve25519 and Curve448. + * + * This family comprises the following Montgomery curves: + * - 255-bit: Bernstein et al., + * _Curve25519: new Diffie-Hellman speed records_, LNCS 3958, 2006. + * The algorithm #PSA_ALG_ECDH performs X25519 when used with this curve. + * - 448-bit: Hamburg, + * _Ed448-Goldilocks, a new elliptic curve_, NIST ECC Workshop, 2015. + * The algorithm #PSA_ALG_ECDH performs X448 when used with this curve. + */ +#define PSA_ECC_FAMILY_MONTGOMERY ((psa_ecc_family_t) 0x41) + +#define PSA_KEY_TYPE_DH_PUBLIC_KEY_BASE ((psa_key_type_t)0x4200) +#define PSA_KEY_TYPE_DH_KEY_PAIR_BASE ((psa_key_type_t)0x7200) +#define PSA_KEY_TYPE_DH_GROUP_MASK ((psa_key_type_t)0x00ff) /** Diffie-Hellman key pair. * - * \param group A value of type ::psa_dh_group_t that identifies the + * \param group A value of type ::psa_dh_family_t that identifies the * Diffie-Hellman group to be used. */ #define PSA_KEY_TYPE_DH_KEY_PAIR(group) \ (PSA_KEY_TYPE_DH_KEY_PAIR_BASE | (group)) /** Diffie-Hellman public key. * - * \param group A value of type ::psa_dh_group_t that identifies the + * \param group A value of type ::psa_dh_family_t that identifies the * Diffie-Hellman group to be used. */ #define PSA_KEY_TYPE_DH_PUBLIC_KEY(group) \ @@ -541,35 +553,21 @@ PSA_KEY_TYPE_DH_PUBLIC_KEY_BASE) /** Extract the group from a Diffie-Hellman key type. */ -#define PSA_KEY_TYPE_GET_GROUP(type) \ - ((psa_dh_group_t) (PSA_KEY_TYPE_IS_DH(type) ? \ +#define PSA_KEY_TYPE_DH_GET_FAMILY(type) \ + ((psa_dh_family_t) (PSA_KEY_TYPE_IS_DH(type) ? \ ((type) & PSA_KEY_TYPE_DH_GROUP_MASK) : \ 0)) -/* The encoding of group identifiers is currently aligned with the - * TLS Supported Groups Registry (formerly known as the - * TLS EC Named Curve Registry) - * https://www.iana.org/assignments/tls-parameters/tls-parameters.xhtml#tls-parameters-8 - * The values are defined by RFC 7919. */ -#define PSA_DH_GROUP_FFDHE2048 ((psa_dh_group_t) 0x0100) -#define PSA_DH_GROUP_FFDHE3072 ((psa_dh_group_t) 0x0101) -#define PSA_DH_GROUP_FFDHE4096 ((psa_dh_group_t) 0x0102) -#define PSA_DH_GROUP_FFDHE6144 ((psa_dh_group_t) 0x0103) -#define PSA_DH_GROUP_FFDHE8192 ((psa_dh_group_t) 0x0104) - -/** Minimum value for a vendor-defined Diffie Hellman group identifier +/** Diffie-Hellman groups defined in RFC 7919 Appendix A. * - * The range for vendor-defined group identifiers is a subset of the IANA - * registry private use range, `0x01fc` - `0x01ff`. + * This family includes groups with the following key sizes (in bits): + * 2048, 3072, 4096, 6144, 8192. A given implementation may support + * all of these sizes or only a subset. */ -#define PSA_DH_GROUP_VENDOR_MIN ((psa_dh_group_t) 0x01fc) -/** Maximum value for a vendor-defined Diffie Hellman group identifier - * - * The range for vendor-defined group identifiers is a subset of the IANA - * registry private use range, `0x01fc` - `0x01ff`. - */ -#define PSA_DH_GROUP_VENDOR_MAX ((psa_dh_group_t) 0x01fd) +#define PSA_DH_FAMILY_RFC7919 ((psa_dh_family_t) 0x03) +#define PSA_GET_KEY_TYPE_BLOCK_SIZE_EXPONENT(type) \ + (((type) >> 8) & 7) /** The block size of a block cipher. * * \param type A cipher key type (value of type #psa_key_type_t). @@ -589,13 +587,9 @@ * \warning This macro may evaluate its argument multiple times. */ #define PSA_BLOCK_CIPHER_BLOCK_SIZE(type) \ - ( \ - (type) == PSA_KEY_TYPE_AES ? 16 : \ - (type) == PSA_KEY_TYPE_DES ? 8 : \ - (type) == PSA_KEY_TYPE_CAMELLIA ? 16 : \ - (type) == PSA_KEY_TYPE_ARC4 ? 1 : \ - (type) == PSA_KEY_TYPE_CHACHA20 ? 1 : \ - 0) + (((type) & PSA_KEY_TYPE_CATEGORY_MASK) == PSA_KEY_TYPE_CATEGORY_SYMMETRIC ? \ + 1u << PSA_GET_KEY_TYPE_BLOCK_SIZE_EXPONENT(type) : \ + 0u) /** Vendor-defined algorithm flag. * @@ -668,22 +662,24 @@ #define PSA_ALG_IS_AEAD(alg) \ (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_AEAD) -/** Whether the specified algorithm is a public-key signature algorithm. +/** Whether the specified algorithm is an asymmetric signature algorithm, + * also known as public-key signature algorithm. * * \param alg An algorithm identifier (value of type #psa_algorithm_t). * - * \return 1 if \p alg is a public-key signature algorithm, 0 otherwise. + * \return 1 if \p alg is an asymmetric signature algorithm, 0 otherwise. * This macro may return either 0 or 1 if \p alg is not a supported * algorithm identifier. */ #define PSA_ALG_IS_SIGN(alg) \ (((alg) & PSA_ALG_CATEGORY_MASK) == PSA_ALG_CATEGORY_SIGN) -/** Whether the specified algorithm is a public-key encryption algorithm. +/** Whether the specified algorithm is an asymmetric encryption algorithm, + * also known as public-key encryption algorithm. * * \param alg An algorithm identifier (value of type #psa_algorithm_t). * - * \return 1 if \p alg is a public-key encryption algorithm, 0 otherwise. + * \return 1 if \p alg is an asymmetric encryption algorithm, 0 otherwise. * This macro may return either 0 or 1 if \p alg is not a supported * algorithm identifier. */ @@ -1201,9 +1197,9 @@ /** Whether the specified algorithm is a hash-and-sign algorithm. * - * Hash-and-sign algorithms are public-key signature algorithms structured - * in two parts: first the calculation of a hash in a way that does not - * depend on the key, then the calculation of a signature from the + * Hash-and-sign algorithms are asymmetric (public-key) signature algorithms + * structured in two parts: first the calculation of a hash in a way that + * does not depend on the key, then the calculation of a signature from the * hash value and the key. * * \param alg An algorithm identifier (value of type #psa_algorithm_t). @@ -1255,7 +1251,7 @@ * #PSA_ALG_IS_HASH(\p hash_alg) is true) to use * for MGF1. * - * \return The corresponding RSA OAEP signature algorithm. + * \return The corresponding RSA OAEP encryption algorithm. * \return Unspecified if \p hash_alg is not a supported * hash algorithm. */ @@ -1477,17 +1473,17 @@ * is padded with zero bits. The byte order is either little-endian * or big-endian depending on the curve type. * - * - For Montgomery curves (curve types `PSA_ECC_CURVE_CURVEXXX`), + * - For Montgomery curves (curve types `PSA_ECC_FAMILY_CURVEXXX`), * the shared secret is the x-coordinate of `d_A Q_B = d_B Q_A` * in little-endian byte order. * The bit size is 448 for Curve448 and 255 for Curve25519. * - For Weierstrass curves over prime fields (curve types - * `PSA_ECC_CURVE_SECPXXX` and `PSA_ECC_CURVE_BRAINPOOL_PXXX`), + * `PSA_ECC_FAMILY_SECPXXX` and `PSA_ECC_FAMILY_BRAINPOOL_PXXX`), * the shared secret is the x-coordinate of `d_A Q_B = d_B Q_A` * in big-endian byte order. * The bit size is `m = ceiling(log_2(p))` for the field `F_p`. * - For Weierstrass curves over binary fields (curve types - * `PSA_ECC_CURVE_SECTXXX`), + * `PSA_ECC_FAMILY_SECTXXX`), * the shared secret is the x-coordinate of `d_A Q_B = d_B Q_A` * in big-endian byte order. * The bit size is `m` for the field `F_{2^m}`. @@ -1535,12 +1531,20 @@ * @{ */ -/** A volatile key only exists as long as the handle to it is not closed. +/** The default lifetime for volatile keys. + * + * A volatile key only exists as long as the handle to it is not closed. * The key material is guaranteed to be erased on a power reset. + * + * A key with this lifetime is typically stored in the RAM area of the + * PSA Crypto subsystem. However this is an implementation choice. + * If an implementation stores data about the key in a non-volatile memory, + * it must release all the resources associated with the key and erase the + * key material if the calling application terminates. */ #define PSA_KEY_LIFETIME_VOLATILE ((psa_key_lifetime_t)0x00000000) -/** The default storage area for persistent keys. +/** The default lifetime for persistent keys. * * A persistent key remains in storage until it is explicitly destroyed or * until the corresponding storage area is wiped. This specification does @@ -1551,9 +1555,77 @@ * This lifetime value is the default storage area for the calling * application. Implementations may offer other storage areas designated * by other lifetime values as implementation-specific extensions. + * See ::psa_key_lifetime_t for more information. */ #define PSA_KEY_LIFETIME_PERSISTENT ((psa_key_lifetime_t)0x00000001) +/** The persistence level of volatile keys. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_VOLATILE ((psa_key_persistence_t)0x00) + +/** The default persistence level for persistent keys. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_DEFAULT ((psa_key_persistence_t)0x01) + +/** A persistence level indicating that a key is never destroyed. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_READ_ONLY ((psa_key_persistence_t)0xff) + +#define PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) \ + ((psa_key_persistence_t)((lifetime) & 0x000000ff)) + +#define PSA_KEY_LIFETIME_GET_LOCATION(lifetime) \ + ((psa_key_location_t)((lifetime) >> 8)) + +/** Whether a key lifetime indicates that the key is volatile. + * + * A volatile key is automatically destroyed by the implementation when + * the application instance terminates. In particular, a volatile key + * is automatically destroyed on a power reset of the device. + * + * A key that is not volatile is persistent. Persistent keys are + * preserved until the application explicitly destroys them or until an + * implementation-specific device management event occurs (for example, + * a factory reset). + * + * \param lifetime The lifetime value to query (value of type + * ::psa_key_lifetime_t). + * + * \return \c 1 if the key is volatile, otherwise \c 0. + */ +#define PSA_KEY_LIFETIME_IS_VOLATILE(lifetime) \ + (PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) == \ + PSA_KEY_PERSISTENCE_VOLATILE) + +/** Construct a lifetime from a persistence level and a location. + * + * \param persistence The persistence level + * (value of type ::psa_key_persistence_t). + * \param location The location indicator + * (value of type ::psa_key_location_t). + * + * \return The constructed lifetime value. + */ +#define PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(persistence, location) \ + ((location) << 8 | (persistence)) + +/** The local storage area for persistent keys. + * + * This storage area is available on all systems that can store persistent + * keys without delegating the storage to a third-party cryptoprocessor. + * + * See ::psa_key_location_t for more information. + */ +#define PSA_KEY_LOCATION_LOCAL_STORAGE ((psa_key_location_t)0x000000) + +#define PSA_KEY_LOCATION_VENDOR_FLAG ((psa_key_location_t)0x800000) + /** The minimum value for a key identifier chosen by the application. */ #define PSA_KEY_ID_USER_MIN ((psa_key_id_t)0x00000001) diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/initial_attestation.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/initial_attestation.h index c125a4d6f4..50dd479c62 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/initial_attestation.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa/initial_attestation.h @@ -216,10 +216,10 @@ psa_initial_attest_get_token_size(size_t challenge_size, * \return Returns error code as specified in \ref psa_status_t */ psa_status_t -tfm_initial_attest_get_public_key(uint8_t *public_key, - size_t public_key_buf_size, - size_t *public_key_len, - psa_ecc_curve_t *elliptic_curve_type); +tfm_initial_attest_get_public_key(uint8_t *public_key, + size_t public_key_buf_size, + size_t *public_key_len, + psa_ecc_family_t *elliptic_curve_type); #ifdef __cplusplus } diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_audit_api.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_audit_api.h new file mode 100644 index 0000000000..d55a0572ac --- /dev/null +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_audit_api.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2018-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef __PSA_AUDIT_API__ +#define __PSA_AUDIT_API__ + +/** + * \brief PSA AUDIT API version + */ +#define PSA_AUDIT_API_VERSION_MAJOR (0) +#define PSA_AUDIT_API_VERSION_MINOR (1) + +#include "psa_audit_defs.h" +#include "psa/error.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * \brief Retrieves a record at the specified index + * + * \details The function retrieves an item specified by index and returns + * it on the buffer provided. The token is passed as a challenge + * value for the encryption scheme + * + * \note Currently the cryptography support is not yet enabled, so the + * token value is not used and must be passed as NULL, with 0 size + * + * \param[in] record_index Index of the record to retrieve + * \param[in] buffer_size Size in bytes of the provided buffer + * \param[in] token Must be set to NULL. Token used as a challenge + * for encryption, to protect against rollback + * attacks + * \param[in] token_size Must be set to 0. Size in bytes of the token + * used as challenge + * \param[out] buffer Buffer used to store the retrieved record + * \param[out] record_size Size in bytes of the retrieved record + * + * \return Returns values as specified by the \ref psa_status_t + * + */ +psa_status_t psa_audit_retrieve_record(const uint32_t record_index, + const uint32_t buffer_size, + const uint8_t *token, + const uint32_t token_size, + uint8_t *buffer, + uint32_t *record_size); +/** + * \brief Returns the total number and size of the records stored + * + * \details The function returns the total size in bytes and the + * total number of records stored + * + * \param[out] num_records Total number of records stored + * \param[out] size Total size of the records stored, in bytes + * + * \return Returns values as specified by the \ref psa_status_t + * + */ +psa_status_t psa_audit_get_info(uint32_t *num_records, uint32_t *size); + +/** + * \brief Returns the size of the record at the specified index + * + * \details The function returns the size of the record at the given index + * provided as input + * + * \param[in] record_index Index of the record to return the size + * \param[out] size Size of the specified record, in bytes + * + * \return Returns values as specified by the \ref psa_status_t + * + */ +psa_status_t psa_audit_get_record_info(const uint32_t record_index, + uint32_t *size); + +/** + * \brief Deletes a record at the specified index + * + * \details The function removes a record at the specified index. It passes + * an authorisation token for removal which is a MAC of the plain text + * + * \note Currently the cryptography support is not yet enabled, so the + * token value is not used and must be passed as NULL, with 0 size + * + * \note This is an experimental API function + * + * \param[in] record_index Index of the record to be removed. Currently, only + * the removal of the oldest entry, i.e. record_index 0 + * is supported + * \param[in] token Must be set to NULL. Token used as authorisation for + * removal of the specified record_index + * \param[in] token_size Must be set to 0. Size in bytes of the token used as + * authorisation for removal + * + * \return Returns values as specified by the \ref psa_status_t + * + */ +psa_status_t psa_audit_delete_record(const uint32_t record_index, + const uint8_t *token, + const uint32_t token_size); +/** + * \brief Adds a record + * + * \details This function adds a record. This is a Secure only callable function + * + * \note This is a Secure only callable API, Non-Secure calls will + * always return error + * + * \param[in] record Pointer to the memory buffer containing the record + * to be added + * + * \return Returns values as specified by the \ref psa_status_t + * + */ +psa_status_t psa_audit_add_record(const struct psa_audit_record *record); + +#ifdef __cplusplus +} +#endif + +#endif /* __PSA_AUDIT_API__ */ diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_audit_defs.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_audit_defs.h new file mode 100644 index 0000000000..479d76d54d --- /dev/null +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_audit_defs.h @@ -0,0 +1,34 @@ +/* + * Copyright (c) 2018-2019, Arm Limited. All rights reserved. + * + * SPDX-License-Identifier: BSD-3-Clause + * + */ + +#ifndef __PSA_AUDIT_DEFS_H__ +#define __PSA_AUDIT_DEFS_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include +#include "tfm_api.h" + +/*! + * \struct psa_audit_record + * + * \brief This structure contains the record that is added to the audit log + * by the requesting secure service + */ +struct psa_audit_record { + uint32_t size; /*!< Size in bytes of the id and payload fields */ + uint32_t id; /*!< ID of the record */ + uint8_t payload[]; /*!< Flexible array member for payload */ +}; + +#ifdef __cplusplus +} +#endif + +#endif /* __PSA_AUDIT_DEFS_H__ */ diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_manifest/sid.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_manifest/sid.h index f9bdf7cf37..87d6186be3 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_manifest/sid.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/psa_manifest/sid.h @@ -134,12 +134,6 @@ extern "C" { #define TFM_SECURE_CLIENT_2_SID (0x0000F0E0U) #define TFM_SECURE_CLIENT_2_VERSION (1U) -/******** TFM_SP_MULTI_CORE_TEST ********/ -#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_SID (0x0000F100U) -#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_0_VERSION (1U) -#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_SID (0x0000F101U) -#define MULTI_CORE_MULTI_CLIENT_CALL_TEST_1_VERSION (1U) - #ifdef __cplusplus } #endif diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/tfm_api.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/tfm_api.h index 09abc399b1..883eb5b8c9 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/tfm_api.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/tfm_api.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2017-2019, Arm Limited. All rights reserved. + * Copyright (c) 2017-2020, Arm Limited. All rights reserved. * * SPDX-License-Identifier: BSD-3-Clause * @@ -112,7 +112,7 @@ psa_handle_t tfm_psa_connect_veneer(uint32_t sid, uint32_t version); * \brief Call a secure function referenced by a connection handle. * * \param[in] handle Handle to connection. - * \param[in] ctrl_param Parameter structure, includes reuqest type, + * \param[in] ctrl_param Parameter structure, includes request type, * in_num and out_num. * \param[in] in_vec Array of input \ref psa_invec structures. * \param[in/out] out_vec Array of output \ref psa_outvec structures. diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/tfm_crypto_defs.h b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/tfm_crypto_defs.h index dd45e3b62d..53c03ce022 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/tfm_crypto_defs.h +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/include/tfm_crypto_defs.h @@ -40,7 +40,6 @@ struct tfm_crypto_pack_iovec { uint16_t step; /*!< Key derivation step */ psa_key_handle_t key_handle; /*!< Key handle */ psa_algorithm_t alg; /*!< Algorithm */ - psa_algorithm_t alg2; /*!< Enrollment Algorithm */ uint32_t op_handle; /*!< Frontend context handle associated to a * multipart operation */ diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_crypto_ipc_api.c b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_crypto_ipc_api.c index 70b3a0dfe5..1e4df7c02d 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_crypto_ipc_api.c +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_crypto_ipc_api.c @@ -735,6 +735,78 @@ psa_status_t psa_hash_clone(const psa_hash_operation_t *source_operation, #endif /* TFM_CRYPTO_HASH_MODULE_DISABLED */ } +psa_status_t psa_hash_compute(psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *hash, + size_t hash_size, + size_t *hash_length) +{ +#ifdef TFM_CRYPTO_HASH_MODULE_DISABLED + return PSA_ERROR_NOT_SUPPORTED; +#else + psa_status_t status; + struct tfm_crypto_pack_iovec iov = { + .sfn_id = TFM_CRYPTO_HASH_COMPUTE_SID, + .alg = alg, + }; + + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)}, + {.base = input, .len = input_length}, + }; + + psa_outvec out_vec[] = { + {.base = hash, .len = hash_size} + }; + + PSA_CONNECT(TFM_CRYPTO); + + status = API_DISPATCH(tfm_crypto_hash_compute, + TFM_CRYPTO_HASH_COMPUTE); + + *hash_length = out_vec[0].len; + + PSA_CLOSE(); + + return status; +#endif /* TFM_CRYPTO_HASH_MODULE_DISABLED */ +} + +psa_status_t psa_hash_compare(psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + const uint8_t *hash, + size_t hash_length) +{ +#ifdef TFM_CRYPTO_HASH_MODULE_DISABLED + return PSA_ERROR_NOT_SUPPORTED; +#else + psa_status_t status; + struct tfm_crypto_pack_iovec iov = { + .sfn_id = TFM_CRYPTO_HASH_COMPARE_SID, + .alg = alg, + }; + + psa_invec in_vec[] = { + {.base = &iov, .len = sizeof(struct tfm_crypto_pack_iovec)}, + {.base = input, .len = input_length}, + {.base = hash, .len = hash_length}, + }; + + PSA_CONNECT(TFM_CRYPTO); + + status = API_DISPATCH_NO_OUTVEC(tfm_crypto_hash_compare, + TFM_CRYPTO_HASH_COMPARE); + + PSA_CLOSE(); + + return status; +#endif /* TFM_CRYPTO_HASH_MODULE_DISABLED */ +} + + + psa_status_t psa_mac_sign_setup(psa_mac_operation_t *operation, psa_key_handle_t handle, psa_algorithm_t alg) @@ -1507,19 +1579,6 @@ psa_status_t psa_get_key_domain_parameters( return status; } -psa_status_t psa_hash_compare(psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - const uint8_t *hash, - const size_t hash_length) -{ - psa_status_t status; - - status = PSA_ERROR_NOT_SUPPORTED; - - return status; -} - psa_status_t psa_aead_update_ad(psa_aead_operation_t *operation, const uint8_t *input, size_t input_length) @@ -1790,20 +1849,6 @@ psa_status_t psa_key_derivation_output_key( #endif /* TFM_CRYPTO_GENERATOR_MODULE_DISABLED */ } -psa_status_t psa_hash_compute(psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - uint8_t *hash, - size_t hash_size, - size_t *hash_length) -{ - psa_status_t status; - - status = PSA_ERROR_NOT_SUPPORTED; - - return status; -} - psa_status_t psa_aead_encrypt_setup(psa_aead_operation_t *operation, psa_key_handle_t handle, psa_algorithm_t alg) diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_initial_attestation_ipc_api.c b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_initial_attestation_ipc_api.c index 78f9dec6f4..b4a8f379e3 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_initial_attestation_ipc_api.c +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_initial_attestation_ipc_api.c @@ -76,10 +76,10 @@ psa_initial_attest_get_token_size(size_t challenge_size, } psa_status_t -tfm_initial_attest_get_public_key(uint8_t *public_key, - size_t public_key_buf_size, - size_t *public_key_len, - psa_ecc_curve_t *elliptic_curve_type) +tfm_initial_attest_get_public_key(uint8_t *public_key, + size_t public_key_buf_size, + size_t *public_key_len, + psa_ecc_family_t *elliptic_curve_type) { psa_handle_t handle = PSA_NULL_HANDLE; psa_status_t status; diff --git a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_platform_ipc_api.c b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_platform_ipc_api.c index c43fe8bf39..0c1edf463f 100644 --- a/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_platform_ipc_api.c +++ b/platform/FEATURE_EXPERIMENTAL_API/FEATURE_PSA/TARGET_TFM/TARGET_TFM_LATEST/src/tfm_platform_ipc_api.c @@ -75,3 +75,4 @@ tfm_platform_ioctl(tfm_platform_ioctl_req_t request, return (enum tfm_platform_err_t) status; } } + diff --git a/tools/psa/tfm/bin_utils/assemble.py b/tools/psa/tfm/bin_utils/assemble.py old mode 100644 new mode 100755 diff --git a/tools/psa/tfm/bin_utils/imgtool.py b/tools/psa/tfm/bin_utils/imgtool.py old mode 100644 new mode 100755 index b5242456f0..78614745b1 --- a/tools/psa/tfm/bin_utils/imgtool.py +++ b/tools/psa/tfm/bin_utils/imgtool.py @@ -1,7 +1,6 @@ #! /usr/bin/env python3 # # Copyright 2017 Linaro Limited -# Copyright (c) 2018-2019, Arm Limited. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -15,240 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. -from __future__ import print_function -import os -import re -import argparse -from imgtool_lib import keys -from imgtool_lib import image -from imgtool_lib import version -import sys -import macro_parser -import fileinput - -sign_bin_size_re = re.compile(r"^\s*RE_SIGN_BIN_SIZE\s*=\s*(.*)") -image_load_address_re = re.compile(r"^\s*RE_IMAGE_LOAD_ADDRESS\s*=\s*(.*)") - -# Returns the last version number if present, or None if not -def get_last_version(path): - if (os.path.isfile(path) == False): # Version file not present - return None - else: # Version file is present, check it has a valid number inside it - with open(path, "r") as oldFile: - fileContents = oldFile.read() - if version.version_re.match(fileContents): # number is valid - return version.decode_version(fileContents) - else: - return None - -def next_version_number(args, defaultVersion, path): - newVersion = None - versionProvided = False - if (version.compare(args.version, defaultVersion) == 0): # Default version - lastVersion = get_last_version(path) - if (lastVersion is not None): - newVersion = version.increment_build_num(lastVersion) - else: - newVersion = version.increment_build_num(defaultVersion) - else: # Version number has been explicitly provided (not using the default) - versionProvided = True - newVersion = args.version - versionString = "{a}.{b}.{c}+{d}".format( - a=str(newVersion.major), - b=str(newVersion.minor), - c=str(newVersion.revision), - d=str(newVersion.build) - ) - if not versionProvided: - with open(path, "w") as newFile: - newFile.write(versionString) - print("**[INFO]** Image version number set to " + versionString) - return newVersion - -def gen_rsa2048(args): - keys.RSAutil.generate().export_private(args.key) - -def gen_rsa3072(args): - keys.RSAutil.generate(key_size=3072).export_private(args.key) - -keygens = { - 'rsa-2048': gen_rsa2048, - 'rsa-3072': gen_rsa3072, } - -def do_keygen(args): - if args.type not in keygens: - msg = "Unexpected key type: {}".format(args.type) - raise argparse.ArgumentTypeError(msg) - keygens[args.type](args) - -def do_getpub(args): - key = keys.load(args.key) - if args.lang == 'c': - key.emit_c() - else: - msg = "Unsupported language, valid are: c" - raise argparse.ArgumentTypeError(msg) - -def do_sign(args): - if args.rsa_pkcs1_15: - keys.sign_rsa_pss = False - - version_num = next_version_number(args, - version.decode_version("0"), - "lastVerNum.txt") - - if args.security_counter is None: - # Security counter has not been explicitly provided, - # generate it from the version number - args.security_counter = ((version_num.major << 24) - + (version_num.minor << 16) - + version_num.revision) - - if "_s.c" in args.layout: - sw_type = "SPE" - elif "_ns.c" in args.layout: - sw_type = "NSPE" - else: - sw_type = "NSPE_SPE" - - pad_size = macro_parser.evaluate_macro(args.layout, sign_bin_size_re, 0, 1) - img = image.Image.load(args.infile, - version=version_num, - header_size=args.header_size, - security_cnt=args.security_counter, - included_header=args.included_header, - pad=pad_size) - key = keys.load(args.key, args.public_key_format) if args.key else None - ram_load_address = macro_parser.evaluate_macro(args.layout, image_load_address_re, 0, 1) - img.sign(sw_type, key, ram_load_address, args.dependencies) - - if pad_size: - img.pad_to(pad_size, args.align) - - img.save(args.outfile) - -def do_flash(args): - image_value_re = re.compile(r"^\s*"+args.macro+"\s*=\s*(.*)") - value = macro_parser.evaluate_macro(args.layout, image_value_re, 0, 1, - True) - if args.setting == 1: - begin_line="set "+args.begin - else: - begin_line=args.begin - - for line in fileinput.input(args.infile, inplace=True): - if line.startswith(begin_line): - if args.division: - value = int(value/int(args.division)) - if args.phexa == 0: - line = begin_line+"="+str(value)+"\n" - else: - line = begin_line+"="+hex(value)+"\n" - sys.stdout.write(line) - -subcmds = { - 'keygen': do_keygen, - 'getpub': do_getpub, - 'sign': do_sign, - 'flash': do_flash, } - - -def get_dependencies(text): - if text is not None: - versions = [] - images = re.findall(r"\((\d+)", text) - if len(images) == 0: - msg = "Image dependency format is invalid: {}".format(text) - raise argparse.ArgumentTypeError(msg) - raw_versions = re.findall(r",\s*([0-9.+]+)\)", text) - if len(images) != len(raw_versions): - msg = '''There's a mismatch between the number of dependency images - and versions in: {}'''.format(text) - raise argparse.ArgumentTypeError(msg) - for raw_version in raw_versions: - try: - versions.append(version.decode_version(raw_version)) - except ValueError as e: - print(e) - dependencies = dict() - dependencies[image.DEP_IMAGES_KEY] = images - dependencies[image.DEP_VERSIONS_KEY] = versions - return dependencies - - -def alignment_value(text): - value = int(text) - if value not in [1, 2, 4, 8]: - msg = "{} must be one of 1, 2, 4 or 8".format(value) - raise argparse.ArgumentTypeError(msg) - return value - -def intparse(text): - """Parse a command line argument as an integer. - - Accepts 0x and other prefixes to allow other bases to be used.""" - return int(text, 0) - -def args(): - parser = argparse.ArgumentParser() - subs = parser.add_subparsers(help='subcommand help', dest='subcmd') - - keygenp = subs.add_parser('keygen', help='Generate pub/private keypair') - keygenp.add_argument('-k', '--key', metavar='filename', required=True) - keygenp.add_argument('-t', '--type', metavar='type', - choices=keygens.keys(), required=True) - - getpub = subs.add_parser('getpub', help='Get public key from keypair') - getpub.add_argument('-k', '--key', metavar='filename', required=True) - getpub.add_argument('-l', '--lang', metavar='lang', default='c') - - sign = subs.add_parser('sign', help='Sign an image with a private key') - sign.add_argument('-l', '--layout', required=True, - help='Location of the file that contains preprocessed macros') - sign.add_argument('-k', '--key', metavar='filename') - sign.add_argument("-K", "--public-key-format", - help='In what format to add the public key to the image manifest: full or hash', - metavar='pub_key_format', choices=['full', 'hash'], default='hash') - sign.add_argument("--align", type=alignment_value, required=True) - sign.add_argument("-v", "--version", type=version.decode_version, - default="0.0.0+0") - sign.add_argument("-d", "--dependencies", type=get_dependencies, - required=False, help='''Add dependence on another image, - format: "(,), ... "''') - sign.add_argument("-s", "--security-counter", type=intparse, - help='Specify explicitly the security counter value') - sign.add_argument("-H", "--header-size", type=intparse, required=True) - sign.add_argument("--included-header", default=False, action='store_true', - help='Image has gap for header') - sign.add_argument("--rsa-pkcs1-15", - help='Use old PKCS#1 v1.5 signature algorithm', - default=False, action='store_true') - sign.add_argument("infile") - sign.add_argument("outfile") - - flash = subs.add_parser('flash', help='modify flash script') - flash.add_argument("infile") - flash.add_argument('-l', '--layout', required=True, - help='Location of the file that contains preprocessed macros') - flash.add_argument('-m', '--macro', required =True, - help='macro symbol string to grep in preprocessed file') - flash.add_argument('-b', '--begin', required=True, - help='begin of line to replace ') - flash.add_argument('-s', '--setting',type=intparse,required=False,default=0, - help='search for window batch set variable') - flash.add_argument('-d', '--division', - required=False,type=intparse,default=0, - help='search for window batch set variable') - flash.add_argument('-p', '--phexa', - required=False,type=intparse,default=1, - help='print value in hexa') - - args = parser.parse_args() - if args.subcmd is None: - print('Must specify a subcommand', file=sys.stderr) - sys.exit(1) - - subcmds[args.subcmd](args) +from imgtool import main if __name__ == '__main__': - args() + main.imgtool() diff --git a/tools/psa/tfm/bin_utils/imgtool/__init__.py b/tools/psa/tfm/bin_utils/imgtool/__init__.py new file mode 100644 index 0000000000..cd3b885e72 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/__init__.py @@ -0,0 +1,15 @@ +# Copyright 2017-2020 Linaro Limited +# +# 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. + +imgtool_version = "1.7.0rc1" diff --git a/tools/psa/tfm/bin_utils/imgtool/boot_record.py b/tools/psa/tfm/bin_utils/imgtool/boot_record.py new file mode 100644 index 0000000000..4112b225a5 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/boot_record.py @@ -0,0 +1,47 @@ +# Copyright (c) 2019, Arm Limited. +# Copyright (c) 2020, Linaro Limited +# +# 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. + +from enum import Enum +import cbor + + +class SwComponent(int, Enum): + """ + Software component property IDs specified by + Arm's PSA Attestation API 1.0 document. + """ + TYPE = 1 + MEASUREMENT_VALUE = 2 + VERSION = 4 + SIGNER_ID = 5 + MEASUREMENT_DESCRIPTION = 6 + + +def create_sw_component_data(sw_type, sw_version, sw_measurement_description, + sw_measurement_value, sw_signer_id): + + # List of software component properties (Key ID + value) + properties = { + SwComponent.TYPE: sw_type, + SwComponent.VERSION: sw_version, + SwComponent.SIGNER_ID: sw_signer_id, + SwComponent.MEASUREMENT_DESCRIPTION: sw_measurement_description, + } + + # Note: The measurement value must be the last item of the property + # list because later it will be modified by the bootloader. + properties[SwComponent.MEASUREMENT_VALUE] = sw_measurement_value + + return cbor.dumps(properties) diff --git a/tools/psa/tfm/bin_utils/imgtool/image.py b/tools/psa/tfm/bin_utils/imgtool/image.py new file mode 100644 index 0000000000..20c2e32233 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/image.py @@ -0,0 +1,574 @@ +# Copyright 2018 Nordic Semiconductor ASA +# Copyright 2017-2020 Linaro Limited +# Copyright 2019-2020 Arm Limited +# +# 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. + +""" +Image signing and management. +""" + +from . import version as versmod +from .boot_record import create_sw_component_data +import click +from enum import Enum +from intelhex import IntelHex +import hashlib +import struct +import os.path +from .keys import rsa, ecdsa, x25519 +from cryptography.hazmat.primitives.asymmetric import ec, padding +from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.kdf.hkdf import HKDF +from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import hashes, hmac +from cryptography.exceptions import InvalidSignature + +IMAGE_MAGIC = 0x96f3b83d +IMAGE_HEADER_SIZE = 32 +BIN_EXT = "bin" +INTEL_HEX_EXT = "hex" +DEFAULT_MAX_SECTORS = 128 +MAX_ALIGN = 8 +DEP_IMAGES_KEY = "images" +DEP_VERSIONS_KEY = "versions" +MAX_SW_TYPE_LENGTH = 12 # Bytes + +# Image header flags. +IMAGE_F = { + 'PIC': 0x0000001, + 'NON_BOOTABLE': 0x0000010, + 'RAM_LOAD': 0x0000020, + 'ENCRYPTED': 0x0000004, +} + +TLV_VALUES = { + 'KEYHASH': 0x01, + 'PUBKEY': 0x02, + 'SHA256': 0x10, + 'RSA2048': 0x20, + 'ECDSA224': 0x21, + 'ECDSA256': 0x22, + 'RSA3072': 0x23, + 'ED25519': 0x24, + 'ENCRSA2048': 0x30, + 'ENCKW128': 0x31, + 'ENCEC256': 0x32, + 'ENCX25519': 0x33, + 'DEPENDENCY': 0x40, + 'SEC_CNT': 0x50, + 'BOOT_RECORD': 0x60, +} + +TLV_SIZE = 4 +TLV_INFO_SIZE = 4 +TLV_INFO_MAGIC = 0x6907 +TLV_PROT_INFO_MAGIC = 0x6908 + +boot_magic = bytes([ + 0x77, 0xc2, 0x95, 0xf3, + 0x60, 0xd2, 0xef, 0x7f, + 0x35, 0x52, 0x50, 0x0f, + 0x2c, 0xb6, 0x79, 0x80, ]) + +STRUCT_ENDIAN_DICT = { + 'little': '<', + 'big': '>' +} + +VerifyResult = Enum('VerifyResult', + """ + OK INVALID_MAGIC INVALID_TLV_INFO_MAGIC INVALID_HASH + INVALID_SIGNATURE + """) + + +class TLV(): + def __init__(self, endian, magic=TLV_INFO_MAGIC): + self.magic = magic + self.buf = bytearray() + self.endian = endian + + def __len__(self): + return TLV_INFO_SIZE + len(self.buf) + + def add(self, kind, payload): + """ + Add a TLV record. Kind should be a string found in TLV_VALUES above. + """ + e = STRUCT_ENDIAN_DICT[self.endian] + if isinstance(kind, int): + buf = struct.pack(e + 'BBH', kind, 0, len(payload)) + else: + buf = struct.pack(e + 'BBH', TLV_VALUES[kind], 0, len(payload)) + self.buf += buf + self.buf += payload + + def get(self): + if len(self.buf) == 0: + return bytes() + e = STRUCT_ENDIAN_DICT[self.endian] + header = struct.pack(e + 'HH', self.magic, len(self)) + return header + bytes(self.buf) + + +class Image(): + + def __init__(self, version=None, header_size=IMAGE_HEADER_SIZE, + pad_header=False, pad=False, confirm=False, align=1, + slot_size=0, max_sectors=DEFAULT_MAX_SECTORS, + overwrite_only=False, endian="little", load_addr=0, + erased_val=None, save_enctlv=False, security_counter=None): + self.version = version or versmod.decode_version("0") + self.header_size = header_size + self.pad_header = pad_header + self.pad = pad + self.confirm = confirm + self.align = align + self.slot_size = slot_size + self.max_sectors = max_sectors + self.overwrite_only = overwrite_only + self.endian = endian + self.base_addr = None + self.load_addr = 0 if load_addr is None else load_addr + self.erased_val = 0xff if erased_val is None else int(erased_val, 0) + self.payload = [] + self.enckey = None + self.save_enctlv = save_enctlv + self.enctlv_len = 0 + + if security_counter == 'auto': + # Security counter has not been explicitly provided, + # generate it from the version number + self.security_counter = ((self.version.major << 24) + + (self.version.minor << 16) + + self.version.revision) + else: + self.security_counter = security_counter + + def __repr__(self): + return "".format( + self.version, + self.header_size, + self.security_counter, + self.base_addr if self.base_addr is not None else "N/A", + self.load_addr, + self.align, + self.slot_size, + self.max_sectors, + self.overwrite_only, + self.endian, + self.__class__.__name__, + len(self.payload)) + + def load(self, path): + """Load an image from a given file""" + ext = os.path.splitext(path)[1][1:].lower() + try: + if ext == INTEL_HEX_EXT: + ih = IntelHex(path) + self.payload = ih.tobinarray() + self.base_addr = ih.minaddr() + else: + with open(path, 'rb') as f: + self.payload = f.read() + except FileNotFoundError: + raise click.UsageError("Input file not found") + + # Add the image header if needed. + if self.pad_header and self.header_size > 0: + if self.base_addr: + # Adjust base_addr for new header + self.base_addr -= self.header_size + self.payload = bytes([self.erased_val] * self.header_size) + \ + self.payload + + self.check_header() + + def save(self, path, hex_addr=None): + """Save an image from a given file""" + ext = os.path.splitext(path)[1][1:].lower() + if ext == INTEL_HEX_EXT: + # input was in binary format, but HEX needs to know the base addr + if self.base_addr is None and hex_addr is None: + raise click.UsageError("No address exists in input file " + "neither was it provided by user") + h = IntelHex() + if hex_addr is not None: + self.base_addr = hex_addr + h.frombytes(bytes=self.payload, offset=self.base_addr) + if self.pad: + trailer_size = self._trailer_size(self.align, self.max_sectors, + self.overwrite_only, + self.enckey, + self.save_enctlv, + self.enctlv_len) + trailer_addr = (self.base_addr + self.slot_size) - trailer_size + padding = bytearray([self.erased_val] * + (trailer_size - len(boot_magic))) + if self.confirm and not self.overwrite_only: + padding[-MAX_ALIGN] = 0x01 # image_ok = 0x01 + padding += boot_magic + h.puts(trailer_addr, bytes(padding)) + h.tofile(path, 'hex') + else: + if self.pad: + self.pad_to(self.slot_size) + with open(path, 'wb') as f: + f.write(self.payload) + + def check_header(self): + if self.header_size > 0 and not self.pad_header: + if any(v != 0 for v in self.payload[0:self.header_size]): + raise click.UsageError("Header padding was not requested and " + "image does not start with zeros") + + def check_trailer(self): + if self.slot_size > 0: + tsize = self._trailer_size(self.align, self.max_sectors, + self.overwrite_only, self.enckey, + self.save_enctlv, self.enctlv_len) + padding = self.slot_size - (len(self.payload) + tsize) + if padding < 0: + msg = "Image size (0x{:x}) + trailer (0x{:x}) exceeds " \ + "requested size 0x{:x}".format( + len(self.payload), tsize, self.slot_size) + raise click.UsageError(msg) + + def ecies_hkdf(self, enckey, plainkey): + if isinstance(enckey, ecdsa.ECDSA256P1Public): + newpk = ec.generate_private_key(ec.SECP256R1(), default_backend()) + shared = newpk.exchange(ec.ECDH(), enckey._get_public()) + else: + newpk = X25519PrivateKey.generate() + shared = newpk.exchange(enckey._get_public()) + derived_key = HKDF( + algorithm=hashes.SHA256(), length=48, salt=None, + info=b'MCUBoot_ECIES_v1', backend=default_backend()).derive(shared) + encryptor = Cipher(algorithms.AES(derived_key[:16]), + modes.CTR(bytes([0] * 16)), + backend=default_backend()).encryptor() + cipherkey = encryptor.update(plainkey) + encryptor.finalize() + mac = hmac.HMAC(derived_key[16:], hashes.SHA256(), + backend=default_backend()) + mac.update(cipherkey) + ciphermac = mac.finalize() + if isinstance(enckey, ecdsa.ECDSA256P1Public): + pubk = newpk.public_key().public_bytes( + encoding=Encoding.X962, + format=PublicFormat.UncompressedPoint) + else: + pubk = newpk.public_key().public_bytes( + encoding=Encoding.Raw, + format=PublicFormat.Raw) + return cipherkey, ciphermac, pubk + + def create(self, key, public_key_format, enckey, dependencies=None, + sw_type=None, custom_tlvs=None): + self.enckey = enckey + + # Calculate the hash of the public key + if key is not None: + pub = key.get_public_bytes() + sha = hashlib.sha256() + sha.update(pub) + pubbytes = sha.digest() + else: + pubbytes = bytes(hashlib.sha256().digest_size) + + protected_tlv_size = 0 + + if self.security_counter is not None: + # Size of the security counter TLV: header ('HH') + payload ('I') + # = 4 + 4 = 8 Bytes + protected_tlv_size += TLV_SIZE + 4 + + if sw_type is not None: + if len(sw_type) > MAX_SW_TYPE_LENGTH: + msg = "'{}' is too long ({} characters) for sw_type. Its " \ + "maximum allowed length is 12 characters.".format( + sw_type, len(sw_type)) + raise click.UsageError(msg) + + image_version = (str(self.version.major) + '.' + + str(self.version.minor) + '.' + + str(self.version.revision)) + + # The image hash is computed over the image header, the image + # itself and the protected TLV area. However, the boot record TLV + # (which is part of the protected area) should contain this hash + # before it is even calculated. For this reason the script fills + # this field with zeros and the bootloader will insert the right + # value later. + digest = bytes(hashlib.sha256().digest_size) + + # Create CBOR encoded boot record + boot_record = create_sw_component_data(sw_type, image_version, + "SHA256", digest, + pubbytes) + + protected_tlv_size += TLV_SIZE + len(boot_record) + + if dependencies is not None: + # Size of a Dependency TLV = Header ('HH') + Payload('IBBHI') + # = 4 + 12 = 16 Bytes + dependencies_num = len(dependencies[DEP_IMAGES_KEY]) + protected_tlv_size += (dependencies_num * 16) + + if custom_tlvs is not None: + for value in custom_tlvs.values(): + protected_tlv_size += TLV_SIZE + len(value) + + if protected_tlv_size != 0: + # Add the size of the TLV info header + protected_tlv_size += TLV_INFO_SIZE + + # At this point the image is already on the payload + # + # This adds the padding if image is not aligned to the 16 Bytes + # in encrypted mode + if self.enckey is not None: + pad_len = len(self.payload) % 16 + if pad_len > 0: + self.payload += bytes(16 - pad_len) + + # This adds the header to the payload as well + self.add_header(enckey, protected_tlv_size) + + prot_tlv = TLV(self.endian, TLV_PROT_INFO_MAGIC) + + # Protected TLVs must be added first, because they are also included + # in the hash calculation + protected_tlv_off = None + if protected_tlv_size != 0: + + e = STRUCT_ENDIAN_DICT[self.endian] + + if self.security_counter is not None: + payload = struct.pack(e + 'I', self.security_counter) + prot_tlv.add('SEC_CNT', payload) + + if sw_type is not None: + prot_tlv.add('BOOT_RECORD', boot_record) + + if dependencies is not None: + for i in range(dependencies_num): + payload = struct.pack( + e + 'B3x'+'BBHI', + int(dependencies[DEP_IMAGES_KEY][i]), + dependencies[DEP_VERSIONS_KEY][i].major, + dependencies[DEP_VERSIONS_KEY][i].minor, + dependencies[DEP_VERSIONS_KEY][i].revision, + dependencies[DEP_VERSIONS_KEY][i].build + ) + prot_tlv.add('DEPENDENCY', payload) + + if custom_tlvs is not None: + for tag, value in custom_tlvs.items(): + prot_tlv.add(tag, value) + + protected_tlv_off = len(self.payload) + self.payload += prot_tlv.get() + + tlv = TLV(self.endian) + + # Note that ecdsa wants to do the hashing itself, which means + # we get to hash it twice. + sha = hashlib.sha256() + sha.update(self.payload) + digest = sha.digest() + + tlv.add('SHA256', digest) + + if key is not None: + if public_key_format == 'hash': + tlv.add('KEYHASH', pubbytes) + else: + tlv.add('PUBKEY', pub) + + # `sign` expects the full image payload (sha256 done internally), + # while `sign_digest` expects only the digest of the payload + + if hasattr(key, 'sign'): + sig = key.sign(bytes(self.payload)) + else: + sig = key.sign_digest(digest) + tlv.add(key.sig_tlv(), sig) + + # At this point the image was hashed + signed, we can remove the + # protected TLVs from the payload (will be re-added later) + if protected_tlv_off is not None: + self.payload = self.payload[:protected_tlv_off] + + if enckey is not None: + plainkey = os.urandom(16) + + if isinstance(enckey, rsa.RSAPublic): + cipherkey = enckey._get_public().encrypt( + plainkey, padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None)) + self.enctlv_len = len(cipherkey) + tlv.add('ENCRSA2048', cipherkey) + elif isinstance(enckey, (ecdsa.ECDSA256P1Public, + x25519.X25519Public)): + cipherkey, mac, pubk = self.ecies_hkdf(enckey, plainkey) + enctlv = pubk + mac + cipherkey + self.enctlv_len = len(enctlv) + if isinstance(enckey, ecdsa.ECDSA256P1Public): + tlv.add('ENCEC256', enctlv) + else: + tlv.add('ENCX25519', enctlv) + + nonce = bytes([0] * 16) + cipher = Cipher(algorithms.AES(plainkey), modes.CTR(nonce), + backend=default_backend()) + encryptor = cipher.encryptor() + img = bytes(self.payload[self.header_size:]) + self.payload[self.header_size:] = \ + encryptor.update(img) + encryptor.finalize() + + self.payload += prot_tlv.get() + self.payload += tlv.get() + + self.check_trailer() + + def add_header(self, enckey, protected_tlv_size): + """Install the image header.""" + + flags = 0 + if enckey is not None: + flags |= IMAGE_F['ENCRYPTED'] + if self.load_addr != 0: + # Indicates that this image should be loaded into RAM + # instead of run directly from flash. + flags |= IMAGE_F['RAM_LOAD'] + + e = STRUCT_ENDIAN_DICT[self.endian] + fmt = (e + + # type ImageHdr struct { + 'I' + # Magic uint32 + 'I' + # LoadAddr uint32 + 'H' + # HdrSz uint16 + 'H' + # PTLVSz uint16 + 'I' + # ImgSz uint32 + 'I' + # Flags uint32 + 'BBHI' + # Vers ImageVersion + 'I' # Pad1 uint32 + ) # } + assert struct.calcsize(fmt) == IMAGE_HEADER_SIZE + header = struct.pack(fmt, + IMAGE_MAGIC, + self.load_addr, + self.header_size, + protected_tlv_size, # TLV Info header + Protected TLVs + len(self.payload) - self.header_size, # ImageSz + flags, + self.version.major, + self.version.minor or 0, + self.version.revision or 0, + self.version.build or 0, + 0) # Pad1 + self.payload = bytearray(self.payload) + self.payload[:len(header)] = header + + def _trailer_size(self, write_size, max_sectors, overwrite_only, enckey, + save_enctlv, enctlv_len): + # NOTE: should already be checked by the argument parser + magic_size = 16 + if overwrite_only: + return MAX_ALIGN * 2 + magic_size + else: + if write_size not in set([1, 2, 4, 8]): + raise click.BadParameter("Invalid alignment: {}".format( + write_size)) + m = DEFAULT_MAX_SECTORS if max_sectors is None else max_sectors + trailer = m * 3 * write_size # status area + if enckey is not None: + if save_enctlv: + # TLV saved by the bootloader is aligned + keylen = (int((enctlv_len - 1) / MAX_ALIGN) + 1) * MAX_ALIGN + else: + keylen = 16 + trailer += keylen * 2 # encryption keys + trailer += MAX_ALIGN * 4 # image_ok/copy_done/swap_info/swap_size + trailer += magic_size + return trailer + + def pad_to(self, size): + """Pad the image to the given size, with the given flash alignment.""" + tsize = self._trailer_size(self.align, self.max_sectors, + self.overwrite_only, self.enckey, + self.save_enctlv, self.enctlv_len) + padding = size - (len(self.payload) + tsize) + pbytes = bytearray([self.erased_val] * padding) + pbytes += bytearray([self.erased_val] * (tsize - len(boot_magic))) + if self.confirm and not self.overwrite_only: + pbytes[-MAX_ALIGN] = 0x01 # image_ok = 0x01 + pbytes += boot_magic + self.payload += pbytes + + @staticmethod + def verify(imgfile, key): + with open(imgfile, "rb") as f: + b = f.read() + + magic, _, header_size, _, img_size = struct.unpack('IIHHI', b[:16]) + version = struct.unpack('BBHI', b[20:28]) + + if magic != IMAGE_MAGIC: + return VerifyResult.INVALID_MAGIC, None, None + + tlv_info = b[header_size+img_size:header_size+img_size+TLV_INFO_SIZE] + magic, tlv_tot = struct.unpack('HH', tlv_info) + if magic != TLV_INFO_MAGIC: + return VerifyResult.INVALID_TLV_INFO_MAGIC, None, None + + sha = hashlib.sha256() + sha.update(b[:header_size+img_size]) + digest = sha.digest() + + tlv_off = header_size + img_size + tlv_end = tlv_off + tlv_tot + tlv_off += TLV_INFO_SIZE # skip tlv info + while tlv_off < tlv_end: + tlv = b[tlv_off:tlv_off+TLV_SIZE] + tlv_type, _, tlv_len = struct.unpack('BBH', tlv) + if tlv_type == TLV_VALUES["SHA256"]: + off = tlv_off + TLV_SIZE + if digest == b[off:off+tlv_len]: + if key is None: + return VerifyResult.OK, version, digest + else: + return VerifyResult.INVALID_HASH, None, None + elif key is not None and tlv_type == TLV_VALUES[key.sig_tlv()]: + off = tlv_off + TLV_SIZE + tlv_sig = b[off:off+tlv_len] + payload = b[:header_size+img_size] + try: + if hasattr(key, 'verify'): + key.verify(tlv_sig, payload) + else: + key.verify_digest(tlv_sig, digest) + return VerifyResult.OK, version, digest + except InvalidSignature: + # continue to next TLV + pass + tlv_off += TLV_SIZE + tlv_len + return VerifyResult.INVALID_SIGNATURE, None, None diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/__init__.py b/tools/psa/tfm/bin_utils/imgtool/keys/__init__.py new file mode 100644 index 0000000000..af6caffaa4 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/__init__.py @@ -0,0 +1,94 @@ +# Copyright 2017 Linaro Limited +# +# 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. + +""" +Cryptographic key management for imgtool. +""" + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric.rsa import ( + RSAPrivateKey, RSAPublicKey) +from cryptography.hazmat.primitives.asymmetric.ec import ( + EllipticCurvePrivateKey, EllipticCurvePublicKey) +from cryptography.hazmat.primitives.asymmetric.ed25519 import ( + Ed25519PrivateKey, Ed25519PublicKey) +from cryptography.hazmat.primitives.asymmetric.x25519 import ( + X25519PrivateKey, X25519PublicKey) + +from .rsa import RSA, RSAPublic, RSAUsageError, RSA_KEY_SIZES +from .ecdsa import ECDSA256P1, ECDSA256P1Public, ECDSAUsageError +from .ed25519 import Ed25519, Ed25519Public, Ed25519UsageError +from .x25519 import X25519, X25519Public, X25519UsageError + + +class PasswordRequired(Exception): + """Raised to indicate that the key is password protected, but a + password was not specified.""" + pass + + +def load(path, passwd=None): + """Try loading a key from the given path. Returns None if the password wasn't specified.""" + with open(path, 'rb') as f: + raw_pem = f.read() + try: + pk = serialization.load_pem_private_key( + raw_pem, + password=passwd, + backend=default_backend()) + # Unfortunately, the crypto library raises unhelpful exceptions, + # so we have to look at the text. + except TypeError as e: + msg = str(e) + if "private key is encrypted" in msg: + return None + raise e + except ValueError: + # This seems to happen if the key is a public key, let's try + # loading it as a public key. + pk = serialization.load_pem_public_key( + raw_pem, + backend=default_backend()) + + if isinstance(pk, RSAPrivateKey): + if pk.key_size not in RSA_KEY_SIZES: + raise Exception("Unsupported RSA key size: " + pk.key_size) + return RSA(pk) + elif isinstance(pk, RSAPublicKey): + if pk.key_size not in RSA_KEY_SIZES: + raise Exception("Unsupported RSA key size: " + pk.key_size) + return RSAPublic(pk) + elif isinstance(pk, EllipticCurvePrivateKey): + if pk.curve.name != 'secp256r1': + raise Exception("Unsupported EC curve: " + pk.curve.name) + if pk.key_size != 256: + raise Exception("Unsupported EC size: " + pk.key_size) + return ECDSA256P1(pk) + elif isinstance(pk, EllipticCurvePublicKey): + if pk.curve.name != 'secp256r1': + raise Exception("Unsupported EC curve: " + pk.curve.name) + if pk.key_size != 256: + raise Exception("Unsupported EC size: " + pk.key_size) + return ECDSA256P1Public(pk) + elif isinstance(pk, Ed25519PrivateKey): + return Ed25519(pk) + elif isinstance(pk, Ed25519PublicKey): + return Ed25519Public(pk) + elif isinstance(pk, X25519PrivateKey): + return X25519(pk) + elif isinstance(pk, X25519PublicKey): + return X25519Public(pk) + else: + raise Exception("Unknown key type: " + str(type(pk))) diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/ecdsa.py b/tools/psa/tfm/bin_utils/imgtool/keys/ecdsa.py new file mode 100644 index 0000000000..81aa321454 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/ecdsa.py @@ -0,0 +1,157 @@ +""" +ECDSA key management +""" + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.hashes import SHA256 + +from .general import KeyClass + +class ECDSAUsageError(Exception): + pass + +class ECDSA256P1Public(KeyClass): + def __init__(self, key): + self.key = key + + def shortname(self): + return "ecdsa" + + def _unsupported(self, name): + raise ECDSAUsageError("Operation {} requires private key".format(name)) + + def _get_public(self): + return self.key + + def get_public_bytes(self): + # The key is embedded into MBUboot in "SubjectPublicKeyInfo" format + return self._get_public().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_private_bytes(self, minimal): + self._unsupported('get_private_bytes') + + def export_private(self, path, passwd=None): + self._unsupported('export_private') + + def export_public(self, path): + """Write the public key to the given file.""" + pem = self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + with open(path, 'wb') as f: + f.write(pem) + + def sig_type(self): + return "ECDSA256_SHA256" + + def sig_tlv(self): + return "ECDSA256" + + def sig_len(self): + # Early versions of MCUboot (< v1.5.0) required ECDSA + # signatures to be padded to 72 bytes. Because the DER + # encoding is done with signed integers, the size of the + # signature will vary depending on whether the high bit is set + # in each value. This padding was done in a + # not-easily-reversible way (by just adding zeros). + # + # The signing code no longer requires this padding, and newer + # versions of MCUboot don't require it. But, continue to + # return the total length so that the padding can be done if + # requested. + return 72 + + def verify(self, signature, payload): + # strip possible paddings added during sign + signature = signature[:signature[1] + 2] + k = self.key + if isinstance(self.key, ec.EllipticCurvePrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=payload, + signature_algorithm=ec.ECDSA(SHA256())) + + +class ECDSA256P1(ECDSA256P1Public): + """ + Wrapper around an ECDSA private key. + """ + + def __init__(self, key): + """key should be an instance of EllipticCurvePrivateKey""" + self.key = key + self.pad_sig = False + + @staticmethod + def generate(): + pk = ec.generate_private_key( + ec.SECP256R1(), + backend=default_backend()) + return ECDSA256P1(pk) + + def _get_public(self): + return self.key.public_key() + + def _build_minimal_ecdsa_privkey(self, der): + ''' + Builds a new DER that only includes the EC private key, removing the + public key that is added as an "optional" BITSTRING. + ''' + offset_PUB = 68 + EXCEPTION_TEXT = "Error parsing ecdsa key. Please submit an issue!" + if der[offset_PUB] != 0xa1: + raise ECDSAUsageError(EXCEPTION_TEXT) + len_PUB = der[offset_PUB + 1] + b = bytearray(der[:-offset_PUB]) + offset_SEQ = 29 + if b[offset_SEQ] != 0x30: + raise ECDSAUsageError(EXCEPTION_TEXT) + b[offset_SEQ + 1] -= len_PUB + offset_OCT_STR = 27 + if b[offset_OCT_STR] != 0x04: + raise ECDSAUsageError(EXCEPTION_TEXT) + b[offset_OCT_STR + 1] -= len_PUB + if b[0] != 0x30 or b[1] != 0x81: + raise ECDSAUsageError(EXCEPTION_TEXT) + b[2] -= len_PUB + return b + + def get_private_bytes(self, minimal): + priv = self.key.private_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption()) + if minimal: + priv = self._build_minimal_ecdsa_privkey(priv) + return priv + + def export_private(self, path, passwd=None): + """Write the private key to the given file, protecting it with the optional password.""" + if passwd is None: + enc = serialization.NoEncryption() + else: + enc = serialization.BestAvailableEncryption(passwd) + pem = self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=enc) + with open(path, 'wb') as f: + f.write(pem) + + def raw_sign(self, payload): + """Return the actual signature""" + return self.key.sign( + data=payload, + signature_algorithm=ec.ECDSA(SHA256())) + + def sign(self, payload): + sig = self.raw_sign(payload) + if self.pad_sig: + # To make fixed length, pad with one or two zeros. + sig += b'\000' * (self.sig_len() - len(sig)) + return sig + else: + return sig diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/ecdsa_test.py b/tools/psa/tfm/bin_utils/imgtool/keys/ecdsa_test.py new file mode 100644 index 0000000000..31fe085900 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/ecdsa_test.py @@ -0,0 +1,99 @@ +""" +Tests for ECDSA keys +""" + +import io +import os.path +import sys +import tempfile +import unittest + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives.asymmetric import ec +from cryptography.hazmat.primitives.hashes import SHA256 + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) + +from imgtool.keys import load, ECDSA256P1, ECDSAUsageError + +class EcKeyGeneration(unittest.TestCase): + + def setUp(self): + self.test_dir = tempfile.TemporaryDirectory() + + def tname(self, base): + return os.path.join(self.test_dir.name, base) + + def tearDown(self): + self.test_dir.cleanup() + + def test_keygen(self): + name1 = self.tname("keygen.pem") + k = ECDSA256P1.generate() + k.export_private(name1, b'secret') + + self.assertIsNone(load(name1)) + + k2 = load(name1, b'secret') + + pubname = self.tname('keygen-pub.pem') + k2.export_public(pubname) + pk2 = load(pubname) + + # We should be able to export the public key from the loaded + # public key, but not the private key. + pk2.export_public(self.tname('keygen-pub2.pem')) + self.assertRaises(ECDSAUsageError, + pk2.export_private, self.tname('keygen-priv2.pem')) + + def test_emit(self): + """Basic sanity check on the code emitters.""" + k = ECDSA256P1.generate() + + ccode = io.StringIO() + k.emit_c_public(ccode) + self.assertIn("ecdsa_pub_key", ccode.getvalue()) + self.assertIn("ecdsa_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k.emit_rust_public(rustcode) + self.assertIn("ECDSA_PUB_KEY", rustcode.getvalue()) + + def test_emit_pub(self): + """Basic sanity check on the code emitters.""" + pubname = self.tname("public.pem") + k = ECDSA256P1.generate() + k.export_public(pubname) + + k2 = load(pubname) + + ccode = io.StringIO() + k2.emit_c_public(ccode) + self.assertIn("ecdsa_pub_key", ccode.getvalue()) + self.assertIn("ecdsa_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k2.emit_rust_public(rustcode) + self.assertIn("ECDSA_PUB_KEY", rustcode.getvalue()) + + def test_sig(self): + k = ECDSA256P1.generate() + buf = b'This is the message' + sig = k.raw_sign(buf) + + # The code doesn't have any verification, so verify this + # manually. + k.key.public_key().verify( + signature=sig, + data=buf, + signature_algorithm=ec.ECDSA(SHA256())) + + # Modify the message to make sure the signature fails. + self.assertRaises(InvalidSignature, + k.key.public_key().verify, + signature=sig, + data=b'This is thE message', + signature_algorithm=ec.ECDSA(SHA256())) + +if __name__ == '__main__': + unittest.main() diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/ed25519.py b/tools/psa/tfm/bin_utils/imgtool/keys/ed25519.py new file mode 100644 index 0000000000..f20c5dc44e --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/ed25519.py @@ -0,0 +1,105 @@ +""" +ED25519 key management +""" + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import ed25519 + +from .general import KeyClass + + +class Ed25519UsageError(Exception): + pass + + +class Ed25519Public(KeyClass): + def __init__(self, key): + self.key = key + + def shortname(self): + return "ed25519" + + def _unsupported(self, name): + raise Ed25519UsageError("Operation {} requires private key".format(name)) + + def _get_public(self): + return self.key + + def get_public_bytes(self): + # The key is embedded into MBUboot in "SubjectPublicKeyInfo" format + return self._get_public().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_private_bytes(self, minimal): + self._unsupported('get_private_bytes') + + def export_private(self, path, passwd=None): + self._unsupported('export_private') + + def export_public(self, path): + """Write the public key to the given file.""" + pem = self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + with open(path, 'wb') as f: + f.write(pem) + + def sig_type(self): + return "ED25519" + + def sig_tlv(self): + return "ED25519" + + def sig_len(self): + return 64 + + +class Ed25519(Ed25519Public): + """ + Wrapper around an ED25519 private key. + """ + + def __init__(self, key): + """key should be an instance of EllipticCurvePrivateKey""" + self.key = key + + @staticmethod + def generate(): + pk = ed25519.Ed25519PrivateKey.generate() + return Ed25519(pk) + + def _get_public(self): + return self.key.public_key() + + def get_private_bytes(self, minimal): + raise Ed25519UsageError("Operation not supported with {} keys".format( + self.shortname())) + + def export_private(self, path, passwd=None): + """ + Write the private key to the given file, protecting it with the + optional password. + """ + if passwd is None: + enc = serialization.NoEncryption() + else: + enc = serialization.BestAvailableEncryption(passwd) + pem = self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=enc) + with open(path, 'wb') as f: + f.write(pem) + + def sign_digest(self, digest): + """Return the actual signature""" + return self.key.sign(data=digest) + + def verify_digest(self, signature, digest): + """Verify that signature is valid for given digest""" + k = self.key + if isinstance(self.key, ed25519.Ed25519PrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=digest) diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/ed25519_test.py b/tools/psa/tfm/bin_utils/imgtool/keys/ed25519_test.py new file mode 100644 index 0000000000..31f43fe9bc --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/ed25519_test.py @@ -0,0 +1,103 @@ +""" +Tests for ECDSA keys +""" + +import hashlib +import io +import os.path +import sys +import tempfile +import unittest + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives.asymmetric import ed25519 + +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../..'))) + +from imgtool.keys import load, Ed25519, Ed25519UsageError + + +class Ed25519KeyGeneration(unittest.TestCase): + + def setUp(self): + self.test_dir = tempfile.TemporaryDirectory() + + def tname(self, base): + return os.path.join(self.test_dir.name, base) + + def tearDown(self): + self.test_dir.cleanup() + + def test_keygen(self): + name1 = self.tname("keygen.pem") + k = Ed25519.generate() + k.export_private(name1, b'secret') + + self.assertIsNone(load(name1)) + + k2 = load(name1, b'secret') + + pubname = self.tname('keygen-pub.pem') + k2.export_public(pubname) + pk2 = load(pubname) + + # We should be able to export the public key from the loaded + # public key, but not the private key. + pk2.export_public(self.tname('keygen-pub2.pem')) + self.assertRaises(Ed25519UsageError, + pk2.export_private, self.tname('keygen-priv2.pem')) + + def test_emit(self): + """Basic sanity check on the code emitters.""" + k = Ed25519.generate() + + ccode = io.StringIO() + k.emit_c_public(ccode) + self.assertIn("ed25519_pub_key", ccode.getvalue()) + self.assertIn("ed25519_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k.emit_rust_public(rustcode) + self.assertIn("ED25519_PUB_KEY", rustcode.getvalue()) + + def test_emit_pub(self): + """Basic sanity check on the code emitters.""" + pubname = self.tname("public.pem") + k = Ed25519.generate() + k.export_public(pubname) + + k2 = load(pubname) + + ccode = io.StringIO() + k2.emit_c_public(ccode) + self.assertIn("ed25519_pub_key", ccode.getvalue()) + self.assertIn("ed25519_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k2.emit_rust_public(rustcode) + self.assertIn("ED25519_PUB_KEY", rustcode.getvalue()) + + def test_sig(self): + k = Ed25519.generate() + buf = b'This is the message' + sha = hashlib.sha256() + sha.update(buf) + digest = sha.digest() + sig = k.sign_digest(digest) + + # The code doesn't have any verification, so verify this + # manually. + k.key.public_key().verify(signature=sig, data=digest) + + # Modify the message to make sure the signature fails. + sha = hashlib.sha256() + sha.update(b'This is thE message') + new_digest = sha.digest() + self.assertRaises(InvalidSignature, + k.key.public_key().verify, + signature=sig, + data=new_digest) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/general.py b/tools/psa/tfm/bin_utils/imgtool/keys/general.py new file mode 100644 index 0000000000..f6b8a09537 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/general.py @@ -0,0 +1,45 @@ +"""General key class.""" + +import sys + +AUTOGEN_MESSAGE = "/* Autogenerated by imgtool.py, do not edit. */" + +class KeyClass(object): + def _emit(self, header, trailer, encoded_bytes, indent, file=sys.stdout, len_format=None): + print(AUTOGEN_MESSAGE, file=file) + print(header, end='', file=file) + for count, b in enumerate(encoded_bytes): + if count % 8 == 0: + print("\n" + indent, end='', file=file) + else: + print(" ", end='', file=file) + print("0x{:02x},".format(b), end='', file=file) + print("\n" + trailer, file=file) + if len_format is not None: + print(len_format.format(len(encoded_bytes)), file=file) + + def emit_c_public(self, file=sys.stdout): + self._emit( + header="const unsigned char {}_pub_key[] = {{".format(self.shortname()), + trailer="};", + encoded_bytes=self.get_public_bytes(), + indent=" ", + len_format="const unsigned int {}_pub_key_len = {{}};".format(self.shortname()), + file=file) + + def emit_rust_public(self, file=sys.stdout): + self._emit( + header="static {}_PUB_KEY: &'static [u8] = &[".format(self.shortname().upper()), + trailer="];", + encoded_bytes=self.get_public_bytes(), + indent=" ", + file=file) + + def emit_private(self, minimal, file=sys.stdout): + self._emit( + header="const unsigned char enc_priv_key[] = {", + trailer="};", + encoded_bytes=self.get_private_bytes(minimal), + indent=" ", + len_format="const unsigned int enc_priv_key_len = {};", + file=file) diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/rsa.py b/tools/psa/tfm/bin_utils/imgtool/keys/rsa.py new file mode 100644 index 0000000000..85c034215c --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/rsa.py @@ -0,0 +1,163 @@ +""" +RSA Key management +""" + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import rsa +from cryptography.hazmat.primitives.asymmetric.padding import PSS, MGF1 +from cryptography.hazmat.primitives.hashes import SHA256 + +from .general import KeyClass + + +# Sizes that bootutil will recognize +RSA_KEY_SIZES = [2048, 3072] + + +class RSAUsageError(Exception): + pass + + +class RSAPublic(KeyClass): + """The public key can only do a few operations""" + def __init__(self, key): + self.key = key + + def key_size(self): + return self.key.key_size + + def shortname(self): + return "rsa" + + def _unsupported(self, name): + raise RSAUsageError("Operation {} requires private key".format(name)) + + def _get_public(self): + return self.key + + def get_public_bytes(self): + # The key embedded into MCUboot is in PKCS1 format. + return self._get_public().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.PKCS1) + + def get_private_bytes(self, minimal): + self._unsupported('get_private_bytes') + + def export_private(self, path, passwd=None): + self._unsupported('export_private') + + def export_public(self, path): + """Write the public key to the given file.""" + pem = self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + with open(path, 'wb') as f: + f.write(pem) + + def sig_type(self): + return "PKCS1_PSS_RSA{}_SHA256".format(self.key_size()) + + def sig_tlv(self): + return"RSA{}".format(self.key_size()) + + def sig_len(self): + return self.key_size() / 8 + + def verify(self, signature, payload): + k = self.key + if isinstance(self.key, rsa.RSAPrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=payload, + padding=PSS(mgf=MGF1(SHA256()), salt_length=32), + algorithm=SHA256()) + + +class RSA(RSAPublic): + """ + Wrapper around an RSA key, with imgtool support. + """ + + def __init__(self, key): + """The key should be a private key from cryptography""" + self.key = key + + @staticmethod + def generate(key_size=2048): + if key_size not in RSA_KEY_SIZES: + raise RSAUsageError("Key size {} is not supported by MCUboot" + .format(key_size)) + pk = rsa.generate_private_key( + public_exponent=65537, + key_size=key_size, + backend=default_backend()) + return RSA(pk) + + def _get_public(self): + return self.key.public_key() + + def _build_minimal_rsa_privkey(self, der): + ''' + Builds a new DER that only includes N/E/D/P/Q RSA parameters; + standard DER private bytes provided by OpenSSL also includes + CRT params (DP/DQ/QP) which can be removed. + ''' + OFFSET_N = 7 # N is always located at this offset + b = bytearray(der) + off = OFFSET_N + if b[off + 1] != 0x82: + raise RSAUsageError("Error parsing N while minimizing") + len_N = (b[off + 2] << 8) + b[off + 3] + 4 + off += len_N + if b[off + 1] != 0x03: + raise RSAUsageError("Error parsing E while minimizing") + len_E = b[off + 2] + 4 + off += len_E + if b[off + 1] != 0x82: + raise RSAUsageError("Error parsing D while minimizing") + len_D = (b[off + 2] << 8) + b[off + 3] + 4 + off += len_D + if b[off + 1] != 0x81: + raise RSAUsageError("Error parsing P while minimizing") + len_P = b[off + 2] + 3 + off += len_P + if b[off + 1] != 0x81: + raise RSAUsageError("Error parsing Q while minimizing") + len_Q = b[off + 2] + 3 + off += len_Q + # adjust DER size for removed elements + b[2] = (off - 4) >> 8 + b[3] = (off - 4) & 0xff + return b[:off] + + def get_private_bytes(self, minimal): + priv = self.key.private_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption()) + if minimal: + priv = self._build_minimal_rsa_privkey(priv) + return priv + + def export_private(self, path, passwd=None): + """Write the private key to the given file, protecting it with the + optional password.""" + if passwd is None: + enc = serialization.NoEncryption() + else: + enc = serialization.BestAvailableEncryption(passwd) + pem = self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=enc) + with open(path, 'wb') as f: + f.write(pem) + + def sign(self, payload): + # The verification code only allows the salt length to be the + # same as the hash length, 32. + return self.key.sign( + data=payload, + padding=PSS(mgf=MGF1(SHA256()), salt_length=32), + algorithm=SHA256()) diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/rsa_test.py b/tools/psa/tfm/bin_utils/imgtool/keys/rsa_test.py new file mode 100644 index 0000000000..b0afa83522 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/rsa_test.py @@ -0,0 +1,115 @@ +""" +Tests for RSA keys +""" + +import io +import os +import sys +import tempfile +import unittest + +from cryptography.exceptions import InvalidSignature +from cryptography.hazmat.primitives.asymmetric.padding import PSS, MGF1 +from cryptography.hazmat.primitives.hashes import SHA256 + +# Setup sys path so 'imgtool' is in it. +sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), + '../..'))) + +from imgtool.keys import load, RSA, RSAUsageError +from imgtool.keys.rsa import RSA_KEY_SIZES + + +class KeyGeneration(unittest.TestCase): + + def setUp(self): + self.test_dir = tempfile.TemporaryDirectory() + + def tname(self, base): + return os.path.join(self.test_dir.name, base) + + def tearDown(self): + self.test_dir.cleanup() + + def test_keygen(self): + # Try generating a RSA key with non-supported size + with self.assertRaises(RSAUsageError): + RSA.generate(key_size=1024) + + for key_size in RSA_KEY_SIZES: + name1 = self.tname("keygen.pem") + k = RSA.generate(key_size=key_size) + k.export_private(name1, b'secret') + + # Try loading the key without a password. + self.assertIsNone(load(name1)) + + k2 = load(name1, b'secret') + + pubname = self.tname('keygen-pub.pem') + k2.export_public(pubname) + pk2 = load(pubname) + + # We should be able to export the public key from the loaded + # public key, but not the private key. + pk2.export_public(self.tname('keygen-pub2.pem')) + self.assertRaises(RSAUsageError, pk2.export_private, + self.tname('keygen-priv2.pem')) + + def test_emit(self): + """Basic sanity check on the code emitters.""" + for key_size in RSA_KEY_SIZES: + k = RSA.generate(key_size=key_size) + + ccode = io.StringIO() + k.emit_c_public(ccode) + self.assertIn("rsa_pub_key", ccode.getvalue()) + self.assertIn("rsa_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k.emit_rust_public(rustcode) + self.assertIn("RSA_PUB_KEY", rustcode.getvalue()) + + def test_emit_pub(self): + """Basic sanity check on the code emitters, from public key.""" + pubname = self.tname("public.pem") + for key_size in RSA_KEY_SIZES: + k = RSA.generate(key_size=key_size) + k.export_public(pubname) + + k2 = load(pubname) + + ccode = io.StringIO() + k2.emit_c_public(ccode) + self.assertIn("rsa_pub_key", ccode.getvalue()) + self.assertIn("rsa_pub_key_len", ccode.getvalue()) + + rustcode = io.StringIO() + k2.emit_rust_public(rustcode) + self.assertIn("RSA_PUB_KEY", rustcode.getvalue()) + + def test_sig(self): + for key_size in RSA_KEY_SIZES: + k = RSA.generate(key_size=key_size) + buf = b'This is the message' + sig = k.sign(buf) + + # The code doesn't have any verification, so verify this + # manually. + k.key.public_key().verify( + signature=sig, + data=buf, + padding=PSS(mgf=MGF1(SHA256()), salt_length=32), + algorithm=SHA256()) + + # Modify the message to make sure the signature fails. + self.assertRaises(InvalidSignature, + k.key.public_key().verify, + signature=sig, + data=b'This is thE message', + padding=PSS(mgf=MGF1(SHA256()), salt_length=32), + algorithm=SHA256()) + + +if __name__ == '__main__': + unittest.main() diff --git a/tools/psa/tfm/bin_utils/imgtool/keys/x25519.py b/tools/psa/tfm/bin_utils/imgtool/keys/x25519.py new file mode 100644 index 0000000000..adb68a1a26 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/keys/x25519.py @@ -0,0 +1,107 @@ +""" +X25519 key management +""" + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization +from cryptography.hazmat.primitives.asymmetric import x25519 + +from .general import KeyClass + + +class X25519UsageError(Exception): + pass + + +class X25519Public(KeyClass): + def __init__(self, key): + self.key = key + + def shortname(self): + return "x25519" + + def _unsupported(self, name): + raise X25519UsageError("Operation {} requires private key".format(name)) + + def _get_public(self): + return self.key + + def get_public_bytes(self): + # The key is embedded into MBUboot in "SubjectPublicKeyInfo" format + return self._get_public().public_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + + def get_private_bytes(self, minimal): + self._unsupported('get_private_bytes') + + def export_private(self, path, passwd=None): + self._unsupported('export_private') + + def export_public(self, path): + """Write the public key to the given file.""" + pem = self._get_public().public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo) + with open(path, 'wb') as f: + f.write(pem) + + def sig_type(self): + return "X25519" + + def sig_tlv(self): + return "X25519" + + def sig_len(self): + return 32 + + +class X25519(X25519Public): + """ + Wrapper around an X25519 private key. + """ + + def __init__(self, key): + """key should be an instance of EllipticCurvePrivateKey""" + self.key = key + + @staticmethod + def generate(): + pk = x25519.X25519PrivateKey.generate() + return X25519(pk) + + def _get_public(self): + return self.key.public_key() + + def get_private_bytes(self, minimal): + return self.key.private_bytes( + encoding=serialization.Encoding.DER, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=serialization.NoEncryption()) + + def export_private(self, path, passwd=None): + """ + Write the private key to the given file, protecting it with the + optional password. + """ + if passwd is None: + enc = serialization.NoEncryption() + else: + enc = serialization.BestAvailableEncryption(passwd) + pem = self.key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.PKCS8, + encryption_algorithm=enc) + with open(path, 'wb') as f: + f.write(pem) + + def sign_digest(self, digest): + """Return the actual signature""" + return self.key.sign(data=digest) + + def verify_digest(self, signature, digest): + """Verify that signature is valid for given digest""" + k = self.key + if isinstance(self.key, x25519.X25519PrivateKey): + k = self.key.public_key() + return k.verify(signature=signature, data=digest) diff --git a/tools/psa/tfm/bin_utils/imgtool/main.py b/tools/psa/tfm/bin_utils/imgtool/main.py new file mode 100755 index 0000000000..e01a26c58a --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/main.py @@ -0,0 +1,386 @@ +#! /usr/bin/env python3 +# +# Copyright 2017-2020 Linaro Limited +# Copyright 2019-2020 Arm Limited +# +# 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. + +import re +import click +import getpass +import imgtool.keys as keys +import sys +from imgtool import image, imgtool_version +from imgtool.version import decode_version +from .keys import ( + RSAUsageError, ECDSAUsageError, Ed25519UsageError, X25519UsageError) + +MIN_PYTHON_VERSION = (3, 6) +if sys.version_info < MIN_PYTHON_VERSION: + sys.exit("Python %s.%s or newer is required by imgtool." + % MIN_PYTHON_VERSION) + + +def gen_rsa2048(keyfile, passwd): + keys.RSA.generate().export_private(path=keyfile, passwd=passwd) + + +def gen_rsa3072(keyfile, passwd): + keys.RSA.generate(key_size=3072).export_private(path=keyfile, + passwd=passwd) + + +def gen_ecdsa_p256(keyfile, passwd): + keys.ECDSA256P1.generate().export_private(keyfile, passwd=passwd) + + +def gen_ecdsa_p224(keyfile, passwd): + print("TODO: p-224 not yet implemented") + + +def gen_ed25519(keyfile, passwd): + keys.Ed25519.generate().export_private(path=keyfile, passwd=passwd) + + +def gen_x25519(keyfile, passwd): + keys.X25519.generate().export_private(path=keyfile, passwd=passwd) + + +valid_langs = ['c', 'rust'] +keygens = { + 'rsa-2048': gen_rsa2048, + 'rsa-3072': gen_rsa3072, + 'ecdsa-p256': gen_ecdsa_p256, + 'ecdsa-p224': gen_ecdsa_p224, + 'ed25519': gen_ed25519, + 'x25519': gen_x25519, +} + + +def load_key(keyfile): + # TODO: better handling of invalid pass-phrase + key = keys.load(keyfile) + if key is not None: + return key + passwd = getpass.getpass("Enter key passphrase: ").encode('utf-8') + return keys.load(keyfile, passwd) + + +def get_password(): + while True: + passwd = getpass.getpass("Enter key passphrase: ") + passwd2 = getpass.getpass("Reenter passphrase: ") + if passwd == passwd2: + break + print("Passwords do not match, try again") + + # Password must be bytes, always use UTF-8 for consistent + # encoding. + return passwd.encode('utf-8') + + +@click.option('-p', '--password', is_flag=True, + help='Prompt for password to protect key') +@click.option('-t', '--type', metavar='type', required=True, + type=click.Choice(keygens.keys()), prompt=True, + help='{}'.format('One of: {}'.format(', '.join(keygens.keys())))) +@click.option('-k', '--key', metavar='filename', required=True) +@click.command(help='Generate pub/private keypair') +def keygen(type, key, password): + password = get_password() if password else None + keygens[type](key, password) + + +@click.option('-l', '--lang', metavar='lang', default=valid_langs[0], + type=click.Choice(valid_langs)) +@click.option('-k', '--key', metavar='filename', required=True) +@click.command(help='Dump public key from keypair') +def getpub(key, lang): + key = load_key(key) + if key is None: + print("Invalid passphrase") + elif lang == 'c': + key.emit_c_public() + elif lang == 'rust': + key.emit_rust_public() + else: + raise ValueError("BUG: should never get here!") + + +@click.option('--minimal', default=False, is_flag=True, + help='Reduce the size of the dumped private key to include only ' + 'the minimum amount of data required to decrypt. This ' + 'might require changes to the build config. Check the docs!' + ) +@click.option('-k', '--key', metavar='filename', required=True) +@click.command(help='Dump private key from keypair') +def getpriv(key, minimal): + key = load_key(key) + if key is None: + print("Invalid passphrase") + try: + key.emit_private(minimal) + except (RSAUsageError, ECDSAUsageError, Ed25519UsageError, + X25519UsageError) as e: + raise click.UsageError(e) + + +@click.argument('imgfile') +@click.option('-k', '--key', metavar='filename') +@click.command(help="Check that signed image can be verified by given key") +def verify(key, imgfile): + key = load_key(key) if key else None + ret, version, digest = image.Image.verify(imgfile, key) + if ret == image.VerifyResult.OK: + print("Image was correctly validated") + print("Image version: {}.{}.{}+{}".format(*version)) + print("Image digest: {}".format(digest.hex())) + return + elif ret == image.VerifyResult.INVALID_MAGIC: + print("Invalid image magic; is this an MCUboot image?") + elif ret == image.VerifyResult.INVALID_TLV_INFO_MAGIC: + print("Invalid TLV info magic; is this an MCUboot image?") + elif ret == image.VerifyResult.INVALID_HASH: + print("Image has an invalid sha256 digest") + elif ret == image.VerifyResult.INVALID_SIGNATURE: + print("No signature found for the given key") + else: + print("Unknown return code: {}".format(ret)) + sys.exit(1) + + +def validate_version(ctx, param, value): + try: + decode_version(value) + return value + except ValueError as e: + raise click.BadParameter("{}".format(e)) + + +def validate_security_counter(ctx, param, value): + if value is not None: + if value.lower() == 'auto': + return 'auto' + else: + try: + return int(value, 0) + except ValueError: + raise click.BadParameter( + "{} is not a valid integer. Please use code literals " + "prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary." + .format(value)) + + +def validate_header_size(ctx, param, value): + min_hdr_size = image.IMAGE_HEADER_SIZE + if value < min_hdr_size: + raise click.BadParameter( + "Minimum value for -H/--header-size is {}".format(min_hdr_size)) + return value + + +def get_dependencies(ctx, param, value): + if value is not None: + versions = [] + images = re.findall(r"\((\d+)", value) + if len(images) == 0: + raise click.BadParameter( + "Image dependency format is invalid: {}".format(value)) + raw_versions = re.findall(r",\s*([0-9.+]+)\)", value) + if len(images) != len(raw_versions): + raise click.BadParameter( + '''There's a mismatch between the number of dependency images + and versions in: {}'''.format(value)) + for raw_version in raw_versions: + try: + versions.append(decode_version(raw_version)) + except ValueError as e: + raise click.BadParameter("{}".format(e)) + dependencies = dict() + dependencies[image.DEP_IMAGES_KEY] = images + dependencies[image.DEP_VERSIONS_KEY] = versions + return dependencies + + +class BasedIntParamType(click.ParamType): + name = 'integer' + + def convert(self, value, param, ctx): + try: + return int(value, 0) + except ValueError: + self.fail('%s is not a valid integer. Please use code literals ' + 'prefixed with 0b/0B, 0o/0O, or 0x/0X as necessary.' + % value, param, ctx) + + +@click.argument('outfile') +@click.argument('infile') +@click.option('--custom-tlv', required=False, nargs=2, default=[], + multiple=True, metavar='[tag] [value]', + help='Custom TLV that will be placed into protected area. ' + 'Add "0x" prefix if the value should be interpreted as an ' + 'integer, otherwise it will be interpreted as a string. ' + 'Specify the option multiple times to add multiple TLVs.') +@click.option('-R', '--erased-val', type=click.Choice(['0', '0xff']), + required=False, + help='The value that is read back from erased flash.') +@click.option('-x', '--hex-addr', type=BasedIntParamType(), required=False, + help='Adjust address in hex output file.') +@click.option('-L', '--load-addr', type=BasedIntParamType(), required=False, + help='Load address for image when it should run from RAM.') +@click.option('--save-enctlv', default=False, is_flag=True, + help='When upgrading, save encrypted key TLVs instead of plain ' + 'keys. Enable when BOOT_SWAP_SAVE_ENCTLV config option ' + 'was set.') +@click.option('-E', '--encrypt', metavar='filename', + help='Encrypt image using the provided public key. ' + '(Not supported in direct-xip or ram-load mode.)') +@click.option('-e', '--endian', type=click.Choice(['little', 'big']), + default='little', help="Select little or big endian") +@click.option('--overwrite-only', default=False, is_flag=True, + help='Use overwrite-only instead of swap upgrades') +@click.option('--boot-record', metavar='sw_type', help='Create CBOR encoded ' + 'boot record TLV. The sw_type represents the role of the ' + 'software component (e.g. CoFM for coprocessor firmware). ' + '[max. 12 characters]') +@click.option('-M', '--max-sectors', type=int, + help='When padding allow for this amount of sectors (defaults ' + 'to 128)') +@click.option('--confirm', default=False, is_flag=True, + help='When padding the image, mark it as confirmed (implies ' + '--pad)') +@click.option('--pad', default=False, is_flag=True, + help='Pad image to --slot-size bytes, adding trailer magic') +@click.option('-S', '--slot-size', type=BasedIntParamType(), required=True, + help='Size of the slot. If the slots have different sizes, use ' + 'the size of the secondary slot.') +@click.option('--pad-header', default=False, is_flag=True, + help='Add --header-size zeroed bytes at the beginning of the ' + 'image') +@click.option('-H', '--header-size', callback=validate_header_size, + type=BasedIntParamType(), required=True) +@click.option('--pad-sig', default=False, is_flag=True, + help='Add 0-2 bytes of padding to ECDSA signature ' + '(for mcuboot <1.5)') +@click.option('-d', '--dependencies', callback=get_dependencies, + required=False, help='''Add dependence on another image, format: + "(,), ... "''') +@click.option('-s', '--security-counter', callback=validate_security_counter, + help='Specify the value of security counter. Use the `auto` ' + 'keyword to automatically generate it from the image version.') +@click.option('-v', '--version', callback=validate_version, required=True) +@click.option('--align', type=click.Choice(['1', '2', '4', '8']), + required=True) +@click.option('--public-key-format', type=click.Choice(['hash', 'full']), + default='hash', help='In what format to add the public key to ' + 'the image manifest: full key or hash of the key.') +@click.option('-k', '--key', metavar='filename') +@click.command(help='''Create a signed or unsigned image\n + INFILE and OUTFILE are parsed as Intel HEX if the params have + .hex extension, otherwise binary format is used''') +def sign(key, public_key_format, align, version, pad_sig, header_size, + pad_header, slot_size, pad, confirm, max_sectors, overwrite_only, + endian, encrypt, infile, outfile, dependencies, load_addr, hex_addr, + erased_val, save_enctlv, security_counter, boot_record, custom_tlv): + + if confirm: + # Confirmed but non-padded images don't make much sense, because + # otherwise there's no trailer area for writing the confirmed status. + pad = True + img = image.Image(version=decode_version(version), header_size=header_size, + pad_header=pad_header, pad=pad, confirm=confirm, + align=int(align), slot_size=slot_size, + max_sectors=max_sectors, overwrite_only=overwrite_only, + endian=endian, load_addr=load_addr, erased_val=erased_val, + save_enctlv=save_enctlv, + security_counter=security_counter) + img.load(infile) + key = load_key(key) if key else None + enckey = load_key(encrypt) if encrypt else None + if enckey and key: + if ((isinstance(key, keys.ECDSA256P1) and + not isinstance(enckey, keys.ECDSA256P1Public)) + or (isinstance(key, keys.RSA) and + not isinstance(enckey, keys.RSAPublic))): + # FIXME + raise click.UsageError("Signing and encryption must use the same " + "type of key") + + if pad_sig and hasattr(key, 'pad_sig'): + key.pad_sig = True + + # Get list of custom protected TLVs from the command-line + custom_tlvs = {} + for tlv in custom_tlv: + tag = int(tlv[0], 0) + if tag in custom_tlvs: + raise click.UsageError('Custom TLV %s already exists.' % hex(tag)) + if tag in image.TLV_VALUES.values(): + raise click.UsageError( + 'Custom TLV %s conflicts with predefined TLV.' % hex(tag)) + + value = tlv[1] + if value.startswith('0x'): + if len(value[2:]) % 2: + raise click.UsageError('Custom TLV length is odd.') + custom_tlvs[tag] = bytes.fromhex(value[2:]) + else: + custom_tlvs[tag] = value.encode('utf-8') + + img.create(key, public_key_format, enckey, dependencies, boot_record, + custom_tlvs) + img.save(outfile, hex_addr) + + +class AliasesGroup(click.Group): + + _aliases = { + "create": "sign", + } + + def list_commands(self, ctx): + cmds = [k for k in self.commands] + aliases = [k for k in self._aliases] + return sorted(cmds + aliases) + + def get_command(self, ctx, cmd_name): + rv = click.Group.get_command(self, ctx, cmd_name) + if rv is not None: + return rv + if cmd_name in self._aliases: + return click.Group.get_command(self, ctx, self._aliases[cmd_name]) + return None + + +@click.command(help='Print imgtool version information') +def version(): + print(imgtool_version) + + +@click.command(cls=AliasesGroup, + context_settings=dict(help_option_names=['-h', '--help'])) +def imgtool(): + pass + + +imgtool.add_command(keygen) +imgtool.add_command(getpub) +imgtool.add_command(getpriv) +imgtool.add_command(verify) +imgtool.add_command(sign) +imgtool.add_command(version) + + +if __name__ == '__main__': + imgtool() diff --git a/tools/psa/tfm/bin_utils/imgtool/version.py b/tools/psa/tfm/bin_utils/imgtool/version.py new file mode 100644 index 0000000000..8910e0b1a4 --- /dev/null +++ b/tools/psa/tfm/bin_utils/imgtool/version.py @@ -0,0 +1,53 @@ +# Copyright 2017 Linaro Limited +# +# 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. + +""" +Semi Semantic Versioning + +Implements a subset of semantic versioning that is supportable by the image +header. +""" + +from collections import namedtuple +import re + +SemiSemVersion = namedtuple('SemiSemVersion', ['major', 'minor', 'revision', + 'build']) + +version_re = re.compile( + r"""^([1-9]\d*|0)(\.([1-9]\d*|0)(\.([1-9]\d*|0)(\+([1-9]\d*|0))?)?)?$""") + + +def decode_version(text): + """Decode the version string, which should be of the form maj.min.rev+build + """ + m = version_re.match(text) + if m: + result = SemiSemVersion( + int(m.group(1)) if m.group(1) else 0, + int(m.group(3)) if m.group(3) else 0, + int(m.group(5)) if m.group(5) else 0, + int(m.group(7)) if m.group(7) else 0) + return result + else: + msg = "Invalid version number, should be maj.min.rev+build with later " + msg += "parts optional" + raise ValueError(msg) + + +if __name__ == '__main__': + print(decode_version("1.2")) + print(decode_version("1.0")) + print(decode_version("0.0.2+75")) + print(decode_version("0.0.0+00")) diff --git a/tools/psa/tfm/bin_utils/macro_parser.py b/tools/psa/tfm/bin_utils/macro_parser.py index 5e489a9c57..5d9418a4e8 100644 --- a/tools/psa/tfm/bin_utils/macro_parser.py +++ b/tools/psa/tfm/bin_utils/macro_parser.py @@ -47,7 +47,7 @@ def parse_and_sum(text): # Opens a file that contains the macro of interest, then finds the macro with # a regular expression, parses the expression that is defined for the given # macro. Lastly it evaluates the expression with the parse_and_sum function -def evaluate_macro(file, regexp, matchGroupKey, matchGroupData): +def evaluate_macro(file, regexp, matchGroupKey, matchGroupData, bracketless=False): regexp_compiled = re.compile(regexp) if os.path.isabs(file): @@ -59,6 +59,9 @@ def evaluate_macro(file, regexp, matchGroupKey, matchGroupData): macroValue = {} with open(configFile, 'r') as macros_preprocessed_file: for line in macros_preprocessed_file: + if bracketless: + line=line.replace("(","") + line=line.replace(")","") m = regexp_compiled.match(line) if m is not None: macroValue[m.group(matchGroupKey)] = \ diff --git a/tools/psa/tfm/bin_utils/wrapper.py b/tools/psa/tfm/bin_utils/wrapper.py new file mode 100755 index 0000000000..7799ce06da --- /dev/null +++ b/tools/psa/tfm/bin_utils/wrapper.py @@ -0,0 +1,126 @@ +#! /usr/bin/env python3 +# +# ----------------------------------------------------------------------------- +# Copyright (c) 2020, Arm Limited. All rights reserved. +# +# SPDX-License-Identifier: BSD-3-Clause +# +# ----------------------------------------------------------------------------- + +import re +import os +import sys +import click + +# Add the cwd to the path so that if there is a version of imgtool in there then +# it gets used over the system imgtool. Used so that imgtool from upstream +# mcuboot is preferred over system imgtool +cwd = os.getcwd() +sys.path = [cwd] + sys.path +import imgtool +import imgtool.main + +parser_path = os.path.abspath(os.path.join(os.path.dirname(__file__), '../')) +sys.path.append(parser_path) +import macro_parser + +sign_bin_size_re = re.compile(r"^\s*RE_SIGN_BIN_SIZE\s*=\s*(.*)") +load_addr_re = re.compile(r"^\s*RE_IMAGE_LOAD_ADDRESS\s*=\s*(.*)") + +#This works around Python 2 and Python 3 handling character encodings +#differently. More information about this issue at +#https://click.palletsprojects.com/en/5.x/python3 +os.environ['LC_ALL'] = 'C.UTF-8' +os.environ['LANG'] = 'C.UTF-8' + +@click.argument('outfile') +@click.argument('infile') +@click.option('-R', '--erased-val', type=click.Choice(['0', '0xff']), + required=False, help='The value that is read back from erased ' + 'flash.') +@click.option('-x', '--hex-addr', type=imgtool.main.BasedIntParamType(), + required=False, help='Adjust address in hex output file.') +@click.option('--save-enctlv', default=False, is_flag=True, + help='When upgrading, save encrypted key TLVs instead of plain ' + 'keys. Enable when BOOT_SWAP_SAVE_ENCTLV config option ' + 'was set.') +@click.option('-E', '--encrypt', metavar='filename', + help='Encrypt image using the provided public key') +@click.option('-e', '--endian', type=click.Choice(['little', 'big']), + default='little', help="Select little or big endian") +@click.option('--overwrite-only', default=False, is_flag=True, + help='Use overwrite-only instead of swap upgrades') +@click.option('-M', '--max-sectors', type=int, + help='When padding allow for this amount of sectors (defaults ' + 'to 128)') +@click.option('--confirm', default=False, is_flag=True, + help='When padding the image, mark it as confirmed') +@click.option('--pad', default=False, is_flag=True, + help='Pad image to the size determined by --layout, adding ' + 'trailer magic') +@click.option('-l', '--layout', help='The file containing the macros of the ' + 'slot sizes') +@click.option('--pad-header', default=False, is_flag=True, + help='Adds --erased-val (defaults to 0xff) --header-size times ' + 'at the beginning of the image') +@click.option('-H', '--header-size', + callback=imgtool.main.validate_header_size, + type=imgtool.main.BasedIntParamType(), required=True) +@click.option('-d', '--dependencies', callback=imgtool.main.get_dependencies, + required=False, help='''Add dependence on another image, format: + "(,), ... "''') +@click.option('-s', '--security-counter', + callback=imgtool.main.validate_security_counter, + help='Specify the value of security counter. Use the `auto` ' + 'keyword to automatically generate it from the image version.') +@click.option('-v', '--version', callback=imgtool.main.validate_version, + required=True) +@click.option('--align', type=click.Choice(['1', '2', '4', '8']), + required=True) +@click.option('--public-key-format', type=click.Choice(['hash', 'full']), + default='hash', help='In what format to add the public key to ' + 'the image manifest: full key or hash of the key.') +@click.option('-k', '--key', metavar='filename') +@click.command(help='''Create a signed or unsigned image\n + INFILE and OUTFILE are parsed as Intel HEX if the params have + .hex extension, otherwise binary format is used''') +def wrap(key, align, version, header_size, pad_header, layout, pad, confirm, + max_sectors, overwrite_only, endian, encrypt, infile, outfile, + dependencies, hex_addr, erased_val, save_enctlv, public_key_format, + security_counter): + + slot_size = macro_parser.evaluate_macro(layout, sign_bin_size_re, 0, 1) + load_addr = macro_parser.evaluate_macro(layout, load_addr_re, 0, 1) + + if "_s" in layout: + boot_record = "SPE" + elif "_ns" in layout: + boot_record = "NSPE" + else: + boot_record = "NSPE_SPE" + + img = imgtool.image.Image(version=imgtool.version.decode_version(version), + header_size=header_size, pad_header=pad_header, + pad=pad, confirm=confirm, align=int(align), + slot_size=slot_size, max_sectors=max_sectors, + overwrite_only=overwrite_only, endian=endian, + load_addr=load_addr, erased_val=erased_val, + save_enctlv=save_enctlv, + security_counter=security_counter) + + img.load(infile) + key = imgtool.main.load_key(key) if key else None + enckey = imgtool.main.load_key(encrypt) if encrypt else None + if enckey and key: + if (isinstance(key, imgtool.keys.RSA) and + not isinstance(enckey, imgtool.keys.RSAPublic)): + # FIXME + raise click.UsageError("Signing and encryption must use the same " + "type of key") + + img.create(key, public_key_format, enckey, dependencies, boot_record) + img.save(outfile, hex_addr) + + +if __name__ == '__main__': + wrap()