1 /*
2  * Copyright (c) 2022 Nuvoton Technology Corporation.
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT nuvoton_npcx_sha
8 
9 #include <errno.h>
10 #include <zephyr/kernel.h>
11 #include <zephyr/crypto/crypto.h>
12 
13 #include <zephyr/logging/log.h>
14 LOG_MODULE_REGISTER(sha_npcx, CONFIG_CRYPTO_LOG_LEVEL);
15 
16 #define NPCX_HASH_CAPS_SUPPORT	(CAP_SEPARATE_IO_BUFS | CAP_SYNC_OPS)
17 #define NPCX_SHA256_HANDLE_SIZE DT_INST_PROP(0, context_buffer_size)
18 #define NPCX_SHA_MAX_SESSION	1
19 
20 /* The status code returns from Nuvoton Cryptographic Library ROM APIs */
21 enum ncl_status {
22 	NCL_STATUS_OK = 0xA5A5,
23 	NCL_STATUS_FAIL = 0x5A5A,
24 	NCL_STATUS_INVALID_PARAM = 0x02,
25 	NCL_STATUS_PARAM_NOT_SUPPORTED,
26 	NCL_STATUS_SYSTEM_BUSY,
27 	NCL_STATUS_AUTHENTICATION_FAIL,
28 	NCL_STATUS_NO_RESPONSE,
29 	NCL_STATUS_HARDWARE_ERROR,
30 };
31 enum ncl_sha_type {
32 	NCL_SHA_TYPE_2_256 = 0,
33 	NCL_SHA_TYPE_2_384 = 1,
34 	NCL_SHA_TYPE_2_512 = 2,
35 	NCL_SHA_TYPE_NUM
36 };
37 
38 /* The following table holds the function pointer for each SHA API in NPCX ROM. */
39 struct npcx_ncl_sha {
40 	/* Get the SHA context size required by SHA APIs. */
41 	uint32_t (*get_context_size)(void);
42 	/* Initial SHA context. */
43 	enum ncl_status (*init_context)(void *ctx);
44 	/* Finalize SHA context. */
45 	enum ncl_status (*finalize_context)(void *ctx);
46 	/* Initiate the SHA hardware module and setups needed parameters. */
47 	enum ncl_status (*init)(void *ctx);
48 	/*
49 	 * Prepare the context buffer for a SHA calculation -  by loading the
50 	 * initial SHA-256/384/512 parameters.
51 	 */
52 	enum ncl_status (*start)(void *ctx, enum ncl_sha_type type);
53 	/*
54 	 * Updates the SHA calculation with the additional data. When the
55 	 * function returns, the hardware and memory buffer shall be ready to
56 	 * accept new data * buffers for SHA calculation and changes to the data
57 	 * in data buffer should no longer effect the SHA calculation.
58 	 */
59 	enum ncl_status (*update)(void *ctx, const uint8_t *data, uint32_t Len);
60 	/* Return the SHA result (digest.) */
61 	enum ncl_status (*finish)(void *ctx, uint8_t *hashDigest);
62 	/* Perform a complete SHA calculation */
63 	enum ncl_status (*calc)(void *ctx, enum ncl_sha_type type, const uint8_t *data,
64 				uint32_t Len, uint8_t *hashDigest);
65 	/* Power on/off the SHA module. */
66 	enum ncl_status (*power)(void *ctx, uint8_t enable);
67 	/* Reset the SHA hardware and terminate any in-progress operations. */
68 	enum ncl_status (*reset)(void *ctx);
69 };
70 
71 /* The start address of the SHA API table. */
72 #define NPCX_NCL_SHA ((const struct npcx_ncl_sha *)DT_INST_REG_ADDR(0))
73 
74 struct npcx_sha_context {
75 	uint8_t handle[NPCX_SHA256_HANDLE_SIZE];
76 } __aligned(4);
77 
78 struct npcx_sha_session {
79 	struct npcx_sha_context npcx_sha_ctx;
80 	enum hash_algo algo;
81 	bool in_use;
82 };
83 
84 struct npcx_sha_session npcx_sessions[NPCX_SHA_MAX_SESSION];
85 
npcx_get_unused_session_index(void)86 static int npcx_get_unused_session_index(void)
87 {
88 	int i;
89 
90 	for (i = 0; i < NPCX_SHA_MAX_SESSION; i++) {
91 		if (!npcx_sessions[i].in_use) {
92 			npcx_sessions[i].in_use = true;
93 			return i;
94 		}
95 	}
96 
97 	return -1;
98 }
npcx_sha_compute(struct hash_ctx * ctx,struct hash_pkt * pkt,bool finish)99 static int npcx_sha_compute(struct hash_ctx *ctx, struct hash_pkt *pkt, bool finish)
100 {
101 	enum ncl_status ret;
102 	struct npcx_sha_session *npcx_session = ctx->drv_sessn_state;
103 	struct npcx_sha_context *npcx_ctx = &npcx_session->npcx_sha_ctx;
104 	enum ncl_sha_type sha_type;
105 
106 	switch (npcx_session->algo) {
107 	case CRYPTO_HASH_ALGO_SHA256:
108 		sha_type = NCL_SHA_TYPE_2_256;
109 		break;
110 	case CRYPTO_HASH_ALGO_SHA384:
111 		sha_type = NCL_SHA_TYPE_2_384;
112 		break;
113 	case CRYPTO_HASH_ALGO_SHA512:
114 		sha_type = NCL_SHA_TYPE_2_512;
115 		break;
116 	default:
117 		LOG_ERR("Unexpected algo: %d", npcx_session->algo);
118 		return -EINVAL;
119 	}
120 
121 	if (!ctx->started) {
122 		ret = NPCX_NCL_SHA->start(npcx_ctx->handle, sha_type);
123 		if (ret != NCL_STATUS_OK) {
124 			LOG_ERR("Could not compute the hash, err:%d", ret);
125 			return -EINVAL;
126 		}
127 		ctx->started = true;
128 	}
129 
130 	if (pkt->in_len != 0) {
131 		ret = NPCX_NCL_SHA->update(npcx_ctx->handle, pkt->in_buf, pkt->in_len);
132 		if (ret != NCL_STATUS_OK) {
133 			LOG_ERR("Could not update the hash, err:%d", ret);
134 			ctx->started = false;
135 			return -EINVAL;
136 		}
137 	}
138 
139 	if (finish) {
140 		ctx->started = false;
141 		ret = NPCX_NCL_SHA->finish(npcx_ctx->handle, pkt->out_buf);
142 		if (ret != NCL_STATUS_OK) {
143 			LOG_ERR("Could not compute the hash, err:%d", ret);
144 			return -EINVAL;
145 		}
146 	}
147 
148 	return 0;
149 }
150 
npcx_hash_session_setup(const struct device * dev,struct hash_ctx * ctx,enum hash_algo algo)151 static int npcx_hash_session_setup(const struct device *dev, struct hash_ctx *ctx,
152 				   enum hash_algo algo)
153 {
154 	int ctx_idx;
155 	struct npcx_sha_context *npcx_ctx;
156 
157 	if (ctx->flags & ~(NPCX_HASH_CAPS_SUPPORT)) {
158 		LOG_ERR("Unsupported flag");
159 		return -EINVAL;
160 	}
161 
162 	if ((algo != CRYPTO_HASH_ALGO_SHA256) && (algo != CRYPTO_HASH_ALGO_SHA384) &&
163 	    (algo != CRYPTO_HASH_ALGO_SHA512)) {
164 		LOG_ERR("Unsupported algo: %d", algo);
165 		return -EINVAL;
166 	}
167 
168 	ctx_idx = npcx_get_unused_session_index();
169 	if (ctx_idx < 0) {
170 		LOG_ERR("No free session for now");
171 		return -ENOSPC;
172 	}
173 
174 	npcx_sessions[ctx_idx].algo = algo;
175 
176 	ctx->drv_sessn_state = &npcx_sessions[ctx_idx];
177 	ctx->started = false;
178 	ctx->hash_hndlr = npcx_sha_compute;
179 
180 	npcx_ctx = &npcx_sessions[ctx_idx].npcx_sha_ctx;
181 	NPCX_NCL_SHA->init_context(npcx_ctx->handle);
182 	NPCX_NCL_SHA->power(npcx_ctx->handle, 1);
183 	NPCX_NCL_SHA->init(npcx_ctx->handle);
184 	NPCX_NCL_SHA->reset(npcx_ctx->handle);
185 
186 	return 0;
187 }
188 
npcx_hash_session_free(const struct device * dev,struct hash_ctx * ctx)189 static int npcx_hash_session_free(const struct device *dev, struct hash_ctx *ctx)
190 {
191 	struct npcx_sha_session *npcx_session = ctx->drv_sessn_state;
192 	struct npcx_sha_context *npcx_ctx = &npcx_session->npcx_sha_ctx;
193 
194 	NPCX_NCL_SHA->reset(npcx_ctx->handle);
195 	NPCX_NCL_SHA->power(npcx_ctx->handle, 0);
196 	NPCX_NCL_SHA->finalize_context(npcx_ctx->handle);
197 	npcx_session->in_use = false;
198 
199 	return 0;
200 }
201 
npcx_query_caps(const struct device * dev)202 static int npcx_query_caps(const struct device *dev)
203 {
204 	return NPCX_HASH_CAPS_SUPPORT;
205 }
206 
207 static struct crypto_driver_api npcx_crypto_api = {
208 	.hash_begin_session = npcx_hash_session_setup,
209 	.hash_free_session = npcx_hash_session_free,
210 	.query_hw_caps = npcx_query_caps,
211 };
212 
213 DEVICE_DT_INST_DEFINE(0, NULL, NULL, NULL, NULL, POST_KERNEL, CONFIG_CRYPTO_INIT_PRIORITY,
214 		      &npcx_crypto_api);
215 BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) == 1,
216 	     "only one 'nuvoton,npcx-sha' compatible node can be supported");
217