Merge crypto updates and misc fixes
* Introduce a GNUTLS backend for crypto algorithms * Change crypto library preference gnutls > gcrypt > nettle > built-in * Remove built-in DES impl * Remove XTS mode from built-in AES impl * Fix seccomp rules to allow resource info getters * Fix migration performance test * Use GDateTime in io/ and net/rocker/ code * Improve docs for -smp -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEE2vOm/bJrYpEtDo4/vobrtBUQT98FAmDu78AACgkQvobrtBUQ T9+TlRAAsk3hQRN8cn+7MtpdGPd1YUn/TDaWDzmTtB19hbOZFizxSNuBIzs/sqtD jttdsBg9he7Z99BKMSTXxNPGoMOBuijruXQpNPRAW4fXmfDh7uwPTaUDI0q1B2UH U67vAUaMt+I/X62BzXdZDx00neSQy30guYmJHiOr02NZMd+r6Ds0XQn/SAsnkU64 rNoBHWTOX0HelSc6qGSZMC+VSTQK/EKbg7HP5YHqgYzxt8IhxAD0bGtFgYgCL/D8 FAEL190CRj4J5CgC7OSRgw82Q068CvpMwo/fhk4Ctlwu2nx5oqnUcLkntalitBD0 lj/73IW8Hdho4pBhI86CD3FU1tJ0eDtAEAUcRLWdHlAFIp5T8D9mWvaBNGD937e1 3obcNurtkQmUPqNuVk1kYVCMuxdexfUrGHEKqONW9GqyXHy2Q8CZqOTNKNUQSJd3 Zx/u+p0/iPGUCiQozBJp5QEPdAlU/2EQyQpo6UQ9Bctf73Pr9cpeOoIEml2kC5Z5 oaK9KGsJUfXt/9sFqYXtXQR24iXFthhHZnQv7mANdaSeOvzu07tUxe7+Iqu4aXb+ 4YHCWcOpHG0btbwOFL/a18XtRJHycr+abxlL9w1MMAeuBfqWMLROzRh4cbqekH5C TogMQpcJn2D835oBqwDv6VgUBoStW14wVMBbHRfpqf0/Oekixxo= =iCvI -----END PGP SIGNATURE----- Merge remote-tracking branch 'remotes/berrange-gitlab/tags/crypto-and-more-pull-request' into staging Merge crypto updates and misc fixes * Introduce a GNUTLS backend for crypto algorithms * Change crypto library preference gnutls > gcrypt > nettle > built-in * Remove built-in DES impl * Remove XTS mode from built-in AES impl * Fix seccomp rules to allow resource info getters * Fix migration performance test * Use GDateTime in io/ and net/rocker/ code * Improve docs for -smp # gpg: Signature made Wed 14 Jul 2021 15:08:00 BST # gpg: using RSA key DAF3A6FDB26B62912D0E8E3FBE86EBB415104FDF # gpg: Good signature from "Daniel P. Berrange <dan@berrange.com>" [full] # gpg: aka "Daniel P. Berrange <berrange@redhat.com>" [full] # Primary key fingerprint: DAF3 A6FD B26B 6291 2D0E 8E3F BE86 EBB4 1510 4FDF * remotes/berrange-gitlab/tags/crypto-and-more-pull-request: (26 commits) qemu-options: rewrite help for -smp options qemu-options: tweak to show that CPU count is optional qemu-options: re-arrange CPU topology options docs: fix typo s/Intel/AMD/ in CPU model notes tests/migration: fix unix socket migration seccomp: don't block getters for resource control syscalls io: use GDateTime for formatting timestamp for websock headers net/rocker: use GDateTime for formatting timestamp in debug messages crypto: prefer gnutls as the crypto backend if new enough crypto: add gnutls pbkdf provider crypto: add gnutls hmac provider crypto: add gnutls hash provider crypto: add gnutls cipher provider crypto: introduce build system for gnutls crypto backend crypto: flip priority of backends to prefer gcrypt crypto: replace 'des-rfb' cipher with 'des' crypto: delete built-in XTS cipher mode support crypto: delete built-in DES implementation crypto: add crypto tests for single block DES-ECB and DES-CBC crypto: drop custom XTS support in gcrypt driver ... Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
This commit is contained in:
commit
d4127349e3
@ -19,8 +19,6 @@
|
||||
*/
|
||||
|
||||
#include "crypto/aes.h"
|
||||
#include "crypto/desrfb.h"
|
||||
#include "crypto/xts.h"
|
||||
|
||||
typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext;
|
||||
struct QCryptoCipherBuiltinAESContext {
|
||||
@ -32,7 +30,6 @@ typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
|
||||
struct QCryptoCipherBuiltinAES {
|
||||
QCryptoCipher base;
|
||||
QCryptoCipherBuiltinAESContext key;
|
||||
QCryptoCipherBuiltinAESContext key_tweak;
|
||||
uint8_t iv[AES_BLOCK_SIZE];
|
||||
};
|
||||
|
||||
@ -194,39 +191,6 @@ static int qcrypto_cipher_aes_decrypt_cbc(QCryptoCipher *cipher,
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_cipher_aes_encrypt_xts(QCryptoCipher *cipher,
|
||||
const void *in, void *out,
|
||||
size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltinAES *ctx
|
||||
= container_of(cipher, QCryptoCipherBuiltinAES, base);
|
||||
|
||||
if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
|
||||
return -1;
|
||||
}
|
||||
xts_encrypt(&ctx->key, &ctx->key_tweak,
|
||||
do_aes_encrypt_ecb, do_aes_decrypt_ecb,
|
||||
ctx->iv, len, out, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_cipher_aes_decrypt_xts(QCryptoCipher *cipher,
|
||||
const void *in, void *out,
|
||||
size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltinAES *ctx
|
||||
= container_of(cipher, QCryptoCipherBuiltinAES, base);
|
||||
|
||||
if (!qcrypto_length_check(len, AES_BLOCK_SIZE, errp)) {
|
||||
return -1;
|
||||
}
|
||||
xts_decrypt(&ctx->key, &ctx->key_tweak,
|
||||
do_aes_encrypt_ecb, do_aes_decrypt_ecb,
|
||||
ctx->iv, len, out, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int qcrypto_cipher_aes_setiv(QCryptoCipher *cipher, const uint8_t *iv,
|
||||
size_t niv, Error **errp)
|
||||
{
|
||||
@ -257,84 +221,16 @@ static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_cbc = {
|
||||
.cipher_free = qcrypto_cipher_ctx_free,
|
||||
};
|
||||
|
||||
static const struct QCryptoCipherDriver qcrypto_cipher_aes_driver_xts = {
|
||||
.cipher_encrypt = qcrypto_cipher_aes_encrypt_xts,
|
||||
.cipher_decrypt = qcrypto_cipher_aes_decrypt_xts,
|
||||
.cipher_setiv = qcrypto_cipher_aes_setiv,
|
||||
.cipher_free = qcrypto_cipher_ctx_free,
|
||||
};
|
||||
|
||||
|
||||
typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB;
|
||||
struct QCryptoCipherBuiltinDESRFB {
|
||||
QCryptoCipher base;
|
||||
|
||||
/* C.f. alg_key_len[QCRYPTO_CIPHER_ALG_DES_RFB] */
|
||||
uint8_t key[8];
|
||||
};
|
||||
|
||||
static int qcrypto_cipher_encrypt_des_rfb(QCryptoCipher *cipher,
|
||||
const void *in, void *out,
|
||||
size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltinDESRFB *ctx
|
||||
= container_of(cipher, QCryptoCipherBuiltinDESRFB, base);
|
||||
size_t i;
|
||||
|
||||
if (!qcrypto_length_check(len, 8, errp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
deskey(ctx->key, EN0);
|
||||
|
||||
for (i = 0; i < len; i += 8) {
|
||||
des((void *)in + i, out + i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_cipher_decrypt_des_rfb(QCryptoCipher *cipher,
|
||||
const void *in, void *out,
|
||||
size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherBuiltinDESRFB *ctx
|
||||
= container_of(cipher, QCryptoCipherBuiltinDESRFB, base);
|
||||
size_t i;
|
||||
|
||||
if (!qcrypto_length_check(len, 8, errp)) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
deskey(ctx->key, DE1);
|
||||
|
||||
for (i = 0; i < len; i += 8) {
|
||||
des((void *)in + i, out + i);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct QCryptoCipherDriver qcrypto_cipher_des_rfb_driver = {
|
||||
.cipher_encrypt = qcrypto_cipher_encrypt_des_rfb,
|
||||
.cipher_decrypt = qcrypto_cipher_decrypt_des_rfb,
|
||||
.cipher_setiv = qcrypto_cipher_no_setiv,
|
||||
.cipher_free = qcrypto_cipher_ctx_free,
|
||||
};
|
||||
|
||||
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
return mode == QCRYPTO_CIPHER_MODE_ECB;
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
@ -356,18 +252,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
if (mode == QCRYPTO_CIPHER_MODE_ECB) {
|
||||
QCryptoCipherBuiltinDESRFB *ctx;
|
||||
|
||||
ctx = g_new0(QCryptoCipherBuiltinDESRFB, 1);
|
||||
ctx->base.driver = &qcrypto_cipher_des_rfb_driver;
|
||||
memcpy(ctx->key, key, sizeof(ctx->key));
|
||||
|
||||
return &ctx->base;
|
||||
}
|
||||
goto bad_mode;
|
||||
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
@ -382,9 +266,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
drv = &qcrypto_cipher_aes_driver_cbc;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
drv = &qcrypto_cipher_aes_driver_xts;
|
||||
break;
|
||||
default:
|
||||
goto bad_mode;
|
||||
}
|
||||
@ -392,19 +273,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
ctx = g_new0(QCryptoCipherBuiltinAES, 1);
|
||||
ctx->base.driver = drv;
|
||||
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
nkey /= 2;
|
||||
if (AES_set_encrypt_key(key + nkey, nkey * 8,
|
||||
&ctx->key_tweak.enc)) {
|
||||
error_setg(errp, "Failed to set encryption key");
|
||||
goto error;
|
||||
}
|
||||
if (AES_set_decrypt_key(key + nkey, nkey * 8,
|
||||
&ctx->key_tweak.dec)) {
|
||||
error_setg(errp, "Failed to set decryption key");
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
if (AES_set_encrypt_key(key, nkey * 8, &ctx->key.enc)) {
|
||||
error_setg(errp, "Failed to set encryption key");
|
||||
goto error;
|
||||
|
@ -18,17 +18,13 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
#include "crypto/xts.h"
|
||||
#endif
|
||||
|
||||
#include <gcrypt.h>
|
||||
|
||||
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
@ -59,10 +55,6 @@ typedef struct QCryptoCipherGcrypt {
|
||||
QCryptoCipher base;
|
||||
gcry_cipher_hd_t handle;
|
||||
size_t blocksize;
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
gcry_cipher_hd_t tweakhandle;
|
||||
uint8_t iv[XTS_BLOCK_SIZE];
|
||||
#endif
|
||||
} QCryptoCipherGcrypt;
|
||||
|
||||
|
||||
@ -178,90 +170,6 @@ static const struct QCryptoCipherDriver qcrypto_gcrypt_ctr_driver = {
|
||||
.cipher_free = qcrypto_gcrypt_ctx_free,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
static void qcrypto_gcrypt_xts_ctx_free(QCryptoCipher *cipher)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
|
||||
|
||||
gcry_cipher_close(ctx->tweakhandle);
|
||||
qcrypto_gcrypt_ctx_free(cipher);
|
||||
}
|
||||
|
||||
static void qcrypto_gcrypt_xts_wrape(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
gcry_error_t err;
|
||||
err = gcry_cipher_encrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
|
||||
g_assert(err == 0);
|
||||
}
|
||||
|
||||
static void qcrypto_gcrypt_xts_wrapd(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
{
|
||||
gcry_error_t err;
|
||||
err = gcry_cipher_decrypt((gcry_cipher_hd_t)ctx, dst, length, src, length);
|
||||
g_assert(err == 0);
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_xts_encrypt(QCryptoCipher *cipher, const void *in,
|
||||
void *out, size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
|
||||
|
||||
if (len & (ctx->blocksize - 1)) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xts_encrypt(ctx->handle, ctx->tweakhandle,
|
||||
qcrypto_gcrypt_xts_wrape, qcrypto_gcrypt_xts_wrapd,
|
||||
ctx->iv, len, out, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_xts_decrypt(QCryptoCipher *cipher, const void *in,
|
||||
void *out, size_t len, Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
|
||||
|
||||
if (len & (ctx->blocksize - 1)) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
xts_decrypt(ctx->handle, ctx->tweakhandle,
|
||||
qcrypto_gcrypt_xts_wrape, qcrypto_gcrypt_xts_wrapd,
|
||||
ctx->iv, len, out, in);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_xts_setiv(QCryptoCipher *cipher,
|
||||
const uint8_t *iv, size_t niv,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGcrypt *ctx = container_of(cipher, QCryptoCipherGcrypt, base);
|
||||
|
||||
if (niv != ctx->blocksize) {
|
||||
error_setg(errp, "Expected IV size %zu not %zu",
|
||||
ctx->blocksize, niv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
memcpy(ctx->iv, iv, niv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct QCryptoCipherDriver qcrypto_gcrypt_xts_driver = {
|
||||
.cipher_encrypt = qcrypto_gcrypt_xts_encrypt,
|
||||
.cipher_decrypt = qcrypto_gcrypt_xts_decrypt,
|
||||
.cipher_setiv = qcrypto_gcrypt_xts_setiv,
|
||||
.cipher_free = qcrypto_gcrypt_xts_ctx_free,
|
||||
};
|
||||
#endif /* CONFIG_QEMU_PRIVATE_XTS */
|
||||
|
||||
|
||||
static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode,
|
||||
const uint8_t *key,
|
||||
@ -278,7 +186,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
gcryalg = GCRY_CIPHER_DES;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
@ -323,12 +231,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
gcrymode = GCRY_CIPHER_MODE_ECB;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
drv = &qcrypto_gcrypt_xts_driver;
|
||||
gcrymode = GCRY_CIPHER_MODE_ECB;
|
||||
#else
|
||||
gcrymode = GCRY_CIPHER_MODE_XTS;
|
||||
#endif
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
gcrymode = GCRY_CIPHER_MODE_CBC;
|
||||
@ -354,44 +257,7 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
ctx->blocksize = gcry_cipher_get_algo_blklen(gcryalg);
|
||||
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
if (ctx->blocksize != XTS_BLOCK_SIZE) {
|
||||
error_setg(errp,
|
||||
"Cipher block size %zu must equal XTS block size %d",
|
||||
ctx->blocksize, XTS_BLOCK_SIZE);
|
||||
goto error;
|
||||
}
|
||||
err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gcry_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
|
||||
/* We're using standard DES cipher from gcrypt, so we need
|
||||
* to munge the key so that the results are the same as the
|
||||
* bizarre RFB variant of DES :-)
|
||||
*/
|
||||
uint8_t *rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
|
||||
err = gcry_cipher_setkey(ctx->handle, rfbkey, nkey);
|
||||
g_free(rfbkey);
|
||||
} else {
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
nkey /= 2;
|
||||
err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot set key: %s", gcry_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
err = gcry_cipher_setkey(ctx->handle, key, nkey);
|
||||
}
|
||||
err = gcry_cipher_setkey(ctx->handle, key, nkey);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot set key: %s", gcry_strerror(err));
|
||||
goto error;
|
||||
@ -400,9 +266,6 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
return &ctx->base;
|
||||
|
||||
error:
|
||||
#ifdef CONFIG_QEMU_PRIVATE_XTS
|
||||
gcry_cipher_close(ctx->tweakhandle);
|
||||
#endif
|
||||
gcry_cipher_close(ctx->handle);
|
||||
g_free(ctx);
|
||||
return NULL;
|
||||
|
335
crypto/cipher-gnutls.c.inc
Normal file
335
crypto/cipher-gnutls.c.inc
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
* QEMU Crypto cipher gnutls algorithms
|
||||
*
|
||||
* Copyright (c) 2021 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.1 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 "qemu/osdep.h"
|
||||
#include "cipherpriv.h"
|
||||
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
#if GNUTLS_VERSION_NUMBER >= 0x030608
|
||||
#define QEMU_GNUTLS_XTS
|
||||
#endif
|
||||
|
||||
bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#ifdef QEMU_GNUTLS_XTS
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
typedef struct QCryptoCipherGnutls QCryptoCipherGnutls;
|
||||
struct QCryptoCipherGnutls {
|
||||
QCryptoCipher base;
|
||||
gnutls_cipher_hd_t handle; /* XTS & CBC mode */
|
||||
gnutls_cipher_algorithm_t galg; /* ECB mode */
|
||||
guint8 *key; /* ECB mode */
|
||||
size_t nkey; /* ECB mode */
|
||||
size_t blocksize;
|
||||
};
|
||||
|
||||
|
||||
static void
|
||||
qcrypto_gnutls_cipher_free(QCryptoCipher *cipher)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
|
||||
|
||||
g_free(ctx->key);
|
||||
if (ctx->handle) {
|
||||
gnutls_cipher_deinit(ctx->handle);
|
||||
}
|
||||
g_free(ctx);
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_cipher_encrypt(QCryptoCipher *cipher,
|
||||
const void *in,
|
||||
void *out,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
|
||||
int err;
|
||||
|
||||
if (len % ctx->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->handle) { /* CBC / XTS mode */
|
||||
err = gnutls_cipher_encrypt2(ctx->handle,
|
||||
in, len,
|
||||
out, len);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot encrypt data: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
} else { /* ECB mode very inefficiently faked with CBC */
|
||||
g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
|
||||
while (len) {
|
||||
gnutls_cipher_hd_t handle;
|
||||
gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
|
||||
int err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
|
||||
|
||||
err = gnutls_cipher_encrypt2(handle,
|
||||
in, ctx->blocksize,
|
||||
out, ctx->blocksize);
|
||||
if (err != 0) {
|
||||
gnutls_cipher_deinit(handle);
|
||||
error_setg(errp, "Cannot encrypt data: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
gnutls_cipher_deinit(handle);
|
||||
|
||||
len -= ctx->blocksize;
|
||||
in += ctx->blocksize;
|
||||
out += ctx->blocksize;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_cipher_decrypt(QCryptoCipher *cipher,
|
||||
const void *in,
|
||||
void *out,
|
||||
size_t len,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
|
||||
int err;
|
||||
|
||||
if (len % ctx->blocksize) {
|
||||
error_setg(errp, "Length %zu must be a multiple of block size %zu",
|
||||
len, ctx->blocksize);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ctx->handle) { /* CBC / XTS mode */
|
||||
err = gnutls_cipher_decrypt2(ctx->handle,
|
||||
in, len,
|
||||
out, len);
|
||||
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot decrypt data: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
} else { /* ECB mode very inefficiently faked with CBC */
|
||||
g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
|
||||
while (len) {
|
||||
gnutls_cipher_hd_t handle;
|
||||
gnutls_datum_t gkey = { (unsigned char *)ctx->key, ctx->nkey };
|
||||
int err = gnutls_cipher_init(&handle, ctx->galg, &gkey, NULL);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_cipher_set_iv(handle, iv, ctx->blocksize);
|
||||
|
||||
err = gnutls_cipher_decrypt2(handle,
|
||||
in, ctx->blocksize,
|
||||
out, ctx->blocksize);
|
||||
if (err != 0) {
|
||||
gnutls_cipher_deinit(handle);
|
||||
error_setg(errp, "Cannot encrypt data: %s",
|
||||
gnutls_strerror(err));
|
||||
return -1;
|
||||
}
|
||||
gnutls_cipher_deinit(handle);
|
||||
|
||||
len -= ctx->blocksize;
|
||||
in += ctx->blocksize;
|
||||
out += ctx->blocksize;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_cipher_setiv(QCryptoCipher *cipher,
|
||||
const uint8_t *iv, size_t niv,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx = container_of(cipher, QCryptoCipherGnutls, base);
|
||||
|
||||
if (niv != ctx->blocksize) {
|
||||
error_setg(errp, "Expected IV size %zu not %zu",
|
||||
ctx->blocksize, niv);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_cipher_set_iv(ctx->handle, (unsigned char *)iv, niv);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static struct QCryptoCipherDriver gnutls_driver = {
|
||||
.cipher_encrypt = qcrypto_gnutls_cipher_encrypt,
|
||||
.cipher_decrypt = qcrypto_gnutls_cipher_decrypt,
|
||||
.cipher_setiv = qcrypto_gnutls_cipher_setiv,
|
||||
.cipher_free = qcrypto_gnutls_cipher_free,
|
||||
};
|
||||
|
||||
static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode,
|
||||
const uint8_t *key,
|
||||
size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoCipherGnutls *ctx;
|
||||
gnutls_datum_t gkey = { (unsigned char *)key, nkey };
|
||||
gnutls_cipher_algorithm_t galg = GNUTLS_CIPHER_UNKNOWN;
|
||||
int err;
|
||||
|
||||
switch (mode) {
|
||||
#ifdef QEMU_GNUTLS_XTS
|
||||
case QCRYPTO_CIPHER_MODE_XTS:
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
galg = GNUTLS_CIPHER_AES_128_XTS;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
galg = GNUTLS_CIPHER_AES_256_XTS;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
#endif
|
||||
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
galg = GNUTLS_CIPHER_AES_128_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
galg = GNUTLS_CIPHER_AES_192_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_AES_256:
|
||||
galg = GNUTLS_CIPHER_AES_256_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
galg = GNUTLS_CIPHER_DES_CBC;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
galg = GNUTLS_CIPHER_3DES_CBC;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (galg == GNUTLS_CIPHER_UNKNOWN) {
|
||||
error_setg(errp, "Unsupported cipher algorithm %s with %s mode",
|
||||
QCryptoCipherAlgorithm_str(alg),
|
||||
QCryptoCipherMode_str(mode));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx = g_new0(QCryptoCipherGnutls, 1);
|
||||
ctx->base.driver = &gnutls_driver;
|
||||
|
||||
if (mode == QCRYPTO_CIPHER_MODE_ECB) {
|
||||
ctx->key = g_new0(guint8, nkey);
|
||||
memcpy(ctx->key, key, nkey);
|
||||
ctx->nkey = nkey;
|
||||
ctx->galg = galg;
|
||||
} else {
|
||||
err = gnutls_cipher_init(&ctx->handle, galg, &gkey, NULL);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize cipher: %s",
|
||||
gnutls_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
}
|
||||
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES ||
|
||||
alg == QCRYPTO_CIPHER_ALG_3DES)
|
||||
ctx->blocksize = 8;
|
||||
else
|
||||
ctx->blocksize = 16;
|
||||
|
||||
/*
|
||||
* Our API contract for requires iv to be optional
|
||||
* but nettle gets unhappy when called by gnutls
|
||||
* in this case, so we just force set a default
|
||||
* all-zeros IV, to match behaviour of other backends.
|
||||
*/
|
||||
if (mode != QCRYPTO_CIPHER_MODE_ECB) {
|
||||
g_autofree unsigned char *iv = g_new0(unsigned char, ctx->blocksize);
|
||||
gnutls_cipher_set_iv(ctx->handle, iv, ctx->blocksize);
|
||||
}
|
||||
|
||||
return &ctx->base;
|
||||
|
||||
error:
|
||||
qcrypto_gnutls_cipher_free(&ctx->base);
|
||||
return NULL;
|
||||
}
|
@ -235,11 +235,11 @@ static const struct QCryptoCipherDriver NAME##_driver_xts = { \
|
||||
DEFINE_XTS(NAME, TYPE, BLEN, ENCRYPT, DECRYPT)
|
||||
|
||||
|
||||
typedef struct QCryptoNettleDESRFB {
|
||||
typedef struct QCryptoNettleDES {
|
||||
QCryptoCipher base;
|
||||
struct des_ctx key;
|
||||
uint8_t iv[DES_BLOCK_SIZE];
|
||||
} QCryptoNettleDESRFB;
|
||||
} QCryptoNettleDES;
|
||||
|
||||
static void des_encrypt_native(const void *ctx, size_t length,
|
||||
uint8_t *dst, const uint8_t *src)
|
||||
@ -253,7 +253,7 @@ static void des_decrypt_native(const void *ctx, size_t length,
|
||||
des_decrypt(ctx, length, dst, src);
|
||||
}
|
||||
|
||||
DEFINE_ECB_CBC_CTR(qcrypto_nettle_des_rfb, QCryptoNettleDESRFB,
|
||||
DEFINE_ECB_CBC_CTR(qcrypto_nettle_des, QCryptoNettleDES,
|
||||
DES_BLOCK_SIZE, des_encrypt_native, des_decrypt_native)
|
||||
|
||||
|
||||
@ -431,7 +431,7 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg,
|
||||
QCryptoCipherMode mode)
|
||||
{
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
case QCRYPTO_CIPHER_ALG_3DES:
|
||||
case QCRYPTO_CIPHER_ALG_AES_128:
|
||||
case QCRYPTO_CIPHER_ALG_AES_192:
|
||||
@ -480,32 +480,28 @@ static QCryptoCipher *qcrypto_cipher_ctx_new(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
switch (alg) {
|
||||
case QCRYPTO_CIPHER_ALG_DES_RFB:
|
||||
case QCRYPTO_CIPHER_ALG_DES:
|
||||
{
|
||||
QCryptoNettleDESRFB *ctx;
|
||||
QCryptoNettleDES *ctx;
|
||||
const QCryptoCipherDriver *drv;
|
||||
uint8_t *rfbkey;
|
||||
|
||||
switch (mode) {
|
||||
case QCRYPTO_CIPHER_MODE_ECB:
|
||||
drv = &qcrypto_nettle_des_rfb_driver_ecb;
|
||||
drv = &qcrypto_nettle_des_driver_ecb;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_CBC:
|
||||
drv = &qcrypto_nettle_des_rfb_driver_cbc;
|
||||
drv = &qcrypto_nettle_des_driver_cbc;
|
||||
break;
|
||||
case QCRYPTO_CIPHER_MODE_CTR:
|
||||
drv = &qcrypto_nettle_des_rfb_driver_ctr;
|
||||
drv = &qcrypto_nettle_des_driver_ctr;
|
||||
break;
|
||||
default:
|
||||
goto bad_cipher_mode;
|
||||
}
|
||||
|
||||
ctx = g_new0(QCryptoNettleDESRFB, 1);
|
||||
ctx = g_new0(QCryptoNettleDES, 1);
|
||||
ctx->base.driver = drv;
|
||||
|
||||
rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
|
||||
des_set_key(&ctx->key, rfbkey);
|
||||
g_free(rfbkey);
|
||||
des_set_key(&ctx->key, key);
|
||||
|
||||
return &ctx->base;
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ static const size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = {
|
||||
[QCRYPTO_CIPHER_ALG_AES_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_192] = 24,
|
||||
[QCRYPTO_CIPHER_ALG_AES_256] = 32,
|
||||
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_DES] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_3DES] = 24,
|
||||
[QCRYPTO_CIPHER_ALG_CAST5_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
|
||||
@ -44,7 +44,7 @@ static const size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {
|
||||
[QCRYPTO_CIPHER_ALG_AES_128] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_192] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_AES_256] = 16,
|
||||
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_DES] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_3DES] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_CAST5_128] = 8,
|
||||
[QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
|
||||
@ -107,9 +107,9 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
|
||||
}
|
||||
|
||||
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES_RFB
|
||||
|| alg == QCRYPTO_CIPHER_ALG_3DES) {
|
||||
error_setg(errp, "XTS mode not compatible with DES-RFB/3DES");
|
||||
if (alg == QCRYPTO_CIPHER_ALG_DES ||
|
||||
alg == QCRYPTO_CIPHER_ALG_3DES) {
|
||||
error_setg(errp, "XTS mode not compatible with DES/3DES");
|
||||
return false;
|
||||
}
|
||||
if (nkey % 2) {
|
||||
@ -132,28 +132,12 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
|
||||
return true;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_GCRYPT) || defined(CONFIG_NETTLE)
|
||||
static uint8_t *
|
||||
qcrypto_cipher_munge_des_rfb_key(const uint8_t *key,
|
||||
size_t nkey)
|
||||
{
|
||||
uint8_t *ret = g_new0(uint8_t, nkey);
|
||||
size_t i;
|
||||
for (i = 0; i < nkey; i++) {
|
||||
uint8_t r = key[i];
|
||||
r = (r & 0xf0) >> 4 | (r & 0x0f) << 4;
|
||||
r = (r & 0xcc) >> 2 | (r & 0x33) << 2;
|
||||
r = (r & 0xaa) >> 1 | (r & 0x55) << 1;
|
||||
ret[i] = r;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_GCRYPT || CONFIG_NETTLE */
|
||||
|
||||
#ifdef CONFIG_GCRYPT
|
||||
#include "cipher-gcrypt.c.inc"
|
||||
#elif defined CONFIG_NETTLE
|
||||
#include "cipher-nettle.c.inc"
|
||||
#elif defined CONFIG_GNUTLS_CRYPTO
|
||||
#include "cipher-gnutls.c.inc"
|
||||
#else
|
||||
#include "cipher-builtin.c.inc"
|
||||
#endif
|
||||
|
416
crypto/desrfb.c
416
crypto/desrfb.c
@ -1,416 +0,0 @@
|
||||
/*
|
||||
* This is D3DES (V5.09) by Richard Outerbridge with the double and
|
||||
* triple-length support removed for use in VNC. Also the bytebit[] array
|
||||
* has been reversed so that the most significant bit in each byte of the
|
||||
* key is ignored, not the least significant.
|
||||
*
|
||||
* These changes are:
|
||||
* Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
|
||||
*
|
||||
* This software 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.
|
||||
*/
|
||||
|
||||
/* D3DES (V5.09) -
|
||||
*
|
||||
* A portable, public domain, version of the Data Encryption Standard.
|
||||
*
|
||||
* Written with Symantec's THINK (Lightspeed) C by Richard Outerbridge.
|
||||
* Thanks to: Dan Hoey for his excellent Initial and Inverse permutation
|
||||
* code; Jim Gillogly & Phil Karn for the DES key schedule code; Dennis
|
||||
* Ferguson, Eric Young and Dana How for comparing notes; and Ray Lau,
|
||||
* for humouring me on.
|
||||
*
|
||||
* Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge.
|
||||
* (GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992.
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include "crypto/desrfb.h"
|
||||
|
||||
static void scrunch(unsigned char *, unsigned long *);
|
||||
static void unscrun(unsigned long *, unsigned char *);
|
||||
static void desfunc(unsigned long *, unsigned long *);
|
||||
static void cookey(unsigned long *);
|
||||
|
||||
static unsigned long KnL[32] = { 0L };
|
||||
|
||||
static const unsigned short bytebit[8] = {
|
||||
01, 02, 04, 010, 020, 040, 0100, 0200 };
|
||||
|
||||
static const unsigned long bigbyte[24] = {
|
||||
0x800000L, 0x400000L, 0x200000L, 0x100000L,
|
||||
0x80000L, 0x40000L, 0x20000L, 0x10000L,
|
||||
0x8000L, 0x4000L, 0x2000L, 0x1000L,
|
||||
0x800L, 0x400L, 0x200L, 0x100L,
|
||||
0x80L, 0x40L, 0x20L, 0x10L,
|
||||
0x8L, 0x4L, 0x2L, 0x1L };
|
||||
|
||||
/* Use the key schedule specified in the Standard (ANSI X3.92-1981). */
|
||||
|
||||
static const unsigned char pc1[56] = {
|
||||
56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17,
|
||||
9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35,
|
||||
62, 54, 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21,
|
||||
13, 5, 60, 52, 44, 36, 28, 20, 12, 4, 27, 19, 11, 3 };
|
||||
|
||||
static const unsigned char totrot[16] = {
|
||||
1, 2, 4, 6, 8, 10, 12, 14, 15, 17, 19, 21, 23, 25, 27, 28 };
|
||||
|
||||
static const unsigned char pc2[48] = {
|
||||
13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9,
|
||||
22, 18, 11, 3, 25, 7, 15, 6, 26, 19, 12, 1,
|
||||
40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32, 47,
|
||||
43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31 };
|
||||
|
||||
/* Thanks to James Gillogly & Phil Karn! */
|
||||
void deskey(unsigned char *key, int edf)
|
||||
{
|
||||
register int i, j, l, m, n;
|
||||
unsigned char pc1m[56], pcr[56];
|
||||
unsigned long kn[32];
|
||||
|
||||
for ( j = 0; j < 56; j++ ) {
|
||||
l = pc1[j];
|
||||
m = l & 07;
|
||||
pc1m[j] = (key[l >> 3] & bytebit[m]) ? 1 : 0;
|
||||
}
|
||||
for( i = 0; i < 16; i++ ) {
|
||||
if( edf == DE1 ) m = (15 - i) << 1;
|
||||
else m = i << 1;
|
||||
n = m + 1;
|
||||
kn[m] = kn[n] = 0L;
|
||||
for( j = 0; j < 28; j++ ) {
|
||||
l = j + totrot[i];
|
||||
if( l < 28 ) pcr[j] = pc1m[l];
|
||||
else pcr[j] = pc1m[l - 28];
|
||||
}
|
||||
for( j = 28; j < 56; j++ ) {
|
||||
l = j + totrot[i];
|
||||
if( l < 56 ) pcr[j] = pc1m[l];
|
||||
else pcr[j] = pc1m[l - 28];
|
||||
}
|
||||
for( j = 0; j < 24; j++ ) {
|
||||
if( pcr[pc2[j]] ) kn[m] |= bigbyte[j];
|
||||
if( pcr[pc2[j + 24]] ) kn[n] |= bigbyte[j];
|
||||
}
|
||||
}
|
||||
cookey(kn);
|
||||
return;
|
||||
}
|
||||
|
||||
static void cookey(register unsigned long *raw1)
|
||||
{
|
||||
register unsigned long *cook, *raw0;
|
||||
unsigned long dough[32];
|
||||
register int i;
|
||||
|
||||
cook = dough;
|
||||
for( i = 0; i < 16; i++, raw1++ ) {
|
||||
raw0 = raw1++;
|
||||
*cook = (*raw0 & 0x00fc0000L) << 6;
|
||||
*cook |= (*raw0 & 0x00000fc0L) << 10;
|
||||
*cook |= (*raw1 & 0x00fc0000L) >> 10;
|
||||
*cook++ |= (*raw1 & 0x00000fc0L) >> 6;
|
||||
*cook = (*raw0 & 0x0003f000L) << 12;
|
||||
*cook |= (*raw0 & 0x0000003fL) << 16;
|
||||
*cook |= (*raw1 & 0x0003f000L) >> 4;
|
||||
*cook++ |= (*raw1 & 0x0000003fL);
|
||||
}
|
||||
usekey(dough);
|
||||
return;
|
||||
}
|
||||
|
||||
void usekey(register unsigned long *from)
|
||||
{
|
||||
register unsigned long *to, *endp;
|
||||
|
||||
to = KnL, endp = &KnL[32];
|
||||
while( to < endp ) *to++ = *from++;
|
||||
return;
|
||||
}
|
||||
|
||||
void des(unsigned char *inblock, unsigned char *outblock)
|
||||
{
|
||||
unsigned long work[2];
|
||||
|
||||
scrunch(inblock, work);
|
||||
desfunc(work, KnL);
|
||||
unscrun(work, outblock);
|
||||
return;
|
||||
}
|
||||
|
||||
static void scrunch(register unsigned char *outof, register unsigned long *into)
|
||||
{
|
||||
*into = (*outof++ & 0xffL) << 24;
|
||||
*into |= (*outof++ & 0xffL) << 16;
|
||||
*into |= (*outof++ & 0xffL) << 8;
|
||||
*into++ |= (*outof++ & 0xffL);
|
||||
*into = (*outof++ & 0xffL) << 24;
|
||||
*into |= (*outof++ & 0xffL) << 16;
|
||||
*into |= (*outof++ & 0xffL) << 8;
|
||||
*into |= (*outof & 0xffL);
|
||||
return;
|
||||
}
|
||||
|
||||
static void unscrun(register unsigned long *outof, register unsigned char *into)
|
||||
{
|
||||
*into++ = (unsigned char)((*outof >> 24) & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 16) & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 8) & 0xffL);
|
||||
*into++ = (unsigned char)(*outof++ & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 24) & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 16) & 0xffL);
|
||||
*into++ = (unsigned char)((*outof >> 8) & 0xffL);
|
||||
*into = (unsigned char)(*outof & 0xffL);
|
||||
return;
|
||||
}
|
||||
|
||||
static const unsigned long SP1[64] = {
|
||||
0x01010400L, 0x00000000L, 0x00010000L, 0x01010404L,
|
||||
0x01010004L, 0x00010404L, 0x00000004L, 0x00010000L,
|
||||
0x00000400L, 0x01010400L, 0x01010404L, 0x00000400L,
|
||||
0x01000404L, 0x01010004L, 0x01000000L, 0x00000004L,
|
||||
0x00000404L, 0x01000400L, 0x01000400L, 0x00010400L,
|
||||
0x00010400L, 0x01010000L, 0x01010000L, 0x01000404L,
|
||||
0x00010004L, 0x01000004L, 0x01000004L, 0x00010004L,
|
||||
0x00000000L, 0x00000404L, 0x00010404L, 0x01000000L,
|
||||
0x00010000L, 0x01010404L, 0x00000004L, 0x01010000L,
|
||||
0x01010400L, 0x01000000L, 0x01000000L, 0x00000400L,
|
||||
0x01010004L, 0x00010000L, 0x00010400L, 0x01000004L,
|
||||
0x00000400L, 0x00000004L, 0x01000404L, 0x00010404L,
|
||||
0x01010404L, 0x00010004L, 0x01010000L, 0x01000404L,
|
||||
0x01000004L, 0x00000404L, 0x00010404L, 0x01010400L,
|
||||
0x00000404L, 0x01000400L, 0x01000400L, 0x00000000L,
|
||||
0x00010004L, 0x00010400L, 0x00000000L, 0x01010004L };
|
||||
|
||||
static const unsigned long SP2[64] = {
|
||||
0x80108020L, 0x80008000L, 0x00008000L, 0x00108020L,
|
||||
0x00100000L, 0x00000020L, 0x80100020L, 0x80008020L,
|
||||
0x80000020L, 0x80108020L, 0x80108000L, 0x80000000L,
|
||||
0x80008000L, 0x00100000L, 0x00000020L, 0x80100020L,
|
||||
0x00108000L, 0x00100020L, 0x80008020L, 0x00000000L,
|
||||
0x80000000L, 0x00008000L, 0x00108020L, 0x80100000L,
|
||||
0x00100020L, 0x80000020L, 0x00000000L, 0x00108000L,
|
||||
0x00008020L, 0x80108000L, 0x80100000L, 0x00008020L,
|
||||
0x00000000L, 0x00108020L, 0x80100020L, 0x00100000L,
|
||||
0x80008020L, 0x80100000L, 0x80108000L, 0x00008000L,
|
||||
0x80100000L, 0x80008000L, 0x00000020L, 0x80108020L,
|
||||
0x00108020L, 0x00000020L, 0x00008000L, 0x80000000L,
|
||||
0x00008020L, 0x80108000L, 0x00100000L, 0x80000020L,
|
||||
0x00100020L, 0x80008020L, 0x80000020L, 0x00100020L,
|
||||
0x00108000L, 0x00000000L, 0x80008000L, 0x00008020L,
|
||||
0x80000000L, 0x80100020L, 0x80108020L, 0x00108000L };
|
||||
|
||||
static const unsigned long SP3[64] = {
|
||||
0x00000208L, 0x08020200L, 0x00000000L, 0x08020008L,
|
||||
0x08000200L, 0x00000000L, 0x00020208L, 0x08000200L,
|
||||
0x00020008L, 0x08000008L, 0x08000008L, 0x00020000L,
|
||||
0x08020208L, 0x00020008L, 0x08020000L, 0x00000208L,
|
||||
0x08000000L, 0x00000008L, 0x08020200L, 0x00000200L,
|
||||
0x00020200L, 0x08020000L, 0x08020008L, 0x00020208L,
|
||||
0x08000208L, 0x00020200L, 0x00020000L, 0x08000208L,
|
||||
0x00000008L, 0x08020208L, 0x00000200L, 0x08000000L,
|
||||
0x08020200L, 0x08000000L, 0x00020008L, 0x00000208L,
|
||||
0x00020000L, 0x08020200L, 0x08000200L, 0x00000000L,
|
||||
0x00000200L, 0x00020008L, 0x08020208L, 0x08000200L,
|
||||
0x08000008L, 0x00000200L, 0x00000000L, 0x08020008L,
|
||||
0x08000208L, 0x00020000L, 0x08000000L, 0x08020208L,
|
||||
0x00000008L, 0x00020208L, 0x00020200L, 0x08000008L,
|
||||
0x08020000L, 0x08000208L, 0x00000208L, 0x08020000L,
|
||||
0x00020208L, 0x00000008L, 0x08020008L, 0x00020200L };
|
||||
|
||||
static const unsigned long SP4[64] = {
|
||||
0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
|
||||
0x00802080L, 0x00800081L, 0x00800001L, 0x00002001L,
|
||||
0x00000000L, 0x00802000L, 0x00802000L, 0x00802081L,
|
||||
0x00000081L, 0x00000000L, 0x00800080L, 0x00800001L,
|
||||
0x00000001L, 0x00002000L, 0x00800000L, 0x00802001L,
|
||||
0x00000080L, 0x00800000L, 0x00002001L, 0x00002080L,
|
||||
0x00800081L, 0x00000001L, 0x00002080L, 0x00800080L,
|
||||
0x00002000L, 0x00802080L, 0x00802081L, 0x00000081L,
|
||||
0x00800080L, 0x00800001L, 0x00802000L, 0x00802081L,
|
||||
0x00000081L, 0x00000000L, 0x00000000L, 0x00802000L,
|
||||
0x00002080L, 0x00800080L, 0x00800081L, 0x00000001L,
|
||||
0x00802001L, 0x00002081L, 0x00002081L, 0x00000080L,
|
||||
0x00802081L, 0x00000081L, 0x00000001L, 0x00002000L,
|
||||
0x00800001L, 0x00002001L, 0x00802080L, 0x00800081L,
|
||||
0x00002001L, 0x00002080L, 0x00800000L, 0x00802001L,
|
||||
0x00000080L, 0x00800000L, 0x00002000L, 0x00802080L };
|
||||
|
||||
static const unsigned long SP5[64] = {
|
||||
0x00000100L, 0x02080100L, 0x02080000L, 0x42000100L,
|
||||
0x00080000L, 0x00000100L, 0x40000000L, 0x02080000L,
|
||||
0x40080100L, 0x00080000L, 0x02000100L, 0x40080100L,
|
||||
0x42000100L, 0x42080000L, 0x00080100L, 0x40000000L,
|
||||
0x02000000L, 0x40080000L, 0x40080000L, 0x00000000L,
|
||||
0x40000100L, 0x42080100L, 0x42080100L, 0x02000100L,
|
||||
0x42080000L, 0x40000100L, 0x00000000L, 0x42000000L,
|
||||
0x02080100L, 0x02000000L, 0x42000000L, 0x00080100L,
|
||||
0x00080000L, 0x42000100L, 0x00000100L, 0x02000000L,
|
||||
0x40000000L, 0x02080000L, 0x42000100L, 0x40080100L,
|
||||
0x02000100L, 0x40000000L, 0x42080000L, 0x02080100L,
|
||||
0x40080100L, 0x00000100L, 0x02000000L, 0x42080000L,
|
||||
0x42080100L, 0x00080100L, 0x42000000L, 0x42080100L,
|
||||
0x02080000L, 0x00000000L, 0x40080000L, 0x42000000L,
|
||||
0x00080100L, 0x02000100L, 0x40000100L, 0x00080000L,
|
||||
0x00000000L, 0x40080000L, 0x02080100L, 0x40000100L };
|
||||
|
||||
static const unsigned long SP6[64] = {
|
||||
0x20000010L, 0x20400000L, 0x00004000L, 0x20404010L,
|
||||
0x20400000L, 0x00000010L, 0x20404010L, 0x00400000L,
|
||||
0x20004000L, 0x00404010L, 0x00400000L, 0x20000010L,
|
||||
0x00400010L, 0x20004000L, 0x20000000L, 0x00004010L,
|
||||
0x00000000L, 0x00400010L, 0x20004010L, 0x00004000L,
|
||||
0x00404000L, 0x20004010L, 0x00000010L, 0x20400010L,
|
||||
0x20400010L, 0x00000000L, 0x00404010L, 0x20404000L,
|
||||
0x00004010L, 0x00404000L, 0x20404000L, 0x20000000L,
|
||||
0x20004000L, 0x00000010L, 0x20400010L, 0x00404000L,
|
||||
0x20404010L, 0x00400000L, 0x00004010L, 0x20000010L,
|
||||
0x00400000L, 0x20004000L, 0x20000000L, 0x00004010L,
|
||||
0x20000010L, 0x20404010L, 0x00404000L, 0x20400000L,
|
||||
0x00404010L, 0x20404000L, 0x00000000L, 0x20400010L,
|
||||
0x00000010L, 0x00004000L, 0x20400000L, 0x00404010L,
|
||||
0x00004000L, 0x00400010L, 0x20004010L, 0x00000000L,
|
||||
0x20404000L, 0x20000000L, 0x00400010L, 0x20004010L };
|
||||
|
||||
static const unsigned long SP7[64] = {
|
||||
0x00200000L, 0x04200002L, 0x04000802L, 0x00000000L,
|
||||
0x00000800L, 0x04000802L, 0x00200802L, 0x04200800L,
|
||||
0x04200802L, 0x00200000L, 0x00000000L, 0x04000002L,
|
||||
0x00000002L, 0x04000000L, 0x04200002L, 0x00000802L,
|
||||
0x04000800L, 0x00200802L, 0x00200002L, 0x04000800L,
|
||||
0x04000002L, 0x04200000L, 0x04200800L, 0x00200002L,
|
||||
0x04200000L, 0x00000800L, 0x00000802L, 0x04200802L,
|
||||
0x00200800L, 0x00000002L, 0x04000000L, 0x00200800L,
|
||||
0x04000000L, 0x00200800L, 0x00200000L, 0x04000802L,
|
||||
0x04000802L, 0x04200002L, 0x04200002L, 0x00000002L,
|
||||
0x00200002L, 0x04000000L, 0x04000800L, 0x00200000L,
|
||||
0x04200800L, 0x00000802L, 0x00200802L, 0x04200800L,
|
||||
0x00000802L, 0x04000002L, 0x04200802L, 0x04200000L,
|
||||
0x00200800L, 0x00000000L, 0x00000002L, 0x04200802L,
|
||||
0x00000000L, 0x00200802L, 0x04200000L, 0x00000800L,
|
||||
0x04000002L, 0x04000800L, 0x00000800L, 0x00200002L };
|
||||
|
||||
static const unsigned long SP8[64] = {
|
||||
0x10001040L, 0x00001000L, 0x00040000L, 0x10041040L,
|
||||
0x10000000L, 0x10001040L, 0x00000040L, 0x10000000L,
|
||||
0x00040040L, 0x10040000L, 0x10041040L, 0x00041000L,
|
||||
0x10041000L, 0x00041040L, 0x00001000L, 0x00000040L,
|
||||
0x10040000L, 0x10000040L, 0x10001000L, 0x00001040L,
|
||||
0x00041000L, 0x00040040L, 0x10040040L, 0x10041000L,
|
||||
0x00001040L, 0x00000000L, 0x00000000L, 0x10040040L,
|
||||
0x10000040L, 0x10001000L, 0x00041040L, 0x00040000L,
|
||||
0x00041040L, 0x00040000L, 0x10041000L, 0x00001000L,
|
||||
0x00000040L, 0x10040040L, 0x00001000L, 0x00041040L,
|
||||
0x10001000L, 0x00000040L, 0x10000040L, 0x10040000L,
|
||||
0x10040040L, 0x10000000L, 0x00040000L, 0x10001040L,
|
||||
0x00000000L, 0x10041040L, 0x00040040L, 0x10000040L,
|
||||
0x10040000L, 0x10001000L, 0x10001040L, 0x00000000L,
|
||||
0x10041040L, 0x00041000L, 0x00041000L, 0x00001040L,
|
||||
0x00001040L, 0x00040040L, 0x10000000L, 0x10041000L };
|
||||
|
||||
static void desfunc(register unsigned long *block, register unsigned long *keys)
|
||||
{
|
||||
register unsigned long fval, work, right, leftt;
|
||||
register int round;
|
||||
|
||||
leftt = block[0];
|
||||
right = block[1];
|
||||
work = ((leftt >> 4) ^ right) & 0x0f0f0f0fL;
|
||||
right ^= work;
|
||||
leftt ^= (work << 4);
|
||||
work = ((leftt >> 16) ^ right) & 0x0000ffffL;
|
||||
right ^= work;
|
||||
leftt ^= (work << 16);
|
||||
work = ((right >> 2) ^ leftt) & 0x33333333L;
|
||||
leftt ^= work;
|
||||
right ^= (work << 2);
|
||||
work = ((right >> 8) ^ leftt) & 0x00ff00ffL;
|
||||
leftt ^= work;
|
||||
right ^= (work << 8);
|
||||
right = ((right << 1) | ((right >> 31) & 1L)) & 0xffffffffL;
|
||||
work = (leftt ^ right) & 0xaaaaaaaaL;
|
||||
leftt ^= work;
|
||||
right ^= work;
|
||||
leftt = ((leftt << 1) | ((leftt >> 31) & 1L)) & 0xffffffffL;
|
||||
|
||||
for( round = 0; round < 8; round++ ) {
|
||||
work = (right << 28) | (right >> 4);
|
||||
work ^= *keys++;
|
||||
fval = SP7[ work & 0x3fL];
|
||||
fval |= SP5[(work >> 8) & 0x3fL];
|
||||
fval |= SP3[(work >> 16) & 0x3fL];
|
||||
fval |= SP1[(work >> 24) & 0x3fL];
|
||||
work = right ^ *keys++;
|
||||
fval |= SP8[ work & 0x3fL];
|
||||
fval |= SP6[(work >> 8) & 0x3fL];
|
||||
fval |= SP4[(work >> 16) & 0x3fL];
|
||||
fval |= SP2[(work >> 24) & 0x3fL];
|
||||
leftt ^= fval;
|
||||
work = (leftt << 28) | (leftt >> 4);
|
||||
work ^= *keys++;
|
||||
fval = SP7[ work & 0x3fL];
|
||||
fval |= SP5[(work >> 8) & 0x3fL];
|
||||
fval |= SP3[(work >> 16) & 0x3fL];
|
||||
fval |= SP1[(work >> 24) & 0x3fL];
|
||||
work = leftt ^ *keys++;
|
||||
fval |= SP8[ work & 0x3fL];
|
||||
fval |= SP6[(work >> 8) & 0x3fL];
|
||||
fval |= SP4[(work >> 16) & 0x3fL];
|
||||
fval |= SP2[(work >> 24) & 0x3fL];
|
||||
right ^= fval;
|
||||
}
|
||||
|
||||
right = (right << 31) | (right >> 1);
|
||||
work = (leftt ^ right) & 0xaaaaaaaaL;
|
||||
leftt ^= work;
|
||||
right ^= work;
|
||||
leftt = (leftt << 31) | (leftt >> 1);
|
||||
work = ((leftt >> 8) ^ right) & 0x00ff00ffL;
|
||||
right ^= work;
|
||||
leftt ^= (work << 8);
|
||||
work = ((leftt >> 2) ^ right) & 0x33333333L;
|
||||
right ^= work;
|
||||
leftt ^= (work << 2);
|
||||
work = ((right >> 16) ^ leftt) & 0x0000ffffL;
|
||||
leftt ^= work;
|
||||
right ^= (work << 16);
|
||||
work = ((right >> 4) ^ leftt) & 0x0f0f0f0fL;
|
||||
leftt ^= work;
|
||||
right ^= (work << 4);
|
||||
*block++ = right;
|
||||
*block = leftt;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Validation sets:
|
||||
*
|
||||
* Single-length key, single-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef
|
||||
* Plain : 0123 4567 89ab cde7
|
||||
* Cipher : c957 4425 6a5e d31d
|
||||
*
|
||||
* Double-length key, single-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210
|
||||
* Plain : 0123 4567 89ab cde7
|
||||
* Cipher : 7f1d 0a77 826b 8aff
|
||||
*
|
||||
* Double-length key, double-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210
|
||||
* Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
|
||||
* Cipher : 27a0 8440 406a df60 278f 47cf 42d6 15d7
|
||||
*
|
||||
* Triple-length key, single-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
|
||||
* Plain : 0123 4567 89ab cde7
|
||||
* Cipher : de0b 7c06 ae5e 0ed5
|
||||
*
|
||||
* Triple-length key, double-length plaintext -
|
||||
* Key : 0123 4567 89ab cdef fedc ba98 7654 3210 89ab cdef 0123 4567
|
||||
* Plain : 0123 4567 89ab cdef 0123 4567 89ab cdff
|
||||
* Cipher : ad0d 1b30 ac17 cf07 0ed1 1c63 81e4 4de5
|
||||
*
|
||||
* d3des V5.0a rwo 9208.07 18:44 Graven Imagery
|
||||
**********************************************************************/
|
104
crypto/hash-gnutls.c
Normal file
104
crypto/hash-gnutls.c
Normal file
@ -0,0 +1,104 @@
|
||||
/*
|
||||
* QEMU Crypto hash algorithms
|
||||
*
|
||||
* Copyright (c) 2021 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.1 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 "qemu/osdep.h"
|
||||
#include <gnutls/crypto.h>
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/hash.h"
|
||||
#include "hashpriv.h"
|
||||
|
||||
|
||||
static int qcrypto_hash_alg_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
|
||||
[QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384,
|
||||
[QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512,
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160,
|
||||
};
|
||||
|
||||
gboolean qcrypto_hash_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
size_t i;
|
||||
const gnutls_digest_algorithm_t *algs;
|
||||
if (alg >= G_N_ELEMENTS(qcrypto_hash_alg_map) ||
|
||||
qcrypto_hash_alg_map[alg] == GNUTLS_DIG_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
algs = gnutls_digest_list();
|
||||
for (i = 0; algs[i] != GNUTLS_DIG_UNKNOWN; i++) {
|
||||
if (algs[i] == qcrypto_hash_alg_map[alg]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_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 hash;
|
||||
|
||||
if (!qcrypto_hash_supports(alg)) {
|
||||
error_setg(errp,
|
||||
"Unknown hash algorithm %d",
|
||||
alg);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gnutls_hash_get_len(qcrypto_hash_alg_map[alg]);
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gnutls_hash_init(&hash, 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++) {
|
||||
gnutls_hash(hash, iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
|
||||
gnutls_hash_deinit(hash, *result);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
QCryptoHashDriver qcrypto_hash_lib_driver = {
|
||||
.hash_bytesv = qcrypto_gnutls_hash_bytesv,
|
||||
};
|
139
crypto/hmac-gnutls.c
Normal file
139
crypto/hmac-gnutls.c
Normal file
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* QEMU Crypto hmac algorithms
|
||||
*
|
||||
* Copyright (c) 2021 Red Hat, Inc.
|
||||
*
|
||||
* Derived from hmac-gcrypt.c:
|
||||
*
|
||||
* Copyright (c) 2016 HUAWEI TECHNOLOGIES CO., LTD.
|
||||
*
|
||||
* This work is licensed under the terms of the GNU GPL, version 2 or
|
||||
* (at your option) any later version. See the COPYING file in the
|
||||
* top-level directory.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "qemu/osdep.h"
|
||||
#include <gnutls/crypto.h>
|
||||
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/hmac.h"
|
||||
#include "hmacpriv.h"
|
||||
|
||||
static int qcrypto_hmac_alg_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_MAC_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_MAC_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA224] = GNUTLS_MAC_SHA224,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_MAC_SHA256,
|
||||
[QCRYPTO_HASH_ALG_SHA384] = GNUTLS_MAC_SHA384,
|
||||
[QCRYPTO_HASH_ALG_SHA512] = GNUTLS_MAC_SHA512,
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_MAC_RMD160,
|
||||
};
|
||||
|
||||
typedef struct QCryptoHmacGnutls QCryptoHmacGnutls;
|
||||
struct QCryptoHmacGnutls {
|
||||
gnutls_hmac_hd_t handle;
|
||||
};
|
||||
|
||||
bool qcrypto_hmac_supports(QCryptoHashAlgorithm alg)
|
||||
{
|
||||
size_t i;
|
||||
const gnutls_digest_algorithm_t *algs;
|
||||
if (alg >= G_N_ELEMENTS(qcrypto_hmac_alg_map) ||
|
||||
qcrypto_hmac_alg_map[alg] == GNUTLS_DIG_UNKNOWN) {
|
||||
return false;
|
||||
}
|
||||
algs = gnutls_digest_list();
|
||||
for (i = 0; algs[i] != GNUTLS_DIG_UNKNOWN; i++) {
|
||||
if (algs[i] == qcrypto_hmac_alg_map[alg]) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void *qcrypto_hmac_ctx_new(QCryptoHashAlgorithm alg,
|
||||
const uint8_t *key, size_t nkey,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmacGnutls *ctx;
|
||||
int err;
|
||||
|
||||
if (!qcrypto_hmac_supports(alg)) {
|
||||
error_setg(errp, "Unsupported hmac algorithm %s",
|
||||
QCryptoHashAlgorithm_str(alg));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ctx = g_new0(QCryptoHmacGnutls, 1);
|
||||
|
||||
err = gnutls_hmac_init(&ctx->handle,
|
||||
qcrypto_hmac_alg_map[alg],
|
||||
(const void *)key, nkey);
|
||||
if (err != 0) {
|
||||
error_setg(errp, "Cannot initialize hmac: %s",
|
||||
gnutls_strerror(err));
|
||||
goto error;
|
||||
}
|
||||
|
||||
return ctx;
|
||||
|
||||
error:
|
||||
g_free(ctx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void
|
||||
qcrypto_gnutls_hmac_ctx_free(QCryptoHmac *hmac)
|
||||
{
|
||||
QCryptoHmacGnutls *ctx;
|
||||
|
||||
ctx = hmac->opaque;
|
||||
gnutls_hmac_deinit(ctx->handle, NULL);
|
||||
|
||||
g_free(ctx);
|
||||
}
|
||||
|
||||
static int
|
||||
qcrypto_gnutls_hmac_bytesv(QCryptoHmac *hmac,
|
||||
const struct iovec *iov,
|
||||
size_t niov,
|
||||
uint8_t **result,
|
||||
size_t *resultlen,
|
||||
Error **errp)
|
||||
{
|
||||
QCryptoHmacGnutls *ctx;
|
||||
uint32_t ret;
|
||||
int i;
|
||||
|
||||
ctx = hmac->opaque;
|
||||
|
||||
for (i = 0; i < niov; i++) {
|
||||
gnutls_hmac(ctx->handle, iov[i].iov_base, iov[i].iov_len);
|
||||
}
|
||||
|
||||
ret = gnutls_hmac_get_len(qcrypto_hmac_alg_map[hmac->alg]);
|
||||
if (ret <= 0) {
|
||||
error_setg(errp, "Unable to get hmac length: %s",
|
||||
gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 hmac %d",
|
||||
*resultlen, ret);
|
||||
return -1;
|
||||
}
|
||||
|
||||
gnutls_hmac_output(ctx->handle, *result);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
QCryptoHmacDriver qcrypto_hmac_lib_driver = {
|
||||
.hmac_bytesv = qcrypto_gnutls_hmac_bytesv,
|
||||
.hmac_free = qcrypto_gnutls_hmac_ctx_free,
|
||||
};
|
@ -35,21 +35,6 @@
|
||||
#include "crypto/random.h"
|
||||
|
||||
/* #define DEBUG_GNUTLS */
|
||||
|
||||
/*
|
||||
* We need to init gcrypt threading if
|
||||
*
|
||||
* - gcrypt < 1.6.0
|
||||
*
|
||||
*/
|
||||
|
||||
#if (defined(CONFIG_GCRYPT) && \
|
||||
(GCRYPT_VERSION_NUMBER < 0x010600))
|
||||
#define QCRYPTO_INIT_GCRYPT_THREADS
|
||||
#else
|
||||
#undef QCRYPTO_INIT_GCRYPT_THREADS
|
||||
#endif
|
||||
|
||||
#ifdef DEBUG_GNUTLS
|
||||
static void qcrypto_gnutls_log(int level, const char *str)
|
||||
{
|
||||
@ -57,55 +42,8 @@ static void qcrypto_gnutls_log(int level, const char *str)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef QCRYPTO_INIT_GCRYPT_THREADS
|
||||
static int qcrypto_gcrypt_mutex_init(void **priv)
|
||||
{ \
|
||||
QemuMutex *lock = NULL;
|
||||
lock = g_new0(QemuMutex, 1);
|
||||
qemu_mutex_init(lock);
|
||||
*priv = lock;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_mutex_destroy(void **priv)
|
||||
{
|
||||
QemuMutex *lock = *priv;
|
||||
qemu_mutex_destroy(lock);
|
||||
g_free(lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_mutex_lock(void **priv)
|
||||
{
|
||||
QemuMutex *lock = *priv;
|
||||
qemu_mutex_lock(lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int qcrypto_gcrypt_mutex_unlock(void **priv)
|
||||
{
|
||||
QemuMutex *lock = *priv;
|
||||
qemu_mutex_unlock(lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct gcry_thread_cbs qcrypto_gcrypt_thread_impl = {
|
||||
(GCRY_THREAD_OPTION_PTHREAD | (GCRY_THREAD_OPTION_VERSION << 8)),
|
||||
NULL,
|
||||
qcrypto_gcrypt_mutex_init,
|
||||
qcrypto_gcrypt_mutex_destroy,
|
||||
qcrypto_gcrypt_mutex_lock,
|
||||
qcrypto_gcrypt_mutex_unlock,
|
||||
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
||||
};
|
||||
#endif /* QCRYPTO_INIT_GCRYPT */
|
||||
|
||||
int qcrypto_init(Error **errp)
|
||||
{
|
||||
#ifdef QCRYPTO_INIT_GCRYPT_THREADS
|
||||
gcry_control(GCRYCTL_SET_THREAD_CBS, &qcrypto_gcrypt_thread_impl);
|
||||
#endif /* QCRYPTO_INIT_GCRYPT_THREADS */
|
||||
|
||||
#ifdef CONFIG_GNUTLS
|
||||
int ret;
|
||||
ret = gnutls_global_init();
|
||||
|
@ -5,7 +5,6 @@ crypto_ss.add(files(
|
||||
'block-qcow.c',
|
||||
'block.c',
|
||||
'cipher.c',
|
||||
'desrfb.c',
|
||||
'hash.c',
|
||||
'hmac.c',
|
||||
'ivgen-essiv.c',
|
||||
@ -24,14 +23,16 @@ crypto_ss.add(files(
|
||||
|
||||
if nettle.found()
|
||||
crypto_ss.add(nettle, files('hash-nettle.c', 'hmac-nettle.c', 'pbkdf-nettle.c'))
|
||||
if xts == 'private'
|
||||
crypto_ss.add(files('xts.c'))
|
||||
endif
|
||||
elif gcrypt.found()
|
||||
crypto_ss.add(gcrypt, files('hash-gcrypt.c', 'hmac-gcrypt.c', 'pbkdf-gcrypt.c'))
|
||||
elif gnutls_crypto.found()
|
||||
crypto_ss.add(gnutls, files('hash-gnutls.c', 'hmac-gnutls.c', 'pbkdf-gnutls.c'))
|
||||
else
|
||||
crypto_ss.add(files('hash-glib.c', 'hmac-glib.c', 'pbkdf-stub.c'))
|
||||
endif
|
||||
if xts == 'private'
|
||||
crypto_ss.add(files('xts.c'))
|
||||
endif
|
||||
|
||||
crypto_ss.add(when: 'CONFIG_SECRET_KEYRING', if_true: files('secret_keyring.c'))
|
||||
crypto_ss.add(when: 'CONFIG_AF_ALG', if_true: files('afalg.c', 'cipher-afalg.c', 'hash-afalg.c'))
|
||||
@ -39,6 +40,9 @@ crypto_ss.add(when: gnutls, if_true: files('tls-cipher-suites.c'))
|
||||
|
||||
util_ss.add(files('aes.c'))
|
||||
util_ss.add(files('init.c'))
|
||||
if gnutls.found()
|
||||
util_ss.add(gnutls)
|
||||
endif
|
||||
|
||||
if gcrypt.found()
|
||||
util_ss.add(gcrypt, files('random-gcrypt.c'))
|
||||
|
90
crypto/pbkdf-gnutls.c
Normal file
90
crypto/pbkdf-gnutls.c
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
|
||||
*
|
||||
* Copyright (c) 2021 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.1 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 "qemu/osdep.h"
|
||||
#include <gnutls/crypto.h>
|
||||
#include "qapi/error.h"
|
||||
#include "crypto/pbkdf.h"
|
||||
|
||||
bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash)
|
||||
{
|
||||
switch (hash) {
|
||||
case QCRYPTO_HASH_ALG_MD5:
|
||||
case QCRYPTO_HASH_ALG_SHA1:
|
||||
case QCRYPTO_HASH_ALG_SHA224:
|
||||
case QCRYPTO_HASH_ALG_SHA256:
|
||||
case QCRYPTO_HASH_ALG_SHA384:
|
||||
case QCRYPTO_HASH_ALG_SHA512:
|
||||
case QCRYPTO_HASH_ALG_RIPEMD160:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int qcrypto_pbkdf2(QCryptoHashAlgorithm hash,
|
||||
const uint8_t *key, size_t nkey,
|
||||
const uint8_t *salt, size_t nsalt,
|
||||
uint64_t iterations,
|
||||
uint8_t *out, size_t nout,
|
||||
Error **errp)
|
||||
{
|
||||
static const int hash_map[QCRYPTO_HASH_ALG__MAX] = {
|
||||
[QCRYPTO_HASH_ALG_MD5] = GNUTLS_DIG_MD5,
|
||||
[QCRYPTO_HASH_ALG_SHA1] = GNUTLS_DIG_SHA1,
|
||||
[QCRYPTO_HASH_ALG_SHA224] = GNUTLS_DIG_SHA224,
|
||||
[QCRYPTO_HASH_ALG_SHA256] = GNUTLS_DIG_SHA256,
|
||||
[QCRYPTO_HASH_ALG_SHA384] = GNUTLS_DIG_SHA384,
|
||||
[QCRYPTO_HASH_ALG_SHA512] = GNUTLS_DIG_SHA512,
|
||||
[QCRYPTO_HASH_ALG_RIPEMD160] = GNUTLS_DIG_RMD160,
|
||||
};
|
||||
int ret;
|
||||
const gnutls_datum_t gkey = { (unsigned char *)key, nkey };
|
||||
const gnutls_datum_t gsalt = { (unsigned char *)salt, nsalt };
|
||||
|
||||
if (iterations > ULONG_MAX) {
|
||||
error_setg_errno(errp, ERANGE,
|
||||
"PBKDF iterations %llu must be less than %lu",
|
||||
(long long unsigned)iterations, ULONG_MAX);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (hash >= G_N_ELEMENTS(hash_map) ||
|
||||
hash_map[hash] == GNUTLS_DIG_UNKNOWN) {
|
||||
error_setg_errno(errp, ENOSYS,
|
||||
"PBKDF does not support hash algorithm %s",
|
||||
QCryptoHashAlgorithm_str(hash));
|
||||
return -1;
|
||||
}
|
||||
|
||||
ret = gnutls_pbkdf2(hash_map[hash],
|
||||
&gkey,
|
||||
&gsalt,
|
||||
iterations,
|
||||
out,
|
||||
nout);
|
||||
if (ret != 0) {
|
||||
error_setg(errp, "Cannot derive password: %s",
|
||||
gnutls_strerror(ret));
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -227,7 +227,7 @@ features are included if using "Host passthrough" or "Host model".
|
||||
Preferred CPU models for AMD x86 hosts
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The following CPU models are preferred for use on Intel hosts.
|
||||
The following CPU models are preferred for use on AMD hosts.
|
||||
Administrators / applications are recommended to use the CPU model that
|
||||
matches the generation of the host CPUs in use. In a deployment with a
|
||||
mixture of host CPU models between machines, if live migration
|
||||
|
@ -25,14 +25,9 @@
|
||||
#if defined(DEBUG_ROCKER)
|
||||
# define DPRINTF(fmt, ...) \
|
||||
do { \
|
||||
struct timeval tv; \
|
||||
char timestr[64]; \
|
||||
time_t now; \
|
||||
gettimeofday(&tv, NULL); \
|
||||
now = tv.tv_sec; \
|
||||
strftime(timestr, sizeof(timestr), "%T", localtime(&now)); \
|
||||
fprintf(stderr, "%s.%06ld ", timestr, tv.tv_usec); \
|
||||
fprintf(stderr, "ROCKER: " fmt, ## __VA_ARGS__); \
|
||||
g_autoptr(GDateTime) now = g_date_time_new_now_local(); \
|
||||
g_autofree char *nowstr = g_date_time_format(now, "%T.%f");\
|
||||
fprintf(stderr, "%s ROCKER: " fmt, nowstr, ## __VA_ARGS__);\
|
||||
} while (0)
|
||||
#else
|
||||
static inline GCC_FMT_ATTR(1, 2) int DPRINTF(const char *fmt, ...)
|
||||
|
@ -177,15 +177,9 @@ qio_channel_websock_handshake_send_res(QIOChannelWebsock *ioc,
|
||||
|
||||
static gchar *qio_channel_websock_date_str(void)
|
||||
{
|
||||
struct tm tm;
|
||||
time_t now = time(NULL);
|
||||
char datebuf[128];
|
||||
g_autoptr(GDateTime) now = g_date_time_new_now_utc();
|
||||
|
||||
gmtime_r(&now, &tm);
|
||||
|
||||
strftime(datebuf, sizeof(datebuf), "%a, %d %b %Y %H:%M:%S GMT", &tm);
|
||||
|
||||
return g_strdup(datebuf);
|
||||
return g_date_time_format(now, "%a, %d %b %Y %H:%M:%S GMT");
|
||||
}
|
||||
|
||||
static void qio_channel_websock_handshake_send_res_err(QIOChannelWebsock *ioc,
|
||||
|
102
meson.build
102
meson.build
@ -823,50 +823,77 @@ if 'CONFIG_OPENGL' in config_host
|
||||
endif
|
||||
|
||||
gnutls = not_found
|
||||
gnutls_crypto = not_found
|
||||
if not get_option('gnutls').auto() or have_system
|
||||
gnutls = dependency('gnutls', version: '>=3.5.18',
|
||||
method: 'pkg-config',
|
||||
required: get_option('gnutls'),
|
||||
kwargs: static_kwargs)
|
||||
# For general TLS support our min gnutls matches
|
||||
# that implied by our platform support matrix
|
||||
#
|
||||
# For the crypto backends, we look for a newer
|
||||
# gnutls:
|
||||
#
|
||||
# Version 3.6.8 is needed to get XTS
|
||||
# Version 3.6.13 is needed to get PBKDF
|
||||
# Version 3.6.14 is needed to get HW accelerated XTS
|
||||
#
|
||||
# If newer enough gnutls isn't available, we can
|
||||
# still use a different crypto backend to satisfy
|
||||
# the platform support requirements
|
||||
gnutls_crypto = dependency('gnutls', version: '>=3.6.14',
|
||||
method: 'pkg-config',
|
||||
required: false,
|
||||
kwargs: static_kwargs)
|
||||
if gnutls_crypto.found()
|
||||
gnutls = gnutls_crypto
|
||||
else
|
||||
# Our min version if all we need is TLS
|
||||
gnutls = dependency('gnutls', version: '>=3.5.18',
|
||||
method: 'pkg-config',
|
||||
required: get_option('gnutls'),
|
||||
kwargs: static_kwargs)
|
||||
endif
|
||||
endif
|
||||
|
||||
# Nettle has priority over gcrypt
|
||||
# We prefer use of gnutls for crypto, unless the options
|
||||
# explicitly asked for nettle or gcrypt.
|
||||
#
|
||||
# If gnutls isn't available for crypto, then we'll prefer
|
||||
# gcrypt over nettle for performance reasons.
|
||||
gcrypt = not_found
|
||||
nettle = not_found
|
||||
xts = 'private'
|
||||
xts = 'none'
|
||||
|
||||
if get_option('nettle').enabled() and get_option('gcrypt').enabled()
|
||||
error('Only one of gcrypt & nettle can be enabled')
|
||||
elif (not get_option('nettle').auto() or have_system) and not get_option('gcrypt').enabled()
|
||||
nettle = dependency('nettle', version: '>=3.4',
|
||||
method: 'pkg-config',
|
||||
required: get_option('nettle'),
|
||||
kwargs: static_kwargs)
|
||||
if nettle.found() and cc.has_header('nettle/xts.h', dependencies: nettle)
|
||||
xts = 'nettle'
|
||||
endif
|
||||
endif
|
||||
if (not get_option('gcrypt').auto() or have_system) and not nettle.found()
|
||||
gcrypt = dependency('libgcrypt', version: '>=1.5',
|
||||
method: 'config-tool',
|
||||
required: get_option('gcrypt'),
|
||||
kwargs: static_kwargs)
|
||||
if gcrypt.found() and cc.compiles('''
|
||||
#include <gcrypt.h>
|
||||
int main(void) {
|
||||
gcry_cipher_hd_t handle;
|
||||
gcry_cipher_open(&handle, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_XTS, 0);
|
||||
return 0;
|
||||
}
|
||||
''', dependencies: gcrypt)
|
||||
xts = 'gcrypt'
|
||||
|
||||
# Explicit nettle/gcrypt request, so ignore gnutls for crypto
|
||||
if get_option('nettle').enabled() or get_option('gcrypt').enabled()
|
||||
gnutls_crypto = not_found
|
||||
endif
|
||||
|
||||
if not gnutls_crypto.found()
|
||||
if (not get_option('gcrypt').auto() or have_system) and not get_option('nettle').enabled()
|
||||
gcrypt = dependency('libgcrypt', version: '>=1.8',
|
||||
method: 'config-tool',
|
||||
required: get_option('gcrypt'),
|
||||
kwargs: static_kwargs)
|
||||
# Debian has removed -lgpg-error from libgcrypt-config
|
||||
# as it "spreads unnecessary dependencies" which in
|
||||
# turn breaks static builds...
|
||||
if gcrypt.found() and enable_static
|
||||
gcrypt = declare_dependency(dependencies: [
|
||||
gcrypt,
|
||||
cc.find_library('gpg-error', required: true, kwargs: static_kwargs)])
|
||||
endif
|
||||
endif
|
||||
# Debian has removed -lgpg-error from libgcrypt-config
|
||||
# as it "spreads unnecessary dependencies" which in
|
||||
# turn breaks static builds...
|
||||
if gcrypt.found() and enable_static
|
||||
gcrypt = declare_dependency(dependencies: [
|
||||
gcrypt,
|
||||
cc.find_library('gpg-error', required: true, kwargs: static_kwargs)])
|
||||
if (not get_option('nettle').auto() or have_system) and not gcrypt.found()
|
||||
nettle = dependency('nettle', version: '>=3.4',
|
||||
method: 'pkg-config',
|
||||
required: get_option('nettle'),
|
||||
kwargs: static_kwargs)
|
||||
if nettle.found() and not cc.has_header('nettle/xts.h', dependencies: nettle)
|
||||
xts = 'private'
|
||||
endif
|
||||
endif
|
||||
endif
|
||||
|
||||
@ -1253,6 +1280,7 @@ config_host_data.set('CONFIG_XKBCOMMON', xkbcommon.found())
|
||||
config_host_data.set('CONFIG_KEYUTILS', keyutils.found())
|
||||
config_host_data.set('CONFIG_GETTID', has_gettid)
|
||||
config_host_data.set('CONFIG_GNUTLS', gnutls.found())
|
||||
config_host_data.set('CONFIG_GNUTLS_CRYPTO', gnutls_crypto.found())
|
||||
config_host_data.set('CONFIG_GCRYPT', gcrypt.found())
|
||||
config_host_data.set('CONFIG_NETTLE', nettle.found())
|
||||
config_host_data.set('CONFIG_QEMU_PRIVATE_XTS', xts == 'private')
|
||||
@ -2988,11 +3016,9 @@ summary(summary_info, bool_yn: true, section: 'Block layer support')
|
||||
summary_info = {}
|
||||
summary_info += {'TLS priority': config_host['CONFIG_TLS_PRIORITY']}
|
||||
summary_info += {'GNUTLS support': gnutls.found()}
|
||||
summary_info += {'GNUTLS crypto': gnutls_crypto.found()}
|
||||
# TODO: add back version
|
||||
summary_info += {'libgcrypt': gcrypt.found()}
|
||||
if gcrypt.found()
|
||||
summary_info += {' XTS': xts != 'private'}
|
||||
endif
|
||||
# TODO: add back version
|
||||
summary_info += {'nettle': nettle.found()}
|
||||
if nettle.found()
|
||||
|
@ -66,7 +66,7 @@
|
||||
# @aes-128: AES with 128 bit / 16 byte keys
|
||||
# @aes-192: AES with 192 bit / 24 byte keys
|
||||
# @aes-256: AES with 256 bit / 32 byte keys
|
||||
# @des-rfb: RFB specific variant of single DES. Do not use except in VNC.
|
||||
# @des: DES with 56 bit / 8 byte keys. Do not use except in VNC. (since 6.1)
|
||||
# @3des: 3DES(EDE) with 192 bit / 24 byte keys (since 2.9)
|
||||
# @cast5-128: Cast5 with 128 bit / 16 byte keys
|
||||
# @serpent-128: Serpent with 128 bit / 16 byte keys
|
||||
@ -80,7 +80,7 @@
|
||||
{ 'enum': 'QCryptoCipherAlgorithm',
|
||||
'prefix': 'QCRYPTO_CIPHER_ALG',
|
||||
'data': ['aes-128', 'aes-192', 'aes-256',
|
||||
'des-rfb', '3des',
|
||||
'des', '3des',
|
||||
'cast5-128',
|
||||
'serpent-128', 'serpent-192', 'serpent-256',
|
||||
'twofish-128', 'twofish-192', 'twofish-256']}
|
||||
|
@ -196,25 +196,38 @@ SRST
|
||||
ERST
|
||||
|
||||
DEF("smp", HAS_ARG, QEMU_OPTION_smp,
|
||||
"-smp [cpus=]n[,maxcpus=cpus][,cores=cores][,threads=threads][,dies=dies][,sockets=sockets]\n"
|
||||
"-smp [[cpus=]n][,maxcpus=cpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]\n"
|
||||
" set the number of CPUs to 'n' [default=1]\n"
|
||||
" maxcpus= maximum number of total cpus, including\n"
|
||||
" maxcpus= maximum number of total CPUs, including\n"
|
||||
" offline CPUs for hotplug, etc\n"
|
||||
" cores= number of CPU cores on one socket (for PC, it's on one die)\n"
|
||||
" threads= number of threads on one CPU core\n"
|
||||
" sockets= number of discrete sockets in the system\n"
|
||||
" dies= number of CPU dies on one socket (for PC only)\n"
|
||||
" sockets= number of discrete sockets in the system\n",
|
||||
" cores= number of CPU cores on one socket (for PC, it's on one die)\n"
|
||||
" threads= number of threads on one CPU core\n",
|
||||
QEMU_ARCH_ALL)
|
||||
SRST
|
||||
``-smp [cpus=]n[,cores=cores][,threads=threads][,dies=dies][,sockets=sockets][,maxcpus=maxcpus]``
|
||||
Simulate an SMP system with n CPUs. On the PC target, up to 255 CPUs
|
||||
are supported. On Sparc32 target, Linux limits the number of usable
|
||||
CPUs to 4. For the PC target, the number of cores per die, the
|
||||
number of threads per cores, the number of dies per packages and the
|
||||
total number of sockets can be specified. Missing values will be
|
||||
computed. If any on the three values is given, the total number of
|
||||
CPUs n can be omitted. maxcpus specifies the maximum number of
|
||||
hotpluggable CPUs.
|
||||
``-smp [[cpus=]n][,maxcpus=maxcpus][,sockets=sockets][,dies=dies][,cores=cores][,threads=threads]``
|
||||
Simulate a SMP system with '\ ``n``\ ' CPUs initially present on
|
||||
the machine type board. On boards supporting CPU hotplug, the optional
|
||||
'\ ``maxcpus``\ ' parameter can be set to enable further CPUs to be
|
||||
added at runtime. If omitted the maximum number of CPUs will be
|
||||
set to match the initial CPU count. Both parameters are subject to
|
||||
an upper limit that is determined by the specific machine type chosen.
|
||||
|
||||
To control reporting of CPU topology information, the number of sockets,
|
||||
dies per socket, cores per die, and threads per core can be specified.
|
||||
The sum `` sockets * cores * dies * threads `` must be equal to the
|
||||
maximum CPU count. CPU targets may only support a subset of the topology
|
||||
parameters. Where a CPU target does not support use of a particular
|
||||
topology parameter, its value should be assumed to be 1 for the purpose
|
||||
of computing the CPU maximum count.
|
||||
|
||||
Either the initial CPU count, or at least one of the topology parameters
|
||||
must be specified. Values for any omitted parameters will be computed
|
||||
from those which are given. Historically preference was given to the
|
||||
coarsest topology parameters when computing missing values (ie sockets
|
||||
preferred over cores, which were preferred over threads), however, this
|
||||
behaviour is considered liable to change.
|
||||
ERST
|
||||
|
||||
DEF("numa", HAS_ARG, QEMU_OPTION_numa,
|
||||
|
@ -97,17 +97,11 @@ static const struct QemuSeccompSyscall denylist[] = {
|
||||
{ SCMP_SYS(vfork), QEMU_SECCOMP_SET_SPAWN },
|
||||
{ SCMP_SYS(execve), QEMU_SECCOMP_SET_SPAWN },
|
||||
/* resource control */
|
||||
{ SCMP_SYS(getpriority), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
{ SCMP_SYS(setpriority), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
{ SCMP_SYS(sched_setparam), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
{ SCMP_SYS(sched_getparam), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
{ SCMP_SYS(sched_setscheduler), QEMU_SECCOMP_SET_RESOURCECTL,
|
||||
ARRAY_SIZE(sched_setscheduler_arg), sched_setscheduler_arg },
|
||||
{ SCMP_SYS(sched_getscheduler), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
{ SCMP_SYS(sched_setaffinity), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
{ SCMP_SYS(sched_getaffinity), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
{ SCMP_SYS(sched_get_priority_max), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
{ SCMP_SYS(sched_get_priority_min), QEMU_SECCOMP_SET_RESOURCECTL },
|
||||
};
|
||||
|
||||
static inline __attribute__((unused)) int
|
||||
|
@ -423,7 +423,7 @@ class Engine(object):
|
||||
progress_history = ret[0]
|
||||
qemu_timings = ret[1]
|
||||
vcpu_timings = ret[2]
|
||||
if uri[0:5] == "unix:":
|
||||
if uri[0:5] == "unix:" and os.path.exists(uri[5:]):
|
||||
os.remove(uri[5:])
|
||||
|
||||
if os.path.exists(srcmonaddr):
|
||||
|
@ -150,10 +150,33 @@ static QCryptoCipherTestData test_data[] = {
|
||||
"b2eb05e2c39be9fcda6c19078c6a9d1b",
|
||||
},
|
||||
{
|
||||
.path = "/crypto/cipher/des-rfb-ecb-56",
|
||||
.alg = QCRYPTO_CIPHER_ALG_DES_RFB,
|
||||
/*
|
||||
* Testing 'password' as plaintext fits
|
||||
* in single AES block, and gives identical
|
||||
* ciphertext in ECB and CBC modes
|
||||
*/
|
||||
.path = "/crypto/cipher/des-ecb-56-one-block",
|
||||
.alg = QCRYPTO_CIPHER_ALG_DES,
|
||||
.mode = QCRYPTO_CIPHER_MODE_ECB,
|
||||
.key = "0123456789abcdef",
|
||||
.key = "80c4a2e691d5b3f7",
|
||||
.plaintext = "70617373776f7264",
|
||||
.ciphertext = "73fa80b66134e403",
|
||||
},
|
||||
{
|
||||
/* See previous comment */
|
||||
.path = "/crypto/cipher/des-cbc-56-one-block",
|
||||
.alg = QCRYPTO_CIPHER_ALG_DES,
|
||||
.mode = QCRYPTO_CIPHER_MODE_CBC,
|
||||
.key = "80c4a2e691d5b3f7",
|
||||
.iv = "0000000000000000",
|
||||
.plaintext = "70617373776f7264",
|
||||
.ciphertext = "73fa80b66134e403",
|
||||
},
|
||||
{
|
||||
.path = "/crypto/cipher/des-ecb-56",
|
||||
.alg = QCRYPTO_CIPHER_ALG_DES,
|
||||
.mode = QCRYPTO_CIPHER_MODE_ECB,
|
||||
.key = "80c4a2e691d5b3f7",
|
||||
.plaintext =
|
||||
"6bc1bee22e409f96e93d7e117393172a"
|
||||
"ae2d8a571e03ac9c9eb76fac45af8e51"
|
||||
@ -165,7 +188,6 @@ static QCryptoCipherTestData test_data[] = {
|
||||
"ffd29f1bb5596ad94ea2d8e6196b7f09"
|
||||
"30d8ed0bf2773af36dd82a6280c20926",
|
||||
},
|
||||
#if defined(CONFIG_NETTLE) || defined(CONFIG_GCRYPT)
|
||||
{
|
||||
/* Borrowed from linux-kernel crypto/testmgr.h */
|
||||
.path = "/crypto/cipher/3des-cbc",
|
||||
@ -283,7 +305,6 @@ static QCryptoCipherTestData test_data[] = {
|
||||
"407772c2ea0e3a7846b991b6e73d5142"
|
||||
"fd51b0c62c6313785ceefccfc4700034",
|
||||
},
|
||||
#endif
|
||||
{
|
||||
/* RFC 2144, Appendix B.1 */
|
||||
.path = "/crypto/cipher/cast5-128",
|
||||
|
@ -104,7 +104,7 @@ static void test_hash_alloc(void)
|
||||
strlen(INPUT_TEXT),
|
||||
&result,
|
||||
&resultlen,
|
||||
NULL);
|
||||
&error_fatal);
|
||||
g_assert(ret == 0);
|
||||
g_assert(resultlen == expected_lens[i]);
|
||||
|
||||
@ -139,7 +139,7 @@ static void test_hash_prealloc(void)
|
||||
strlen(INPUT_TEXT),
|
||||
&result,
|
||||
&resultlen,
|
||||
NULL);
|
||||
&error_fatal);
|
||||
g_assert(ret == 0);
|
||||
|
||||
g_assert(resultlen == expected_lens[i]);
|
||||
@ -176,7 +176,7 @@ static void test_hash_iov(void)
|
||||
iov, 3,
|
||||
&result,
|
||||
&resultlen,
|
||||
NULL);
|
||||
&error_fatal);
|
||||
g_assert(ret == 0);
|
||||
g_assert(resultlen == expected_lens[i]);
|
||||
for (j = 0; j < resultlen; j++) {
|
||||
@ -210,7 +210,7 @@ static void test_hash_digest(void)
|
||||
INPUT_TEXT,
|
||||
strlen(INPUT_TEXT),
|
||||
&digest,
|
||||
NULL);
|
||||
&error_fatal);
|
||||
g_assert(ret == 0);
|
||||
g_assert_cmpstr(digest, ==, expected_outputs[i]);
|
||||
g_free(digest);
|
||||
@ -234,7 +234,7 @@ static void test_hash_base64(void)
|
||||
INPUT_TEXT,
|
||||
strlen(INPUT_TEXT),
|
||||
&digest,
|
||||
NULL);
|
||||
&error_fatal);
|
||||
g_assert(ret == 0);
|
||||
g_assert_cmpstr(digest, ==, expected_outputs_b64[i]);
|
||||
g_free(digest);
|
||||
@ -243,7 +243,8 @@ static void test_hash_base64(void)
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
g_assert(qcrypto_init(NULL) == 0);
|
||||
int ret = qcrypto_init(&error_fatal);
|
||||
g_assert(ret == 0);
|
||||
|
||||
g_test_init(&argc, &argv, NULL);
|
||||
g_test_add_func("/crypto/hash/iov", test_hash_iov);
|
||||
|
@ -89,7 +89,6 @@ static void test_hmac_alloc(void)
|
||||
QCryptoHmac *hmac = NULL;
|
||||
uint8_t *result = NULL;
|
||||
size_t resultlen = 0;
|
||||
Error *err = NULL;
|
||||
const char *exp_output = NULL;
|
||||
int ret;
|
||||
size_t j;
|
||||
@ -101,14 +100,12 @@ static void test_hmac_alloc(void)
|
||||
exp_output = data->hex_digest;
|
||||
|
||||
hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY,
|
||||
strlen(KEY), &err);
|
||||
g_assert(err == NULL);
|
||||
strlen(KEY), &error_fatal);
|
||||
g_assert(hmac != NULL);
|
||||
|
||||
ret = qcrypto_hmac_bytes(hmac, (const char *)INPUT_TEXT,
|
||||
strlen(INPUT_TEXT), &result,
|
||||
&resultlen, &err);
|
||||
g_assert(err == NULL);
|
||||
&resultlen, &error_fatal);
|
||||
g_assert(ret == 0);
|
||||
|
||||
for (j = 0; j < resultlen; j++) {
|
||||
@ -131,7 +128,6 @@ static void test_hmac_prealloc(void)
|
||||
QCryptoHmac *hmac = NULL;
|
||||
uint8_t *result = NULL;
|
||||
size_t resultlen = 0;
|
||||
Error *err = NULL;
|
||||
const char *exp_output = NULL;
|
||||
int ret;
|
||||
size_t j;
|
||||
@ -146,14 +142,12 @@ static void test_hmac_prealloc(void)
|
||||
result = g_new0(uint8_t, resultlen);
|
||||
|
||||
hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY,
|
||||
strlen(KEY), &err);
|
||||
g_assert(err == NULL);
|
||||
strlen(KEY), &error_fatal);
|
||||
g_assert(hmac != NULL);
|
||||
|
||||
ret = qcrypto_hmac_bytes(hmac, (const char *)INPUT_TEXT,
|
||||
strlen(INPUT_TEXT), &result,
|
||||
&resultlen, &err);
|
||||
g_assert(err == NULL);
|
||||
&resultlen, &error_fatal);
|
||||
g_assert(ret == 0);
|
||||
|
||||
exp_output = data->hex_digest;
|
||||
@ -177,7 +171,6 @@ static void test_hmac_iov(void)
|
||||
QCryptoHmac *hmac = NULL;
|
||||
uint8_t *result = NULL;
|
||||
size_t resultlen = 0;
|
||||
Error *err = NULL;
|
||||
const char *exp_output = NULL;
|
||||
int ret;
|
||||
size_t j;
|
||||
@ -194,13 +187,11 @@ static void test_hmac_iov(void)
|
||||
exp_output = data->hex_digest;
|
||||
|
||||
hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY,
|
||||
strlen(KEY), &err);
|
||||
g_assert(err == NULL);
|
||||
strlen(KEY), &error_fatal);
|
||||
g_assert(hmac != NULL);
|
||||
|
||||
ret = qcrypto_hmac_bytesv(hmac, iov, 3, &result,
|
||||
&resultlen, &err);
|
||||
g_assert(err == NULL);
|
||||
&resultlen, &error_fatal);
|
||||
g_assert(ret == 0);
|
||||
|
||||
for (j = 0; j < resultlen; j++) {
|
||||
@ -222,7 +213,6 @@ static void test_hmac_digest(void)
|
||||
QCryptoHmacTestData *data = &test_data[i];
|
||||
QCryptoHmac *hmac = NULL;
|
||||
uint8_t *result = NULL;
|
||||
Error *err = NULL;
|
||||
const char *exp_output = NULL;
|
||||
int ret;
|
||||
|
||||
@ -233,14 +223,12 @@ static void test_hmac_digest(void)
|
||||
exp_output = data->hex_digest;
|
||||
|
||||
hmac = qcrypto_hmac_new(data->alg, (const uint8_t *)KEY,
|
||||
strlen(KEY), &err);
|
||||
g_assert(err == NULL);
|
||||
strlen(KEY), &error_fatal);
|
||||
g_assert(hmac != NULL);
|
||||
|
||||
ret = qcrypto_hmac_digest(hmac, (const char *)INPUT_TEXT,
|
||||
strlen(INPUT_TEXT), (char **)&result,
|
||||
&err);
|
||||
g_assert(err == NULL);
|
||||
&error_fatal);
|
||||
g_assert(ret == 0);
|
||||
|
||||
g_assert_cmpstr((const char *)result, ==, exp_output);
|
||||
|
@ -136,8 +136,15 @@ struct QCryptoIVGenTestData {
|
||||
static void test_ivgen(const void *opaque)
|
||||
{
|
||||
const struct QCryptoIVGenTestData *data = opaque;
|
||||
uint8_t *iv = g_new0(uint8_t, data->niv);
|
||||
QCryptoIVGen *ivgen = qcrypto_ivgen_new(
|
||||
g_autofree uint8_t *iv = g_new0(uint8_t, data->niv);
|
||||
g_autoptr(QCryptoIVGen) ivgen = NULL;
|
||||
|
||||
if (!qcrypto_cipher_supports(data->cipheralg,
|
||||
QCRYPTO_CIPHER_MODE_ECB)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ivgen = qcrypto_ivgen_new(
|
||||
data->ivalg,
|
||||
data->cipheralg,
|
||||
data->hashalg,
|
||||
@ -152,9 +159,6 @@ static void test_ivgen(const void *opaque)
|
||||
&error_abort);
|
||||
|
||||
g_assert(memcmp(iv, data->iv, data->niv) == 0);
|
||||
|
||||
qcrypto_ivgen_free(ivgen);
|
||||
g_free(iv);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
|
@ -229,10 +229,8 @@ static QCryptoPbkdfTestData test_data[] = {
|
||||
},
|
||||
|
||||
/* non-RFC misc test data */
|
||||
#ifdef CONFIG_NETTLE
|
||||
{
|
||||
/* empty password test.
|
||||
* Broken with libgcrypt <= 1.5.0, hence CONFIG_NETTLE */
|
||||
/* empty password test. */
|
||||
.path = "/crypto/pbkdf/nonrfc/sha1/iter2",
|
||||
.hash = QCRYPTO_HASH_ALG_SHA1,
|
||||
.iterations = 2,
|
||||
@ -244,7 +242,6 @@ static QCryptoPbkdfTestData test_data[] = {
|
||||
"\xbf\x03\xe1\x1c\x71\xca\x79\x4e\x07\x97",
|
||||
.nout = 20
|
||||
},
|
||||
#endif
|
||||
{
|
||||
/* Password exceeds block size test */
|
||||
.path = "/crypto/pbkdf/nonrfc/sha256/iter1200",
|
||||
|
20
ui/vnc.c
20
ui/vnc.c
@ -2733,6 +2733,19 @@ static void authentication_failed(VncState *vs)
|
||||
vnc_client_error(vs);
|
||||
}
|
||||
|
||||
static void
|
||||
vnc_munge_des_rfb_key(unsigned char *key, size_t nkey)
|
||||
{
|
||||
size_t i;
|
||||
for (i = 0; i < nkey; i++) {
|
||||
uint8_t r = key[i];
|
||||
r = (r & 0xf0) >> 4 | (r & 0x0f) << 4;
|
||||
r = (r & 0xcc) >> 2 | (r & 0x33) << 2;
|
||||
r = (r & 0xaa) >> 1 | (r & 0x55) << 1;
|
||||
key[i] = r;
|
||||
}
|
||||
}
|
||||
|
||||
static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
|
||||
{
|
||||
unsigned char response[VNC_AUTH_CHALLENGE_SIZE];
|
||||
@ -2757,9 +2770,10 @@ static int protocol_client_auth_vnc(VncState *vs, uint8_t *data, size_t len)
|
||||
pwlen = strlen(vs->vd->password);
|
||||
for (i=0; i<sizeof(key); i++)
|
||||
key[i] = i<pwlen ? vs->vd->password[i] : 0;
|
||||
vnc_munge_des_rfb_key(key, sizeof(key));
|
||||
|
||||
cipher = qcrypto_cipher_new(
|
||||
QCRYPTO_CIPHER_ALG_DES_RFB,
|
||||
QCRYPTO_CIPHER_ALG_DES,
|
||||
QCRYPTO_CIPHER_MODE_ECB,
|
||||
key, G_N_ELEMENTS(key),
|
||||
&err);
|
||||
@ -4045,9 +4059,9 @@ void vnc_display_open(const char *id, Error **errp)
|
||||
goto fail;
|
||||
}
|
||||
if (!qcrypto_cipher_supports(
|
||||
QCRYPTO_CIPHER_ALG_DES_RFB, QCRYPTO_CIPHER_MODE_ECB)) {
|
||||
QCRYPTO_CIPHER_ALG_DES, QCRYPTO_CIPHER_MODE_ECB)) {
|
||||
error_setg(errp,
|
||||
"Cipher backend does not support DES RFB algorithm");
|
||||
"Cipher backend does not support DES algorithm");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user