提交 879c26fb 编写于 作者: P Peter Maydell

Merge remote-tracking branch 'remotes/berrange/tags/pull-qcrypto-2016-03-17-3' into staging

Merge QCrypto 2016/03/17 v3

# gpg: Signature made Thu 17 Mar 2016 16:51:32 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-2016-03-17-3:
  crypto: implement the LUKS block encryption format
  crypto: add block encryption framework
  crypto: wire up XTS mode for cipher APIs
  crypto: refactor code for dealing with AES cipher
  crypto: import an implementation of the XTS cipher mode
  crypto: add support for the twofish cipher algorithm
  crypto: add support for the serpent cipher algorithm
  crypto: add support for the cast5-128 cipher algorithm
  crypto: skip testing of unsupported cipher algorithms
  crypto: add support for anti-forensic split algorithm
  crypto: add support for generating initialization vectors
  crypto: add support for PBKDF2 algorithm
  crypto: add cryptographic random byte source
Signed-off-by: NPeter Maydell <peter.maydell@linaro.org>
#######################################################################
# Common libraries for tools and emulators
stub-obj-y = stubs/
stub-obj-y = stubs/ crypto/
util-obj-y = util/ qobject/ qapi/
util-obj-y += qmp-introspect.o qapi-types.o qapi-visit.o qapi-event.o
......
......@@ -306,8 +306,10 @@ gtkabi=""
gtk_gl="no"
gnutls=""
gnutls_hash=""
gnutls_rnd=""
nettle=""
gcrypt=""
gcrypt_kdf="no"
vte=""
virglrenderer=""
tpm="yes"
......@@ -2201,6 +2203,13 @@ if test "$gnutls" != "no"; then
gnutls_hash="no"
fi
# gnutls_rnd requires >= 2.11.0
if $pkg_config --exists "gnutls >= 2.11.0"; then
gnutls_rnd="yes"
else
gnutls_rnd="no"
fi
if $pkg_config --exists 'gnutls >= 3.0'; then
gnutls_gcrypt=no
gnutls_nettle=yes
......@@ -2228,9 +2237,11 @@ if test "$gnutls" != "no"; then
else
gnutls="no"
gnutls_hash="no"
gnutls_rnd="no"
fi
else
gnutls_hash="no"
gnutls_rnd="no"
fi
......@@ -2292,6 +2303,19 @@ if test "$gcrypt" != "no"; then
if test -z "$nettle"; then
nettle="no"
fi
cat > $TMPC << EOF
#include <gcrypt.h>
int main(void) {
gcry_kdf_derive(NULL, 0, GCRY_KDF_PBKDF2,
GCRY_MD_SHA256,
NULL, 0, 0, 0, NULL);
return 0;
}
EOF
if compile_prog "$gcrypt_cflags" "$gcrypt_libs" ; then
gcrypt_kdf=yes
fi
else
if test "$gcrypt" = "yes"; then
feature_not_found "gcrypt" "Install gcrypt devel"
......@@ -4714,7 +4738,9 @@ echo "GTK support $gtk"
echo "GTK GL support $gtk_gl"
echo "GNUTLS support $gnutls"
echo "GNUTLS hash $gnutls_hash"
echo "GNUTLS rnd $gnutls_rnd"
echo "libgcrypt $gcrypt"
echo "libgcrypt kdf $gcrypt_kdf"
if test "$nettle" = "yes"; then
echo "nettle $nettle ($nettle_version)"
else
......@@ -5092,8 +5118,14 @@ fi
if test "$gnutls_hash" = "yes" ; then
echo "CONFIG_GNUTLS_HASH=y" >> $config_host_mak
fi
if test "$gnutls_rnd" = "yes" ; then
echo "CONFIG_GNUTLS_RND=y" >> $config_host_mak
fi
if test "$gcrypt" = "yes" ; then
echo "CONFIG_GCRYPT=y" >> $config_host_mak
if test "$gcrypt_kdf" = "yes" ; then
echo "CONFIG_GCRYPT_KDF=y" >> $config_host_mak
fi
fi
if test "$nettle" = "yes" ; then
echo "CONFIG_NETTLE=y" >> $config_host_mak
......
......@@ -8,6 +8,23 @@ crypto-obj-y += tlscredsanon.o
crypto-obj-y += tlscredsx509.o
crypto-obj-y += tlssession.o
crypto-obj-y += secret.o
crypto-obj-$(CONFIG_GCRYPT) += random-gcrypt.o
crypto-obj-$(if $(CONFIG_GCRYPT),n,$(CONFIG_GNUTLS_RND)) += random-gnutls.o
crypto-obj-y += pbkdf.o
crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o
crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT_KDF)) += pbkdf-gcrypt.o
crypto-obj-y += ivgen.o
crypto-obj-y += ivgen-essiv.o
crypto-obj-y += ivgen-plain.o
crypto-obj-y += ivgen-plain64.o
crypto-obj-y += afsplit.o
crypto-obj-y += xts.o
crypto-obj-y += block.o
crypto-obj-y += block-qcow.o
crypto-obj-y += block-luks.o
# Let the userspace emulators avoid linking gnutls/etc
crypto-aes-obj-y = aes.o
stub-obj-y += random-stub.o
stub-obj-y += pbkdf-stub.o
/*
* QEMU Crypto anti forensic information splitter
*
* Copyright (c) 2015-2016 Red Hat, Inc.
*
* Derived from cryptsetup package lib/luks1/af.c
*
* Copyright (C) 2004, Clemens Fruhwirth <clemens@endorphin.org>
* Copyright (C) 2009-2012, Red Hat, Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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 "qemu/osdep.h"
#include "crypto/afsplit.h"
#include "crypto/random.h"
static void qcrypto_afsplit_xor(size_t blocklen,
const uint8_t *in1,
const uint8_t *in2,
uint8_t *out)
{
size_t i;
for (i = 0; i < blocklen; i++) {
out[i] = in1[i] ^ in2[i];
}
}
static int qcrypto_afsplit_hash(QCryptoHashAlgorithm hash,
size_t blocklen,
uint8_t *block,
Error **errp)
{
size_t digestlen = qcrypto_hash_digest_len(hash);
size_t hashcount = blocklen / digestlen;
size_t finallen = blocklen % digestlen;
uint32_t i;
if (finallen) {
hashcount++;
} else {
finallen = digestlen;
}
for (i = 0; i < hashcount; i++) {
uint8_t *out = NULL;
size_t outlen = 0;
uint32_t iv = cpu_to_be32(i);
struct iovec in[] = {
{ .iov_base = &iv,
.iov_len = sizeof(iv) },
{ .iov_base = block + (i * digestlen),
.iov_len = (i == (hashcount - 1)) ? finallen : digestlen },
};
if (qcrypto_hash_bytesv(hash,
in,
G_N_ELEMENTS(in),
&out, &outlen,
errp) < 0) {
return -1;
}
assert(outlen == digestlen);
memcpy(block + (i * digestlen), out,
(i == (hashcount - 1)) ? finallen : digestlen);
g_free(out);
}
return 0;
}
int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash,
size_t blocklen,
uint32_t stripes,
const uint8_t *in,
uint8_t *out,
Error **errp)
{
uint8_t *block = g_new0(uint8_t, blocklen);
size_t i;
int ret = -1;
for (i = 0; i < (stripes - 1); i++) {
if (qcrypto_random_bytes(out + (i * blocklen), blocklen, errp) < 0) {
goto cleanup;
}
qcrypto_afsplit_xor(blocklen,
out + (i * blocklen),
block,
block);
if (qcrypto_afsplit_hash(hash, blocklen, block,
errp) < 0) {
goto cleanup;
}
}
qcrypto_afsplit_xor(blocklen,
in,
block,
out + (i * blocklen));
ret = 0;
cleanup:
g_free(block);
return ret;
}
int qcrypto_afsplit_decode(QCryptoHashAlgorithm hash,
size_t blocklen,
uint32_t stripes,
const uint8_t *in,
uint8_t *out,
Error **errp)
{
uint8_t *block = g_new0(uint8_t, blocklen);
size_t i;
int ret = -1;
for (i = 0; i < (stripes - 1); i++) {
qcrypto_afsplit_xor(blocklen,
in + (i * blocklen),
block,
block);
if (qcrypto_afsplit_hash(hash, blocklen, block,
errp) < 0) {
goto cleanup;
}
}
qcrypto_afsplit_xor(blocklen,
in + (i * blocklen),
block,
out);
ret = 0;
cleanup:
g_free(block);
return ret;
}
此差异已折叠。
/*
* QEMU Crypto block device encryption LUKS format
*
* Copyright (c) 2015-2016 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_BLOCK_LUKS_H__
#define QCRYPTO_BLOCK_LUKS_H__
#include "crypto/blockpriv.h"
extern const QCryptoBlockDriver qcrypto_block_driver_luks;
#endif /* QCRYPTO_BLOCK_LUKS_H__ */
/*
* QEMU Crypto block device encryption QCow/QCow2 AES-CBC format
*
* Copyright (c) 2015-2016 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/>.
*
*/
/*
* Note that the block encryption implemented in this file is broken
* by design. This exists only to allow data to be liberated from
* existing qcow[2] images and should not be used in any new areas.
*/
#include "qemu/osdep.h"
#include "crypto/block-qcow.h"
#include "crypto/secret.h"
#define QCRYPTO_BLOCK_QCOW_SECTOR_SIZE 512
static bool
qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED,
size_t buf_size G_GNUC_UNUSED)
{
return false;
}
static int
qcrypto_block_qcow_init(QCryptoBlock *block,
const char *keysecret,
Error **errp)
{
char *password;
int ret;
uint8_t keybuf[16];
int len;
memset(keybuf, 0, 16);
password = qcrypto_secret_lookup_as_utf8(keysecret, errp);
if (!password) {
return -1;
}
len = strlen(password);
memcpy(keybuf, password, MIN(len, sizeof(keybuf)));
g_free(password);
block->niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128,
QCRYPTO_CIPHER_MODE_CBC);
block->ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_PLAIN64,
0, 0, NULL, 0, errp);
if (!block->ivgen) {
ret = -ENOTSUP;
goto fail;
}
block->cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128,
QCRYPTO_CIPHER_MODE_CBC,
keybuf, G_N_ELEMENTS(keybuf),
errp);
if (!block->cipher) {
ret = -ENOTSUP;
goto fail;
}
block->payload_offset = 0;
return 0;
fail:
qcrypto_cipher_free(block->cipher);
qcrypto_ivgen_free(block->ivgen);
return ret;
}
static int
qcrypto_block_qcow_open(QCryptoBlock *block,
QCryptoBlockOpenOptions *options,
QCryptoBlockReadFunc readfunc G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED,
unsigned int flags,
Error **errp)
{
if (flags & QCRYPTO_BLOCK_OPEN_NO_IO) {
return 0;
} else {
if (!options->u.qcow.key_secret) {
error_setg(errp,
"Parameter 'key-secret' is required for cipher");
return -1;
}
return qcrypto_block_qcow_init(block,
options->u.qcow.key_secret, errp);
}
}
static int
qcrypto_block_qcow_create(QCryptoBlock *block,
QCryptoBlockCreateOptions *options,
QCryptoBlockInitFunc initfunc G_GNUC_UNUSED,
QCryptoBlockWriteFunc writefunc G_GNUC_UNUSED,
void *opaque G_GNUC_UNUSED,
Error **errp)
{
if (!options->u.qcow.key_secret) {
error_setg(errp, "Parameter 'key-secret' is required for cipher");
return -1;
}
/* QCow2 has no special header, since everything is hardwired */
return qcrypto_block_qcow_init(block, options->u.qcow.key_secret, errp);
}
static void
qcrypto_block_qcow_cleanup(QCryptoBlock *block)
{
}
static int
qcrypto_block_qcow_decrypt(QCryptoBlock *block,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp)
{
return qcrypto_block_decrypt_helper(block->cipher,
block->niv, block->ivgen,
QCRYPTO_BLOCK_QCOW_SECTOR_SIZE,
startsector, buf, len, errp);
}
static int
qcrypto_block_qcow_encrypt(QCryptoBlock *block,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp)
{
return qcrypto_block_encrypt_helper(block->cipher,
block->niv, block->ivgen,
QCRYPTO_BLOCK_QCOW_SECTOR_SIZE,
startsector, buf, len, errp);
}
const QCryptoBlockDriver qcrypto_block_driver_qcow = {
.open = qcrypto_block_qcow_open,
.create = qcrypto_block_qcow_create,
.cleanup = qcrypto_block_qcow_cleanup,
.decrypt = qcrypto_block_qcow_decrypt,
.encrypt = qcrypto_block_qcow_encrypt,
.has_format = qcrypto_block_qcow_has_format,
};
/*
* QEMU Crypto block device encryption QCow/QCow2 AES-CBC format
*
* Copyright (c) 2015-2016 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_BLOCK_QCOW_H__
#define QCRYPTO_BLOCK_QCOW_H__
#include "crypto/blockpriv.h"
extern const QCryptoBlockDriver qcrypto_block_driver_qcow;
#endif /* QCRYPTO_BLOCK_QCOW_H__ */
/*
* QEMU Crypto block device encryption
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/blockpriv.h"
#include "crypto/block-qcow.h"
#include "crypto/block-luks.h"
static const QCryptoBlockDriver *qcrypto_block_drivers[] = {
[Q_CRYPTO_BLOCK_FORMAT_QCOW] = &qcrypto_block_driver_qcow,
[Q_CRYPTO_BLOCK_FORMAT_LUKS] = &qcrypto_block_driver_luks,
};
bool qcrypto_block_has_format(QCryptoBlockFormat format,
const uint8_t *buf,
size_t len)
{
const QCryptoBlockDriver *driver;
if (format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
!qcrypto_block_drivers[format]) {
return false;
}
driver = qcrypto_block_drivers[format];
return driver->has_format(buf, len);
}
QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options,
QCryptoBlockReadFunc readfunc,
void *opaque,
unsigned int flags,
Error **errp)
{
QCryptoBlock *block = g_new0(QCryptoBlock, 1);
block->format = options->format;
if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
!qcrypto_block_drivers[options->format]) {
error_setg(errp, "Unsupported block driver %d", options->format);
g_free(block);
return NULL;
}
block->driver = qcrypto_block_drivers[options->format];
if (block->driver->open(block, options,
readfunc, opaque, flags, errp) < 0) {
g_free(block);
return NULL;
}
return block;
}
QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
QCryptoBlockInitFunc initfunc,
QCryptoBlockWriteFunc writefunc,
void *opaque,
Error **errp)
{
QCryptoBlock *block = g_new0(QCryptoBlock, 1);
block->format = options->format;
if (options->format >= G_N_ELEMENTS(qcrypto_block_drivers) ||
!qcrypto_block_drivers[options->format]) {
error_setg(errp, "Unsupported block driver %d", options->format);
g_free(block);
return NULL;
}
block->driver = qcrypto_block_drivers[options->format];
if (block->driver->create(block, options, initfunc,
writefunc, opaque, errp) < 0) {
g_free(block);
return NULL;
}
return block;
}
int qcrypto_block_decrypt(QCryptoBlock *block,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp)
{
return block->driver->decrypt(block, startsector, buf, len, errp);
}
int qcrypto_block_encrypt(QCryptoBlock *block,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp)
{
return block->driver->encrypt(block, startsector, buf, len, errp);
}
QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block)
{
return block->cipher;
}
QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block)
{
return block->ivgen;
}
QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block)
{
return block->kdfhash;
}
uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block)
{
return block->payload_offset;
}
void qcrypto_block_free(QCryptoBlock *block)
{
if (!block) {
return;
}
block->driver->cleanup(block);
qcrypto_cipher_free(block->cipher);
qcrypto_ivgen_free(block->ivgen);
g_free(block);
}
int qcrypto_block_decrypt_helper(QCryptoCipher *cipher,
size_t niv,
QCryptoIVGen *ivgen,
int sectorsize,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp)
{
uint8_t *iv;
int ret = -1;
iv = niv ? g_new0(uint8_t, niv) : NULL;
while (len > 0) {
size_t nbytes;
if (niv) {
if (qcrypto_ivgen_calculate(ivgen,
startsector,
iv, niv,
errp) < 0) {
goto cleanup;
}
if (qcrypto_cipher_setiv(cipher,
iv, niv,
errp) < 0) {
goto cleanup;
}
}
nbytes = len > sectorsize ? sectorsize : len;
if (qcrypto_cipher_decrypt(cipher, buf, buf,
nbytes, errp) < 0) {
goto cleanup;
}
startsector++;
buf += nbytes;
len -= nbytes;
}
ret = 0;
cleanup:
g_free(iv);
return ret;
}
int qcrypto_block_encrypt_helper(QCryptoCipher *cipher,
size_t niv,
QCryptoIVGen *ivgen,
int sectorsize,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp)
{
uint8_t *iv;
int ret = -1;
iv = niv ? g_new0(uint8_t, niv) : NULL;
while (len > 0) {
size_t nbytes;
if (niv) {
if (qcrypto_ivgen_calculate(ivgen,
startsector,
iv, niv,
errp) < 0) {
goto cleanup;
}
if (qcrypto_cipher_setiv(cipher,
iv, niv,
errp) < 0) {
goto cleanup;
}
}
nbytes = len > sectorsize ? sectorsize : len;
if (qcrypto_cipher_encrypt(cipher, buf, buf,
nbytes, errp) < 0) {
goto cleanup;
}
startsector++;
buf += nbytes;
len -= nbytes;
}
ret = 0;
cleanup:
g_free(iv);
return ret;
}
/*
* QEMU Crypto block device encryption
*
* Copyright (c) 2015-2016 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_BLOCK_PRIV_H__
#define QCRYPTO_BLOCK_PRIV_H__
#include "crypto/block.h"
typedef struct QCryptoBlockDriver QCryptoBlockDriver;
struct QCryptoBlock {
QCryptoBlockFormat format;
const QCryptoBlockDriver *driver;
void *opaque;
QCryptoCipher *cipher;
QCryptoIVGen *ivgen;
QCryptoHashAlgorithm kdfhash;
size_t niv;
uint64_t payload_offset; /* In bytes */
};
struct QCryptoBlockDriver {
int (*open)(QCryptoBlock *block,
QCryptoBlockOpenOptions *options,
QCryptoBlockReadFunc readfunc,
void *opaque,
unsigned int flags,
Error **errp);
int (*create)(QCryptoBlock *block,
QCryptoBlockCreateOptions *options,
QCryptoBlockInitFunc initfunc,
QCryptoBlockWriteFunc writefunc,
void *opaque,
Error **errp);
void (*cleanup)(QCryptoBlock *block);
int (*encrypt)(QCryptoBlock *block,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp);
int (*decrypt)(QCryptoBlock *block,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp);
bool (*has_format)(const uint8_t *buf,
size_t buflen);
};
int qcrypto_block_decrypt_helper(QCryptoCipher *cipher,
size_t niv,
QCryptoIVGen *ivgen,
int sectorsize,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp);
int qcrypto_block_encrypt_helper(QCryptoCipher *cipher,
size_t niv,
QCryptoIVGen *ivgen,
int sectorsize,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp);
#endif /* QCRYPTO_BLOCK_PRIV_H__ */
......@@ -21,11 +21,17 @@
#include "qemu/osdep.h"
#include "crypto/aes.h"
#include "crypto/desrfb.h"
#include "crypto/xts.h"
typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext;
struct QCryptoCipherBuiltinAESContext {
AES_KEY enc;
AES_KEY dec;
};
typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES;
struct QCryptoCipherBuiltinAES {
AES_KEY encrypt_key;
AES_KEY decrypt_key;
QCryptoCipherBuiltinAESContext key;
QCryptoCipherBuiltinAESContext key_tweak;
uint8_t iv[AES_BLOCK_SIZE];
};
typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB;
......@@ -67,6 +73,82 @@ static void qcrypto_cipher_free_aes(QCryptoCipher *cipher)
}
static void qcrypto_cipher_aes_ecb_encrypt(AES_KEY *key,
const void *in,
void *out,
size_t len)
{
const uint8_t *inptr = in;
uint8_t *outptr = out;
while (len) {
if (len > AES_BLOCK_SIZE) {
AES_encrypt(inptr, outptr, key);
inptr += AES_BLOCK_SIZE;
outptr += AES_BLOCK_SIZE;
len -= AES_BLOCK_SIZE;
} else {
uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
memcpy(tmp1, inptr, len);
/* Fill with 0 to avoid valgrind uninitialized reads */
memset(tmp1 + len, 0, sizeof(tmp1) - len);
AES_encrypt(tmp1, tmp2, key);
memcpy(outptr, tmp2, len);
len = 0;
}
}
}
static void qcrypto_cipher_aes_ecb_decrypt(AES_KEY *key,
const void *in,
void *out,
size_t len)
{
const uint8_t *inptr = in;
uint8_t *outptr = out;
while (len) {
if (len > AES_BLOCK_SIZE) {
AES_decrypt(inptr, outptr, key);
inptr += AES_BLOCK_SIZE;
outptr += AES_BLOCK_SIZE;
len -= AES_BLOCK_SIZE;
} else {
uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
memcpy(tmp1, inptr, len);
/* Fill with 0 to avoid valgrind uninitialized reads */
memset(tmp1 + len, 0, sizeof(tmp1) - len);
AES_decrypt(tmp1, tmp2, key);
memcpy(outptr, tmp2, len);
len = 0;
}
}
}
static void qcrypto_cipher_aes_xts_encrypt(const void *ctx,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
const QCryptoCipherBuiltinAESContext *aesctx = ctx;
qcrypto_cipher_aes_ecb_encrypt((AES_KEY *)&aesctx->enc,
src, dst, length);
}
static void qcrypto_cipher_aes_xts_decrypt(const void *ctx,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
const QCryptoCipherBuiltinAESContext *aesctx = ctx;
qcrypto_cipher_aes_ecb_decrypt((AES_KEY *)&aesctx->dec,
src, dst, length);
}
static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher,
const void *in,
void *out,
......@@ -75,29 +157,26 @@ static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher,
{
QCryptoCipherBuiltin *ctxt = cipher->opaque;
if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) {
const uint8_t *inptr = in;
uint8_t *outptr = out;
while (len) {
if (len > AES_BLOCK_SIZE) {
AES_encrypt(inptr, outptr, &ctxt->state.aes.encrypt_key);
inptr += AES_BLOCK_SIZE;
outptr += AES_BLOCK_SIZE;
len -= AES_BLOCK_SIZE;
} else {
uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
memcpy(tmp1, inptr, len);
/* Fill with 0 to avoid valgrind uninitialized reads */
memset(tmp1 + len, 0, sizeof(tmp1) - len);
AES_encrypt(tmp1, tmp2, &ctxt->state.aes.encrypt_key);
memcpy(outptr, tmp2, len);
len = 0;
}
}
} else {
switch (cipher->mode) {
case QCRYPTO_CIPHER_MODE_ECB:
qcrypto_cipher_aes_ecb_encrypt(&ctxt->state.aes.key.enc,
in, out, len);
break;
case QCRYPTO_CIPHER_MODE_CBC:
AES_cbc_encrypt(in, out, len,
&ctxt->state.aes.encrypt_key,
&ctxt->state.aes.key.enc,
ctxt->state.aes.iv, 1);
break;
case QCRYPTO_CIPHER_MODE_XTS:
xts_encrypt(&ctxt->state.aes.key,
&ctxt->state.aes.key_tweak,
qcrypto_cipher_aes_xts_encrypt,
qcrypto_cipher_aes_xts_decrypt,
ctxt->state.aes.iv,
len, out, in);
break;
default:
g_assert_not_reached();
}
return 0;
......@@ -112,29 +191,26 @@ static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher,
{
QCryptoCipherBuiltin *ctxt = cipher->opaque;
if (cipher->mode == QCRYPTO_CIPHER_MODE_ECB) {
const uint8_t *inptr = in;
uint8_t *outptr = out;
while (len) {
if (len > AES_BLOCK_SIZE) {
AES_decrypt(inptr, outptr, &ctxt->state.aes.decrypt_key);
inptr += AES_BLOCK_SIZE;
outptr += AES_BLOCK_SIZE;
len -= AES_BLOCK_SIZE;
} else {
uint8_t tmp1[AES_BLOCK_SIZE], tmp2[AES_BLOCK_SIZE];
memcpy(tmp1, inptr, len);
/* Fill with 0 to avoid valgrind uninitialized reads */
memset(tmp1 + len, 0, sizeof(tmp1) - len);
AES_decrypt(tmp1, tmp2, &ctxt->state.aes.decrypt_key);
memcpy(outptr, tmp2, len);
len = 0;
}
}
} else {
switch (cipher->mode) {
case QCRYPTO_CIPHER_MODE_ECB:
qcrypto_cipher_aes_ecb_decrypt(&ctxt->state.aes.key.dec,
in, out, len);
break;
case QCRYPTO_CIPHER_MODE_CBC:
AES_cbc_encrypt(in, out, len,
&ctxt->state.aes.decrypt_key,
&ctxt->state.aes.key.dec,
ctxt->state.aes.iv, 0);
break;
case QCRYPTO_CIPHER_MODE_XTS:
xts_decrypt(&ctxt->state.aes.key,
&ctxt->state.aes.key_tweak,
qcrypto_cipher_aes_xts_encrypt,
qcrypto_cipher_aes_xts_decrypt,
ctxt->state.aes.iv,
len, out, in);
break;
default:
g_assert_not_reached();
}
return 0;
......@@ -166,21 +242,46 @@ static int qcrypto_cipher_init_aes(QCryptoCipher *cipher,
QCryptoCipherBuiltin *ctxt;
if (cipher->mode != QCRYPTO_CIPHER_MODE_CBC &&
cipher->mode != QCRYPTO_CIPHER_MODE_ECB) {
cipher->mode != QCRYPTO_CIPHER_MODE_ECB &&
cipher->mode != QCRYPTO_CIPHER_MODE_XTS) {
error_setg(errp, "Unsupported cipher mode %d", cipher->mode);
return -1;
}
ctxt = g_new0(QCryptoCipherBuiltin, 1);
if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.encrypt_key) != 0) {
error_setg(errp, "Failed to set encryption key");
goto error;
}
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
if (AES_set_encrypt_key(key, nkey * 4, &ctxt->state.aes.key.enc) != 0) {
error_setg(errp, "Failed to set encryption key");
goto error;
}
if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.decrypt_key) != 0) {
error_setg(errp, "Failed to set decryption key");
goto error;
if (AES_set_decrypt_key(key, nkey * 4, &ctxt->state.aes.key.dec) != 0) {
error_setg(errp, "Failed to set decryption key");
goto error;
}
if (AES_set_encrypt_key(key + (nkey / 2), nkey * 4,
&ctxt->state.aes.key_tweak.enc) != 0) {
error_setg(errp, "Failed to set encryption key");
goto error;
}
if (AES_set_decrypt_key(key + (nkey / 2), nkey * 4,
&ctxt->state.aes.key_tweak.dec) != 0) {
error_setg(errp, "Failed to set decryption key");
goto error;
}
} else {
if (AES_set_encrypt_key(key, nkey * 8, &ctxt->state.aes.key.enc) != 0) {
error_setg(errp, "Failed to set encryption key");
goto error;
}
if (AES_set_decrypt_key(key, nkey * 8, &ctxt->state.aes.key.dec) != 0) {
error_setg(errp, "Failed to set decryption key");
goto error;
}
}
ctxt->blocksize = AES_BLOCK_SIZE;
......@@ -322,7 +423,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
cipher->alg = alg;
cipher->mode = mode;
if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
goto error;
}
......
......@@ -19,6 +19,8 @@
*/
#include "qemu/osdep.h"
#include "crypto/xts.h"
#include <gcrypt.h>
......@@ -29,6 +31,12 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
case QCRYPTO_CIPHER_ALG_AES_128:
case QCRYPTO_CIPHER_ALG_AES_192:
case QCRYPTO_CIPHER_ALG_AES_256:
case QCRYPTO_CIPHER_ALG_CAST5_128:
case QCRYPTO_CIPHER_ALG_SERPENT_128:
case QCRYPTO_CIPHER_ALG_SERPENT_192:
case QCRYPTO_CIPHER_ALG_SERPENT_256:
case QCRYPTO_CIPHER_ALG_TWOFISH_128:
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
return true;
default:
return false;
......@@ -38,7 +46,9 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
typedef struct QCryptoCipherGcrypt QCryptoCipherGcrypt;
struct QCryptoCipherGcrypt {
gcry_cipher_hd_t handle;
gcry_cipher_hd_t tweakhandle;
size_t blocksize;
uint8_t *iv;
};
QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
......@@ -53,6 +63,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
case QCRYPTO_CIPHER_MODE_XTS:
gcrymode = GCRY_CIPHER_MODE_ECB;
break;
case QCRYPTO_CIPHER_MODE_CBC:
......@@ -63,7 +74,7 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
return NULL;
}
if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
return NULL;
}
......@@ -84,6 +95,30 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
gcryalg = GCRY_CIPHER_AES256;
break;
case QCRYPTO_CIPHER_ALG_CAST5_128:
gcryalg = GCRY_CIPHER_CAST5;
break;
case QCRYPTO_CIPHER_ALG_SERPENT_128:
gcryalg = GCRY_CIPHER_SERPENT128;
break;
case QCRYPTO_CIPHER_ALG_SERPENT_192:
gcryalg = GCRY_CIPHER_SERPENT192;
break;
case QCRYPTO_CIPHER_ALG_SERPENT_256:
gcryalg = GCRY_CIPHER_SERPENT256;
break;
case QCRYPTO_CIPHER_ALG_TWOFISH_128:
gcryalg = GCRY_CIPHER_TWOFISH128;
break;
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
gcryalg = GCRY_CIPHER_TWOFISH;
break;
default:
error_setg(errp, "Unsupported cipher algorithm %d", alg);
return NULL;
......@@ -101,6 +136,14 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
gcry_strerror(err));
goto error;
}
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
err = gcry_cipher_open(&ctx->tweakhandle, gcryalg, gcrymode, 0);
if (err != 0) {
error_setg(errp, "Cannot initialize cipher: %s",
gcry_strerror(err));
goto error;
}
}
if (cipher->alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
/* We're using standard DES cipher from gcrypt, so we need
......@@ -112,13 +155,44 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
g_free(rfbkey);
ctx->blocksize = 8;
} else {
err = gcry_cipher_setkey(ctx->handle, key, nkey);
ctx->blocksize = 16;
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
nkey /= 2;
err = gcry_cipher_setkey(ctx->handle, key, nkey);
if (err != 0) {
error_setg(errp, "Cannot set key: %s",
gcry_strerror(err));
goto error;
}
err = gcry_cipher_setkey(ctx->tweakhandle, key + nkey, nkey);
} else {
err = gcry_cipher_setkey(ctx->handle, key, nkey);
}
if (err != 0) {
error_setg(errp, "Cannot set key: %s",
gcry_strerror(err));
goto error;
}
switch (cipher->alg) {
case QCRYPTO_CIPHER_ALG_AES_128:
case QCRYPTO_CIPHER_ALG_AES_192:
case QCRYPTO_CIPHER_ALG_AES_256:
case QCRYPTO_CIPHER_ALG_SERPENT_128:
case QCRYPTO_CIPHER_ALG_SERPENT_192:
case QCRYPTO_CIPHER_ALG_SERPENT_256:
case QCRYPTO_CIPHER_ALG_TWOFISH_128:
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
ctx->blocksize = 16;
break;
case QCRYPTO_CIPHER_ALG_CAST5_128:
ctx->blocksize = 8;
break;
default:
g_assert_not_reached();
}
}
if (err != 0) {
error_setg(errp, "Cannot set key: %s",
gcry_strerror(err));
goto error;
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
ctx->iv = g_new0(uint8_t, ctx->blocksize);
}
cipher->opaque = ctx;
......@@ -126,6 +200,9 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
error:
gcry_cipher_close(ctx->handle);
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
gcry_cipher_close(ctx->tweakhandle);
}
g_free(ctx);
g_free(cipher);
return NULL;
......@@ -140,11 +217,35 @@ void qcrypto_cipher_free(QCryptoCipher *cipher)
}
ctx = cipher->opaque;
gcry_cipher_close(ctx->handle);
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
gcry_cipher_close(ctx->tweakhandle);
}
g_free(ctx->iv);
g_free(ctx);
g_free(cipher);
}
static void qcrypto_gcrypt_xts_encrypt(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_decrypt(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);
}
int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
const void *in,
void *out,
......@@ -160,13 +261,20 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
return -1;
}
err = gcry_cipher_encrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot encrypt data: %s",
gcry_strerror(err));
return -1;
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
xts_encrypt(ctx->handle, ctx->tweakhandle,
qcrypto_gcrypt_xts_encrypt,
qcrypto_gcrypt_xts_decrypt,
ctx->iv, len, out, in);
} else {
err = gcry_cipher_encrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot encrypt data: %s",
gcry_strerror(err));
return -1;
}
}
return 0;
......@@ -188,13 +296,20 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
return -1;
}
err = gcry_cipher_decrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot decrypt data: %s",
gcry_strerror(err));
return -1;
if (cipher->mode == QCRYPTO_CIPHER_MODE_XTS) {
xts_decrypt(ctx->handle, ctx->tweakhandle,
qcrypto_gcrypt_xts_encrypt,
qcrypto_gcrypt_xts_decrypt,
ctx->iv, len, out, in);
} else {
err = gcry_cipher_decrypt(ctx->handle,
out, len,
in, len);
if (err != 0) {
error_setg(errp, "Cannot decrypt data: %s",
gcry_strerror(err));
return -1;
}
}
return 0;
......@@ -213,12 +328,16 @@ int qcrypto_cipher_setiv(QCryptoCipher *cipher,
return -1;
}
gcry_cipher_reset(ctx->handle);
err = gcry_cipher_setiv(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set IV: %s",
if (ctx->iv) {
memcpy(ctx->iv, iv, niv);
} else {
gcry_cipher_reset(ctx->handle);
err = gcry_cipher_setiv(ctx->handle, iv, niv);
if (err != 0) {
error_setg(errp, "Cannot set IV: %s",
gcry_strerror(err));
return -1;
return -1;
}
}
return 0;
......
......@@ -19,10 +19,15 @@
*/
#include "qemu/osdep.h"
#include "crypto/xts.h"
#include <nettle/nettle-types.h>
#include <nettle/aes.h>
#include <nettle/des.h>
#include <nettle/cbc.h>
#include <nettle/cast128.h>
#include <nettle/serpent.h>
#include <nettle/twofish.h>
#if CONFIG_NETTLE_VERSION_MAJOR < 3
typedef nettle_crypt_func nettle_cipher_func;
......@@ -39,16 +44,23 @@ static nettle_cipher_func aes_decrypt_wrapper;
static nettle_cipher_func des_encrypt_wrapper;
static nettle_cipher_func des_decrypt_wrapper;
typedef struct QCryptoNettleAES {
struct aes_ctx enc;
struct aes_ctx dec;
} QCryptoNettleAES;
static void aes_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
uint8_t *dst, const uint8_t *src)
{
aes_encrypt(ctx, length, dst, src);
const QCryptoNettleAES *aesctx = ctx;
aes_encrypt(&aesctx->enc, length, dst, src);
}
static void aes_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
uint8_t *dst, const uint8_t *src)
{
aes_decrypt(ctx, length, dst, src);
const QCryptoNettleAES *aesctx = ctx;
aes_decrypt(&aesctx->dec, length, dst, src);
}
static void des_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
......@@ -63,12 +75,52 @@ static void des_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
des_decrypt(ctx, length, dst, src);
}
static void cast128_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
uint8_t *dst, const uint8_t *src)
{
cast128_encrypt(ctx, length, dst, src);
}
static void cast128_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
uint8_t *dst, const uint8_t *src)
{
cast128_decrypt(ctx, length, dst, src);
}
static void serpent_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
uint8_t *dst, const uint8_t *src)
{
serpent_encrypt(ctx, length, dst, src);
}
static void serpent_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
uint8_t *dst, const uint8_t *src)
{
serpent_decrypt(ctx, length, dst, src);
}
static void twofish_encrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
uint8_t *dst, const uint8_t *src)
{
twofish_encrypt(ctx, length, dst, src);
}
static void twofish_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,
uint8_t *dst, const uint8_t *src)
{
twofish_decrypt(ctx, length, dst, src);
}
typedef struct QCryptoCipherNettle QCryptoCipherNettle;
struct QCryptoCipherNettle {
void *ctx_encrypt;
void *ctx_decrypt;
/* Primary cipher context for all modes */
void *ctx;
/* Second cipher context for XTS mode only */
void *ctx_tweak;
/* Cipher callbacks for both contexts */
nettle_cipher_func *alg_encrypt;
nettle_cipher_func *alg_decrypt;
uint8_t *iv;
size_t blocksize;
};
......@@ -80,6 +132,13 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)
case QCRYPTO_CIPHER_ALG_AES_128:
case QCRYPTO_CIPHER_ALG_AES_192:
case QCRYPTO_CIPHER_ALG_AES_256:
case QCRYPTO_CIPHER_ALG_CAST5_128:
case QCRYPTO_CIPHER_ALG_SERPENT_128:
case QCRYPTO_CIPHER_ALG_SERPENT_192:
case QCRYPTO_CIPHER_ALG_SERPENT_256:
case QCRYPTO_CIPHER_ALG_TWOFISH_128:
case QCRYPTO_CIPHER_ALG_TWOFISH_192:
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
return true;
default:
return false;
......@@ -99,13 +158,14 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
switch (mode) {
case QCRYPTO_CIPHER_MODE_ECB:
case QCRYPTO_CIPHER_MODE_CBC:
case QCRYPTO_CIPHER_MODE_XTS:
break;
default:
error_setg(errp, "Unsupported cipher mode %d", mode);
return NULL;
}
if (!qcrypto_cipher_validate_key_length(alg, nkey, errp)) {
if (!qcrypto_cipher_validate_key_length(alg, mode, nkey, errp)) {
return NULL;
}
......@@ -117,10 +177,9 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
switch (alg) {
case QCRYPTO_CIPHER_ALG_DES_RFB:
ctx->ctx_encrypt = g_new0(struct des_ctx, 1);
ctx->ctx_decrypt = NULL; /* 1 ctx can do both */
ctx->ctx = g_new0(struct des_ctx, 1);
rfbkey = qcrypto_cipher_munge_des_rfb_key(key, nkey);
des_set_key(ctx->ctx_encrypt, rfbkey);
des_set_key(ctx->ctx, rfbkey);
g_free(rfbkey);
ctx->alg_encrypt = des_encrypt_wrapper;
......@@ -132,17 +191,95 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,
case QCRYPTO_CIPHER_ALG_AES_128:
case QCRYPTO_CIPHER_ALG_AES_192:
case QCRYPTO_CIPHER_ALG_AES_256:
ctx->ctx_encrypt = g_new0(struct aes_ctx, 1);
ctx->ctx_decrypt = g_new0(struct aes_ctx, 1);
aes_set_encrypt_key(ctx->ctx_encrypt, nkey, key);
aes_set_decrypt_key(ctx->ctx_decrypt, nkey, key);
ctx->ctx = g_new0(QCryptoNettleAES, 1);
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
ctx->ctx_tweak = g_new0(QCryptoNettleAES, 1);
nkey /= 2;
aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx)->enc,
nkey, key);
aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx)->dec,
nkey, key);
aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx_tweak)->enc,
nkey, key + nkey);
aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx_tweak)->dec,
nkey, key + nkey);
} else {
aes_set_encrypt_key(&((QCryptoNettleAES *)ctx->ctx)->enc,
nkey, key);
aes_set_decrypt_key(&((QCryptoNettleAES *)ctx->ctx)->dec,
nkey, key);
}
ctx->alg_encrypt = aes_encrypt_wrapper;
ctx->alg_decrypt = aes_decrypt_wrapper;
ctx->blocksize = AES_BLOCK_SIZE;
break;
case QCRYPTO_CIPHER_ALG_CAST5_128:
ctx->ctx = g_new0(struct cast128_ctx, 1);
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
ctx->ctx_tweak = g_new0(struct cast128_ctx, 1);
nkey /= 2;
cast5_set_key(ctx->ctx, nkey, key);
cast5_set_key(ctx->ctx_tweak, nkey, key + nkey);
} else {
cast5_set_key(ctx->ctx, nkey, key);
}
ctx->alg_encrypt = cast128_encrypt_wrapper;
ctx->alg_decrypt = cast128_decrypt_wrapper;
ctx->blocksize = CAST128_BLOCK_SIZE;
break;
case QCRYPTO_CIPHER_ALG_SERPENT_128:
case QCRYPTO_CIPHER_ALG_SERPENT_192:
case QCRYPTO_CIPHER_ALG_SERPENT_256:
ctx->ctx = g_new0(struct serpent_ctx, 1);
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
ctx->ctx_tweak = g_new0(struct serpent_ctx, 1);
nkey /= 2;
serpent_set_key(ctx->ctx, nkey, key);
serpent_set_key(ctx->ctx_tweak, nkey, key + nkey);
} else {
serpent_set_key(ctx->ctx, nkey, key);
}
ctx->alg_encrypt = serpent_encrypt_wrapper;
ctx->alg_decrypt = serpent_decrypt_wrapper;
ctx->blocksize = SERPENT_BLOCK_SIZE;
break;
case QCRYPTO_CIPHER_ALG_TWOFISH_128:
case QCRYPTO_CIPHER_ALG_TWOFISH_192:
case QCRYPTO_CIPHER_ALG_TWOFISH_256:
ctx->ctx = g_new0(struct twofish_ctx, 1);
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
ctx->ctx_tweak = g_new0(struct twofish_ctx, 1);
nkey /= 2;
twofish_set_key(ctx->ctx, nkey, key);
twofish_set_key(ctx->ctx_tweak, nkey, key + nkey);
} else {
twofish_set_key(ctx->ctx, nkey, key);
}
ctx->alg_encrypt = twofish_encrypt_wrapper;
ctx->alg_decrypt = twofish_decrypt_wrapper;
ctx->blocksize = TWOFISH_BLOCK_SIZE;
break;
default:
error_setg(errp, "Unsupported cipher algorithm %d", alg);
goto error;
......@@ -170,8 +307,8 @@ void qcrypto_cipher_free(QCryptoCipher *cipher)
ctx = cipher->opaque;
g_free(ctx->iv);
g_free(ctx->ctx_encrypt);
g_free(ctx->ctx_decrypt);
g_free(ctx->ctx);
g_free(ctx->ctx_tweak);
g_free(ctx);
g_free(cipher);
}
......@@ -193,14 +330,21 @@ int qcrypto_cipher_encrypt(QCryptoCipher *cipher,
switch (cipher->mode) {
case QCRYPTO_CIPHER_MODE_ECB:
ctx->alg_encrypt(ctx->ctx_encrypt, len, out, in);
ctx->alg_encrypt(ctx->ctx, len, out, in);
break;
case QCRYPTO_CIPHER_MODE_CBC:
cbc_encrypt(ctx->ctx_encrypt, ctx->alg_encrypt,
cbc_encrypt(ctx->ctx, ctx->alg_encrypt,
ctx->blocksize, ctx->iv,
len, out, in);
break;
case QCRYPTO_CIPHER_MODE_XTS:
xts_encrypt(ctx->ctx, ctx->ctx_tweak,
ctx->alg_encrypt, ctx->alg_encrypt,
ctx->iv, len, out, in);
break;
default:
error_setg(errp, "Unsupported cipher algorithm %d",
cipher->alg);
......@@ -226,15 +370,26 @@ int qcrypto_cipher_decrypt(QCryptoCipher *cipher,
switch (cipher->mode) {
case QCRYPTO_CIPHER_MODE_ECB:
ctx->alg_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt,
len, out, in);
ctx->alg_decrypt(ctx->ctx, len, out, in);
break;
case QCRYPTO_CIPHER_MODE_CBC:
cbc_decrypt(ctx->ctx_decrypt ? ctx->ctx_decrypt : ctx->ctx_encrypt,
ctx->alg_decrypt, ctx->blocksize, ctx->iv,
cbc_decrypt(ctx->ctx, ctx->alg_decrypt,
ctx->blocksize, ctx->iv,
len, out, in);
break;
case QCRYPTO_CIPHER_MODE_XTS:
if (ctx->blocksize != XTS_BLOCK_SIZE) {
error_setg(errp, "Block size must be %d not %zu",
XTS_BLOCK_SIZE, ctx->blocksize);
return -1;
}
xts_decrypt(ctx->ctx, ctx->ctx_tweak,
ctx->alg_encrypt, ctx->alg_decrypt,
ctx->iv, len, out, in);
break;
default:
error_setg(errp, "Unsupported cipher algorithm %d",
cipher->alg);
......
......@@ -27,6 +27,13 @@ static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = {
[QCRYPTO_CIPHER_ALG_AES_192] = 24,
[QCRYPTO_CIPHER_ALG_AES_256] = 32,
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
[QCRYPTO_CIPHER_ALG_CAST5_128] = 16,
[QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
[QCRYPTO_CIPHER_ALG_SERPENT_192] = 24,
[QCRYPTO_CIPHER_ALG_SERPENT_256] = 32,
[QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16,
[QCRYPTO_CIPHER_ALG_TWOFISH_192] = 24,
[QCRYPTO_CIPHER_ALG_TWOFISH_256] = 32,
};
static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {
......@@ -34,11 +41,19 @@ static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {
[QCRYPTO_CIPHER_ALG_AES_192] = 16,
[QCRYPTO_CIPHER_ALG_AES_256] = 16,
[QCRYPTO_CIPHER_ALG_DES_RFB] = 8,
[QCRYPTO_CIPHER_ALG_CAST5_128] = 8,
[QCRYPTO_CIPHER_ALG_SERPENT_128] = 16,
[QCRYPTO_CIPHER_ALG_SERPENT_192] = 16,
[QCRYPTO_CIPHER_ALG_SERPENT_256] = 16,
[QCRYPTO_CIPHER_ALG_TWOFISH_128] = 16,
[QCRYPTO_CIPHER_ALG_TWOFISH_192] = 16,
[QCRYPTO_CIPHER_ALG_TWOFISH_256] = 16,
};
static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX] = {
[QCRYPTO_CIPHER_MODE_ECB] = false,
[QCRYPTO_CIPHER_MODE_CBC] = true,
[QCRYPTO_CIPHER_MODE_XTS] = true,
};
......@@ -79,6 +94,7 @@ size_t qcrypto_cipher_get_iv_len(QCryptoCipherAlgorithm alg,
static bool
qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
QCryptoCipherMode mode,
size_t nkey,
Error **errp)
{
......@@ -88,10 +104,27 @@ qcrypto_cipher_validate_key_length(QCryptoCipherAlgorithm alg,
return false;
}
if (alg_key_len[alg] != nkey) {
error_setg(errp, "Cipher key length %zu should be %zu",
nkey, alg_key_len[alg]);
return false;
if (mode == QCRYPTO_CIPHER_MODE_XTS) {
if (alg == QCRYPTO_CIPHER_ALG_DES_RFB) {
error_setg(errp, "XTS mode not compatible with DES-RFB");
return false;
}
if (nkey % 2) {
error_setg(errp, "XTS cipher key length should be a multiple of 2");
return false;
}
if (alg_key_len[alg] != (nkey / 2)) {
error_setg(errp, "Cipher key length %zu should be %zu",
nkey, alg_key_len[alg] * 2);
return false;
}
} else {
if (alg_key_len[alg] != nkey) {
error_setg(errp, "Cipher key length %zu should be %zu",
nkey, alg_key_len[alg]);
return false;
}
}
return true;
}
......
/*
* QEMU Crypto block IV generator - essiv
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/ivgen-essiv.h"
typedef struct QCryptoIVGenESSIV QCryptoIVGenESSIV;
struct QCryptoIVGenESSIV {
QCryptoCipher *cipher;
};
static int qcrypto_ivgen_essiv_init(QCryptoIVGen *ivgen,
const uint8_t *key, size_t nkey,
Error **errp)
{
uint8_t *salt;
size_t nhash;
size_t nsalt;
QCryptoIVGenESSIV *essiv = g_new0(QCryptoIVGenESSIV, 1);
/* Not necessarily the same as nkey */
nsalt = qcrypto_cipher_get_key_len(ivgen->cipher);
nhash = qcrypto_hash_digest_len(ivgen->hash);
/* Salt must be larger of hash size or key size */
salt = g_new0(uint8_t, MAX(nhash, nsalt));
if (qcrypto_hash_bytes(ivgen->hash, (const gchar *)key, nkey,
&salt, &nhash,
errp) < 0) {
g_free(essiv);
return -1;
}
/* Now potentially truncate salt to match cipher key len */
essiv->cipher = qcrypto_cipher_new(ivgen->cipher,
QCRYPTO_CIPHER_MODE_ECB,
salt, MIN(nhash, nsalt),
errp);
if (!essiv->cipher) {
g_free(essiv);
g_free(salt);
return -1;
}
g_free(salt);
ivgen->private = essiv;
return 0;
}
static int qcrypto_ivgen_essiv_calculate(QCryptoIVGen *ivgen,
uint64_t sector,
uint8_t *iv, size_t niv,
Error **errp)
{
QCryptoIVGenESSIV *essiv = ivgen->private;
size_t ndata = qcrypto_cipher_get_block_len(ivgen->cipher);
uint8_t *data = g_new(uint8_t, ndata);
sector = cpu_to_le64(sector);
memcpy(data, (uint8_t *)&sector, ndata);
if (sizeof(sector) < ndata) {
memset(data + sizeof(sector), 0, ndata - sizeof(sector));
}
if (qcrypto_cipher_encrypt(essiv->cipher,
data,
data,
ndata,
errp) < 0) {
g_free(data);
return -1;
}
if (ndata > niv) {
ndata = niv;
}
memcpy(iv, data, ndata);
if (ndata < niv) {
memset(iv + ndata, 0, niv - ndata);
}
g_free(data);
return 0;
}
static void qcrypto_ivgen_essiv_cleanup(QCryptoIVGen *ivgen)
{
QCryptoIVGenESSIV *essiv = ivgen->private;
qcrypto_cipher_free(essiv->cipher);
g_free(essiv);
}
struct QCryptoIVGenDriver qcrypto_ivgen_essiv = {
.init = qcrypto_ivgen_essiv_init,
.calculate = qcrypto_ivgen_essiv_calculate,
.cleanup = qcrypto_ivgen_essiv_cleanup,
};
/*
* QEMU Crypto block IV generator - essiv
*
* Copyright (c) 2015-2016 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/ivgenpriv.h"
#ifndef QCRYPTO_IVGEN_ESSIV_H__
#define QCRYPTO_IVGEN_ESSIV_H__
extern struct QCryptoIVGenDriver qcrypto_ivgen_essiv;
#endif /* QCRYPTO_IVGEN_ESSIV_H__ */
/*
* QEMU Crypto block IV generator - plain
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/ivgen-plain.h"
static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen,
const uint8_t *key, size_t nkey,
Error **errp)
{
return 0;
}
static int qcrypto_ivgen_plain_calculate(QCryptoIVGen *ivgen,
uint64_t sector,
uint8_t *iv, size_t niv,
Error **errp)
{
size_t ivprefix;
uint32_t shortsector = cpu_to_le32((sector & 0xffffffff));
ivprefix = sizeof(shortsector);
if (ivprefix > niv) {
ivprefix = niv;
}
memcpy(iv, &shortsector, ivprefix);
if (ivprefix < niv) {
memset(iv + ivprefix, 0, niv - ivprefix);
}
return 0;
}
static void qcrypto_ivgen_plain_cleanup(QCryptoIVGen *ivgen)
{
}
struct QCryptoIVGenDriver qcrypto_ivgen_plain = {
.init = qcrypto_ivgen_plain_init,
.calculate = qcrypto_ivgen_plain_calculate,
.cleanup = qcrypto_ivgen_plain_cleanup,
};
/*
* QEMU Crypto block IV generator - plain
*
* Copyright (c) 2015-2016 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/ivgenpriv.h"
#ifndef QCRYPTO_IVGEN_PLAIN_H__
#define QCRYPTO_IVGEN_PLAIN_H__
extern struct QCryptoIVGenDriver qcrypto_ivgen_plain;
#endif /* QCRYPTO_IVGEN_PLAIN_H__ */
/*
* QEMU Crypto block IV generator - plain
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/ivgen-plain.h"
static int qcrypto_ivgen_plain_init(QCryptoIVGen *ivgen,
const uint8_t *key, size_t nkey,
Error **errp)
{
return 0;
}
static int qcrypto_ivgen_plain_calculate(QCryptoIVGen *ivgen,
uint64_t sector,
uint8_t *iv, size_t niv,
Error **errp)
{
size_t ivprefix;
ivprefix = sizeof(sector);
sector = cpu_to_le64(sector);
if (ivprefix > niv) {
ivprefix = niv;
}
memcpy(iv, &sector, ivprefix);
if (ivprefix < niv) {
memset(iv + ivprefix, 0, niv - ivprefix);
}
return 0;
}
static void qcrypto_ivgen_plain_cleanup(QCryptoIVGen *ivgen)
{
}
struct QCryptoIVGenDriver qcrypto_ivgen_plain64 = {
.init = qcrypto_ivgen_plain_init,
.calculate = qcrypto_ivgen_plain_calculate,
.cleanup = qcrypto_ivgen_plain_cleanup,
};
/*
* QEMU Crypto block IV generator - plain64
*
* Copyright (c) 2015-2016 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/ivgenpriv.h"
#ifndef QCRYPTO_IVGEN_PLAIN64_H__
#define QCRYPTO_IVGEN_PLAIN64_H__
extern struct QCryptoIVGenDriver qcrypto_ivgen_plain64;
#endif /* QCRYPTO_IVGEN_PLAIN64_H__ */
/*
* QEMU Crypto block IV generator
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/ivgenpriv.h"
#include "crypto/ivgen-plain.h"
#include "crypto/ivgen-plain64.h"
#include "crypto/ivgen-essiv.h"
QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg,
QCryptoCipherAlgorithm cipheralg,
QCryptoHashAlgorithm hash,
const uint8_t *key, size_t nkey,
Error **errp)
{
QCryptoIVGen *ivgen = g_new0(QCryptoIVGen, 1);
ivgen->algorithm = alg;
ivgen->cipher = cipheralg;
ivgen->hash = hash;
switch (alg) {
case QCRYPTO_IVGEN_ALG_PLAIN:
ivgen->driver = &qcrypto_ivgen_plain;
break;
case QCRYPTO_IVGEN_ALG_PLAIN64:
ivgen->driver = &qcrypto_ivgen_plain64;
break;
case QCRYPTO_IVGEN_ALG_ESSIV:
ivgen->driver = &qcrypto_ivgen_essiv;
break;
default:
error_setg(errp, "Unknown block IV generator algorithm %d", alg);
g_free(ivgen);
return NULL;
}
if (ivgen->driver->init(ivgen, key, nkey, errp) < 0) {
g_free(ivgen);
return NULL;
}
return ivgen;
}
int qcrypto_ivgen_calculate(QCryptoIVGen *ivgen,
uint64_t sector,
uint8_t *iv, size_t niv,
Error **errp)
{
return ivgen->driver->calculate(ivgen, sector, iv, niv, errp);
}
QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen)
{
return ivgen->algorithm;
}
QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen)
{
return ivgen->cipher;
}
QCryptoHashAlgorithm qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen)
{
return ivgen->hash;
}
void qcrypto_ivgen_free(QCryptoIVGen *ivgen)
{
if (!ivgen) {
return;
}
ivgen->driver->cleanup(ivgen);
g_free(ivgen);
}
/*
* QEMU Crypto block IV generator
*
* Copyright (c) 2015-2016 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_IVGEN_PRIV_H__
#define QCRYPTO_IVGEN_PRIV_H__
#include "crypto/ivgen.h"
typedef struct QCryptoIVGenDriver QCryptoIVGenDriver;
struct QCryptoIVGenDriver {
int (*init)(QCryptoIVGen *ivgen,
const uint8_t *key, size_t nkey,
Error **errp);
int (*calculate)(QCryptoIVGen *ivgen,
uint64_t sector,
uint8_t *iv, size_t niv,
Error **errp);
void (*cleanup)(QCryptoIVGen *ivgen);
};
struct QCryptoIVGen {
QCryptoIVGenDriver *driver;
void *private;
QCryptoIVGenAlgorithm algorithm;
QCryptoCipherAlgorithm cipher;
QCryptoHashAlgorithm hash;
};
#endif /* QCRYPTO_IVGEN_PRIV_H__ */
/*
* QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/pbkdf.h"
#include "gcrypt.h"
bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash)
{
switch (hash) {
case QCRYPTO_HASH_ALG_MD5:
case QCRYPTO_HASH_ALG_SHA1:
case QCRYPTO_HASH_ALG_SHA256:
return true;
default:
return false;
}
}
int qcrypto_pbkdf2(QCryptoHashAlgorithm hash,
const uint8_t *key, size_t nkey,
const uint8_t *salt, size_t nsalt,
unsigned int iterations,
uint8_t *out, size_t nout,
Error **errp)
{
static const int hash_map[QCRYPTO_HASH_ALG__MAX] = {
[QCRYPTO_HASH_ALG_MD5] = GCRY_MD_MD5,
[QCRYPTO_HASH_ALG_SHA1] = GCRY_MD_SHA1,
[QCRYPTO_HASH_ALG_SHA256] = GCRY_MD_SHA256,
};
int ret;
if (hash >= G_N_ELEMENTS(hash_map) ||
hash_map[hash] == GCRY_MD_NONE) {
error_setg(errp, "Unexpected hash algorithm %d", hash);
return -1;
}
ret = gcry_kdf_derive(key, nkey, GCRY_KDF_PBKDF2,
hash_map[hash],
salt, nsalt, iterations,
nout, out);
if (ret != 0) {
error_setg(errp, "Cannot derive password: %s",
gcry_strerror(ret));
return -1;
}
return 0;
}
/*
* QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/pbkdf.h"
#include "nettle/pbkdf2.h"
bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash)
{
switch (hash) {
case QCRYPTO_HASH_ALG_SHA1:
case QCRYPTO_HASH_ALG_SHA256:
return true;
default:
return false;
}
}
int qcrypto_pbkdf2(QCryptoHashAlgorithm hash,
const uint8_t *key, size_t nkey,
const uint8_t *salt, size_t nsalt,
unsigned int iterations,
uint8_t *out, size_t nout,
Error **errp)
{
switch (hash) {
case QCRYPTO_HASH_ALG_SHA1:
pbkdf2_hmac_sha1(nkey, key,
iterations,
nsalt, salt,
nout, out);
break;
case QCRYPTO_HASH_ALG_SHA256:
pbkdf2_hmac_sha256(nkey, key,
iterations,
nsalt, salt,
nout, out);
break;
default:
error_setg_errno(errp, ENOSYS,
"PBKDF does not support hash algorithm %d", hash);
return -1;
}
return 0;
}
/*
* QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/pbkdf.h"
bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash G_GNUC_UNUSED)
{
return false;
}
int qcrypto_pbkdf2(QCryptoHashAlgorithm hash G_GNUC_UNUSED,
const uint8_t *key G_GNUC_UNUSED,
size_t nkey G_GNUC_UNUSED,
const uint8_t *salt G_GNUC_UNUSED,
size_t nsalt G_GNUC_UNUSED,
unsigned int iterations G_GNUC_UNUSED,
uint8_t *out G_GNUC_UNUSED,
size_t nout G_GNUC_UNUSED,
Error **errp)
{
error_setg_errno(errp, ENOSYS,
"No crypto library supporting PBKDF in this build");
return -1;
}
/*
* QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/pbkdf.h"
#ifndef _WIN32
#include <sys/resource.h>
#endif
static int qcrypto_pbkdf2_get_thread_cpu(unsigned long long *val_ms,
Error **errp)
{
#ifdef _WIN32
FILETIME creation_time, exit_time, kernel_time, user_time;
ULARGE_INTEGER thread_time;
if (!GetThreadTimes(GetCurrentThread(), &creation_time, &exit_time,
&kernel_time, &user_time)) {
error_setg(errp, "Unable to get thread CPU usage");
return -1;
}
thread_time.LowPart = user_time.dwLowDateTime;
thread_time.HighPart = user_time.dwHighDateTime;
/* QuadPart is units of 100ns and we want ms as unit */
*val_ms = thread_time.QuadPart / 10000ll;
return 0;
#elif defined(RUSAGE_THREAD)
struct rusage ru;
if (getrusage(RUSAGE_THREAD, &ru) < 0) {
error_setg_errno(errp, errno, "Unable to get thread CPU usage");
return -1;
}
*val_ms = ((ru.ru_utime.tv_sec * 1000ll) +
(ru.ru_utime.tv_usec / 1000));
return 0;
#else
*val_ms = 0;
error_setg(errp, "Unable to calculate thread CPU usage on this platform");
return -1;
#endif
}
int qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash,
const uint8_t *key, size_t nkey,
const uint8_t *salt, size_t nsalt,
Error **errp)
{
uint8_t out[32];
long long int iterations = (1 << 15);
unsigned long long delta_ms, start_ms, end_ms;
while (1) {
if (qcrypto_pbkdf2_get_thread_cpu(&start_ms, errp) < 0) {
return -1;
}
if (qcrypto_pbkdf2(hash,
key, nkey,
salt, nsalt,
iterations,
out, sizeof(out),
errp) < 0) {
return -1;
}
if (qcrypto_pbkdf2_get_thread_cpu(&end_ms, errp) < 0) {
return -1;
}
delta_ms = end_ms - start_ms;
if (delta_ms > 500) {
break;
} else if (delta_ms < 100) {
iterations = iterations * 10;
} else {
iterations = (iterations * 1000 / delta_ms);
}
}
iterations = iterations * 1000 / delta_ms;
if (iterations > INT32_MAX) {
error_setg(errp, "Iterations %lld too large for a 32-bit int",
iterations);
return -1;
}
return iterations;
}
/*
* QEMU Crypto random number provider
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/random.h"
#include <gcrypt.h>
int qcrypto_random_bytes(uint8_t *buf,
size_t buflen,
Error **errp G_GNUC_UNUSED)
{
gcry_randomize(buf, buflen, GCRY_STRONG_RANDOM);
return 0;
}
/*
* QEMU Crypto random number provider
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/random.h"
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
int qcrypto_random_bytes(uint8_t *buf,
size_t buflen,
Error **errp)
{
int ret;
ret = gnutls_rnd(GNUTLS_RND_RANDOM, buf, buflen);
if (ret < 0) {
error_setg(errp, "Cannot get random bytes: %s",
gnutls_strerror(ret));
return -1;
}
return 0;
}
/*
* QEMU Crypto random number provider
*
* Copyright (c) 2015-2016 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 "qemu/osdep.h"
#include "crypto/random.h"
int qcrypto_random_bytes(uint8_t *buf G_GNUC_UNUSED,
size_t buflen G_GNUC_UNUSED,
Error **errp)
{
error_setg(errp, "No random byte source provided in this build");
return -1;
}
/*
* QEMU Crypto XTS cipher mode
*
* Copyright (c) 2015-2016 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/>.
*
* This code is originally derived from public domain / WTFPL code in
* LibTomCrypt crytographic library http://libtom.org. The XTS code
* was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com)
* to the LibTom Projects
*
*/
#include "qemu/osdep.h"
#include "crypto/xts.h"
static void xts_mult_x(uint8_t *I)
{
int x;
uint8_t t, tt;
for (x = t = 0; x < 16; x++) {
tt = I[x] >> 7;
I[x] = ((I[x] << 1) | t) & 0xFF;
t = tt;
}
if (tt) {
I[0] ^= 0x87;
}
}
/**
* xts_tweak_uncrypt:
* @param ctxt: the cipher context
* @param func: the cipher function
* @src: buffer providing the cipher text of XTS_BLOCK_SIZE bytes
* @dst: buffer to output the plain text of XTS_BLOCK_SIZE bytes
* @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
*
* Decrypt data with a tweak
*/
static void xts_tweak_decrypt(const void *ctx,
xts_cipher_func *func,
const uint8_t *src,
uint8_t *dst,
uint8_t *iv)
{
unsigned long x;
/* tweak encrypt block i */
for (x = 0; x < XTS_BLOCK_SIZE; x++) {
dst[x] = src[x] ^ iv[x];
}
func(ctx, XTS_BLOCK_SIZE, dst, dst);
for (x = 0; x < XTS_BLOCK_SIZE; x++) {
dst[x] = dst[x] ^ iv[x];
}
/* LFSR the tweak */
xts_mult_x(iv);
}
void xts_decrypt(const void *datactx,
const void *tweakctx,
xts_cipher_func *encfunc,
xts_cipher_func *decfunc,
uint8_t *iv,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE];
unsigned long i, m, mo, lim;
/* get number of blocks */
m = length >> 4;
mo = length & 15;
/* must have at least one full block */
g_assert(m != 0);
if (mo == 0) {
lim = m;
} else {
lim = m - 1;
}
/* encrypt the iv */
encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv);
for (i = 0; i < lim; i++) {
xts_tweak_decrypt(datactx, decfunc, src, dst, T);
src += XTS_BLOCK_SIZE;
dst += XTS_BLOCK_SIZE;
}
/* if length is not a multiple of XTS_BLOCK_SIZE then */
if (mo > 0) {
memcpy(CC, T, XTS_BLOCK_SIZE);
xts_mult_x(CC);
/* PP = tweak decrypt block m-1 */
xts_tweak_decrypt(datactx, decfunc, src, PP, CC);
/* Pm = first length % XTS_BLOCK_SIZE bytes of PP */
for (i = 0; i < mo; i++) {
CC[i] = src[XTS_BLOCK_SIZE + i];
dst[XTS_BLOCK_SIZE + i] = PP[i];
}
for (; i < XTS_BLOCK_SIZE; i++) {
CC[i] = PP[i];
}
/* Pm-1 = Tweak uncrypt CC */
xts_tweak_decrypt(datactx, decfunc, CC, dst, T);
}
/* Decrypt the iv back */
decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T);
}
/**
* xts_tweak_crypt:
* @param ctxt: the cipher context
* @param func: the cipher function
* @src: buffer providing the plain text of XTS_BLOCK_SIZE bytes
* @dst: buffer to output the cipher text of XTS_BLOCK_SIZE bytes
* @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
*
* Encrypt data with a tweak
*/
static void xts_tweak_encrypt(const void *ctx,
xts_cipher_func *func,
const uint8_t *src,
uint8_t *dst,
uint8_t *iv)
{
unsigned long x;
/* tweak encrypt block i */
for (x = 0; x < XTS_BLOCK_SIZE; x++) {
dst[x] = src[x] ^ iv[x];
}
func(ctx, XTS_BLOCK_SIZE, dst, dst);
for (x = 0; x < XTS_BLOCK_SIZE; x++) {
dst[x] = dst[x] ^ iv[x];
}
/* LFSR the tweak */
xts_mult_x(iv);
}
void xts_encrypt(const void *datactx,
const void *tweakctx,
xts_cipher_func *encfunc,
xts_cipher_func *decfunc,
uint8_t *iv,
size_t length,
uint8_t *dst,
const uint8_t *src)
{
uint8_t PP[XTS_BLOCK_SIZE], CC[XTS_BLOCK_SIZE], T[XTS_BLOCK_SIZE];
unsigned long i, m, mo, lim;
/* get number of blocks */
m = length >> 4;
mo = length & 15;
/* must have at least one full block */
g_assert(m != 0);
if (mo == 0) {
lim = m;
} else {
lim = m - 1;
}
/* encrypt the iv */
encfunc(tweakctx, XTS_BLOCK_SIZE, T, iv);
for (i = 0; i < lim; i++) {
xts_tweak_encrypt(datactx, encfunc, src, dst, T);
dst += XTS_BLOCK_SIZE;
src += XTS_BLOCK_SIZE;
}
/* if length is not a multiple of XTS_BLOCK_SIZE then */
if (mo > 0) {
/* CC = tweak encrypt block m-1 */
xts_tweak_encrypt(datactx, encfunc, src, CC, T);
/* Cm = first length % XTS_BLOCK_SIZE bytes of CC */
for (i = 0; i < mo; i++) {
PP[i] = src[XTS_BLOCK_SIZE + i];
dst[XTS_BLOCK_SIZE + i] = CC[i];
}
for (; i < XTS_BLOCK_SIZE; i++) {
PP[i] = CC[i];
}
/* Cm-1 = Tweak encrypt PP */
xts_tweak_encrypt(datactx, encfunc, PP, dst, T);
}
/* Decrypt the iv back */
decfunc(tweakctx, XTS_BLOCK_SIZE, iv, T);
}
/*
* QEMU Crypto anti forensic information splitter
*
* Copyright (c) 2015-2016 Red Hat, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU 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_AFSPLIT_H__
#define QCRYPTO_AFSPLIT_H__
#include "crypto/hash.h"
/**
* This module implements the anti-forensic splitter that is specified
* as part of the LUKS format:
*
* http://clemens.endorphin.org/cryptography
* http://clemens.endorphin.org/TKS1-draft.pdf
*
* The core idea is to take a short piece of data (key material)
* and process it to expand it to a much larger piece of data.
* The expansion process is reversible, to obtain the original
* short data. The key property of the expansion is that if any
* byte in the larger data set is changed / missing, it should be
* impossible to recreate the original short data.
*
* <example>
* <title>Creating a large split key for storage</title>
* <programlisting>
* size_t nkey = 32;
* uint32_t stripes = 32768; // To produce a 1 MB split key
* uint8_t *masterkey = ....a 32-byte AES key...
* uint8_t *splitkey;
*
* splitkey = g_new0(uint8_t, nkey * stripes);
*
* if (qcrypto_afsplit_encode(QCRYPTO_HASH_ALG_SHA256,
* nkey, stripes,
* masterkey, splitkey, errp) < 0) {
* g_free(splitkey);
* g_free(masterkey);
* return -1;
* }
*
* ...store splitkey somewhere...
*
* g_free(splitkey);
* g_free(masterkey);
* </programlisting>
* </example>
*
* <example>
* <title>Retrieving a master key from storage</title>
* <programlisting>
* size_t nkey = 32;
* uint32_t stripes = 32768; // To produce a 1 MB split key
* uint8_t *masterkey;
* uint8_t *splitkey = .... read in 1 MB of data...
*
* masterkey = g_new0(uint8_t, nkey);
*
* if (qcrypto_afsplit_decode(QCRYPTO_HASH_ALG_SHA256,
* nkey, stripes,
* splitkey, masterkey, errp) < 0) {
* g_free(splitkey);
* g_free(masterkey);
* return -1;
* }
*
* ..decrypt data with masterkey...
*
* g_free(splitkey);
* g_free(masterkey);
* </programlisting>
* </example>
*/
/**
* qcrypto_afsplit_encode:
* @hash: the hash algorithm to use for data expansion
* @blocklen: the size of @in in bytes
* @stripes: the number of times to expand @in in size
* @in: the master key to be expanded in size
* @out: preallocated buffer to hold the split key
* @errp: pointer to a NULL-initialized error object
*
* Split the data in @in, which is @blocklen bytes in
* size, to form a larger piece of data @out, which is
* @blocklen * @stripes bytes in size.
*
* Returns: 0 on success, -1 on error;
*/
int qcrypto_afsplit_encode(QCryptoHashAlgorithm hash,
size_t blocklen,
uint32_t stripes,
const uint8_t *in,
uint8_t *out,
Error **errp);
/**
* qcrypto_afsplit_decode:
* @hash: the hash algorithm to use for data compression
* @blocklen: the size of @out in bytes
* @stripes: the number of times to decrease @in in size
* @in: the split key to be recombined
* @out: preallocated buffer to hold the master key
* @errp: pointer to a NULL-initialized error object
*
* Join the data in @in, which is @blocklen * @stripes
* bytes in size, to form the original small piece of
* data @out, which is @blocklen bytes in size.
*
* Returns: 0 on success, -1 on error;
*/
int qcrypto_afsplit_decode(QCryptoHashAlgorithm hash,
size_t blocklen,
uint32_t stripes,
const uint8_t *in,
uint8_t *out,
Error **errp);
#endif /* QCRYPTO_AFSPLIT_H__ */
/*
* QEMU Crypto block device encryption
*
* Copyright (c) 2015-2016 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_BLOCK_H__
#define QCRYPTO_BLOCK_H__
#include "crypto/cipher.h"
#include "crypto/ivgen.h"
typedef struct QCryptoBlock QCryptoBlock;
/* See also QCryptoBlockFormat, QCryptoBlockCreateOptions
* and QCryptoBlockOpenOptions in qapi/crypto.json */
typedef ssize_t (*QCryptoBlockReadFunc)(QCryptoBlock *block,
size_t offset,
uint8_t *buf,
size_t buflen,
Error **errp,
void *opaque);
typedef ssize_t (*QCryptoBlockInitFunc)(QCryptoBlock *block,
size_t headerlen,
Error **errp,
void *opaque);
typedef ssize_t (*QCryptoBlockWriteFunc)(QCryptoBlock *block,
size_t offset,
const uint8_t *buf,
size_t buflen,
Error **errp,
void *opaque);
/**
* qcrypto_block_has_format:
* @format: the encryption format
* @buf: the data from head of the volume
* @len: the length of @buf in bytes
*
* Given @len bytes of data from the head of a storage volume
* in @buf, probe to determine if the volume has the encryption
* format specified in @format.
*
* Returns: true if the data in @buf matches @format
*/
bool qcrypto_block_has_format(QCryptoBlockFormat format,
const uint8_t *buf,
size_t buflen);
typedef enum {
QCRYPTO_BLOCK_OPEN_NO_IO = (1 << 0),
} QCryptoBlockOpenFlags;
/**
* qcrypto_block_open:
* @options: the encryption options
* @readfunc: callback for reading data from the volume
* @opaque: data to pass to @readfunc
* @flags: bitmask of QCryptoBlockOpenFlags values
* @errp: pointer to a NULL-initialized error object
*
* Create a new block encryption object for an existing
* storage volume encrypted with format identified by
* the parameters in @options.
*
* This will use @readfunc to initialize the encryption
* context based on the volume header(s), extracting the
* master key(s) as required.
*
* If @flags contains QCRYPTO_BLOCK_OPEN_NO_IO then
* the open process will be optimized to skip any parts
* that are only required to perform I/O. In particular
* this would usually avoid the need to decrypt any
* master keys. The only thing that can be done with
* the resulting QCryptoBlock object would be to query
* metadata such as the payload offset. There will be
* no cipher or ivgen objects available.
*
* If any part of initializing the encryption context
* fails an error will be returned. This could be due
* to the volume being in the wrong format, a cipher
* or IV generator algorithm that is not supported,
* or incorrect passphrases.
*
* Returns: a block encryption format, or NULL on error
*/
QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options,
QCryptoBlockReadFunc readfunc,
void *opaque,
unsigned int flags,
Error **errp);
/**
* qcrypto_block_create:
* @format: the encryption format
* @initfunc: callback for initializing volume header
* @writefunc: callback for writing data to the volume header
* @opaque: data to pass to @initfunc and @writefunc
* @errp: pointer to a NULL-initialized error object
*
* Create a new block encryption object for initializing
* a storage volume to be encrypted with format identified
* by the parameters in @options.
*
* This method will allocate space for a new volume header
* using @initfunc and then write header data using @writefunc,
* generating new master keys, etc as required. Any existing
* data present on the volume will be irrevocably destroyed.
*
* If any part of initializing the encryption context
* fails an error will be returned. This could be due
* to the volume being in the wrong format, a cipher
* or IV generator algorithm that is not supported,
* or incorrect passphrases.
*
* Returns: a block encryption format, or NULL on error
*/
QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
QCryptoBlockInitFunc initfunc,
QCryptoBlockWriteFunc writefunc,
void *opaque,
Error **errp);
/**
* @qcrypto_block_decrypt:
* @block: the block encryption object
* @startsector: the sector from which @buf was read
* @buf: the buffer to decrypt
* @len: the length of @buf in bytes
* @errp: pointer to a NULL-initialized error object
*
* Decrypt @len bytes of cipher text in @buf, writing
* plain text back into @buf
*
* Returns 0 on success, -1 on failure
*/
int qcrypto_block_decrypt(QCryptoBlock *block,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp);
/**
* @qcrypto_block_encrypt:
* @block: the block encryption object
* @startsector: the sector to which @buf will be written
* @buf: the buffer to decrypt
* @len: the length of @buf in bytes
* @errp: pointer to a NULL-initialized error object
*
* Encrypt @len bytes of plain text in @buf, writing
* cipher text back into @buf
*
* Returns 0 on success, -1 on failure
*/
int qcrypto_block_encrypt(QCryptoBlock *block,
uint64_t startsector,
uint8_t *buf,
size_t len,
Error **errp);
/**
* qcrypto_block_get_cipher:
* @block: the block encryption object
*
* Get the cipher to use for payload encryption
*
* Returns: the cipher object
*/
QCryptoCipher *qcrypto_block_get_cipher(QCryptoBlock *block);
/**
* qcrypto_block_get_ivgen:
* @block: the block encryption object
*
* Get the initialization vector generator to use for
* payload encryption
*
* Returns: the IV generator object
*/
QCryptoIVGen *qcrypto_block_get_ivgen(QCryptoBlock *block);
/**
* qcrypto_block_get_kdf_hash:
* @block: the block encryption object
*
* Get the hash algorithm used with the key derivation
* function
*
* Returns: the hash algorithm
*/
QCryptoHashAlgorithm qcrypto_block_get_kdf_hash(QCryptoBlock *block);
/**
* qcrypto_block_get_payload_offset:
* @block: the block encryption object
*
* Get the offset to the payload indicated by the
* encryption header, in bytes.
*
* Returns: the payload offset in bytes
*/
uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block);
/**
* qcrypto_block_free:
* @block: the block encryption object
*
* Release all resources associated with the encryption
* object
*/
void qcrypto_block_free(QCryptoBlock *block);
#endif /* QCRYPTO_BLOCK_H__ */
/*
* QEMU Crypto block IV generator
*
* Copyright (c) 2015-2016 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_IVGEN_H__
#define QCRYPTO_IVGEN_H__
#include "crypto/cipher.h"
#include "crypto/hash.h"
/**
* This module provides a framework for generating initialization
* vectors for block encryption schemes using chained cipher modes
* CBC. The principle is that each disk sector is assigned a unique
* initialization vector for use for encryption of data in that
* sector.
*
* <example>
* <title>Encrypting block data with initialiation vectors</title>
* <programlisting>
* uint8_t *data = ....data to encrypt...
* size_t ndata = XXX;
* uint8_t *key = ....some encryption key...
* size_t nkey = XXX;
* uint8_t *iv;
* size_t niv;
* size_t sector = 0;
*
* g_assert((ndata % 512) == 0);
*
* QCryptoIVGen *ivgen = qcrypto_ivgen_new(QCRYPTO_IVGEN_ALG_ESSIV,
* QCRYPTO_CIPHER_ALG_AES_128,
* QCRYPTO_HASH_ALG_SHA256,
* key, nkey, errp);
* if (!ivgen) {
* return -1;
* }
*
* QCryptoCipher *cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128,
* QCRYPTO_CIPHER_MODE_CBC,
* key, nkey, errp);
* if (!cipher) {
* goto error;
* }
*
* niv = qcrypto_cipher_get_iv_len(QCRYPTO_CIPHER_ALG_AES_128,
* QCRYPTO_CIPHER_MODE_CBC);
* iv = g_new0(uint8_t, niv);
*
*
* while (ndata) {
* if (qcrypto_ivgen_calculate(ivgen, sector, iv, niv, errp) < 0) {
* goto error;
* }
* if (qcrypto_cipher_setiv(cipher, iv, niv, errp) < 0) {
* goto error;
* }
* if (qcrypto_cipher_encrypt(cipher,
* data + (sector * 512),
* data + (sector * 512),
* 512, errp) < 0) {
* goto error;
* }
* sector++;
* ndata -= 512;
* }
*
* g_free(iv);
* qcrypto_ivgen_free(ivgen);
* qcrypto_cipher_free(cipher);
* return 0;
*
*error:
* g_free(iv);
* qcrypto_ivgen_free(ivgen);
* qcrypto_cipher_free(cipher);
* return -1;
* </programlisting>
* </example>
*/
typedef struct QCryptoIVGen QCryptoIVGen;
/* See also QCryptoIVGenAlgorithm enum in qapi/crypto.json */
/**
* qcrypto_ivgen_new:
* @alg: the initialization vector generation algorithm
* @cipheralg: the cipher algorithm or 0
* @hash: the hash algorithm or 0
* @key: the encryption key or NULL
* @nkey: the size of @key in bytes
*
* Create a new initialization vector generator that uses
* the algorithm @alg. Whether the remaining parameters
* are required or not depends on the choice of @alg
* requested.
*
* - QCRYPTO_IVGEN_ALG_PLAIN
*
* The IVs are generated by the 32-bit truncated sector
* number. This should never be used for block devices
* that are larger than 2^32 sectors in size.
* All the other parameters are unused.
*
* - QCRYPTO_IVGEN_ALG_PLAIN64
*
* The IVs are generated by the 64-bit sector number.
* All the other parameters are unused.
*
* - QCRYPTO_IVGEN_ALG_ESSIV:
*
* The IVs are generated by encrypting the 64-bit sector
* number with a hash of an encryption key. The @cipheralg,
* @hash, @key and @nkey parameters are all required.
*
* Returns: a new IV generator, or NULL on error
*/
QCryptoIVGen *qcrypto_ivgen_new(QCryptoIVGenAlgorithm alg,
QCryptoCipherAlgorithm cipheralg,
QCryptoHashAlgorithm hash,
const uint8_t *key, size_t nkey,
Error **errp);
/**
* qcrypto_ivgen_calculate:
* @ivgen: the IV generator object
* @sector: the 64-bit sector number
* @iv: a pre-allocated buffer to hold the generated IV
* @niv: the number of bytes in @iv
* @errp: pointer to a NULL-initialized error object
*
* Calculate a new initialiation vector for the data
* to be stored in sector @sector. The IV will be
* written into the buffer @iv of size @niv.
*
* Returns: 0 on success, -1 on error
*/
int qcrypto_ivgen_calculate(QCryptoIVGen *ivgen,
uint64_t sector,
uint8_t *iv, size_t niv,
Error **errp);
/**
* qcrypto_ivgen_get_algorithm:
* @ivgen: the IV generator object
*
* Get the algorithm used by this IV generator
*
* Returns: the IV generator algorithm
*/
QCryptoIVGenAlgorithm qcrypto_ivgen_get_algorithm(QCryptoIVGen *ivgen);
/**
* qcrypto_ivgen_get_cipher:
* @ivgen: the IV generator object
*
* Get the cipher algorithm used by this IV generator (if
* applicable)
*
* Returns: the cipher algorithm
*/
QCryptoCipherAlgorithm qcrypto_ivgen_get_cipher(QCryptoIVGen *ivgen);
/**
* qcrypto_ivgen_get_hash:
* @ivgen: the IV generator object
*
* Get the hash algorithm used by this IV generator (if
* applicable)
*
* Returns: the hash algorithm
*/
QCryptoHashAlgorithm qcrypto_ivgen_get_hash(QCryptoIVGen *ivgen);
/**
* qcrypto_ivgen_free:
* @ivgen: the IV generator object
*
* Release all resources associated with @ivgen, or a no-op
* if @ivgen is NULL
*/
void qcrypto_ivgen_free(QCryptoIVGen *ivgen);
#endif /* QCRYPTO_IVGEN_H__ */
/*
* QEMU Crypto PBKDF support (Password-Based Key Derivation Function)
*
* Copyright (c) 2015-2016 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_PBKDF_H__
#define QCRYPTO_PBKDF_H__
#include "crypto/hash.h"
/**
* This module provides an interface to the PBKDF2 algorithm
*
* https://en.wikipedia.org/wiki/PBKDF2
*
* <example>
* <title>Generating an AES encryption key from a user password</title>
* <programlisting>
* #include "crypto/cipher.h"
* #include "crypto/random.h"
* #include "crypto/pbkdf.h"
*
* ....
*
* char *password = "a-typical-awful-user-password";
* size_t nkey = qcrypto_cipher_get_key_len(QCRYPTO_CIPHER_ALG_AES_128);
* uint8_t *salt = g_new0(uint8_t, nkey);
* uint8_t *key = g_new0(uint8_t, nkey);
* int iterations;
* QCryptoCipher *cipher;
*
* if (qcrypto_random_bytes(salt, nkey, errp) < 0) {
* g_free(key);
* g_free(salt);
* return -1;
* }
*
* iterations = qcrypto_pbkdf2_count_iters(QCRYPTO_HASH_ALG_SHA256,
* (const uint8_t *)password,
* strlen(password),
* salt, nkey, errp);
* if (iterations < 0) {
* g_free(key);
* g_free(salt);
* return -1;
* }
*
* if (qcrypto_pbkdf2(QCRYPTO_HASH_ALG_SHA256,
* (const uint8_t *)password, strlen(password),
* salt, nkey, iterations, key, nkey, errp) < 0) {
* g_free(key);
* g_free(salt);
* return -1;
* }
*
* g_free(salt);
*
* cipher = qcrypto_cipher_new(QCRYPTO_CIPHER_ALG_AES_128,
* QCRYPTO_CIPHER_MODE_ECB,
* key, nkey, errp);
* g_free(key);
*
* ....encrypt some data...
*
* qcrypto_cipher_free(cipher);
* </programlisting>
* </example>
*
*/
/**
* qcrypto_pbkdf2_supports:
* @hash: the hash algorithm
*
* Determine if the current build supports the PBKDF2 algorithm
* in combination with the hash @hash.
*
* Returns true if supported, false otherwise
*/
bool qcrypto_pbkdf2_supports(QCryptoHashAlgorithm hash);
/**
* qcrypto_pbkdf2:
* @hash: the hash algorithm to use
* @key: the user password / key
* @nkey: the length of @key in bytes
* @salt: a random salt
* @nsalt: length of @salt in bytes
* @iterations: the number of iterations to compute
* @out: pointer to pre-allocated buffer to hold output
* @nout: length of @out in bytes
* @errp: pointer to a NULL-initialized error object
*
* Apply the PBKDF2 algorithm to derive an encryption
* key from a user password provided in @key. The
* @salt parameter is used to perturb the algorithm.
* The @iterations count determines how many times
* the hashing process is run, which influences how
* hard it is to crack the key. The number of @iterations
* should be large enough such that the algorithm takes
* 1 second or longer to derive a key. The derived key
* will be stored in the preallocated buffer @out.
*
* Returns: 0 on success, -1 on error
*/
int qcrypto_pbkdf2(QCryptoHashAlgorithm hash,
const uint8_t *key, size_t nkey,
const uint8_t *salt, size_t nsalt,
unsigned int iterations,
uint8_t *out, size_t nout,
Error **errp);
/**
* qcrypto_pbkdf2_count_iters:
* @hash: the hash algorithm to use
* @key: the user password / key
* @nkey: the length of @key in bytes
* @salt: a random salt
* @nsalt: length of @salt in bytes
* @errp: pointer to a NULL-initialized error object
*
* Time the PBKDF2 algorithm to determine how many
* iterations are required to derive an encryption
* key from a user password provided in @key in 1
* second of compute time. The result of this can
* be used as a the @iterations parameter of a later
* call to qcrypto_pbkdf2().
*
* Returns: number of iterations in 1 second, -1 on error
*/
int qcrypto_pbkdf2_count_iters(QCryptoHashAlgorithm hash,
const uint8_t *key, size_t nkey,
const uint8_t *salt, size_t nsalt,
Error **errp);
#endif /* QCRYPTO_PBKDF_H__ */
/*
* QEMU Crypto random number provider
*
* Copyright (c) 2015-2016 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_RANDOM_H__
#define QCRYPTO_RANDOM_H__
#include "qemu-common.h"
#include "qapi/error.h"
/**
* qcrypto_random_bytes:
* @buf: the buffer to fill
* @buflen: length of @buf in bytes
* @errp: pointer to a NULL-initialized error object
*
* Fill @buf with @buflen bytes of cryptographically strong
* random data
*
* Returns 0 on sucess, -1 on error
*/
int qcrypto_random_bytes(uint8_t *buf,
size_t buflen,
Error **errp);
#endif /* QCRYPTO_RANDOM_H__ */
/*
* QEMU Crypto XTS cipher mode
*
* Copyright (c) 2015-2016 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/>.
*
* This code is originally derived from public domain / WTFPL code in
* LibTomCrypt crytographic library http://libtom.org. The XTS code
* was donated by Elliptic Semiconductor Inc (www.ellipticsemi.com)
* to the LibTom Projects
*
*/
#ifndef QCRYPTO_XTS_H_
#define QCRYPTO_XTS_H_
#include "qemu-common.h"
#include "qapi/error.h"
#define XTS_BLOCK_SIZE 16
typedef void xts_cipher_func(const void *ctx,
size_t length,
uint8_t *dst,
const uint8_t *src);
/**
* xts_decrypt:
* @datactx: the cipher context for data decryption
* @tweakctx: the cipher context for tweak decryption
* @encfunc: the cipher function for encryption
* @decfunc: the cipher function for decryption
* @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
* @length: the length of @dst and @src
* @dst: buffer to hold the decrypted plaintext
* @src: buffer providing the ciphertext
*
* Decrypts @src into @dst
*/
void xts_decrypt(const void *datactx,
const void *tweakctx,
xts_cipher_func *encfunc,
xts_cipher_func *decfunc,
uint8_t *iv,
size_t length,
uint8_t *dst,
const uint8_t *src);
/**
* xts_decrypt:
* @datactx: the cipher context for data encryption
* @tweakctx: the cipher context for tweak encryption
* @encfunc: the cipher function for encryption
* @decfunc: the cipher function for decryption
* @iv: the initialization vector tweak of XTS_BLOCK_SIZE bytes
* @length: the length of @dst and @src
* @dst: buffer to hold the encrypted ciphertext
* @src: buffer providing the plaintext
*
* Decrypts @src into @dst
*/
void xts_encrypt(const void *datactx,
const void *tweakctx,
xts_cipher_func *encfunc,
xts_cipher_func *decfunc,
uint8_t *iv,
size_t length,
uint8_t *dst,
const uint8_t *src);
#endif /* QCRYPTO_XTS_H_ */
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册