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