arm-trusted-firmware/plat/st/common/stm32mp_crypto_lib.c
Yann Gautier 5c506c7375 fix(st): update comment on encryption key
On STM32MP2, the encryption key is 32 bytes, the key duplication
(done for 16 bytes OTP) is not done. Update the comment to precise that.

Change-Id: I6fc4d652fdd462808918e85f6e5bd0d68d10d436
Yann Gautier <yann.gautier@foss.st.com>
2023-09-27 18:41:46 +02:00

673 lines
17 KiB
C

/*
* Copyright (c) 2022-2023, STMicroelectronics - All Rights Reserved
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <assert.h>
#include <endian.h>
#include <errno.h>
#include <common/debug.h>
#include <drivers/auth/crypto_mod.h>
#include <drivers/io/io_storage.h>
#include <drivers/st/bsec.h>
#include <drivers/st/stm32_hash.h>
#include <drivers/st/stm32_pka.h>
#include <drivers/st/stm32_rng.h>
#include <drivers/st/stm32_saes.h>
#include <lib/utils.h>
#include <lib/xlat_tables/xlat_tables_v2.h>
#include <mbedtls/asn1.h>
#include <mbedtls/md.h>
#include <mbedtls/oid.h>
#include <mbedtls/platform.h>
#include <mbedtls/x509.h>
#include <plat/common/platform.h>
#include <tools_share/firmware_encrypted.h>
#include <platform_def.h>
#define CRYPTO_HASH_MAX_SIZE 32U
#define CRYPTO_SIGN_MAX_SIZE 64U
#define CRYPTO_PUBKEY_MAX_SIZE 64U
#define CRYPTO_MAX_TAG_SIZE 16U
/* brainpoolP256t1 OID is not defined in mbedTLS */
#define OID_EC_GRP_BP256T1 MBEDTLS_OID_EC_BRAINPOOL_V1 "\x08"
#if STM32MP_CRYPTO_ROM_LIB
struct stm32mp_auth_ops {
uint32_t (*verify_signature)(uint8_t *hash_in, uint8_t *pubkey_in,
uint8_t *signature, uint32_t ecc_algo);
};
static struct stm32mp_auth_ops auth_ops;
#endif
static void crypto_lib_init(void)
{
boot_api_context_t *boot_context __maybe_unused;
int ret;
NOTICE("TRUSTED_BOARD_BOOT support enabled\n");
ret = stm32_hash_register();
if (ret != 0) {
ERROR("HASH init (%d)\n", ret);
panic();
}
if (stm32mp_is_closed_device() || stm32mp_is_auth_supported()) {
#if STM32MP_CRYPTO_ROM_LIB
boot_context = (boot_api_context_t *)stm32mp_get_boot_ctx_address();
auth_ops.verify_signature = boot_context->bootrom_ecdsa_verify_signature;
#else
/* Use hardware peripherals */
if (stm32_rng_init() != 0) {
panic();
}
if (stm32_saes_driver_init() != 0) {
panic();
}
if (stm32_pka_init() != 0) {
panic();
}
#endif
}
}
static int get_plain_pk_from_asn1(void *pk_ptr, unsigned int pk_len, void **plain_pk,
size_t *len, int *pk_alg)
{
int ret;
mbedtls_pk_context mbedtls_pk = {0};
unsigned char *p, *end;
mbedtls_asn1_buf alg_params = {0};
mbedtls_asn1_buf alg_oid = {0};
*plain_pk = NULL;
*len = 0U;
/* Parse the public key */
mbedtls_pk_init(&mbedtls_pk);
p = (unsigned char *)pk_ptr;
end = (unsigned char *)(p + pk_len);
ret = mbedtls_asn1_get_tag(&p, end, len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE);
if (ret != 0) {
return -EINVAL;
}
end = p + *len;
ret = mbedtls_asn1_get_alg(&p, end, &alg_oid, &alg_params);
if (ret != 0) {
VERBOSE("%s: mbedtls_asn1_get_alg (%d)\n", __func__, ret);
return -EINVAL;
}
if (pk_alg != NULL) {
if ((strlen(MBEDTLS_OID_EC_GRP_SECP256R1) == alg_params.len) &&
(memcmp(MBEDTLS_OID_EC_GRP_SECP256R1, alg_params.p, alg_params.len) == 0)) {
*pk_alg = BOOT_API_ECDSA_ALGO_TYPE_P256NIST;
} else if ((strlen(OID_EC_GRP_BP256T1) == alg_params.len) &&
(memcmp(OID_EC_GRP_BP256T1, alg_params.p, alg_params.len) == 0)) {
*pk_alg = BOOT_API_ECDSA_ALGO_TYPE_BRAINPOOL256;
} else {
ERROR("%s: Algorithm is not supported\n", __func__);
return -EINVAL;
}
}
ret = mbedtls_asn1_get_bitstring_null(&p, end, len);
if (ret != 0) {
VERBOSE("%s: mbedtls_asn1_get_bitstring_null (%d)\n", __func__, ret);
return -EINVAL;
}
/* We remove the ident (0x04) first byte. */
if ((*len < 1U) || (p[0] != MBEDTLS_ASN1_OCTET_STRING)) {
VERBOSE("%s: not expected len or tag\n", __func__);
return -EINVAL;
}
*len = *len - 1U;
*plain_pk = p + 1U;
return 0;
}
#if STM32MP_CRYPTO_ROM_LIB
uint32_t verify_signature(uint8_t *hash_in, uint8_t *pubkey_in,
uint8_t *signature, uint32_t ecc_algo)
{
int ret;
ret = mmap_add_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_BASE,
STM32MP_ROM_SIZE_2MB_ALIGNED, MT_CODE | MT_SECURE);
if (ret != 0) {
VERBOSE("%s: mmap_add_dynamic_region (%d)\n", __func__, ret);
return CRYPTO_ERR_SIGNATURE;
}
ret = auth_ops.verify_signature(hash_in, pubkey_in, signature, ecc_algo);
if (ret != BOOT_API_RETURN_OK) {
VERBOSE("%s: auth_ops.verify_sign (%d)\n", __func__, ret);
ret = CRYPTO_ERR_SIGNATURE;
} else {
ret = 0;
}
mmap_remove_dynamic_region(STM32MP_ROM_BASE, STM32MP_ROM_SIZE_2MB_ALIGNED);
return ret;
}
static int crypto_convert_pk(void *full_pk_ptr, unsigned int full_pk_len,
void **hashed_pk_ptr, unsigned int *hashed_pk_len)
{
size_t len;
int ret;
ret = get_plain_pk_from_asn1(full_pk_ptr, full_pk_len, hashed_pk_ptr, &len, NULL);
if (ret == 0) {
*hashed_pk_len = (unsigned int)len;
}
return ret;
}
#else /* STM32MP_CRYPTO_ROM_LIB*/
static uint32_t verify_signature(uint8_t *hash_in, uint8_t *pubkey_in,
uint8_t *signature, uint32_t ecc_algo)
{
int ret = -1;
enum stm32_pka_ecdsa_curve_id cid;
switch (ecc_algo) {
case BOOT_API_ECDSA_ALGO_TYPE_P256NIST:
#if PKA_USE_NIST_P256
cid = PKA_NIST_P256;
ret = 0;
#else
WARN("%s nist_p256 requested but not included\n", __func__);
#endif
break;
case BOOT_API_ECDSA_ALGO_TYPE_BRAINPOOL256:
#if PKA_USE_BRAINPOOL_P256T1
cid = PKA_BRAINPOOL_P256T1;
ret = 0;
#else
WARN("%s brainpool_p256t1 requested but not included\n", __func__);
#endif
break;
default:
WARN("%s unexpected ecc_algo(%u)\n", __func__, ecc_algo);
break;
}
if (ret < 0) {
return CRYPTO_ERR_SIGNATURE;
}
ret = stm32_pka_ecdsa_verif(hash_in,
BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES,
signature, BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES / 2U,
signature + BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES / 2U,
BOOT_API_ECDSA_SIGNATURE_LEN_IN_BYTES / 2U,
pubkey_in, BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES / 2U,
pubkey_in + BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES / 2U,
BOOT_API_ECDSA_PUB_KEY_LEN_IN_BYTES / 2U, cid);
if (ret < 0) {
return CRYPTO_ERR_SIGNATURE;
}
return 0;
}
static int crypto_convert_pk(void *full_pk_ptr, unsigned int full_pk_len,
void **hashed_pk_ptr, unsigned int *hashed_pk_len)
{
static uint8_t st_pk[CRYPTO_PUBKEY_MAX_SIZE + sizeof(uint32_t)];
int ret;
void *plain_pk;
size_t len;
int curve_id;
uint32_t cid;
ret = get_plain_pk_from_asn1(full_pk_ptr, full_pk_len, &plain_pk, &len, &curve_id);
if ((ret != 0) || (len > CRYPTO_PUBKEY_MAX_SIZE)) {
return -EINVAL;
}
cid = curve_id; /* we want value of curve_id (1 or 2) in a uint32_t */
memcpy(st_pk, &cid, sizeof(cid));
memcpy(st_pk + sizeof(cid), plain_pk, len);
*hashed_pk_ptr = st_pk;
*hashed_pk_len = (unsigned int)(len + sizeof(cid));
return 0;
}
#endif /* STM32MP_CRYPTO_ROM_LIB */
static int get_plain_digest_from_asn1(void *digest_ptr, unsigned int digest_len,
uint8_t **out, size_t *out_len, mbedtls_md_type_t *md_alg)
{
int ret;
mbedtls_asn1_buf hash_oid, params;
size_t len;
unsigned char *p, *end;
*out = NULL;
*out_len = 0U;
/* Digest info should be an MBEDTLS_ASN1_SEQUENCE */
p = (unsigned char *)digest_ptr;
end = p + digest_len;
ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_CONSTRUCTED |
MBEDTLS_ASN1_SEQUENCE);
if (ret != 0) {
return ret;
}
/* Get the hash algorithm */
ret = mbedtls_asn1_get_alg(&p, end, &hash_oid, &params);
if (ret != 0) {
return ret;
}
ret = mbedtls_oid_get_md_alg(&hash_oid, md_alg);
if (ret != 0) {
return ret;
}
ret = mbedtls_asn1_get_tag(&p, end, &len, MBEDTLS_ASN1_OCTET_STRING);
if (ret != 0) {
return ret;
}
/* Length of hash must match the algorithm's size */
if (len != BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES) {
return -1;
}
*out = p;
*out_len = len;
return 0;
}
static int crypto_verify_signature(void *data_ptr, unsigned int data_len,
void *sig_ptr, unsigned int sig_len,
void *sig_alg, unsigned int sig_alg_len,
void *pk_ptr, unsigned int pk_len)
{
uint8_t image_hash[CRYPTO_HASH_MAX_SIZE] = {0};
uint8_t sig[CRYPTO_SIGN_MAX_SIZE];
uint8_t my_pk[CRYPTO_PUBKEY_MAX_SIZE];
int ret;
size_t len;
mbedtls_asn1_sequence seq;
mbedtls_asn1_sequence *cur;
unsigned char *p, *end;
int curve_id;
mbedtls_asn1_buf sig_oid, sig_params;
mbedtls_md_type_t md_alg;
mbedtls_pk_type_t pk_alg;
size_t bignum_len = sizeof(sig) / 2U;
unsigned int seq_num = 0U;
if (!stm32mp_is_closed_device() && !stm32mp_is_auth_supported()) {
return CRYPTO_SUCCESS;
}
/* Get pointers to signature OID and parameters */
p = (unsigned char *)sig_alg;
end = (unsigned char *)(p + sig_alg_len);
ret = mbedtls_asn1_get_alg(&p, end, &sig_oid, &sig_params);
if (ret != 0) {
VERBOSE("%s: mbedtls_asn1_get_alg (%d)\n", __func__, ret);
return CRYPTO_ERR_SIGNATURE;
}
/* Get the actual signature algorithm (MD + PK) */
ret = mbedtls_oid_get_sig_alg(&sig_oid, &md_alg, &pk_alg);
if (ret != 0) {
VERBOSE("%s: mbedtls_oid_get_sig_alg (%d)\n", __func__, ret);
return CRYPTO_ERR_SIGNATURE;
}
if ((md_alg != MBEDTLS_MD_SHA256) || (pk_alg != MBEDTLS_PK_ECDSA)) {
VERBOSE("%s: md_alg=%u pk_alg=%u\n", __func__, md_alg, pk_alg);
return CRYPTO_ERR_SIGNATURE;
}
ret = get_plain_pk_from_asn1(pk_ptr, pk_len, &pk_ptr, &len, &curve_id);
if (ret != 0) {
VERBOSE("%s: get_plain_pk_from_asn1 (%d)\n", __func__, ret);
return CRYPTO_ERR_SIGNATURE;
}
/* We expect a known pk_len */
if (len != sizeof(my_pk)) {
VERBOSE("%s: pk_len=%zu sizeof(my_pk)=%zu)\n", __func__, len, sizeof(my_pk));
return CRYPTO_ERR_SIGNATURE;
}
/* Need to copy as auth_ops.verify_signature
* expects aligned public key.
*/
memcpy(my_pk, pk_ptr, sizeof(my_pk));
/* Get the signature (bitstring) */
p = (unsigned char *)sig_ptr;
end = (unsigned char *)(p + sig_len);
ret = mbedtls_asn1_get_bitstring_null(&p, end, &len);
if (ret != 0) {
VERBOSE("%s: mbedtls_asn1_get_bitstring_null (%d)\n", __func__, ret);
return CRYPTO_ERR_SIGNATURE;
}
/* Get r and s from sequence */
ret = mbedtls_asn1_get_sequence_of(&p, end, &seq, MBEDTLS_ASN1_INTEGER);
if (ret != 0) {
VERBOSE("%s: mbedtls_asn1_get_sequence_of (%d)\n", __func__, ret);
return CRYPTO_ERR_SIGNATURE;
}
/* We expect only 2 integers (r and s) from the sequence */
if (seq.next->next != NULL) {
cur = seq.next;
mbedtls_asn1_sequence *next;
VERBOSE("%s: nb seq != 2\n", __func__);
/* Free all the sequences */
while (cur != NULL) {
next = cur->next;
mbedtls_free(cur);
cur = next;
}
return CRYPTO_ERR_SIGNATURE;
}
/*
* ECDSA signatures are composed of a tuple (R,S) where R and S are between 0 and n.
* This means that the R and S can have a maximum of 32 each, but can also be smaller.
* Also seen the integer sequence may (sometime) start with 0x00 as MSB, but we can only
* manage exactly 2*32 bytes, we remove this higher byte if there are not 00,
* we will fail either.
*/
cur = &seq;
memset(sig, 0U, sizeof(sig));
while (cur != NULL) {
size_t skip = 0U;
size_t seek = seq_num * bignum_len;
if (cur->buf.len > bignum_len) {
/* Remove extra 0x00 bytes */
skip = cur->buf.len - bignum_len;
} else if (cur->buf.len < bignum_len) {
/* Add padding to match HW required size */
seek += (bignum_len % cur->buf.len);
}
if (seek + cur->buf.len > sizeof(sig) + skip) {
panic();
}
memcpy(sig + seek, cur->buf.p + skip, cur->buf.len - skip);
cur = cur->next;
seq_num++;
}
/* Need to free allocated 'next' in mbedtls_asn1_get_sequence_of */
mbedtls_free(seq.next);
/* Compute hash for the data covered by the signature */
stm32_hash_init(HASH_SHA256);
ret = stm32_hash_final_update((uint8_t *)data_ptr, data_len, image_hash);
if (ret != 0) {
VERBOSE("%s: stm32_hash_final_update (%d)\n", __func__, ret);
return CRYPTO_ERR_SIGNATURE;
}
return verify_signature(image_hash, my_pk, sig, curve_id);
}
static int crypto_verify_hash(void *data_ptr, unsigned int data_len,
void *digest_info_ptr,
unsigned int digest_info_len)
{
int ret;
uint8_t calc_hash[BOOT_API_SHA256_DIGEST_SIZE_IN_BYTES];
unsigned char *p;
mbedtls_md_type_t md_alg;
size_t len;
/* we receive an asn1 encapsulated digest, we flatten it */
ret = get_plain_digest_from_asn1(digest_info_ptr,
digest_info_len, &p, &len,
&md_alg);
if ((ret != 0) || (md_alg != MBEDTLS_MD_SHA256) || (len != sizeof(calc_hash))) {
return CRYPTO_ERR_HASH;
}
digest_info_ptr = p;
digest_info_len = len;
stm32_hash_init(HASH_SHA256);
ret = stm32_hash_final_update(data_ptr, data_len, calc_hash);
if (ret != 0) {
VERBOSE("%s: hash failed\n", __func__);
return CRYPTO_ERR_HASH;
}
ret = memcmp(calc_hash, digest_info_ptr, digest_info_len);
if (ret != 0) {
VERBOSE("%s: not expected digest\n", __func__);
ret = CRYPTO_ERR_HASH;
}
return ret;
}
#if !defined(DECRYPTION_SUPPORT_none)
static int derive_key(uint8_t *key, size_t *key_len, size_t len,
unsigned int *flags, const uint8_t *img_id, size_t img_id_len)
{
size_t i, j;
assert(*key_len >= 32U);
/*
* Not a real derivation yet
*
* We expect a 32 bytes key, if OTP is only 16 bytes
* => duplicate.
*/
for (i = 0U, j = len; j < 32U;
i += sizeof(uint32_t), j += sizeof(uint32_t)) {
memcpy(key + j, key + i, sizeof(uint32_t));
}
*key_len = 32U;
/* Variable 'key' store a real key */
*flags = 0U;
return 0;
}
int plat_get_enc_key_info(enum fw_enc_status_t fw_enc_status, uint8_t *key,
size_t *key_len, unsigned int *flags,
const uint8_t *img_id, size_t img_id_len)
{
uint32_t otp_idx;
uint32_t otp_len;
size_t read_len;
size_t i;
if (fw_enc_status == FW_ENC_WITH_BSSK) {
return -EINVAL;
}
if (stm32_get_otp_index(ENCKEY_OTP, &otp_idx, &otp_len) != 0) {
VERBOSE("%s: get %s index error\n", __func__, ENCKEY_OTP);
return -EINVAL;
}
if (otp_len > (*key_len * CHAR_BIT)) {
VERBOSE("%s: length Error otp_len=%u key_len=%zu\n", __func__,
otp_len, *key_len * CHAR_BIT);
return -EINVAL;
}
read_len = otp_len / CHAR_BIT;
assert(read_len % sizeof(uint32_t) == 0);
for (i = 0U; i < read_len / sizeof(uint32_t); i++) {
uint32_t tmp;
uint32_t otp_val;
if (stm32_get_otp_value_from_idx(otp_idx + i, &otp_val) != 0) {
zeromem(key, *key_len);
VERBOSE("%s: unable to read from otp\n", __func__);
return -EINVAL;
}
tmp = bswap32(otp_val);
memcpy(key + i * sizeof(uint32_t), &tmp, sizeof(tmp));
}
/* Now we have the OTP values in key till read_len */
if (derive_key(key, key_len, read_len, flags, img_id,
img_id_len) != 0) {
zeromem(key, *key_len);
return -EINVAL;
}
return 0;
}
static enum stm32_saes_key_selection select_key(unsigned int key_flags)
{
if ((key_flags & ENC_KEY_IS_IDENTIFIER) != 0U) {
panic();
}
/* Use the provided key buffer */
return STM32_SAES_KEY_SOFT;
}
static int stm32_decrypt_aes_gcm(void *data, size_t data_len,
const void *key, unsigned int key_len,
unsigned int key_flags,
const void *iv, unsigned int iv_len,
const void *tag, unsigned int tag_len)
{
int ret;
struct stm32_saes_context ctx;
unsigned char tag_buf[CRYPTO_MAX_TAG_SIZE];
enum stm32_saes_key_selection key_mode;
unsigned int diff = 0U;
unsigned int i;
key_mode = select_key(key_flags);
ret = stm32_saes_init(&ctx, true, STM32_SAES_MODE_GCM, key_mode, key,
key_len, iv, iv_len);
if (ret != 0) {
return CRYPTO_ERR_INIT;
}
ret = stm32_saes_update_assodata(&ctx, true, NULL, 0U);
if (ret != 0) {
return CRYPTO_ERR_DECRYPTION;
}
ret = stm32_saes_update_load(&ctx, true, data, data, data_len);
if (ret != 0) {
return CRYPTO_ERR_DECRYPTION;
}
ret = stm32_saes_final(&ctx, tag_buf, sizeof(tag_buf));
if (ret != 0) {
return CRYPTO_ERR_DECRYPTION;
}
/* Check tag in "constant-time" */
for (i = 0U; i < tag_len; i++) {
diff |= ((const unsigned char *)tag)[i] ^ tag_buf[i];
}
if (diff != 0U) {
return CRYPTO_ERR_DECRYPTION;
}
return CRYPTO_SUCCESS;
}
/*
* Authenticated decryption of an image
*
*/
static int crypto_auth_decrypt(enum crypto_dec_algo dec_algo, void *data_ptr, size_t len,
const void *key, unsigned int key_len, unsigned int key_flags,
const void *iv, unsigned int iv_len, const void *tag,
unsigned int tag_len)
{
int rc = -1;
uint32_t real_iv[4];
switch (dec_algo) {
case CRYPTO_GCM_DECRYPT:
/*
* GCM expect a Nonce
* The AES IV is the nonce (a uint32_t[3])
* then a counter (a uint32_t big endian)
* The counter starts at 2.
*/
memcpy(real_iv, iv, iv_len);
real_iv[3] = htobe32(0x2U);
rc = stm32_decrypt_aes_gcm(data_ptr, len, key, key_len, key_flags,
real_iv, sizeof(real_iv), tag, tag_len);
break;
default:
rc = CRYPTO_ERR_DECRYPTION;
break;
}
if (rc != 0) {
return rc;
}
return CRYPTO_SUCCESS;
}
REGISTER_CRYPTO_LIB("stm32_crypto_lib",
crypto_lib_init,
crypto_verify_signature,
crypto_verify_hash,
NULL,
crypto_auth_decrypt,
crypto_convert_pk);
#else /* No decryption support */
REGISTER_CRYPTO_LIB("stm32_crypto_lib",
crypto_lib_init,
crypto_verify_signature,
crypto_verify_hash,
NULL,
NULL,
crypto_convert_pk);
#endif