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