1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 #include <string.h>
7 
8 #include <zephyr/init.h>
9 #include <zephyr/kernel.h>
10 #include <zephyr/logging/log.h>
11 
12 #include <psa/protected_storage.h>
13 
14 #include "tls_internal.h"
15 
16 LOG_MODULE_REGISTER(tls_credentials_trusted,
17 		    CONFIG_TLS_CREDENTIALS_LOG_LEVEL);
18 
19 /* This implementation uses the PSA Protected Storage API to store:
20  * - credentials with an UID constructed as:
21  *	[ C2E0 ] | [ type as uint16_t ] | [ tag as uint32_t ]
22  * - credential ToC with an UID constructed as:
23  *	[ C2E0 ] | [ ffff as uint16_t ] | [ ffffffff as uint32_t ]
24  *
25  * The ToC contains a list of CONFIG_TLS_MAX_CREDENTIALS_NUMBER UIDs
26  * of credentials, can be 0 if slot is free.
27  */
28 
29 #define PSA_PS_CRED_ID		0xC2E0ULL
30 
31 #define CRED_MAX_SLOTS	CONFIG_TLS_MAX_CREDENTIALS_NUMBER
32 
33 /* Global temporary pool of credentials to be used by TLS contexts. */
34 static struct tls_credential credentials[CRED_MAX_SLOTS];
35 
36 /* Credentials Table Of Content copy of the one stored in Protected Storage */
37 static psa_storage_uid_t credentials_toc[CRED_MAX_SLOTS];
38 
39 /* A mutex for protecting access to the credentials array. */
40 static struct k_mutex credential_lock;
41 
42 /* Construct PSA PS uid from tag & type */
tls_credential_get_uid(uint32_t tag,uint16_t type)43 static inline psa_storage_uid_t tls_credential_get_uid(uint32_t tag,
44 						       uint16_t type)
45 {
46 	return PSA_PS_CRED_ID << 48 |
47 	       (type & 0xffffULL) << 32 |
48 	       (tag & 0xffffffff);
49 }
50 
51 #define PSA_PS_CRED_TOC_ID tls_credential_get_uid(0xffffffff, 0xffff)
52 
53 /* Get the TAG from an UID */
tls_credential_uid_to_tag(psa_storage_uid_t uid)54 static inline sec_tag_t tls_credential_uid_to_tag(psa_storage_uid_t uid)
55 {
56 	return (uid & 0xffffffff);
57 }
58 
59 /* Get the TYPE from an UID */
tls_credential_uid_to_type(psa_storage_uid_t uid)60 static inline int tls_credential_uid_to_type(psa_storage_uid_t uid)
61 {
62 	return ((uid >> 32) & 0xffff);
63 }
64 
credentials_toc_get(void)65 static int credentials_toc_get(void)
66 {
67 	psa_status_t status;
68 	size_t len;
69 
70 	status = psa_ps_get(PSA_PS_CRED_TOC_ID, 0, sizeof(credentials_toc),
71 			    credentials_toc, &len);
72 	if (status == PSA_ERROR_DOES_NOT_EXIST) {
73 		return -ENOENT;
74 	} else if (status != PSA_SUCCESS) {
75 		return -EIO;
76 	}
77 
78 	return 0;
79 }
80 
credentials_toc_write(void)81 static int credentials_toc_write(void)
82 {
83 	psa_status_t status;
84 
85 	status = psa_ps_set(PSA_PS_CRED_TOC_ID, sizeof(credentials_toc),
86 			    credentials_toc, 0);
87 	if (status != PSA_SUCCESS) {
88 		return -EIO;
89 	}
90 
91 	return 0;
92 }
93 
credentials_toc_update(unsigned int slot,psa_storage_uid_t uid)94 static int credentials_toc_update(unsigned int slot, psa_storage_uid_t uid)
95 {
96 	int ret;
97 
98 	if (slot >= CRED_MAX_SLOTS) {
99 		return -EINVAL;
100 	}
101 
102 	credentials_toc[slot] = uid;
103 
104 	ret = credentials_toc_write();
105 	if (ret) {
106 		return ret;
107 	}
108 
109 	return credentials_toc_get();
110 }
111 
tls_credential_toc_find_slot(psa_storage_uid_t uid)112 static unsigned int tls_credential_toc_find_slot(psa_storage_uid_t uid)
113 {
114 	unsigned int slot;
115 
116 	for (slot = 0; slot < CRED_MAX_SLOTS; ++slot) {
117 		if (credentials_toc[slot] == uid) {
118 			return slot;
119 		}
120 	}
121 
122 	return CRED_MAX_SLOTS;
123 }
124 
credentials_init(void)125 static int credentials_init(void)
126 {
127 	struct psa_storage_info_t info;
128 	unsigned int sync = 0;
129 	psa_status_t status;
130 	unsigned int slot;
131 	int ret;
132 
133 	/* Retrieve Table of Content from storage */
134 	ret = credentials_toc_get();
135 	if (ret == -ENOENT) {
136 		memset(credentials_toc, 0, sizeof(credentials_toc));
137 		return 0;
138 	} else if (ret != 0) {
139 		return -EIO;
140 	}
141 
142 	/* Check validity of ToC */
143 	for (slot = 0; slot < CRED_MAX_SLOTS; ++slot) {
144 		if (credentials_toc[slot] == 0) {
145 			continue;
146 		}
147 
148 		status = psa_ps_get_info(credentials_toc[slot], &info);
149 		if (status == PSA_ERROR_DOES_NOT_EXIST) {
150 			LOG_WRN("Credential %d doesn't exist in storage", slot);
151 			credentials_toc[slot] = 0;
152 			sync = 1;
153 		} else if (status != PSA_SUCCESS) {
154 			return -EIO;
155 		}
156 	}
157 
158 	if (sync != 0) {
159 		ret = credentials_toc_write();
160 		if (ret != 0) {
161 			return -EIO;
162 		}
163 	}
164 
165 	return 0;
166 }
167 SYS_INIT(credentials_init, POST_KERNEL, 0);
168 
unused_credential_get(void)169 static struct tls_credential *unused_credential_get(void)
170 {
171 	int i;
172 
173 	for (i = 0; i < ARRAY_SIZE(credentials); i++) {
174 		if (credentials[i].type == TLS_CREDENTIAL_NONE) {
175 			return &credentials[i];
176 		}
177 	}
178 
179 	return NULL;
180 }
181 
182 /* Get a credential struct from an UID */
credential_get_from_uid(psa_storage_uid_t uid)183 static struct tls_credential *credential_get_from_uid(psa_storage_uid_t uid)
184 {
185 	struct tls_credential *credential;
186 	struct psa_storage_info_t info;
187 	psa_status_t status;
188 
189 	if (tls_credential_toc_find_slot(uid) == CRED_MAX_SLOTS) {
190 		return NULL;
191 	}
192 
193 	credential = unused_credential_get();
194 	if (credential == NULL) {
195 		return NULL;
196 	}
197 
198 	status = psa_ps_get_info(uid, &info);
199 	if (status != PSA_SUCCESS) {
200 		return NULL;
201 	}
202 
203 	credential->buf = k_malloc(info.size);
204 	if (credential->buf == NULL) {
205 		return NULL;
206 	}
207 
208 	status = psa_ps_get(uid, 0, info.size, (void *)credential->buf,
209 			    &credential->len);
210 	if (status != PSA_SUCCESS) {
211 		k_free((void *)credential->buf);
212 		credential->buf = NULL;
213 		return NULL;
214 	}
215 
216 	credential->tag = tls_credential_uid_to_tag(uid);
217 	credential->type = tls_credential_uid_to_type(uid);
218 
219 	return credential;
220 }
221 
222 /* Get a credential struct from a TAG and TYPE values */
credential_get(sec_tag_t tag,enum tls_credential_type type)223 struct tls_credential *credential_get(sec_tag_t tag,
224 				      enum tls_credential_type type)
225 {
226 	return credential_get_from_uid(tls_credential_get_uid(tag, type));
227 }
228 
229 
230 /* Get the following credential filtered by a TAG valud */
credential_next_get(sec_tag_t tag,struct tls_credential * iter)231 struct tls_credential *credential_next_get(sec_tag_t tag,
232 					   struct tls_credential *iter)
233 {
234 	psa_storage_uid_t uid;
235 	unsigned int slot;
236 
237 	if (!iter) {
238 		slot = 0;
239 	} else {
240 		uid = tls_credential_get_uid(iter->tag, iter->type);
241 
242 		slot = tls_credential_toc_find_slot(uid);
243 		if (slot == CRED_MAX_SLOTS) {
244 			return NULL;
245 		}
246 
247 		slot++;
248 	}
249 
250 	for (; slot < CRED_MAX_SLOTS; slot++) {
251 		uid = credentials_toc[slot];
252 		if (uid == 0) {
253 			continue;
254 		}
255 
256 		if (tls_credential_uid_to_type(uid) != TLS_CREDENTIAL_NONE &&
257 		    tls_credential_uid_to_tag(uid) == tag) {
258 			return credential_get_from_uid(uid);
259 		}
260 	}
261 
262 	return NULL;
263 }
264 
credentials_lock(void)265 void credentials_lock(void)
266 {
267 	k_mutex_lock(&credential_lock, K_FOREVER);
268 }
269 
credentials_unlock(void)270 void credentials_unlock(void)
271 {
272 	int i;
273 
274 	/* Erase & free all retrieved credentials */
275 	for (i = 0; i < ARRAY_SIZE(credentials); i++) {
276 		if (credentials[i].buf) {
277 			k_free((void *)credentials[i].buf);
278 		}
279 		memset(&credentials[i], 0, sizeof(credentials[i]));
280 	}
281 
282 	k_mutex_unlock(&credential_lock);
283 }
284 
tls_credential_add(sec_tag_t tag,enum tls_credential_type type,const void * cred,size_t credlen)285 int tls_credential_add(sec_tag_t tag, enum tls_credential_type type,
286 		       const void *cred, size_t credlen)
287 {
288 	psa_storage_uid_t uid = tls_credential_get_uid(tag, type);
289 	psa_storage_create_flags_t create_flags = 0;
290 	psa_status_t status;
291 	unsigned int slot;
292 	int ret = 0;
293 
294 	/* tag 0xffffffff type 0xffff are reserved */
295 	if (tag == 0xffffffff && type == 0xffff) {
296 		ret = -EINVAL;
297 		goto cleanup;
298 	}
299 
300 	k_mutex_lock(&credential_lock, K_FOREVER);
301 
302 	if (tls_credential_toc_find_slot(uid) != CRED_MAX_SLOTS) {
303 		ret = -EEXIST;
304 		goto cleanup;
305 	}
306 
307 	slot = tls_credential_toc_find_slot(0);
308 	if (slot == CRED_MAX_SLOTS) {
309 		ret = -ENOMEM;
310 		goto cleanup;
311 	}
312 
313 	/* TODO: Set create_flags depending on tag value ? */
314 
315 	status = psa_ps_set(uid, credlen, cred, create_flags);
316 	if (status != PSA_SUCCESS) {
317 		ret = -EIO;
318 		goto cleanup;
319 	}
320 
321 	ret = credentials_toc_update(slot, uid);
322 
323 cleanup:
324 	k_mutex_unlock(&credential_lock);
325 
326 	return ret;
327 }
328 
tls_credential_get(sec_tag_t tag,enum tls_credential_type type,void * cred,size_t * credlen)329 int tls_credential_get(sec_tag_t tag, enum tls_credential_type type,
330 		       void *cred, size_t *credlen)
331 {
332 	struct psa_storage_info_t info;
333 	psa_storage_uid_t uid = tls_credential_get_uid(tag, type);
334 	psa_status_t status;
335 	unsigned int slot;
336 	int ret = 0;
337 
338 	/* tag 0xffffffff type 0xffff are reserved */
339 	if (tag == 0xffffffff && type == 0xffff) {
340 		ret = -EINVAL;
341 		goto cleanup;
342 	}
343 
344 	k_mutex_lock(&credential_lock, K_FOREVER);
345 
346 	slot = tls_credential_toc_find_slot(uid);
347 	if (slot == CRED_MAX_SLOTS) {
348 		ret = -ENOENT;
349 		goto cleanup;
350 	}
351 
352 	status = psa_ps_get_info(uid, &info);
353 	if (status == PSA_ERROR_DOES_NOT_EXIST) {
354 		ret = -ENOENT;
355 		goto cleanup;
356 	} else if (status != PSA_SUCCESS) {
357 		ret = -EIO;
358 		goto cleanup;
359 	}
360 
361 	if (info.size > *credlen) {
362 		ret = -EFBIG;
363 		goto cleanup;
364 	}
365 
366 	status = psa_ps_get(uid, 0, info.size, cred, credlen);
367 	if (status != PSA_SUCCESS) {
368 		ret = -EIO;
369 		goto cleanup;
370 	}
371 
372 cleanup:
373 	k_mutex_unlock(&credential_lock);
374 
375 	return ret;
376 }
377 
tls_credential_delete(sec_tag_t tag,enum tls_credential_type type)378 int tls_credential_delete(sec_tag_t tag, enum tls_credential_type type)
379 {
380 	psa_storage_uid_t uid = tls_credential_get_uid(tag, type);
381 	psa_status_t status;
382 	unsigned int slot;
383 	int ret = 0;
384 
385 	/* tag 0xffffffff type 0xffff are reserved */
386 	if (tag == 0xffffffff && type == 0xffff) {
387 		ret = -EINVAL;
388 		goto cleanup;
389 	}
390 
391 	k_mutex_lock(&credential_lock, K_FOREVER);
392 
393 	slot = tls_credential_toc_find_slot(uid);
394 	if (slot == CRED_MAX_SLOTS) {
395 		ret = -ENOENT;
396 		goto cleanup;
397 	}
398 
399 	ret = credentials_toc_update(slot, 0);
400 	if (ret != 0) {
401 		goto cleanup;
402 	}
403 
404 	status = psa_ps_remove(uid);
405 	if (status == PSA_ERROR_DOES_NOT_EXIST) {
406 		ret = -ENOENT;
407 		goto cleanup;
408 	} else if (status != PSA_SUCCESS) {
409 		ret = -EIO;
410 		goto cleanup;
411 	}
412 
413 cleanup:
414 	k_mutex_unlock(&credential_lock);
415 
416 	return ret;
417 }
418