1 /*
2  *  PSA ITS simulator over stdio files.
3  */
4 /*
5  *  Copyright The Mbed TLS Contributors
6  *  SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later
7  */
8 
9 #include "common.h"
10 
11 #if defined(MBEDTLS_PSA_ITS_FILE_C)
12 
13 #include "mbedtls/platform.h"
14 
15 #if defined(_WIN32)
16 #include <windows.h>
17 #endif
18 
19 #include "psa_crypto_its.h"
20 
21 #include <limits.h>
22 #include <stdint.h>
23 #include <stdio.h>
24 #include <string.h>
25 
26 #if !defined(PSA_ITS_STORAGE_PREFIX)
27 #define PSA_ITS_STORAGE_PREFIX ""
28 #endif
29 
30 #define PSA_ITS_STORAGE_FILENAME_PATTERN "%08x%08x"
31 #define PSA_ITS_STORAGE_SUFFIX ".psa_its"
32 #define PSA_ITS_STORAGE_FILENAME_LENGTH         \
33     (sizeof(PSA_ITS_STORAGE_PREFIX) - 1 +    /*prefix without terminating 0*/ \
34      16 +  /*UID (64-bit number in hex)*/                               \
35      sizeof(PSA_ITS_STORAGE_SUFFIX) - 1 +    /*suffix without terminating 0*/ \
36      1 /*terminating null byte*/)
37 #define PSA_ITS_STORAGE_TEMP \
38     PSA_ITS_STORAGE_PREFIX "tempfile" PSA_ITS_STORAGE_SUFFIX
39 
40 /* The maximum value of psa_storage_info_t.size */
41 #define PSA_ITS_MAX_SIZE 0xffffffff
42 
43 #define PSA_ITS_MAGIC_STRING "PSA\0ITS\0"
44 #define PSA_ITS_MAGIC_LENGTH 8
45 
46 /* As rename fails on Windows if the new filepath already exists,
47  * use MoveFileExA with the MOVEFILE_REPLACE_EXISTING flag instead.
48  * Returns 0 on success, nonzero on failure. */
49 #if defined(_WIN32)
50 #define rename_replace_existing(oldpath, newpath) \
51     (!MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING))
52 #else
53 #define rename_replace_existing(oldpath, newpath) rename(oldpath, newpath)
54 #endif
55 
56 typedef struct {
57     uint8_t magic[PSA_ITS_MAGIC_LENGTH];
58     uint8_t size[sizeof(uint32_t)];
59     uint8_t flags[sizeof(psa_storage_create_flags_t)];
60 } psa_its_file_header_t;
61 
psa_its_fill_filename(psa_storage_uid_t uid,char * filename)62 static void psa_its_fill_filename(psa_storage_uid_t uid, char *filename)
63 {
64     /* Break up the UID into two 32-bit pieces so as not to rely on
65      * long long support in snprintf. */
66     mbedtls_snprintf(filename, PSA_ITS_STORAGE_FILENAME_LENGTH,
67                      "%s" PSA_ITS_STORAGE_FILENAME_PATTERN "%s",
68                      PSA_ITS_STORAGE_PREFIX,
69                      (unsigned) (uid >> 32),
70                      (unsigned) (uid & 0xffffffff),
71                      PSA_ITS_STORAGE_SUFFIX);
72 }
73 
psa_its_read_file(psa_storage_uid_t uid,struct psa_storage_info_t * p_info,FILE ** p_stream)74 static psa_status_t psa_its_read_file(psa_storage_uid_t uid,
75                                       struct psa_storage_info_t *p_info,
76                                       FILE **p_stream)
77 {
78     char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
79     psa_its_file_header_t header;
80     size_t n;
81 
82     *p_stream = NULL;
83     psa_its_fill_filename(uid, filename);
84     *p_stream = fopen(filename, "rb");
85     if (*p_stream == NULL) {
86         return PSA_ERROR_DOES_NOT_EXIST;
87     }
88 
89     /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
90     mbedtls_setbuf(*p_stream, NULL);
91 
92     n = fread(&header, 1, sizeof(header), *p_stream);
93     if (n != sizeof(header)) {
94         return PSA_ERROR_DATA_CORRUPT;
95     }
96     if (memcmp(header.magic, PSA_ITS_MAGIC_STRING,
97                PSA_ITS_MAGIC_LENGTH) != 0) {
98         return PSA_ERROR_DATA_CORRUPT;
99     }
100 
101     p_info->size = (header.size[0] |
102                     header.size[1] << 8 |
103                     header.size[2] << 16 |
104                     header.size[3] << 24);
105     p_info->flags = (header.flags[0] |
106                      header.flags[1] << 8 |
107                      header.flags[2] << 16 |
108                      header.flags[3] << 24);
109     return PSA_SUCCESS;
110 }
111 
psa_its_get_info(psa_storage_uid_t uid,struct psa_storage_info_t * p_info)112 psa_status_t psa_its_get_info(psa_storage_uid_t uid,
113                               struct psa_storage_info_t *p_info)
114 {
115     psa_status_t status;
116     FILE *stream = NULL;
117     status = psa_its_read_file(uid, p_info, &stream);
118     if (stream != NULL) {
119         fclose(stream);
120     }
121     return status;
122 }
123 
psa_its_get(psa_storage_uid_t uid,uint32_t data_offset,uint32_t data_length,void * p_data,size_t * p_data_length)124 psa_status_t psa_its_get(psa_storage_uid_t uid,
125                          uint32_t data_offset,
126                          uint32_t data_length,
127                          void *p_data,
128                          size_t *p_data_length)
129 {
130     psa_status_t status;
131     FILE *stream = NULL;
132     size_t n;
133     struct psa_storage_info_t info;
134 
135     status = psa_its_read_file(uid, &info, &stream);
136     if (status != PSA_SUCCESS) {
137         goto exit;
138     }
139     status = PSA_ERROR_INVALID_ARGUMENT;
140     if (data_offset + data_length < data_offset) {
141         goto exit;
142     }
143 #if SIZE_MAX < 0xffffffff
144     if (data_offset + data_length > SIZE_MAX) {
145         goto exit;
146     }
147 #endif
148     if (data_offset + data_length > info.size) {
149         goto exit;
150     }
151 
152     status = PSA_ERROR_STORAGE_FAILURE;
153 #if LONG_MAX < 0xffffffff
154     while (data_offset > LONG_MAX) {
155         if (fseek(stream, LONG_MAX, SEEK_CUR) != 0) {
156             goto exit;
157         }
158         data_offset -= LONG_MAX;
159     }
160 #endif
161     if (fseek(stream, data_offset, SEEK_CUR) != 0) {
162         goto exit;
163     }
164     n = fread(p_data, 1, data_length, stream);
165     if (n != data_length) {
166         goto exit;
167     }
168     status = PSA_SUCCESS;
169     if (p_data_length != NULL) {
170         *p_data_length = n;
171     }
172 
173 exit:
174     if (stream != NULL) {
175         fclose(stream);
176     }
177     return status;
178 }
179 
psa_its_set(psa_storage_uid_t uid,uint32_t data_length,const void * p_data,psa_storage_create_flags_t create_flags)180 psa_status_t psa_its_set(psa_storage_uid_t uid,
181                          uint32_t data_length,
182                          const void *p_data,
183                          psa_storage_create_flags_t create_flags)
184 {
185     if (uid == 0) {
186         return PSA_ERROR_INVALID_HANDLE;
187     }
188 
189     psa_status_t status = PSA_ERROR_STORAGE_FAILURE;
190     char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
191     FILE *stream = NULL;
192     psa_its_file_header_t header;
193     size_t n;
194 
195     memcpy(header.magic, PSA_ITS_MAGIC_STRING, PSA_ITS_MAGIC_LENGTH);
196     MBEDTLS_PUT_UINT32_LE(data_length, header.size, 0);
197     MBEDTLS_PUT_UINT32_LE(create_flags, header.flags, 0);
198 
199     psa_its_fill_filename(uid, filename);
200     stream = fopen(PSA_ITS_STORAGE_TEMP, "wb");
201 
202     if (stream == NULL) {
203         goto exit;
204     }
205 
206     /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */
207     mbedtls_setbuf(stream, NULL);
208 
209     status = PSA_ERROR_INSUFFICIENT_STORAGE;
210     n = fwrite(&header, 1, sizeof(header), stream);
211     if (n != sizeof(header)) {
212         goto exit;
213     }
214     if (data_length != 0) {
215         n = fwrite(p_data, 1, data_length, stream);
216         if (n != data_length) {
217             goto exit;
218         }
219     }
220     status = PSA_SUCCESS;
221 
222 exit:
223     if (stream != NULL) {
224         int ret = fclose(stream);
225         if (status == PSA_SUCCESS && ret != 0) {
226             status = PSA_ERROR_INSUFFICIENT_STORAGE;
227         }
228     }
229     if (status == PSA_SUCCESS) {
230         if (rename_replace_existing(PSA_ITS_STORAGE_TEMP, filename) != 0) {
231             status = PSA_ERROR_STORAGE_FAILURE;
232         }
233     }
234     /* The temporary file may still exist, but only in failure cases where
235      * we're already reporting an error. So there's nothing we can do on
236      * failure. If the function succeeded, and in some error cases, the
237      * temporary file doesn't exist and so remove() is expected to fail.
238      * Thus we just ignore the return status of remove(). */
239     (void) remove(PSA_ITS_STORAGE_TEMP);
240     return status;
241 }
242 
psa_its_remove(psa_storage_uid_t uid)243 psa_status_t psa_its_remove(psa_storage_uid_t uid)
244 {
245     char filename[PSA_ITS_STORAGE_FILENAME_LENGTH];
246     FILE *stream;
247     psa_its_fill_filename(uid, filename);
248     stream = fopen(filename, "rb");
249     if (stream == NULL) {
250         return PSA_ERROR_DOES_NOT_EXIST;
251     }
252     fclose(stream);
253     if (remove(filename) != 0) {
254         return PSA_ERROR_STORAGE_FAILURE;
255     }
256     return PSA_SUCCESS;
257 }
258 
259 #endif /* MBEDTLS_PSA_ITS_FILE_C */
260