mirror of https://github.com/ARMmbed/mbed-os.git
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.pull/14333/head
parent
b0da9d0cdf
commit
260a33574b
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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__ */
|
||||
|
|
@ -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 <stdint.h>
|
||||
#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__ */
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -75,3 +75,4 @@ tfm_platform_ioctl(tfm_platform_ioctl_req_t request,
|
|||
return (enum tfm_platform_err_t) status;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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: "(<image_ID>,<image_version>), ... "''')
|
||||
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()
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
@ -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)
|
||||
|
|
@ -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 "<Image version={}, header_size={}, security_counter={}, \
|
||||
base_addr={}, load_addr={}, align={}, slot_size={}, \
|
||||
max_sectors={}, overwrite_only={}, endian={} format={}, \
|
||||
payloadlen=0x{:x}>".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
|
||||
|
|
@ -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)))
|
||||
|
|
@ -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
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
@ -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())
|
||||
|
|
@ -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()
|
||||
|
|
@ -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)
|
||||
|
|
@ -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:
|
||||
"(<image_ID>,<image_version>), ... "''')
|
||||
@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()
|
||||
|
|
@ -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"))
|
||||
|
|
@ -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)] = \
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
"(<image_ID>,<image_version>), ... "''')
|
||||
@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()
|
||||
Loading…
Reference in New Issue