/* * Copyright (c) 2023 Basalte bv * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_mcux_dcp #include LOG_MODULE_REGISTER(mcux_dcp, CONFIG_CRYPTO_LOG_LEVEL); #include #include #include #include #include #include #include #define CRYPTO_DCP_CIPHER_CAPS (CAP_RAW_KEY | CAP_SEPARATE_IO_BUFS |\ CAP_SYNC_OPS | CAP_NO_IV_PREFIX) #define CRYPTO_DCP_HASH_CAPS (CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS) struct crypto_dcp_session { dcp_handle_t handle; dcp_hash_ctx_t hash_ctx; bool in_use; }; struct crypto_dcp_config { DCP_Type *base; }; struct crypto_dcp_data { struct crypto_dcp_session sessions[CONFIG_CRYPTO_MCUX_DCP_MAX_SESSION]; }; /* Helper function to convert common FSL error status codes to errno codes */ static inline int fsl_to_errno(status_t status) { switch (status) { case kStatus_Success: return 0; case kStatus_InvalidArgument: return -EINVAL; case kStatus_Timeout: return -EAGAIN; } return -1; } static struct crypto_dcp_session *get_session(const struct device *dev) { struct crypto_dcp_data *data = dev->data; for (size_t i = 0; i < CONFIG_CRYPTO_MCUX_DCP_MAX_SESSION; ++i) { if (!data->sessions[i].in_use) { data->sessions[i].in_use = true; return &data->sessions[i]; } } return NULL; } static inline void free_session(struct crypto_dcp_session *session) { session->in_use = false; } static int crypto_dcp_query_hw_caps(const struct device *dev) { ARG_UNUSED(dev); return CRYPTO_DCP_CIPHER_CAPS | CRYPTO_DCP_HASH_CAPS; } static int crypto_dcp_aes_cbc_encrypt(struct cipher_ctx *ctx, struct cipher_pkt *pkt, uint8_t *iv) { const struct crypto_dcp_config *cfg = ctx->device->config; struct crypto_dcp_session *session = ctx->drv_sessn_state; status_t status; size_t iv_bytes; uint8_t *p_iv, iv_loc[16]; if ((ctx->flags & CAP_NO_IV_PREFIX) == 0U) { /* Prefix IV to ciphertext, which is default behavior of Zephyr * crypto API, unless CAP_NO_IV_PREFIX is requested. */ iv_bytes = 16U; memcpy(pkt->out_buf, iv, 16U); p_iv = iv; } else { iv_bytes = 0U; memcpy(iv_loc, iv, 16U); p_iv = iv_loc; } sys_cache_data_disable(); status = DCP_AES_EncryptCbc(cfg->base, &session->handle, pkt->in_buf, pkt->out_buf + iv_bytes, pkt->in_len, p_iv); sys_cache_data_enable(); if (status != kStatus_Success) { return fsl_to_errno(status); } pkt->out_len = pkt->in_len + iv_bytes; return 0; } static int crypto_dcp_aes_cbc_decrypt(struct cipher_ctx *ctx, struct cipher_pkt *pkt, uint8_t *iv) { const struct crypto_dcp_config *cfg = ctx->device->config; struct crypto_dcp_session *session = ctx->drv_sessn_state; status_t status; size_t iv_bytes; uint8_t *p_iv, iv_loc[16]; if ((ctx->flags & CAP_NO_IV_PREFIX) == 0U) { iv_bytes = 16U; p_iv = iv; } else { iv_bytes = 0U; memcpy(iv_loc, iv, 16U); p_iv = iv_loc; } sys_cache_data_disable(); status = DCP_AES_DecryptCbc(cfg->base, &session->handle, pkt->in_buf + iv_bytes, pkt->out_buf, pkt->in_len, p_iv); sys_cache_data_enable(); if (status != kStatus_Success) { return fsl_to_errno(status); } pkt->out_len = pkt->in_len - iv_bytes; return 0; } static int crypto_dcp_aes_ecb_encrypt(struct cipher_ctx *ctx, struct cipher_pkt *pkt) { const struct crypto_dcp_config *cfg = ctx->device->config; struct crypto_dcp_session *session = ctx->drv_sessn_state; status_t status; sys_cache_data_disable(); status = DCP_AES_EncryptEcb(cfg->base, &session->handle, pkt->in_buf, pkt->out_buf, pkt->in_len); sys_cache_data_enable(); if (status != kStatus_Success) { return fsl_to_errno(status); } pkt->out_len = pkt->in_len; return 0; } static int crypto_dcp_aes_ecb_decrypt(struct cipher_ctx *ctx, struct cipher_pkt *pkt) { const struct crypto_dcp_config *cfg = ctx->device->config; struct crypto_dcp_session *session = ctx->drv_sessn_state; status_t status; sys_cache_data_disable(); status = DCP_AES_DecryptEcb(cfg->base, &session->handle, pkt->in_buf, pkt->out_buf, pkt->in_len); sys_cache_data_enable(); if (status != kStatus_Success) { return fsl_to_errno(status); } pkt->out_len = pkt->in_len; return 0; } static int crypto_dcp_cipher_begin_session(const struct device *dev, struct cipher_ctx *ctx, enum cipher_algo algo, enum cipher_mode mode, enum cipher_op op_type) { const struct crypto_dcp_config *cfg = dev->config; struct crypto_dcp_session *session; status_t status; if (algo != CRYPTO_CIPHER_ALGO_AES || (mode != CRYPTO_CIPHER_MODE_CBC && mode != CRYPTO_CIPHER_MODE_ECB)) { return -ENOTSUP; } if (ctx->flags & ~(CRYPTO_DCP_CIPHER_CAPS)) { return -ENOTSUP; } session = get_session(dev); if (session == NULL) { return -ENOSPC; } if (mode == CRYPTO_CIPHER_MODE_CBC) { if (op_type == CRYPTO_CIPHER_OP_DECRYPT) { ctx->ops.cbc_crypt_hndlr = crypto_dcp_aes_cbc_decrypt; } else { ctx->ops.cbc_crypt_hndlr = crypto_dcp_aes_cbc_encrypt; } } else { if (op_type == CRYPTO_CIPHER_OP_DECRYPT) { ctx->ops.block_crypt_hndlr = crypto_dcp_aes_ecb_decrypt; } else { ctx->ops.block_crypt_hndlr = crypto_dcp_aes_ecb_encrypt; } } ctx->drv_sessn_state = session; status = DCP_AES_SetKey(cfg->base, &session->handle, ctx->key.bit_stream, ctx->keylen); if (status != kStatus_Success) { free_session(session); return fsl_to_errno(status); } return 0; } static int crypto_dcp_cipher_free_session(const struct device *dev, struct cipher_ctx *ctx) { struct crypto_dcp_session *session; ARG_UNUSED(dev); session = ctx->drv_sessn_state; free_session(session); return 0; } static int crypto_dcp_sha256(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish) { const struct crypto_dcp_config *cfg = ctx->device->config; struct crypto_dcp_session *session = ctx->drv_sessn_state; status_t status; sys_cache_data_disable(); status = DCP_HASH_Update(cfg->base, &session->hash_ctx, pkt->in_buf, pkt->in_len); sys_cache_data_enable(); if (status != kStatus_Success) { return fsl_to_errno(status); } if (finish) { sys_cache_data_disable(); status = DCP_HASH_Finish(cfg->base, &session->hash_ctx, pkt->out_buf, NULL); sys_cache_data_enable(); } return fsl_to_errno(status); } static int crypto_dcp_hash_begin_session(const struct device *dev, struct hash_ctx *ctx, enum hash_algo algo) { const struct crypto_dcp_config *cfg = dev->config; struct crypto_dcp_session *session; status_t status; if (algo != CRYPTO_HASH_ALGO_SHA256) { return -ENOTSUP; } if (ctx->flags & ~(CRYPTO_DCP_HASH_CAPS)) { return -ENOTSUP; } session = get_session(dev); if (session == NULL) { return -ENOSPC; } status = DCP_HASH_Init(cfg->base, &session->handle, &session->hash_ctx, kDCP_Sha256); if (status != kStatus_Success) { free_session(session); return fsl_to_errno(status); } ctx->drv_sessn_state = session; ctx->hash_hndlr = crypto_dcp_sha256; return 0; } static int crypto_dcp_hash_free_session(const struct device *dev, struct hash_ctx *ctx) { struct crypto_dcp_session *session; ARG_UNUSED(dev); session = ctx->drv_sessn_state; free_session(session); return 0; } static int crypto_dcp_init(const struct device *dev) { const struct crypto_dcp_config *cfg = dev->config; struct crypto_dcp_data *data = dev->data; dcp_config_t hal_cfg; DCP_GetDefaultConfig(&hal_cfg); DCP_Init(cfg->base, &hal_cfg); /* Assign unique channels/key slots to each session */ for (size_t i = 0; i < CONFIG_CRYPTO_MCUX_DCP_MAX_SESSION; ++i) { data->sessions[i].in_use = false; data->sessions[i].handle.channel = kDCP_Channel0 << i; data->sessions[i].handle.keySlot = kDCP_KeySlot0 + i; data->sessions[i].handle.swapConfig = kDCP_NoSwap; } return 0; } static const struct crypto_driver_api crypto_dcp_api = { .query_hw_caps = crypto_dcp_query_hw_caps, .cipher_begin_session = crypto_dcp_cipher_begin_session, .cipher_free_session = crypto_dcp_cipher_free_session, .hash_begin_session = crypto_dcp_hash_begin_session, .hash_free_session = crypto_dcp_hash_free_session, }; #define CRYPTO_DCP_DEFINE(inst) \ static const struct crypto_dcp_config crypto_dcp_config_##inst = { \ .base = (DCP_Type *)DT_INST_REG_ADDR(inst), \ }; \ static struct crypto_dcp_data crypto_dcp_data_##inst; \ DEVICE_DT_INST_DEFINE(inst, crypto_dcp_init, NULL, \ &crypto_dcp_data_##inst, &crypto_dcp_config_##inst, \ POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY, &crypto_dcp_api); DT_INST_FOREACH_STATUS_OKAY(CRYPTO_DCP_DEFINE)