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