Browse Source
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCAAGBQJWdDmJAAoJEL6G67QVEE/fJ4EP+gNC4ErBDpbg+I4RhLHv/FsF i2iEYkmBfFzUmiB8iSFlJY12XJ/CPnbrWks0WNHIoarUgtGuvfqH91KGkERxJQFE TkD65EOhNHWlP1zaI5r5ZMizhOdO6EPa0pbS/QH/UCy5qwu5IbG5EesOw00d7nL8 5gT39ehXAFljutRygWsa1JyOkDB04WNehVsly/l2t9v16aQUDJOC5mWVuoInrDEo ye1VG2Cx7Y1/FRo3fFCDwzYD+8jgxIBAu8Igwjk/95VbfBVl769PBAilQRc5zMHt //eMNdul6GooVKmu/K1JWkmjIZJFUiboEMgPJElWV1y8bWmhh++4J6EVb55owoDk VRv84cqiaYErVb+56gaImr92GSKezll0APWz6YlDsFZgPClCPnUjDSr39+23t1h4 LprirtbkAjw73T92kuQ7kzbXElWm7rSfcx5u1/S6YPP+EDzZpW9+h62lKGGnuS2M bzwFOOmWHe1MhbRSh+BOzGBf1wWhMSCKgLAOmPuRQ8slS91vfE66bIlqpIKBGgfn 42t0wZCEW8bqIe8xry5pC5UoDfm3cVDhgGHGyMLWWDMez0qDchaAkWNkIDtc8Juv a1WqE/0lP/sVb36yLVANvt1/Qvpg6M3JwMbTjVaJl2eTDDtwho4PK+Chxx0a8BGl Z6oGj1rmvCDD2Dsi/EXI =g+9G -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/berrange/tags/pull-qcrypto-secrets-base-2015-12-18-1' into staging Merge QCryptoSecret object support # gpg: Signature made Fri 18 Dec 2015 16:51:21 GMT using RSA key ID 15104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" * remotes/berrange/tags/pull-qcrypto-secrets-base-2015-12-18-1: crypto: add support for loading encrypted x509 keys crypto: add QCryptoSecret object class for password/key handling qga: convert to use error checked base64 decode qemu-char: convert to use error checked base64 decode util: add base64 decoding function Signed-off-by: Peter Maydell <peter.maydell@linaro.org>pull/33/head
20 changed files with 1533 additions and 11 deletions
@ -0,0 +1,513 @@ |
|||
/*
|
|||
* QEMU crypto secret support |
|||
* |
|||
* Copyright (c) 2015 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
#include "crypto/secret.h" |
|||
#include "crypto/cipher.h" |
|||
#include "qom/object_interfaces.h" |
|||
#include "qemu/base64.h" |
|||
#include "trace.h" |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_load_data(QCryptoSecret *secret, |
|||
uint8_t **output, |
|||
size_t *outputlen, |
|||
Error **errp) |
|||
{ |
|||
char *data = NULL; |
|||
size_t length = 0; |
|||
GError *gerr = NULL; |
|||
|
|||
*output = NULL; |
|||
*outputlen = 0; |
|||
|
|||
if (secret->file) { |
|||
if (secret->data) { |
|||
error_setg(errp, |
|||
"'file' and 'data' are mutually exclusive"); |
|||
return; |
|||
} |
|||
if (!g_file_get_contents(secret->file, &data, &length, &gerr)) { |
|||
error_setg(errp, |
|||
"Unable to read %s: %s", |
|||
secret->file, gerr->message); |
|||
g_error_free(gerr); |
|||
return; |
|||
} |
|||
*output = (uint8_t *)data; |
|||
*outputlen = length; |
|||
} else if (secret->data) { |
|||
*outputlen = strlen(secret->data); |
|||
*output = (uint8_t *)g_strdup(secret->data); |
|||
} else { |
|||
error_setg(errp, "Either 'file' or 'data' must be provided"); |
|||
} |
|||
} |
|||
|
|||
|
|||
static void qcrypto_secret_decrypt(QCryptoSecret *secret, |
|||
const uint8_t *input, |
|||
size_t inputlen, |
|||
uint8_t **output, |
|||
size_t *outputlen, |
|||
Error **errp) |
|||
{ |
|||
uint8_t *key = NULL, *ciphertext = NULL, *iv = NULL; |
|||
size_t keylen, ciphertextlen, ivlen; |
|||
QCryptoCipher *aes = NULL; |
|||
uint8_t *plaintext = NULL; |
|||
|
|||
*output = NULL; |
|||
*outputlen = 0; |
|||
|
|||
if (qcrypto_secret_lookup(secret->keyid, |
|||
&key, &keylen, |
|||
errp) < 0) { |
|||
goto cleanup; |
|||
} |
|||
|
|||
if (keylen != 32) { |
|||
error_setg(errp, "Key should be 32 bytes in length"); |
|||
goto cleanup; |
|||
} |
|||
|
|||
if (!secret->iv) { |
|||
error_setg(errp, "IV is required to decrypt secret"); |
|||
goto cleanup; |
|||
} |
|||
|
|||
iv = qbase64_decode(secret->iv, -1, &ivlen, errp); |
|||
if (!iv) { |
|||
goto cleanup; |
|||
} |
|||
if (ivlen != 16) { |
|||
error_setg(errp, "IV should be 16 bytes in length not %zu", |
|||
ivlen); |
|||
goto cleanup; |
|||
} |
|||
|
|||
aes = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_256, |
|||
QCRYPTO_CIPHER_MODE_CBC, |
|||
key, keylen, |
|||
errp); |
|||
if (!aes) { |
|||
goto cleanup; |
|||
} |
|||
|
|||
if (qcrypto_cipher_setiv(aes, iv, ivlen, errp) < 0) { |
|||
goto cleanup; |
|||
} |
|||
|
|||
if (secret->format == QCRYPTO_SECRET_FORMAT_BASE64) { |
|||
ciphertext = qbase64_decode((const gchar*)input, |
|||
inputlen, |
|||
&ciphertextlen, |
|||
errp); |
|||
if (!ciphertext) { |
|||
goto cleanup; |
|||
} |
|||
plaintext = g_new0(uint8_t, ciphertextlen + 1); |
|||
} else { |
|||
ciphertextlen = inputlen; |
|||
plaintext = g_new0(uint8_t, inputlen + 1); |
|||
} |
|||
if (qcrypto_cipher_decrypt(aes, |
|||
ciphertext ? ciphertext : input, |
|||
plaintext, |
|||
ciphertextlen, |
|||
errp) < 0) { |
|||
plaintext = NULL; |
|||
goto cleanup; |
|||
} |
|||
|
|||
if (plaintext[ciphertextlen - 1] > 16 || |
|||
plaintext[ciphertextlen - 1] > ciphertextlen) { |
|||
error_setg(errp, "Incorrect number of padding bytes (%d) " |
|||
"found on decrypted data", |
|||
(int)plaintext[ciphertextlen - 1]); |
|||
g_free(plaintext); |
|||
plaintext = NULL; |
|||
goto cleanup; |
|||
} |
|||
|
|||
/* Even though plaintext may contain arbitrary NUL
|
|||
* ensure it is explicitly NUL terminated. |
|||
*/ |
|||
ciphertextlen -= plaintext[ciphertextlen - 1]; |
|||
plaintext[ciphertextlen] = '\0'; |
|||
|
|||
*output = plaintext; |
|||
*outputlen = ciphertextlen; |
|||
|
|||
cleanup: |
|||
g_free(ciphertext); |
|||
g_free(iv); |
|||
g_free(key); |
|||
qcrypto_cipher_free(aes); |
|||
} |
|||
|
|||
|
|||
static void qcrypto_secret_decode(const uint8_t *input, |
|||
size_t inputlen, |
|||
uint8_t **output, |
|||
size_t *outputlen, |
|||
Error **errp) |
|||
{ |
|||
*output = qbase64_decode((const gchar*)input, |
|||
inputlen, |
|||
outputlen, |
|||
errp); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_prop_set_loaded(Object *obj, |
|||
bool value, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
|
|||
if (value) { |
|||
Error *local_err = NULL; |
|||
uint8_t *input = NULL; |
|||
size_t inputlen = 0; |
|||
uint8_t *output = NULL; |
|||
size_t outputlen = 0; |
|||
|
|||
qcrypto_secret_load_data(secret, &input, &inputlen, &local_err); |
|||
if (local_err) { |
|||
error_propagate(errp, local_err); |
|||
return; |
|||
} |
|||
|
|||
if (secret->keyid) { |
|||
qcrypto_secret_decrypt(secret, input, inputlen, |
|||
&output, &outputlen, &local_err); |
|||
g_free(input); |
|||
if (local_err) { |
|||
error_propagate(errp, local_err); |
|||
return; |
|||
} |
|||
input = output; |
|||
inputlen = outputlen; |
|||
} else { |
|||
if (secret->format != QCRYPTO_SECRET_FORMAT_RAW) { |
|||
qcrypto_secret_decode(input, inputlen, |
|||
&output, &outputlen, &local_err); |
|||
g_free(input); |
|||
if (local_err) { |
|||
error_propagate(errp, local_err); |
|||
return; |
|||
} |
|||
input = output; |
|||
inputlen = outputlen; |
|||
} |
|||
} |
|||
|
|||
secret->rawdata = input; |
|||
secret->rawlen = inputlen; |
|||
} else { |
|||
g_free(secret->rawdata); |
|||
secret->rawlen = 0; |
|||
} |
|||
} |
|||
|
|||
|
|||
static bool |
|||
qcrypto_secret_prop_get_loaded(Object *obj, |
|||
Error **errp G_GNUC_UNUSED) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
return secret->data != NULL; |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_prop_set_format(Object *obj, |
|||
int value, |
|||
Error **errp G_GNUC_UNUSED) |
|||
{ |
|||
QCryptoSecret *creds = QCRYPTO_SECRET(obj); |
|||
|
|||
creds->format = value; |
|||
} |
|||
|
|||
|
|||
static int |
|||
qcrypto_secret_prop_get_format(Object *obj, |
|||
Error **errp G_GNUC_UNUSED) |
|||
{ |
|||
QCryptoSecret *creds = QCRYPTO_SECRET(obj); |
|||
|
|||
return creds->format; |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_prop_set_data(Object *obj, |
|||
const char *value, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
|
|||
g_free(secret->data); |
|||
secret->data = g_strdup(value); |
|||
} |
|||
|
|||
|
|||
static char * |
|||
qcrypto_secret_prop_get_data(Object *obj, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
return g_strdup(secret->data); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_prop_set_file(Object *obj, |
|||
const char *value, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
|
|||
g_free(secret->file); |
|||
secret->file = g_strdup(value); |
|||
} |
|||
|
|||
|
|||
static char * |
|||
qcrypto_secret_prop_get_file(Object *obj, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
return g_strdup(secret->file); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_prop_set_iv(Object *obj, |
|||
const char *value, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
|
|||
g_free(secret->iv); |
|||
secret->iv = g_strdup(value); |
|||
} |
|||
|
|||
|
|||
static char * |
|||
qcrypto_secret_prop_get_iv(Object *obj, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
return g_strdup(secret->iv); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_prop_set_keyid(Object *obj, |
|||
const char *value, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
|
|||
g_free(secret->keyid); |
|||
secret->keyid = g_strdup(value); |
|||
} |
|||
|
|||
|
|||
static char * |
|||
qcrypto_secret_prop_get_keyid(Object *obj, |
|||
Error **errp) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
return g_strdup(secret->keyid); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_complete(UserCreatable *uc, Error **errp) |
|||
{ |
|||
object_property_set_bool(OBJECT(uc), true, "loaded", errp); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_init(Object *obj) |
|||
{ |
|||
object_property_add_bool(obj, "loaded", |
|||
qcrypto_secret_prop_get_loaded, |
|||
qcrypto_secret_prop_set_loaded, |
|||
NULL); |
|||
object_property_add_enum(obj, "format", |
|||
"QCryptoSecretFormat", |
|||
QCryptoSecretFormat_lookup, |
|||
qcrypto_secret_prop_get_format, |
|||
qcrypto_secret_prop_set_format, |
|||
NULL); |
|||
object_property_add_str(obj, "data", |
|||
qcrypto_secret_prop_get_data, |
|||
qcrypto_secret_prop_set_data, |
|||
NULL); |
|||
object_property_add_str(obj, "file", |
|||
qcrypto_secret_prop_get_file, |
|||
qcrypto_secret_prop_set_file, |
|||
NULL); |
|||
object_property_add_str(obj, "keyid", |
|||
qcrypto_secret_prop_get_keyid, |
|||
qcrypto_secret_prop_set_keyid, |
|||
NULL); |
|||
object_property_add_str(obj, "iv", |
|||
qcrypto_secret_prop_get_iv, |
|||
qcrypto_secret_prop_set_iv, |
|||
NULL); |
|||
} |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_finalize(Object *obj) |
|||
{ |
|||
QCryptoSecret *secret = QCRYPTO_SECRET(obj); |
|||
|
|||
g_free(secret->iv); |
|||
g_free(secret->file); |
|||
g_free(secret->keyid); |
|||
g_free(secret->rawdata); |
|||
g_free(secret->data); |
|||
} |
|||
|
|||
static void |
|||
qcrypto_secret_class_init(ObjectClass *oc, void *data) |
|||
{ |
|||
UserCreatableClass *ucc = USER_CREATABLE_CLASS(oc); |
|||
|
|||
ucc->complete = qcrypto_secret_complete; |
|||
} |
|||
|
|||
|
|||
int qcrypto_secret_lookup(const char *secretid, |
|||
uint8_t **data, |
|||
size_t *datalen, |
|||
Error **errp) |
|||
{ |
|||
Object *obj; |
|||
QCryptoSecret *secret; |
|||
|
|||
obj = object_resolve_path_component( |
|||
object_get_objects_root(), secretid); |
|||
if (!obj) { |
|||
error_setg(errp, "No secret with id '%s'", secretid); |
|||
return -1; |
|||
} |
|||
|
|||
secret = (QCryptoSecret *) |
|||
object_dynamic_cast(obj, |
|||
TYPE_QCRYPTO_SECRET); |
|||
if (!secret) { |
|||
error_setg(errp, "Object with id '%s' is not a secret", |
|||
secretid); |
|||
return -1; |
|||
} |
|||
|
|||
if (!secret->rawdata) { |
|||
error_setg(errp, "Secret with id '%s' has no data", |
|||
secretid); |
|||
return -1; |
|||
} |
|||
|
|||
*data = g_new0(uint8, secret->rawlen + 1); |
|||
memcpy(*data, secret->rawdata, secret->rawlen); |
|||
(*data)[secret->rawlen] = '\0'; |
|||
*datalen = secret->rawlen; |
|||
|
|||
return 0; |
|||
} |
|||
|
|||
|
|||
char *qcrypto_secret_lookup_as_utf8(const char *secretid, |
|||
Error **errp) |
|||
{ |
|||
uint8_t *data; |
|||
size_t datalen; |
|||
|
|||
if (qcrypto_secret_lookup(secretid, |
|||
&data, |
|||
&datalen, |
|||
errp) < 0) { |
|||
return NULL; |
|||
} |
|||
|
|||
if (!g_utf8_validate((const gchar*)data, datalen, NULL)) { |
|||
error_setg(errp, |
|||
"Data from secret %s is not valid UTF-8", |
|||
secretid); |
|||
g_free(data); |
|||
return NULL; |
|||
} |
|||
|
|||
return (char *)data; |
|||
} |
|||
|
|||
|
|||
char *qcrypto_secret_lookup_as_base64(const char *secretid, |
|||
Error **errp) |
|||
{ |
|||
uint8_t *data; |
|||
size_t datalen; |
|||
char *ret; |
|||
|
|||
if (qcrypto_secret_lookup(secretid, |
|||
&data, |
|||
&datalen, |
|||
errp) < 0) { |
|||
return NULL; |
|||
} |
|||
|
|||
ret = g_base64_encode(data, datalen); |
|||
g_free(data); |
|||
return ret; |
|||
} |
|||
|
|||
|
|||
static const TypeInfo qcrypto_secret_info = { |
|||
.parent = TYPE_OBJECT, |
|||
.name = TYPE_QCRYPTO_SECRET, |
|||
.instance_size = sizeof(QCryptoSecret), |
|||
.instance_init = qcrypto_secret_init, |
|||
.instance_finalize = qcrypto_secret_finalize, |
|||
.class_size = sizeof(QCryptoSecretClass), |
|||
.class_init = qcrypto_secret_class_init, |
|||
.interfaces = (InterfaceInfo[]) { |
|||
{ TYPE_USER_CREATABLE }, |
|||
{ } |
|||
} |
|||
}; |
|||
|
|||
|
|||
static void |
|||
qcrypto_secret_register_types(void) |
|||
{ |
|||
type_register_static(&qcrypto_secret_info); |
|||
} |
|||
|
|||
|
|||
type_init(qcrypto_secret_register_types); |
|||
@ -0,0 +1,148 @@ |
|||
/*
|
|||
* QEMU crypto secret support |
|||
* |
|||
* Copyright (c) 2015 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
#ifndef QCRYPTO_SECRET_H__ |
|||
#define QCRYPTO_SECRET_H__ |
|||
|
|||
#include "qemu-common.h" |
|||
#include "qapi/error.h" |
|||
#include "qom/object.h" |
|||
|
|||
#define TYPE_QCRYPTO_SECRET "secret" |
|||
#define QCRYPTO_SECRET(obj) \ |
|||
OBJECT_CHECK(QCryptoSecret, (obj), TYPE_QCRYPTO_SECRET) |
|||
|
|||
typedef struct QCryptoSecret QCryptoSecret; |
|||
typedef struct QCryptoSecretClass QCryptoSecretClass; |
|||
|
|||
/**
|
|||
* QCryptoSecret: |
|||
* |
|||
* The QCryptoSecret object provides storage of secrets, |
|||
* which may be user passwords, encryption keys or any |
|||
* other kind of sensitive data that is represented as |
|||
* a sequence of bytes. |
|||
* |
|||
* The sensitive data associated with the secret can |
|||
* be provided directly via the 'data' property, or |
|||
* indirectly via the 'file' property. In the latter |
|||
* case there is support for file descriptor passing |
|||
* via the usual /dev/fdset/NN syntax that QEMU uses. |
|||
* |
|||
* The data for a secret can be provided in two formats, |
|||
* either as a UTF-8 string (the default), or as base64 |
|||
* encoded 8-bit binary data. The latter is appropriate |
|||
* for raw encryption keys, while the former is appropriate |
|||
* for user entered passwords. |
|||
* |
|||
* The data may be optionally encrypted with AES-256-CBC, |
|||
* and the decryption key provided by another |
|||
* QCryptoSecret instance identified by the 'keyid' |
|||
* property. When passing sensitive data directly |
|||
* via the 'data' property it is strongly recommended |
|||
* to use the AES encryption facility to prevent the |
|||
* sensitive data being exposed in the process listing |
|||
* or system log files. |
|||
* |
|||
* Providing data directly, insecurely (suitable for |
|||
* ad hoc developer testing only) |
|||
* |
|||
* $QEMU -object secret,id=sec0,data=letmein |
|||
* |
|||
* Providing data indirectly: |
|||
* |
|||
* # printf "letmein" > password.txt |
|||
* # $QEMU \ |
|||
* -object secret,id=sec0,file=password.txt |
|||
* |
|||
* Using a master encryption key with data. |
|||
* |
|||
* The master key needs to be created as 32 secure |
|||
* random bytes (optionally base64 encoded) |
|||
* |
|||
* # openssl rand -base64 32 > key.b64 |
|||
* # KEY=$(base64 -d key.b64 | hexdump -v -e '/1 "%02X"') |
|||
* |
|||
* Each secret to be encrypted needs to have a random |
|||
* initialization vector generated. These do not need |
|||
* to be kept secret |
|||
* |
|||
* # openssl rand -base64 16 > iv.b64 |
|||
* # IV=$(base64 -d iv.b64 | hexdump -v -e '/1 "%02X"') |
|||
* |
|||
* A secret to be defined can now be encrypted |
|||
* |
|||
* # SECRET=$(printf "letmein" | |
|||
* openssl enc -aes-256-cbc -a -K $KEY -iv $IV) |
|||
* |
|||
* When launching QEMU, create a master secret pointing |
|||
* to key.b64 and specify that to be used to decrypt |
|||
* the user password |
|||
* |
|||
* # $QEMU \ |
|||
* -object secret,id=secmaster0,format=base64,file=key.b64 \ |
|||
* -object secret,id=sec0,keyid=secmaster0,format=base64,\ |
|||
* data=$SECRET,iv=$(<iv.b64) |
|||
* |
|||
* When encrypting, the data can still be provided via an |
|||
* external file, in which case it is possible to use either |
|||
* raw binary data, or base64 encoded. This example uses |
|||
* raw format |
|||
* |
|||
* # printf "letmein" | |
|||
* openssl enc -aes-256-cbc -K $KEY -iv $IV -o pw.aes |
|||
* # $QEMU \ |
|||
* -object secret,id=secmaster0,format=base64,file=key.b64 \ |
|||
* -object secret,id=sec0,keyid=secmaster0,\ |
|||
* file=pw.aes,iv=$(<iv.b64) |
|||
* |
|||
* Note that the ciphertext can be in either raw or base64 |
|||
* format, as indicated by the 'format' parameter, but the |
|||
* plaintext resulting from decryption is expected to always |
|||
* be in raw format. |
|||
*/ |
|||
|
|||
struct QCryptoSecret { |
|||
Object parent_obj; |
|||
uint8_t *rawdata; |
|||
size_t rawlen; |
|||
QCryptoSecretFormat format; |
|||
char *data; |
|||
char *file; |
|||
char *keyid; |
|||
char *iv; |
|||
}; |
|||
|
|||
|
|||
struct QCryptoSecretClass { |
|||
ObjectClass parent_class; |
|||
}; |
|||
|
|||
|
|||
extern int qcrypto_secret_lookup(const char *secretid, |
|||
uint8_t **data, |
|||
size_t *datalen, |
|||
Error **errp); |
|||
extern char *qcrypto_secret_lookup_as_utf8(const char *secretid, |
|||
Error **errp); |
|||
extern char *qcrypto_secret_lookup_as_base64(const char *secretid, |
|||
Error **errp); |
|||
|
|||
#endif /* QCRYPTO_SECRET_H__ */ |
|||
@ -0,0 +1,58 @@ |
|||
/*
|
|||
* QEMU base64 helpers |
|||
* |
|||
* Copyright (c) 2015 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
#ifndef QEMU_BASE64_H__ |
|||
#define QEMU_BASE64_H__ |
|||
|
|||
#include "qemu-common.h" |
|||
|
|||
|
|||
/**
|
|||
* qbase64_decode: |
|||
* @input: the (possibly) base64 encoded text |
|||
* @in_len: length of @input or -1 if NUL terminated |
|||
* @out_len: filled with length of decoded data |
|||
* @errp: pointer to a NULL-initialized error object |
|||
* |
|||
* Attempt to decode the (possibly) base64 encoded |
|||
* text provided in @input. If the @input text may |
|||
* contain embedded NUL characters, or may not be |
|||
* NUL terminated, then @in_len must be set to the |
|||
* known size of the @input buffer. |
|||
* |
|||
* Note that embedded NULs, or lack of a NUL terminator |
|||
* are considered invalid base64 data and errors |
|||
* will be reported to this effect. |
|||
* |
|||
* If decoding is successful, the decoded data will |
|||
* be returned and @out_len set to indicate the |
|||
* number of bytes in the decoded data. The caller |
|||
* must use g_free() to free the returned data when |
|||
* it is no longer required. |
|||
* |
|||
* Returns: the decoded data or NULL |
|||
*/ |
|||
uint8_t *qbase64_decode(const char *input, |
|||
size_t in_len, |
|||
size_t *out_len, |
|||
Error **errp); |
|||
|
|||
|
|||
#endif /* QEMU_BUFFER_H__ */ |
|||
@ -0,0 +1,109 @@ |
|||
/*
|
|||
* QEMU base64 helper test |
|||
* |
|||
* Copyright (c) 2015 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
#include <glib.h> |
|||
|
|||
#include "qemu/base64.h" |
|||
|
|||
|
|||
static void test_base64_good(void) |
|||
{ |
|||
const char input[] = |
|||
"QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n" |
|||
"lzc2VkIHRoZSBzY29ycGlvbi4="; |
|||
const char expect[] = "Because we focused on the snake, " |
|||
"we missed the scorpion."; |
|||
|
|||
size_t len; |
|||
uint8_t *actual = qbase64_decode(input, |
|||
-1, |
|||
&len, |
|||
&error_abort); |
|||
|
|||
g_assert(actual != NULL); |
|||
g_assert_cmpint(len, ==, strlen(expect)); |
|||
g_assert_cmpstr((char *)actual, ==, expect); |
|||
g_free(actual); |
|||
} |
|||
|
|||
|
|||
static void test_base64_bad(const char *input, |
|||
size_t input_len) |
|||
{ |
|||
size_t len; |
|||
Error *err = NULL; |
|||
uint8_t *actual = qbase64_decode(input, |
|||
input_len, |
|||
&len, |
|||
&err); |
|||
|
|||
g_assert(err != NULL); |
|||
g_assert(actual == NULL); |
|||
g_assert_cmpint(len, ==, 0); |
|||
error_free(err); |
|||
} |
|||
|
|||
|
|||
static void test_base64_embedded_nul(void) |
|||
{ |
|||
/* We put a NUL character in the middle of the base64
|
|||
* text which is invalid data, given the expected length */ |
|||
const char input[] = |
|||
"QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\0" |
|||
"lzc2VkIHRoZSBzY29ycGlvbi4="; |
|||
|
|||
test_base64_bad(input, G_N_ELEMENTS(input) - 1); |
|||
} |
|||
|
|||
|
|||
static void test_base64_not_nul_terminated(void) |
|||
{ |
|||
const char input[] = |
|||
"QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW\n" |
|||
"lzc2VkIHRoZSBzY29ycGlvbi4="; |
|||
|
|||
/* Using '-2' to make us drop the trailing NUL, thus
|
|||
* creating an invalid base64 sequence for decoding */ |
|||
test_base64_bad(input, G_N_ELEMENTS(input) - 2); |
|||
} |
|||
|
|||
|
|||
static void test_base64_invalid_chars(void) |
|||
{ |
|||
/* We put a single quote character in the middle
|
|||
* of the base64 text which is invalid data */ |
|||
const char input[] = |
|||
"QmVjYXVzZSB3ZSBmb2N1c2VkIG9uIHRoZSBzbmFrZSwgd2UgbW'" |
|||
"lzc2VkIHRoZSBzY29ycGlvbi4="; |
|||
|
|||
test_base64_bad(input, strlen(input)); |
|||
} |
|||
|
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
g_test_init(&argc, &argv, NULL); |
|||
g_test_add_func("/util/base64/good", test_base64_good); |
|||
g_test_add_func("/util/base64/embedded-nul", test_base64_embedded_nul); |
|||
g_test_add_func("/util/base64/not-nul-terminated", |
|||
test_base64_not_nul_terminated); |
|||
g_test_add_func("/util/base64/invalid-chars", test_base64_invalid_chars); |
|||
return g_test_run(); |
|||
} |
|||
@ -0,0 +1,452 @@ |
|||
/*
|
|||
* QEMU Crypto secret handling |
|||
* |
|||
* Copyright (c) 2015 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
#include <glib.h> |
|||
|
|||
#include "crypto/init.h" |
|||
#include "crypto/secret.h" |
|||
|
|||
static void test_secret_direct(void) |
|||
{ |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"data", "123456", |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_utf8("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, "123456"); |
|||
|
|||
object_unparent(sec); |
|||
g_free(pw); |
|||
} |
|||
|
|||
|
|||
static void test_secret_indirect_good(void) |
|||
{ |
|||
Object *sec; |
|||
char *fname = NULL; |
|||
int fd = g_file_open_tmp("secretXXXXXX", |
|||
&fname, |
|||
NULL); |
|||
|
|||
g_assert(fd >= 0); |
|||
g_assert_nonnull(fname); |
|||
|
|||
g_assert(write(fd, "123456", 6) == 6); |
|||
|
|||
sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"file", fname, |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_utf8("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, "123456"); |
|||
|
|||
object_unparent(sec); |
|||
g_free(pw); |
|||
close(fd); |
|||
g_free(fname); |
|||
} |
|||
|
|||
|
|||
static void test_secret_indirect_badfile(void) |
|||
{ |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
NULL, |
|||
"file", "does-not-exist", |
|||
NULL); |
|||
|
|||
g_assert(sec == NULL); |
|||
} |
|||
|
|||
|
|||
static void test_secret_indirect_emptyfile(void) |
|||
{ |
|||
Object *sec; |
|||
char *fname = NULL; |
|||
int fd = g_file_open_tmp("secretXXXXXX", |
|||
&fname, |
|||
NULL); |
|||
|
|||
g_assert(fd >= 0); |
|||
g_assert_nonnull(fname); |
|||
|
|||
sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"file", fname, |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_utf8("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, ""); |
|||
|
|||
object_unparent(sec); |
|||
g_free(pw); |
|||
close(fd); |
|||
g_free(fname); |
|||
} |
|||
|
|||
|
|||
static void test_secret_noconv_base64_good(void) |
|||
{ |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"data", "MTIzNDU2", |
|||
"format", "base64", |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_base64("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, "MTIzNDU2"); |
|||
|
|||
object_unparent(sec); |
|||
g_free(pw); |
|||
} |
|||
|
|||
|
|||
static void test_secret_noconv_base64_bad(void) |
|||
{ |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
NULL, |
|||
"data", "MTI$NDU2", |
|||
"format", "base64", |
|||
NULL); |
|||
|
|||
g_assert(sec == NULL); |
|||
} |
|||
|
|||
|
|||
static void test_secret_noconv_utf8(void) |
|||
{ |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"data", "123456", |
|||
"format", "raw", |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_utf8("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, "123456"); |
|||
|
|||
object_unparent(sec); |
|||
g_free(pw); |
|||
} |
|||
|
|||
|
|||
static void test_secret_conv_base64_utf8valid(void) |
|||
{ |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"data", "MTIzNDU2", |
|||
"format", "base64", |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_utf8("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, "123456"); |
|||
|
|||
object_unparent(sec); |
|||
g_free(pw); |
|||
} |
|||
|
|||
|
|||
static void test_secret_conv_base64_utf8invalid(void) |
|||
{ |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"data", "f0VMRgIBAQAAAA==", |
|||
"format", "base64", |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_utf8("sec0", |
|||
NULL); |
|||
g_assert(pw == NULL); |
|||
|
|||
object_unparent(sec); |
|||
} |
|||
|
|||
|
|||
static void test_secret_conv_utf8_base64(void) |
|||
{ |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"data", "123456", |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_base64("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, "MTIzNDU2"); |
|||
|
|||
object_unparent(sec); |
|||
g_free(pw); |
|||
} |
|||
|
|||
|
|||
static void test_secret_crypt_raw(void) |
|||
{ |
|||
Object *master = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"master", |
|||
&error_abort, |
|||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", |
|||
"format", "base64", |
|||
NULL); |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"data", |
|||
"\xCC\xBF\xF7\x09\x46\x19\x0B\x52\x2A\x3A\xB4\x6B\xCD\x7A\xB0\xB0", |
|||
"format", "raw", |
|||
"keyid", "master", |
|||
"iv", "0I7Gw/TKuA+Old2W2apQ3g==", |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_utf8("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, "123456"); |
|||
|
|||
object_unparent(sec); |
|||
object_unparent(master); |
|||
g_free(pw); |
|||
} |
|||
|
|||
|
|||
static void test_secret_crypt_base64(void) |
|||
{ |
|||
Object *master = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"master", |
|||
&error_abort, |
|||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", |
|||
"format", "base64", |
|||
NULL); |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
&error_abort, |
|||
"data", "zL/3CUYZC1IqOrRrzXqwsA==", |
|||
"format", "base64", |
|||
"keyid", "master", |
|||
"iv", "0I7Gw/TKuA+Old2W2apQ3g==", |
|||
NULL); |
|||
|
|||
char *pw = qcrypto_secret_lookup_as_utf8("sec0", |
|||
&error_abort); |
|||
|
|||
g_assert_cmpstr(pw, ==, "123456"); |
|||
|
|||
object_unparent(sec); |
|||
object_unparent(master); |
|||
g_free(pw); |
|||
} |
|||
|
|||
|
|||
static void test_secret_crypt_short_key(void) |
|||
{ |
|||
Object *master = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"master", |
|||
&error_abort, |
|||
"data", "9miloPQCzGy+TL6aonfzVc", |
|||
"format", "base64", |
|||
NULL); |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
NULL, |
|||
"data", "zL/3CUYZC1IqOrRrzXqwsA==", |
|||
"format", "raw", |
|||
"keyid", "master", |
|||
"iv", "0I7Gw/TKuA+Old2W2apQ3g==", |
|||
NULL); |
|||
|
|||
g_assert(sec == NULL); |
|||
object_unparent(master); |
|||
} |
|||
|
|||
|
|||
static void test_secret_crypt_short_iv(void) |
|||
{ |
|||
Object *master = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"master", |
|||
&error_abort, |
|||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", |
|||
"format", "base64", |
|||
NULL); |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
NULL, |
|||
"data", "zL/3CUYZC1IqOrRrzXqwsA==", |
|||
"format", "raw", |
|||
"keyid", "master", |
|||
"iv", "0I7Gw/TKuA+Old2W2a", |
|||
NULL); |
|||
|
|||
g_assert(sec == NULL); |
|||
object_unparent(master); |
|||
} |
|||
|
|||
|
|||
static void test_secret_crypt_missing_iv(void) |
|||
{ |
|||
Object *master = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"master", |
|||
&error_abort, |
|||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", |
|||
"format", "base64", |
|||
NULL); |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
NULL, |
|||
"data", "zL/3CUYZC1IqOrRrzXqwsA==", |
|||
"format", "raw", |
|||
"keyid", "master", |
|||
NULL); |
|||
|
|||
g_assert(sec == NULL); |
|||
object_unparent(master); |
|||
} |
|||
|
|||
|
|||
static void test_secret_crypt_bad_iv(void) |
|||
{ |
|||
Object *master = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"master", |
|||
&error_abort, |
|||
"data", "9miloPQCzGy+TL6aonfzVcptibCmCIhKzrnlfwiWivk=", |
|||
"format", "base64", |
|||
NULL); |
|||
Object *sec = object_new_with_props( |
|||
TYPE_QCRYPTO_SECRET, |
|||
object_get_objects_root(), |
|||
"sec0", |
|||
NULL, |
|||
"data", "zL/3CUYZC1IqOrRrzXqwsA==", |
|||
"format", "raw", |
|||
"keyid", "master", |
|||
"iv", "0I7Gw/TK$$uA+Old2W2a", |
|||
NULL); |
|||
|
|||
g_assert(sec == NULL); |
|||
object_unparent(master); |
|||
} |
|||
|
|||
|
|||
int main(int argc, char **argv) |
|||
{ |
|||
module_call_init(MODULE_INIT_QOM); |
|||
g_test_init(&argc, &argv, NULL); |
|||
|
|||
g_assert(qcrypto_init(NULL) == 0); |
|||
|
|||
g_test_add_func("/crypto/secret/direct", |
|||
test_secret_direct); |
|||
g_test_add_func("/crypto/secret/indirect/good", |
|||
test_secret_indirect_good); |
|||
g_test_add_func("/crypto/secret/indirect/badfile", |
|||
test_secret_indirect_badfile); |
|||
g_test_add_func("/crypto/secret/indirect/emptyfile", |
|||
test_secret_indirect_emptyfile); |
|||
|
|||
g_test_add_func("/crypto/secret/noconv/base64/good", |
|||
test_secret_noconv_base64_good); |
|||
g_test_add_func("/crypto/secret/noconv/base64/bad", |
|||
test_secret_noconv_base64_bad); |
|||
g_test_add_func("/crypto/secret/noconv/utf8", |
|||
test_secret_noconv_utf8); |
|||
g_test_add_func("/crypto/secret/conv/base64/utf8valid", |
|||
test_secret_conv_base64_utf8valid); |
|||
g_test_add_func("/crypto/secret/conv/base64/utf8invalid", |
|||
test_secret_conv_base64_utf8invalid); |
|||
g_test_add_func("/crypto/secret/conv/utf8/base64", |
|||
test_secret_conv_utf8_base64); |
|||
|
|||
g_test_add_func("/crypto/secret/crypt/raw", |
|||
test_secret_crypt_raw); |
|||
g_test_add_func("/crypto/secret/crypt/base64", |
|||
test_secret_crypt_base64); |
|||
g_test_add_func("/crypto/secret/crypt/shortkey", |
|||
test_secret_crypt_short_key); |
|||
g_test_add_func("/crypto/secret/crypt/shortiv", |
|||
test_secret_crypt_short_iv); |
|||
g_test_add_func("/crypto/secret/crypt/missingiv", |
|||
test_secret_crypt_missing_iv); |
|||
g_test_add_func("/crypto/secret/crypt/badiv", |
|||
test_secret_crypt_bad_iv); |
|||
|
|||
return g_test_run(); |
|||
} |
|||
@ -0,0 +1,60 @@ |
|||
/*
|
|||
* QEMU base64 helpers |
|||
* |
|||
* Copyright (c) 2015 Red Hat, Inc. |
|||
* |
|||
* This library is free software; you can redistribute it and/or |
|||
* modify it under the terms of the GNU Lesser General Public |
|||
* License as published by the Free Software Foundation; either |
|||
* version 2 of the License, or (at your option) any later version. |
|||
* |
|||
* This library is distributed in the hope that it will be useful, |
|||
* but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
|||
* Lesser General Public License for more details. |
|||
* |
|||
* You should have received a copy of the GNU Lesser General Public |
|||
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
|
|||
* |
|||
*/ |
|||
|
|||
#include <config-host.h> |
|||
|
|||
#include "qemu/base64.h" |
|||
|
|||
static const char *base64_valid_chars = |
|||
"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=\n"; |
|||
|
|||
uint8_t *qbase64_decode(const char *input, |
|||
size_t in_len, |
|||
size_t *out_len, |
|||
Error **errp) |
|||
{ |
|||
*out_len = 0; |
|||
|
|||
if (in_len != -1) { |
|||
/* Lack of NUL terminator is an error */ |
|||
if (input[in_len] != '\0') { |
|||
error_setg(errp, "Base64 data is not NUL terminated"); |
|||
return NULL; |
|||
} |
|||
/* Check there's no NULs embedded since we expect
|
|||
* this to be valid base64 data */ |
|||
if (memchr(input, '\0', in_len) != NULL) { |
|||
error_setg(errp, "Base64 data contains embedded NUL characters"); |
|||
return NULL; |
|||
} |
|||
|
|||
/* Now we know its a valid nul terminated string
|
|||
* strspn is safe to use... */ |
|||
} else { |
|||
in_len = strlen(input); |
|||
} |
|||
|
|||
if (strspn(input, base64_valid_chars) != in_len) { |
|||
error_setg(errp, "Base64 data contains invalid characters"); |
|||
return NULL; |
|||
} |
|||
|
|||
return g_base64_decode(input, out_len); |
|||
} |
|||
Loading…
Reference in new issue