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