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