Line data Source code
1 : /**
2 : * The MIT License (MIT)
3 : *
4 : * Copyright (c) 2021 RSK Labs Ltd
5 : *
6 : * Permission is hereby granted, free of charge, to any person obtaining a copy
7 : * of this software and associated documentation files (the "Software"), to
8 : * deal in the Software without restriction, including without limitation the
9 : * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 : * sell copies of the Software, and to permit persons to whom the Software is
11 : * furnished to do so, subject to the following conditions:
12 : *
13 : * The above copyright notice and this permission notice shall be included in
14 : * all copies or substantial portions of the Software.
15 : *
16 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 : * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 : * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 : * IN THE SOFTWARE.
23 : */
24 :
25 : #include <stdio.h>
26 : #include <string.h>
27 : #include <stdlib.h>
28 :
29 : #include <openenclave/corelibc/stdlib.h>
30 : #include <openenclave/seal.h>
31 : #include <sys/mount.h>
32 :
33 : #include "hal/platform.h"
34 : #include "hal/log.h"
35 : #include "hal/constants.h"
36 : #include "sha256.h"
37 : #include "secret_store.h"
38 : #include "hsm_t.h"
39 :
40 : #define SEAL_POLICY_UNIQUE 1
41 : #define SEAL_POLICY_PRODUCT 2
42 :
43 : #ifdef DEBUG_BUILD
44 : #define SEAL_POLICY SEAL_POLICY_PRODUCT
45 : #else
46 : #define SEAL_POLICY SEAL_POLICY_UNIQUE
47 : #endif
48 :
49 : #define SEST_ERROR (0)
50 :
51 : #define MAX_BLOB_SIZE (1024 * 1024)
52 :
53 : // To avoid performing dynamic memory allocation whenever we need to manipulate
54 : // data, we use these static buffers to store the sealed and unsealed data,
55 : // respectively.
56 : static uint8_t G_sealed_buffer[MAX_BLOB_SIZE];
57 : static uint8_t G_unsealed_buffer[MAX_BLOB_SIZE];
58 :
59 : // The sha256 context used for hash operations.
60 : static SHA256_CTX G_sha256_ctx;
61 :
62 : // The file format of the sealed secrets. This is the format we use to read and
63 : // write sealed the data to disk.
64 : typedef struct {
65 : size_t blob_size;
66 : uint8_t* blob;
67 : } sealed_secret_t;
68 :
69 : /**
70 : * @brief Adds a header to the given input data.
71 : * The header is composed of the sha256 hash of the key.
72 : * The resulting data is stored in the destination buffer.
73 : *
74 : * @param key The key to hash and set the header for.
75 : * @param src The input data to prepend the key hash to.
76 : * @param src_size The size of the input data.
77 : * @param dest The destination buffer for generated data.
78 : * @param dest_size The size of the destination buffer.
79 : *
80 : * @returns The number of bytes written to the destination buffer, or SEST_ERROR
81 : * upon error.
82 : */
83 11 : static size_t add_header(const char* key,
84 : const uint8_t* src,
85 : size_t src_size,
86 : uint8_t* dest,
87 : size_t dest_size) {
88 11 : if (dest_size < HASH_LENGTH + src_size) {
89 : LOG("Failed to add header - destination buffer is too small\n");
90 1 : return SEST_ERROR;
91 : }
92 :
93 10 : sha256_init(&G_sha256_ctx);
94 10 : sha256_update(&G_sha256_ctx, (const uint8_t*)key, strlen(key));
95 10 : sha256_final(&G_sha256_ctx, dest);
96 :
97 10 : if (src_size)
98 7 : platform_memmove(dest + HASH_LENGTH, src, src_size);
99 :
100 10 : return HASH_LENGTH + src_size;
101 : }
102 :
103 : /**
104 : * @brief Checks if the data buffer contains the correct header for the given
105 : * key.
106 : *
107 : * @param key The key to validate the header against.
108 : * @param data The buffer containing the data to validate.
109 : * @param data_length The length of the data buffer.
110 : *
111 : * @returns true if the header is valid, false otherwise.
112 : */
113 3 : static bool is_header_valid(const char* key,
114 : const uint8_t* data,
115 : size_t data_length) {
116 3 : if (data_length < HASH_LENGTH) {
117 0 : return false;
118 : }
119 :
120 : uint8_t expected_header[HASH_LENGTH];
121 3 : add_header(key, NULL, 0, expected_header, HASH_LENGTH);
122 :
123 3 : return memcmp(expected_header, data, HASH_LENGTH) == 0;
124 : }
125 :
126 : /**
127 : * @brief Obtains the plaintext data from a sealed secret.
128 : *
129 : * @param sealed_secret The sealed secret to unseal.
130 : * @param dest The destination buffer for the unsealed data. This must be
131 : * pre-allocated and large enough to hold the unsealed data.
132 : * @param dest_length The length of the pre-allocated destination buffer.
133 : *
134 : * @returns the length of the unsealed data, or SEST_ERROR upon error
135 : */
136 4 : static uint8_t unseal_data(const sealed_secret_t* sealed_secret,
137 : uint8_t* dest,
138 : size_t dest_length) {
139 : #ifndef SIM_BUILD
140 4 : uint8_t* plaintext = NULL;
141 4 : size_t plaintext_size = 0;
142 4 : oe_result_t result = oe_unseal(sealed_secret->blob,
143 4 : sealed_secret->blob_size,
144 : NULL,
145 : 0,
146 : &plaintext,
147 : &plaintext_size);
148 4 : if (result != OE_OK) {
149 : LOG("Unsealing failed with result=%u (%s)\n",
150 : result,
151 : oe_result_str(result));
152 1 : goto unseal_data_error;
153 : }
154 :
155 3 : if (plaintext_size > dest_length) {
156 : LOG("Unsealed data is too large\n");
157 0 : goto unseal_data_error;
158 : }
159 :
160 3 : platform_memmove(dest, plaintext, plaintext_size);
161 3 : oe_free(plaintext);
162 3 : return plaintext_size;
163 :
164 1 : unseal_data_error:
165 1 : if (plaintext)
166 0 : oe_free(plaintext);
167 1 : return SEST_ERROR;
168 : #else
169 : // *************************************************** //
170 : // UNSAFE SIMULATOR-ONLY UNSEAL IMPLEMENTATION //
171 : // NOT FOR PRODUCTION USE //
172 : if (sealed_secret->blob_size > MAX_BLOB_SIZE) {
173 : LOG("Sealed blob size is too large\n");
174 : return SEST_ERROR;
175 : }
176 :
177 : if (sealed_secret->blob_size > dest_length) {
178 : LOG("Unsealed data is too large\n");
179 : return SEST_ERROR;
180 : }
181 :
182 : platform_memmove(dest, sealed_secret->blob, sealed_secret->blob_size);
183 :
184 : return sealed_secret->blob_size;
185 : // *************************************************** //
186 : #endif
187 : }
188 :
189 : /**
190 : * @brief Seals the given data into a sealed secret.
191 : *
192 : * @param data The data to seal.
193 : * @param data_length The length of the data to seal.
194 : * @param sealed_secret The destination for the sealed secret.
195 : */
196 7 : static bool seal_data(uint8_t* data,
197 : size_t data_length,
198 : sealed_secret_t* sealed_secret) {
199 : #ifndef SIM_BUILD
200 7 : uint8_t* blob = NULL;
201 7 : size_t blob_size = 0;
202 7 : const oe_seal_setting_t settings[] = {OE_SEAL_SET_POLICY(SEAL_POLICY)};
203 7 : oe_result_t result = oe_seal(NULL,
204 : settings,
205 : sizeof(settings) / sizeof(settings[0]),
206 : data,
207 : data_length,
208 : NULL,
209 : 0,
210 : &blob,
211 : &blob_size);
212 7 : if (result != OE_OK) {
213 : LOG("Sealing failed with result=%u (%s)\n",
214 : result,
215 : oe_result_str(result));
216 1 : oe_free(blob);
217 1 : return false;
218 : }
219 :
220 6 : sealed_secret->blob = blob;
221 6 : sealed_secret->blob_size = blob_size;
222 6 : return true;
223 : #else
224 : // *************************************************** //
225 : // UNSAFE SIMULATOR-ONLY SEAL IMPLEMENTATION //
226 : // NOT FOR PRODUCTION USE //
227 : sealed_secret->blob = oe_malloc(data_length);
228 : memcpy(sealed_secret->blob, data, data_length);
229 : sealed_secret->blob_size = data_length;
230 : return true;
231 : // *************************************************** //
232 : #endif
233 : }
234 :
235 : // Public API
236 17 : bool sest_init() {
237 17 : explicit_bzero(G_sealed_buffer, sizeof(G_sealed_buffer));
238 17 : explicit_bzero(G_unsealed_buffer, sizeof(G_unsealed_buffer));
239 17 : return true;
240 : }
241 :
242 26 : bool sest_exists(char* key) {
243 : LOG("Attempting determine secret existence for <%s>...\n", key);
244 :
245 : bool exists;
246 26 : oe_result_t oe_result = ocall_kvstore_exists(&exists, key);
247 :
248 26 : if (oe_result != OE_OK) {
249 : LOG("Key-value store exists query failed with result=%u (%s)\n",
250 : oe_result,
251 : oe_result_str(oe_result));
252 1 : return false;
253 : }
254 :
255 25 : return exists;
256 : }
257 :
258 7 : uint8_t sest_read(char* key, uint8_t* dest, size_t dest_length) {
259 : LOG("Attempting to read secret for <%s>...\n", key);
260 :
261 7 : size_t blob_size = 0;
262 7 : oe_result_t oe_result = ocall_kvstore_get(
263 : &blob_size, key, G_sealed_buffer, sizeof(G_sealed_buffer));
264 7 : if (oe_result != OE_OK) {
265 : LOG("Key-value store read failed with result=%u (%s)\n",
266 : oe_result,
267 : oe_result_str(oe_result));
268 1 : goto sest_read_error;
269 : }
270 :
271 6 : if (!blob_size) {
272 : LOG("No secret found for key <%s>\n", key);
273 1 : goto sest_read_error;
274 : }
275 :
276 : // This is just an extra sanity check, this can never happen in
277 : // practice since the ocall will fail if the blob size
278 : // is too large for the buffer.
279 5 : if (blob_size > sizeof(G_sealed_buffer)) {
280 : LOG("Sealed blob too large\n");
281 1 : goto sest_read_error;
282 : }
283 :
284 4 : sealed_secret_t sealed_secret = {
285 : .blob_size = blob_size,
286 : .blob = G_sealed_buffer,
287 : };
288 :
289 4 : uint8_t unsealed_length = unseal_data(
290 : &sealed_secret, G_unsealed_buffer, sizeof(G_unsealed_buffer));
291 4 : if (unsealed_length == SEST_ERROR) {
292 : LOG("Unable to read secret stored in key <%s>\n", key);
293 1 : goto sest_read_error;
294 : }
295 :
296 3 : if (!is_header_valid(key, G_unsealed_buffer, unsealed_length)) {
297 : LOG("Secret header validation failed for key <%s>\n", key);
298 1 : goto sest_read_error;
299 : }
300 :
301 : // Skip the header and copy the plaintext data to the destination buffer.
302 2 : uint8_t* plaintext = G_unsealed_buffer + HASH_LENGTH;
303 2 : size_t plaintext_size = unsealed_length - HASH_LENGTH;
304 :
305 2 : if (plaintext_size > dest_length) {
306 : LOG("Unsealed data is too large\n");
307 1 : goto sest_read_error;
308 : }
309 1 : platform_memmove(dest, plaintext, plaintext_size);
310 :
311 : // Clean up the buffers used to store the sealed and unsealed data.
312 1 : explicit_bzero(G_sealed_buffer, sizeof(G_sealed_buffer));
313 1 : explicit_bzero(G_unsealed_buffer, sizeof(G_unsealed_buffer));
314 :
315 1 : return plaintext_size;
316 :
317 6 : sest_read_error:
318 6 : explicit_bzero(G_sealed_buffer, sizeof(G_sealed_buffer));
319 6 : explicit_bzero(G_unsealed_buffer, sizeof(G_unsealed_buffer));
320 6 : return SEST_ERROR;
321 : }
322 :
323 9 : bool sest_write(char* key, uint8_t* secret, size_t secret_length) {
324 : LOG("Attempting to write secret for <%s>...\n", key);
325 9 : if (!secret_length) {
326 : LOG("Invalid zero-length secret given for key <%s>\n", key);
327 1 : return false;
328 : }
329 :
330 8 : sealed_secret_t sealed_secret = {
331 : .blob_size = 0,
332 : .blob = NULL,
333 : };
334 :
335 8 : size_t unsealed_size = add_header(key,
336 : secret,
337 : secret_length,
338 : G_unsealed_buffer,
339 : sizeof(G_unsealed_buffer));
340 8 : if (unsealed_size == SEST_ERROR) {
341 : LOG("Error adding header to secret\n");
342 1 : goto sest_write_error;
343 : }
344 :
345 7 : if (!seal_data(G_unsealed_buffer, unsealed_size, &sealed_secret)) {
346 : LOG("Error sealing secret for key <%s>\n", key);
347 1 : goto sest_write_error;
348 : }
349 :
350 6 : if (sealed_secret.blob_size > MAX_BLOB_SIZE) {
351 : LOG("Sealed blob too large\n");
352 0 : goto sest_write_error;
353 : }
354 :
355 6 : bool save_success = false;
356 6 : oe_result_t oe_result = ocall_kvstore_save(
357 : &save_success, key, sealed_secret.blob, sealed_secret.blob_size);
358 6 : if (oe_result != OE_OK) {
359 : LOG("Key-value store write failed with result=%u (%s)\n",
360 : oe_result,
361 : oe_result_str(oe_result));
362 1 : goto sest_write_error;
363 : }
364 :
365 5 : if (!save_success) {
366 : LOG("Error saving secret for key <%s>\n", key);
367 1 : goto sest_write_error;
368 : }
369 :
370 4 : oe_free(sealed_secret.blob);
371 4 : return true;
372 :
373 4 : sest_write_error:
374 4 : explicit_bzero(G_unsealed_buffer, sizeof(G_unsealed_buffer));
375 4 : if (sealed_secret.blob)
376 2 : oe_free(sealed_secret.blob);
377 4 : return false;
378 : }
379 :
380 3 : bool sest_remove(char* key) {
381 3 : bool remove_success = false;
382 3 : oe_result_t oe_result = ocall_kvstore_remove(&remove_success, key);
383 3 : if (oe_result != OE_OK) {
384 : LOG("ocall_oestore_remove_secret() failed with result=%u (%s)\n",
385 : oe_result,
386 : oe_result_str(oe_result));
387 1 : return false;
388 : }
389 :
390 2 : if (!remove_success) {
391 : LOG("Error removing secret for key <%s>\n", key);
392 1 : return false;
393 : }
394 :
395 1 : return true;
396 : }
|