/src/mbedtls/library/psa_its_file.c
Line | Count | Source (jump to first uncovered line) |
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 | 70 | #define PSA_ITS_STORAGE_PREFIX "" |
28 | | #endif |
29 | | |
30 | | #define PSA_ITS_STORAGE_FILENAME_PATTERN "%08x%08x" |
31 | 70 | #define PSA_ITS_STORAGE_SUFFIX ".psa_its" |
32 | | #define PSA_ITS_STORAGE_FILENAME_LENGTH \ |
33 | 35 | (sizeof(PSA_ITS_STORAGE_PREFIX) - 1 + /*prefix without terminating 0*/ \ |
34 | 35 | 16 + /*UID (64-bit number in hex)*/ \ |
35 | 35 | sizeof(PSA_ITS_STORAGE_SUFFIX) - 1 + /*suffix without terminating 0*/ \ |
36 | 35 | 1 /*terminating null byte*/) |
37 | | #define PSA_ITS_STORAGE_TEMP \ |
38 | 0 | 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 | 0 | #define PSA_ITS_MAGIC_STRING "PSA\0ITS\0" |
44 | 0 | #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 | 0 | #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 | | |
62 | | static void psa_its_fill_filename(psa_storage_uid_t uid, char *filename) |
63 | 35 | { |
64 | | /* Break up the UID into two 32-bit pieces so as not to rely on |
65 | | * long long support in snprintf. */ |
66 | 35 | mbedtls_snprintf(filename, PSA_ITS_STORAGE_FILENAME_LENGTH, |
67 | 35 | "%s" PSA_ITS_STORAGE_FILENAME_PATTERN "%s", |
68 | 35 | PSA_ITS_STORAGE_PREFIX, |
69 | 35 | (unsigned) (uid >> 32), |
70 | 35 | (unsigned) (uid & 0xffffffff), |
71 | 35 | PSA_ITS_STORAGE_SUFFIX); |
72 | 35 | } |
73 | | |
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 | 35 | { |
78 | 35 | char filename[PSA_ITS_STORAGE_FILENAME_LENGTH]; |
79 | 35 | psa_its_file_header_t header; |
80 | 35 | size_t n; |
81 | | |
82 | 35 | *p_stream = NULL; |
83 | 35 | psa_its_fill_filename(uid, filename); |
84 | 35 | *p_stream = fopen(filename, "rb"); |
85 | 35 | if (*p_stream == NULL) { |
86 | 35 | return PSA_ERROR_DOES_NOT_EXIST; |
87 | 35 | } |
88 | | |
89 | | /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ |
90 | 0 | mbedtls_setbuf(*p_stream, NULL); |
91 | |
|
92 | 0 | n = fread(&header, 1, sizeof(header), *p_stream); |
93 | 0 | if (n != sizeof(header)) { |
94 | 0 | return PSA_ERROR_DATA_CORRUPT; |
95 | 0 | } |
96 | 0 | if (memcmp(header.magic, PSA_ITS_MAGIC_STRING, |
97 | 0 | PSA_ITS_MAGIC_LENGTH) != 0) { |
98 | 0 | return PSA_ERROR_DATA_CORRUPT; |
99 | 0 | } |
100 | | |
101 | 0 | p_info->size = MBEDTLS_GET_UINT32_LE(header.size, 0); |
102 | 0 | p_info->flags = MBEDTLS_GET_UINT32_LE(header.flags, 0); |
103 | |
|
104 | 0 | return PSA_SUCCESS; |
105 | 0 | } |
106 | | |
107 | | psa_status_t psa_its_get_info(psa_storage_uid_t uid, |
108 | | struct psa_storage_info_t *p_info) |
109 | 35 | { |
110 | 35 | psa_status_t status; |
111 | 35 | FILE *stream = NULL; |
112 | 35 | status = psa_its_read_file(uid, p_info, &stream); |
113 | 35 | if (stream != NULL) { |
114 | 0 | fclose(stream); |
115 | 0 | } |
116 | 35 | return status; |
117 | 35 | } |
118 | | |
119 | | psa_status_t psa_its_get(psa_storage_uid_t uid, |
120 | | uint32_t data_offset, |
121 | | uint32_t data_length, |
122 | | void *p_data, |
123 | | size_t *p_data_length) |
124 | 0 | { |
125 | 0 | psa_status_t status; |
126 | 0 | FILE *stream = NULL; |
127 | 0 | size_t n; |
128 | 0 | struct psa_storage_info_t info; |
129 | |
|
130 | 0 | status = psa_its_read_file(uid, &info, &stream); |
131 | 0 | if (status != PSA_SUCCESS) { |
132 | 0 | goto exit; |
133 | 0 | } |
134 | 0 | status = PSA_ERROR_INVALID_ARGUMENT; |
135 | 0 | if (data_offset + data_length < data_offset) { |
136 | 0 | goto exit; |
137 | 0 | } |
138 | | #if SIZE_MAX < 0xffffffff |
139 | | if (data_offset + data_length > SIZE_MAX) { |
140 | | goto exit; |
141 | | } |
142 | | #endif |
143 | 0 | if (data_offset + data_length > info.size) { |
144 | 0 | goto exit; |
145 | 0 | } |
146 | | |
147 | 0 | status = PSA_ERROR_STORAGE_FAILURE; |
148 | | #if LONG_MAX < 0xffffffff |
149 | | while (data_offset > LONG_MAX) { |
150 | | if (fseek(stream, LONG_MAX, SEEK_CUR) != 0) { |
151 | | goto exit; |
152 | | } |
153 | | data_offset -= LONG_MAX; |
154 | | } |
155 | | #endif |
156 | 0 | if (fseek(stream, data_offset, SEEK_CUR) != 0) { |
157 | 0 | goto exit; |
158 | 0 | } |
159 | 0 | n = fread(p_data, 1, data_length, stream); |
160 | 0 | if (n != data_length) { |
161 | 0 | goto exit; |
162 | 0 | } |
163 | 0 | status = PSA_SUCCESS; |
164 | 0 | if (p_data_length != NULL) { |
165 | 0 | *p_data_length = n; |
166 | 0 | } |
167 | |
|
168 | 0 | exit: |
169 | 0 | if (stream != NULL) { |
170 | 0 | fclose(stream); |
171 | 0 | } |
172 | 0 | return status; |
173 | 0 | } |
174 | | |
175 | | psa_status_t psa_its_set(psa_storage_uid_t uid, |
176 | | uint32_t data_length, |
177 | | const void *p_data, |
178 | | psa_storage_create_flags_t create_flags) |
179 | 0 | { |
180 | 0 | if (uid == 0) { |
181 | 0 | return PSA_ERROR_INVALID_HANDLE; |
182 | 0 | } |
183 | | |
184 | 0 | psa_status_t status = PSA_ERROR_STORAGE_FAILURE; |
185 | 0 | char filename[PSA_ITS_STORAGE_FILENAME_LENGTH]; |
186 | 0 | FILE *stream = NULL; |
187 | 0 | psa_its_file_header_t header; |
188 | 0 | size_t n; |
189 | |
|
190 | 0 | memcpy(header.magic, PSA_ITS_MAGIC_STRING, PSA_ITS_MAGIC_LENGTH); |
191 | 0 | MBEDTLS_PUT_UINT32_LE(data_length, header.size, 0); |
192 | 0 | MBEDTLS_PUT_UINT32_LE(create_flags, header.flags, 0); |
193 | |
|
194 | 0 | psa_its_fill_filename(uid, filename); |
195 | 0 | stream = fopen(PSA_ITS_STORAGE_TEMP, "wb"); |
196 | |
|
197 | 0 | if (stream == NULL) { |
198 | 0 | goto exit; |
199 | 0 | } |
200 | | |
201 | | /* Ensure no stdio buffering of secrets, as such buffers cannot be wiped. */ |
202 | 0 | mbedtls_setbuf(stream, NULL); |
203 | |
|
204 | 0 | status = PSA_ERROR_INSUFFICIENT_STORAGE; |
205 | 0 | n = fwrite(&header, 1, sizeof(header), stream); |
206 | 0 | if (n != sizeof(header)) { |
207 | 0 | goto exit; |
208 | 0 | } |
209 | 0 | if (data_length != 0) { |
210 | 0 | n = fwrite(p_data, 1, data_length, stream); |
211 | 0 | if (n != data_length) { |
212 | 0 | goto exit; |
213 | 0 | } |
214 | 0 | } |
215 | 0 | status = PSA_SUCCESS; |
216 | |
|
217 | 0 | exit: |
218 | 0 | if (stream != NULL) { |
219 | 0 | int ret = fclose(stream); |
220 | 0 | if (status == PSA_SUCCESS && ret != 0) { |
221 | 0 | status = PSA_ERROR_INSUFFICIENT_STORAGE; |
222 | 0 | } |
223 | 0 | } |
224 | 0 | if (status == PSA_SUCCESS) { |
225 | 0 | if (rename_replace_existing(PSA_ITS_STORAGE_TEMP, filename) != 0) { |
226 | 0 | status = PSA_ERROR_STORAGE_FAILURE; |
227 | 0 | } |
228 | 0 | } |
229 | | /* The temporary file may still exist, but only in failure cases where |
230 | | * we're already reporting an error. So there's nothing we can do on |
231 | | * failure. If the function succeeded, and in some error cases, the |
232 | | * temporary file doesn't exist and so remove() is expected to fail. |
233 | | * Thus we just ignore the return status of remove(). */ |
234 | 0 | (void) remove(PSA_ITS_STORAGE_TEMP); |
235 | 0 | return status; |
236 | 0 | } |
237 | | |
238 | | psa_status_t psa_its_remove(psa_storage_uid_t uid) |
239 | 0 | { |
240 | 0 | char filename[PSA_ITS_STORAGE_FILENAME_LENGTH]; |
241 | 0 | FILE *stream; |
242 | 0 | psa_its_fill_filename(uid, filename); |
243 | 0 | stream = fopen(filename, "rb"); |
244 | 0 | if (stream == NULL) { |
245 | 0 | return PSA_ERROR_DOES_NOT_EXIST; |
246 | 0 | } |
247 | 0 | fclose(stream); |
248 | 0 | if (remove(filename) != 0) { |
249 | 0 | return PSA_ERROR_STORAGE_FAILURE; |
250 | 0 | } |
251 | 0 | return PSA_SUCCESS; |
252 | 0 | } |
253 | | |
254 | | #endif /* MBEDTLS_PSA_ITS_FILE_C */ |