mirror of
https://github.com/yuzu-emu/unicorn
synced 2024-11-25 02:28:31 +00:00
5019f39c15
Introduce a new crypto/ directory that will (eventually) contain all the cryptographic related code. This initially defines a wrapper for initializing gnutls and for computing hashes with gnutls. The former ensures that gnutls is guaranteed to be initialized exactly once in QEMU regardless of CLI args. The block quorum code currently fails to initialize gnutls so it only works by luck, if VNC server TLS is not requested. The hash APIs avoids the need to litter the rest of the code with preprocessor checks and simplifies callers by allocating the correct amount of memory for the requested hash. Backports commit ddbb0d09661f5fce21b335ba9aea8202d189b98e from qemu
199 lines
5.4 KiB
C
199 lines
5.4 KiB
C
/*
|
|
* QEMU Crypto hash algorithms
|
|
*
|
|
* 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/hash.h"
|
|
|
|
#ifdef CONFIG_GNUTLS_HASH
|
|
#include <gnutls/gnutls.h>
|
|
#include <gnutls/crypto.h>
|
|
|
|
static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG_LAST] = {
|
|
GNUTLS_DIG_MD5,
|
|
GNUTLS_DIG_SHA1,
|
|
GNUTLS_DIG_SHA256,
|
|
};
|
|
|
|
gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
|
|
{
|
|
if (alg < (sizeof(qcrypto_hash_alg_map) / sizeof(qcrypto_hash_alg_map[0]))) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
|
|
const struct iovec *iov,
|
|
size_t niov,
|
|
uint8_t **result,
|
|
size_t *resultlen,
|
|
Error **errp)
|
|
{
|
|
int i, ret;
|
|
gnutls_hash_hd_t dig;
|
|
|
|
if (alg >= (sizeof(qcrypto_hash_alg_map) / sizeof(qcrypto_hash_alg_map[0]))) {
|
|
error_setg(errp,
|
|
"Unknown hash algorithm %d",
|
|
alg);
|
|
return -1;
|
|
}
|
|
|
|
ret = gnutls_hash_init(&dig, qcrypto_hash_alg_map[alg]);
|
|
|
|
if (ret < 0) {
|
|
error_setg(errp,
|
|
"Unable to initialize hash algorithm: %s",
|
|
gnutls_strerror(ret));
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < niov; i++) {
|
|
ret = gnutls_hash(dig, iov[i].iov_base, iov[i].iov_len);
|
|
if (ret < 0) {
|
|
error_setg(errp,
|
|
"Unable process hash data: %s",
|
|
gnutls_strerror(ret));
|
|
goto error;
|
|
}
|
|
}
|
|
|
|
ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]);
|
|
if (ret <= 0) {
|
|
error_setg(errp,
|
|
"Unable to get hash length: %s",
|
|
gnutls_strerror(ret));
|
|
goto error;
|
|
}
|
|
if (*resultlen == 0) {
|
|
*resultlen = ret;
|
|
*result = g_new0(uint8_t, *resultlen);
|
|
} else if (*resultlen != ret) {
|
|
error_setg(errp,
|
|
"Result buffer size %zu is smaller than hash %d",
|
|
*resultlen, ret);
|
|
goto error;
|
|
}
|
|
|
|
gnutls_hash_deinit(dig, *result);
|
|
return 0;
|
|
|
|
error:
|
|
gnutls_hash_deinit(dig, NULL);
|
|
return -1;
|
|
}
|
|
|
|
#else /* ! CONFIG_GNUTLS_HASH */
|
|
|
|
gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
int qcrypto_hash_bytesv(QCryptoHashAlgorithm alg,
|
|
const struct iovec *iov,
|
|
size_t niov,
|
|
uint8_t **result,
|
|
size_t *resultlen,
|
|
Error **errp)
|
|
{
|
|
error_setg(errp,
|
|
"Hash algorithm %d not supported without GNUTLS",
|
|
alg);
|
|
return -1;
|
|
}
|
|
|
|
#endif /* ! CONFIG_GNUTLS_HASH */
|
|
|
|
int qcrypto_hash_bytes(QCryptoHashAlgorithm alg,
|
|
const char *buf,
|
|
size_t len,
|
|
uint8_t **result,
|
|
size_t *resultlen,
|
|
Error **errp)
|
|
{
|
|
struct iovec iov = { (char *)buf, len };
|
|
return qcrypto_hash_bytesv(alg, &iov, 1, result, resultlen, errp);
|
|
}
|
|
|
|
static const char hex[] = "0123456789abcdef";
|
|
|
|
int qcrypto_hash_digestv(QCryptoHashAlgorithm alg,
|
|
const struct iovec *iov,
|
|
size_t niov,
|
|
char **digest,
|
|
Error **errp)
|
|
{
|
|
uint8_t *result = NULL;
|
|
size_t resultlen = 0;
|
|
size_t i;
|
|
|
|
if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
*digest = g_new0(char, (resultlen * 2) + 1);
|
|
for (i = 0 ; i < resultlen ; i++) {
|
|
(*digest)[(i * 2)] = hex[(result[i] >> 4) & 0xf];
|
|
(*digest)[(i * 2) + 1] = hex[result[i] & 0xf];
|
|
}
|
|
(*digest)[resultlen * 2] = '\0';
|
|
g_free(result);
|
|
return 0;
|
|
}
|
|
|
|
int qcrypto_hash_digest(QCryptoHashAlgorithm alg,
|
|
const char *buf,
|
|
size_t len,
|
|
char **digest,
|
|
Error **errp)
|
|
{
|
|
struct iovec iov = { (char *)buf, len };
|
|
|
|
return qcrypto_hash_digestv(alg, &iov, 1, digest, errp);
|
|
}
|
|
|
|
int qcrypto_hash_base64v(QCryptoHashAlgorithm alg,
|
|
const struct iovec *iov,
|
|
size_t niov,
|
|
char **base64,
|
|
Error **errp)
|
|
{
|
|
uint8_t *result = NULL;
|
|
size_t resultlen = 0;
|
|
|
|
if (qcrypto_hash_bytesv(alg, iov, niov, &result, &resultlen, errp) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
*base64 = g_base64_encode(result, resultlen);
|
|
g_free(result);
|
|
return 0;
|
|
}
|
|
|
|
int qcrypto_hash_base64(QCryptoHashAlgorithm alg,
|
|
const char *buf,
|
|
size_t len,
|
|
char **base64,
|
|
Error **errp)
|
|
{
|
|
struct iovec iov = { (char *)buf, len };
|
|
|
|
return qcrypto_hash_base64v(alg, &iov, 1, base64, errp);
|
|
}
|