xref: /openbmc/qemu/crypto/secret.c (revision 42f7a448)
1 /*
2  * QEMU crypto secret support
3  *
4  * Copyright (c) 2015 Red Hat, Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18  *
19  */
20 
21 #include "qemu/osdep.h"
22 #include "crypto/secret.h"
23 #include "crypto/cipher.h"
24 #include "qom/object_interfaces.h"
25 #include "qemu/base64.h"
26 #include "trace.h"
27 
28 
29 static void
30 qcrypto_secret_load_data(QCryptoSecret *secret,
31                          uint8_t **output,
32                          size_t *outputlen,
33                          Error **errp)
34 {
35     char *data = NULL;
36     size_t length = 0;
37     GError *gerr = NULL;
38 
39     *output = NULL;
40     *outputlen = 0;
41 
42     if (secret->file) {
43         if (secret->data) {
44             error_setg(errp,
45                        "'file' and 'data' are mutually exclusive");
46             return;
47         }
48         if (!g_file_get_contents(secret->file, &data, &length, &gerr)) {
49             error_setg(errp,
50                        "Unable to read %s: %s",
51                        secret->file, gerr->message);
52             g_error_free(gerr);
53             return;
54         }
55         *output = (uint8_t *)data;
56         *outputlen = length;
57     } else if (secret->data) {
58         *outputlen = strlen(secret->data);
59         *output = (uint8_t *)g_strdup(secret->data);
60     } else {
61         error_setg(errp, "Either 'file' or 'data' must be provided");
62     }
63 }
64 
65 
66 static void qcrypto_secret_decrypt(QCryptoSecret *secret,
67                                    const uint8_t *input,
68                                    size_t inputlen,
69                                    uint8_t **output,
70                                    size_t *outputlen,
71                                    Error **errp)
72 {
73     uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL;
74     size_t keylen, ciphertextlen, ivlen;
75     QCryptoCipher *aes = NULL;
76     uint8_t *plaintext = NULL;
77 
78     *output = NULL;
79     *outputlen = 0;
80 
81     if (qcrypto_secret_lookup(secret->keyid,
82                               &key, &keylen,
83                               errp) < 0) {
84         goto cleanup;
85     }
86 
87     if (keylen != 32) {
88         error_setg(errp, "Key should be 32 bytes in length");
89         goto cleanup;
90     }
91 
92     if (!secret->iv) {
93         error_setg(errp, "IV is required to decrypt secret");
94         goto cleanup;
95     }
96 
97     iv = qbase64_decode(secret->iv, -1, &ivlen, errp);
98     if (!iv) {
99         goto cleanup;
100     }
101     if (ivlen != 16) {
102         error_setg(errp, "IV should be 16 bytes in length not %zu",
103                    ivlen);
104         goto cleanup;
105     }
106 
107     aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256,
108                              QCRYPTO_CIPHER_MODE_CBC,
109                              key, keylen,
110                              errp);
111     if (!aes) {
112         goto cleanup;
113     }
114 
115     if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) {
116         goto cleanup;
117     }
118 
119     if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) {
120         ciphertext = qbase64_decode((const gchar*)input,
121                                     inputlen,
122                                     &ciphertextlen,
123                                     errp);
124         if (!ciphertext) {
125             goto cleanup;
126         }
127         plaintext = g_new0(uint8_t, ciphertextlen + 1);
128     } else {
129         ciphertextlen = inputlen;
130         plaintext = g_new0(uint8_t, inputlen + 1);
131     }
132     if (qcrypto_cipher_decrypt(aes,
133                                ciphertext ? ciphertext : input,
134                                plaintext,
135                                ciphertextlen,
136                                errp) < 0) {
137         plaintext = NULL;
138         goto cleanup;
139     }
140 
141     if (plaintext[ciphertextlen - 1] > 16 ||
142         plaintext[ciphertextlen - 1] > ciphertextlen) {
143         error_setg(errp, "Incorrect number of padding bytes (%d) "
144                    "found on decrypted data",
145                    (int)plaintext[ciphertextlen - 1]);
146         g_free(plaintext);
147         plaintext = NULL;
148         goto cleanup;
149     }
150 
151     /* Even though plaintext may contain arbitrary NUL
152      * ensure it is explicitly NUL terminated.
153      */
154     ciphertextlen -= plaintext[ciphertextlen - 1];
155     plaintext[ciphertextlen] = '\0';
156 
157     *output = plaintext;
158     *outputlen = ciphertextlen;
159 
160  cleanup:
161     g_free(ciphertext);
162     g_free(iv);
163     g_free(key);
164     qcrypto_cipher_free(aes);
165 }
166 
167 
168 static void qcrypto_secret_decode(const uint8_t *input,
169                                   size_t inputlen,
170                                   uint8_t **output,
171                                   size_t *outputlen,
172                                   Error **errp)
173 {
174     *output = qbase64_decode((const gchar*)input,
175                              inputlen,
176                              outputlen,
177                              errp);
178 }
179 
180 
181 static void
182 qcrypto_secret_prop_set_loaded(Object *obj,
183                                bool value,
184                                Error **errp)
185 {
186     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
187 
188     if (value) {
189         Error *local_err = NULL;
190         uint8_t *input = NULL;
191         size_t inputlen = 0;
192         uint8_t *output = NULL;
193         size_t outputlen = 0;
194 
195         qcrypto_secret_load_data(secret, &input, &inputlen, &local_err);
196         if (local_err) {
197             error_propagate(errp, local_err);
198             return;
199         }
200 
201         if (secret->keyid) {
202             qcrypto_secret_decrypt(secret, input, inputlen,
203                                    &output, &outputlen, &local_err);
204             g_free(input);
205             if (local_err) {
206                 error_propagate(errp, local_err);
207                 return;
208             }
209             input = output;
210             inputlen = outputlen;
211         } else {
212             if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) {
213                 qcrypto_secret_decode(input, inputlen,
214                                       &output, &outputlen, &local_err);
215                 g_free(input);
216                 if (local_err) {
217                     error_propagate(errp, local_err);
218                     return;
219                 }
220                 input = output;
221                 inputlen = outputlen;
222             }
223         }
224 
225         secret->rawdata = input;
226         secret->rawlen = inputlen;
227     } else {
228         g_free(secret->rawdata);
229         secret->rawlen = 0;
230     }
231 }
232 
233 
234 static bool
235 qcrypto_secret_prop_get_loaded(Object *obj,
236                                Error **errp G_GNUC_UNUSED)
237 {
238     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
239     return secret->data != NULL;
240 }
241 
242 
243 static void
244 qcrypto_secret_prop_set_format(Object *obj,
245                                int value,
246                                Error **errp G_GNUC_UNUSED)
247 {
248     QCryptoSecret *creds = QCRYPTO_SECRET(obj);
249 
250     creds->format = value;
251 }
252 
253 
254 static int
255 qcrypto_secret_prop_get_format(Object *obj,
256                                Error **errp G_GNUC_UNUSED)
257 {
258     QCryptoSecret *creds = QCRYPTO_SECRET(obj);
259 
260     return creds->format;
261 }
262 
263 
264 static void
265 qcrypto_secret_prop_set_data(Object *obj,
266                              const char *value,
267                              Error **errp)
268 {
269     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
270 
271     g_free(secret->data);
272     secret->data = g_strdup(value);
273 }
274 
275 
276 static char *
277 qcrypto_secret_prop_get_data(Object *obj,
278                              Error **errp)
279 {
280     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
281     return g_strdup(secret->data);
282 }
283 
284 
285 static void
286 qcrypto_secret_prop_set_file(Object *obj,
287                              const char *value,
288                              Error **errp)
289 {
290     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
291 
292     g_free(secret->file);
293     secret->file = g_strdup(value);
294 }
295 
296 
297 static char *
298 qcrypto_secret_prop_get_file(Object *obj,
299                              Error **errp)
300 {
301     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
302     return g_strdup(secret->file);
303 }
304 
305 
306 static void
307 qcrypto_secret_prop_set_iv(Object *obj,
308                            const char *value,
309                            Error **errp)
310 {
311     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
312 
313     g_free(secret->iv);
314     secret->iv = g_strdup(value);
315 }
316 
317 
318 static char *
319 qcrypto_secret_prop_get_iv(Object *obj,
320                            Error **errp)
321 {
322     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
323     return g_strdup(secret->iv);
324 }
325 
326 
327 static void
328 qcrypto_secret_prop_set_keyid(Object *obj,
329                               const char *value,
330                               Error **errp)
331 {
332     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
333 
334     g_free(secret->keyid);
335     secret->keyid = g_strdup(value);
336 }
337 
338 
339 static char *
340 qcrypto_secret_prop_get_keyid(Object *obj,
341                               Error **errp)
342 {
343     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
344     return g_strdup(secret->keyid);
345 }
346 
347 
348 static void
349 qcrypto_secret_complete(UserCreatable *uc, Error **errp)
350 {
351     object_property_set_bool(OBJECT(uc), true, "loaded", errp);
352 }
353 
354 
355 static void
356 qcrypto_secret_init(Object *obj)
357 {
358     object_property_add_bool(obj, "loaded",
359                              qcrypto_secret_prop_get_loaded,
360                              qcrypto_secret_prop_set_loaded,
361                              NULL);
362     object_property_add_enum(obj, "format",
363                              "QCryptoSecretFormat",
364                              QCryptoSecretFormat_lookup,
365                              qcrypto_secret_prop_get_format,
366                              qcrypto_secret_prop_set_format,
367                              NULL);
368     object_property_add_str(obj, "data",
369                             qcrypto_secret_prop_get_data,
370                             qcrypto_secret_prop_set_data,
371                             NULL);
372     object_property_add_str(obj, "file",
373                             qcrypto_secret_prop_get_file,
374                             qcrypto_secret_prop_set_file,
375                             NULL);
376     object_property_add_str(obj, "keyid",
377                             qcrypto_secret_prop_get_keyid,
378                             qcrypto_secret_prop_set_keyid,
379                             NULL);
380     object_property_add_str(obj, "iv",
381                             qcrypto_secret_prop_get_iv,
382                             qcrypto_secret_prop_set_iv,
383                             NULL);
384 }
385 
386 
387 static void
388 qcrypto_secret_finalize(Object *obj)
389 {
390     QCryptoSecret *secret = QCRYPTO_SECRET(obj);
391 
392     g_free(secret->iv);
393     g_free(secret->file);
394     g_free(secret->keyid);
395     g_free(secret->rawdata);
396     g_free(secret->data);
397 }
398 
399 static void
400 qcrypto_secret_class_init(ObjectClass *oc, void *data)
401 {
402     UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc);
403 
404     ucc->complete = qcrypto_secret_complete;
405 }
406 
407 
408 int qcrypto_secret_lookup(const char *secretid,
409                           uint8_t **data,
410                           size_t *datalen,
411                           Error **errp)
412 {
413     Object *obj;
414     QCryptoSecret *secret;
415 
416     obj = object_resolve_path_component(
417         object_get_objects_root(), secretid);
418     if (!obj) {
419         error_setg(errp, "No secret with id '%s'", secretid);
420         return -1;
421     }
422 
423     secret = (QCryptoSecret *)
424         object_dynamic_cast(obj,
425                             TYPE_QCRYPTO_SECRET);
426     if (!secret) {
427         error_setg(errp, "Object with id '%s' is not a secret",
428                    secretid);
429         return -1;
430     }
431 
432     if (!secret->rawdata) {
433         error_setg(errp, "Secret with id '%s' has no data",
434                    secretid);
435         return -1;
436     }
437 
438     *data = g_new0(uint8_t, secret->rawlen + 1);
439     memcpy(*data, secret->rawdata, secret->rawlen);
440     (*data)[secret->rawlen] = '\0';
441     *datalen = secret->rawlen;
442 
443     return 0;
444 }
445 
446 
447 char *qcrypto_secret_lookup_as_utf8(const char *secretid,
448                                     Error **errp)
449 {
450     uint8_t *data;
451     size_t datalen;
452 
453     if (qcrypto_secret_lookup(secretid,
454                               &data,
455                               &datalen,
456                               errp) < 0) {
457         return NULL;
458     }
459 
460     if (!g_utf8_validate((const gchar*)data, datalen, NULL)) {
461         error_setg(errp,
462                    "Data from secret %s is not valid UTF-8",
463                    secretid);
464         g_free(data);
465         return NULL;
466     }
467 
468     return (char *)data;
469 }
470 
471 
472 char *qcrypto_secret_lookup_as_base64(const char *secretid,
473                                       Error **errp)
474 {
475     uint8_t *data;
476     size_t datalen;
477     char *ret;
478 
479     if (qcrypto_secret_lookup(secretid,
480                               &data,
481                               &datalen,
482                               errp) < 0) {
483         return NULL;
484     }
485 
486     ret = g_base64_encode(data, datalen);
487     g_free(data);
488     return ret;
489 }
490 
491 
492 static const TypeInfo qcrypto_secret_info = {
493     .parent = TYPE_OBJECT,
494     .name = TYPE_QCRYPTO_SECRET,
495     .instance_size = sizeof(QCryptoSecret),
496     .instance_init = qcrypto_secret_init,
497     .instance_finalize = qcrypto_secret_finalize,
498     .class_size = sizeof(QCryptoSecretClass),
499     .class_init = qcrypto_secret_class_init,
500     .interfaces = (InterfaceInfo[]) {
501         { TYPE_USER_CREATABLE },
502         { }
503     }
504 };
505 
506 
507 static void
508 qcrypto_secret_register_types(void)
509 {
510     type_register_static(&qcrypto_secret_info);
511 }
512 
513 
514 type_init(qcrypto_secret_register_types);
515