Support LUKS encryption in block devices
Subsystems (3)
v3 cover Support LUKS encryption in block devices
This series was previously submitted here:
v1: https://lists.gnu.org/archive/html/qemu-devel/2015-11/msg04748.html
v2: https://lists.gnu.org/archive/html/qemu-block/2016-01/msg00534.html
This series depends on these previously submitted
patches to the block tools, that Kevin has in his
block queue:
https://lists.gnu.org/archive/html/qemu-block/2016-02/msg00361.html
As can be guessed from the subject, the primary goal of this patch series is to support LUKS encryption in the QEMU block layer. QEMU has increasingly been adding native clients for network block protocols (RBD, gluster, NFS, iSCSI, etc) and apps like OpenStack are embracing them as it is much easier to deal with this from a management POV than to deal with the kernel block layer & userspace tools. Unfortunately when using QEMU native clients, apps are locked out of using dm-crypt and LUKS which is undesirable.
This series introduces two new features to the block layer. First there is a general purpose 'luks' format driver which can be layered over any other existing block driver. eg it can be layed above RBD, iSCSI, etc. Second the qcow2 file format is extended so that its embedded encryption can be replaced with the LUKS data format. While you could just layer the general purpose luks driver over qcow2, this is slightly less desirable, as it removes the ability to reliably auto-detect that LUKS is used by QEMU, as opposed to used by the guest OS. Having use of LUKS encoded in the qcow2 header addresses this.
The code is designed such that there is a strict separation between the full disk encryption format and the block I/O layer. Thus there is an generic API for dealing with full disk encryption added to the crypto/ subsystem. The block layer merely calls this FDE API when required, which serves to minimize the code present in the already complex block layer.
The big change in this series is the addition of an integration test in tests/qemu-iotests/145. This uses 'sudo cryptsetup' to create LUKS volumes on the host kernel and perform I/O to/from them. This is validated against QEMU's implementation using qemu-io. Conversely we also create LUKS volumes wth qemu-img and then validate that the kernel can use them. This proves good interoperability between the two implementations across a wide range of crypto algorithms.
The big thing that I have *not* addressed is Fam's idea about modifying the generic block layer so that it can automatically instantitate a LUKS block driver above the qcow2 driver, if qcow2 indicates that it needs encryption support. I'm still not 100% sure this is the right way to go, but I've not had time to investigate, since the focus since last posting has been on dm-crypt interoperability for the generic 'luks' block driver.
The first 11 patches add some supporting APIs and algorithms to the crypto subsystem.
The 12-13 patches introduce the general full disk encryption API to the crypto subsystem and LUKS implementation
Patches 14-18 do a little preparation to the block code for future patches.
Patch 19 addes the new 'luks' block driver format along with a test for interoperability with dm-crypt/cryptsetup
Patches 20-23 convert qcow & qcow2 to the new FDE APIs, enabling LUKS in qcow2 (but not qcow) at the same time.
Patches 24-25 clean up the horrible password handling cruft in the block layer and monitor.
Patch 26 blocks use of the legacy qcow[2] encryption from the system emulators.
Changes since v2:
- Added support for XTS cipher usage mode
- Added support for serpent, twofish & cast5 ciphers
- Use qemu/osdep.h where applicable (Eric)
- Use MAX macro where applicable (Eric)
- Use bool instead of gboolean (Eric)
- Use bytes instead of sectors in API to future proof for 4k sector sizes (Eric)
- Add asserts for LUKS structs matching spec size (Eric)
- Switch to XTS+plain64 instead of CBC+essiv for new LUKS volumes
- Add qemu I/O test 145 for dm-crypt interoperability
- Rearrange vol creation so that we treat size as the logical guest volume size, instead of physical container size (Fam).
- Push malloc of bounce buffer out of loop in LUKS block driver (Eric)
- Many misc typos/docs fixes (Fam, Eric)
Changes since v1:
- Unit testing coverage of the full disk encryption APIs
- Functional testing of LUKS driver via the qemu-iotests for the block layer
- Use GNUTLS random API for the random byte source
- Use Makefile.objs for conditional compilation of pbkdf files (Fam)
- Use 'key-secret' instead of 'key-id' as property for decryption key (Paolo)
- Rename 'fde' to 'crypto' in block code (Kevin)
- Fix accounting of encryption header clusters when checking refcounts (Kevin)
- Rename format 'qcowaes' to 'qcow' (Eric)
- Fix qapi syntax for marking optional parameters (Eric)
- Add assertions in I/O path for BDRV_O_NO_IO flag
- Cleanup return codes / error reporting (Kevin)
- Other misc fixes identified in testing
Changes since WIP:
- QAPI parameters defined for the encryption key ID
- The qcow2 integration of LUKS is working, using extra allocated clusters to store LUKS header instead of trying to expand the main qcow2 header region to > 1 cluster
- qemu-img info now works without prompting for decryption password
- Unit testing of pbkdf2, afsplit and ivgen APis.
Daniel P. Berrange (26):
crypto: add cryptographic random byte source
crypto: add support for PBKDF2 algorithm
crypto: add support for generating initialization vectors
crypto: add support for anti-forensic split algorithm
crypto: skip testing of unsupported cipher algorithms
crypto: add support for the cast5-128 cipher algorithm
crypto: add support for the serpent cipher algorithm
crypto: add support for the twofish cipher algorithm
crypto: import an implementation of the XTS cipher mode
crypto: refactor code for dealing with AES cipher
crypto: wire up XTS mode for cipher APIs
crypto: add block encryption framework
crypto: implement the LUKS block encryption format
block: add flag to indicate that no I/O will be performed qemu-img/qemu-io: don't prompt for passwords if not required
tests: redirect stderr to stdout for iotests
tests: refactor python I/O tests helper main method
tests: add output filter to python I/O tests helper
block: add generic full disk encryption driver
qcow2: make qcow2_encrypt_sectors encrypt in place
qcow2: convert QCow2 to use QCryptoBlock for encryption
qcow: make encrypt_sectors encrypt in place
qcow: convert QCow to use QCryptoBlock for encryption
block: rip out all traces of password prompting
block: remove all encryption handling APIs
block: remove support for legecy AES qcow/qcow2 encryption
Makefile.objs | 2 +- block.c | 99 +- block/Makefile.objs | 2 + block/crypto.c | 568 +++++++++++ block/io.c | 2 + block/qapi.c | 2 +- block/qcow.c | 199 ++-- block/qcow2-cluster.c | 53 +- block/qcow2-refcount.c | 10 + block/qcow2.c | 499 +++++++-- block/qcow2.h | 24 +- blockdev.c | 41 +- crypto/Makefile.objs | 17 + crypto/afsplit.c | 158 +++ crypto/block-luks.c | 1313 ++++++++++++++++++++++++ crypto/block-luks.h | 28 + crypto/block-qcow.c | 173 ++++ crypto/block-qcow.h | 28 + crypto/block.c | 260 +++++ crypto/blockpriv.h | 92 ++ crypto/cipher-builtin.c | 207 +++- crypto/cipher-gcrypt.c | 171 +++- crypto/cipher-nettle.c | 197 +++- crypto/cipher.c | 41 +- crypto/ivgen-essiv.c | 118 +++ crypto/ivgen-essiv.h | 28 + crypto/ivgen-plain.c | 59 ++ crypto/ivgen-plain.h | 28 + crypto/ivgen-plain64.c | 59 ++ crypto/ivgen-plain64.h | 28 + crypto/ivgen.c | 99 ++ crypto/ivgenpriv.h | 49 + crypto/pbkdf-gcrypt.c | 68 ++ crypto/pbkdf-nettle.c | 65 ++ crypto/pbkdf-stub.c | 41 + crypto/pbkdf.c | 77 ++ crypto/random-gcrypt.c | 33 + crypto/random-gnutls.c | 43 + crypto/random-stub.c | 31 + crypto/xts.c | 256 +++++ docs/specs/qcow2.txt | 77 ++ hmp.c | 31 - hw/usb/dev-storage.c | 34 - include/block/block.h | 6 +- include/block/block_int.h | 1 - include/crypto/afsplit.h | 135 +++ include/crypto/block.h | 232 +++++ include/crypto/ivgen.h | 206 ++++ include/crypto/pbkdf.h | 152 +++ include/crypto/random.h | 44 + include/crypto/xts.h | 86 ++ include/monitor/monitor.h | 7 - include/qemu/osdep.h | 2 - monitor.c | 68 -- qapi/block-core.json | 46 +- qapi/crypto.json | 145 ++- qemu-img.c | 66 +- qemu-io.c | 21 - qmp.c | 10 +- tests/.gitignore | 5 + tests/Makefile | 10 + tests/qemu-iotests/049 | 2 +- tests/qemu-iotests/049.out | 10 +- tests/qemu-iotests/082.out | 189 ++++ tests/qemu-iotests/087 | 30 +- tests/qemu-iotests/087.out | 28 +- tests/qemu-iotests/134 | 18 +- tests/qemu-iotests/134.out | 33 +- tests/qemu-iotests/145 | 521 ++++++++++ tests/qemu-iotests/145.out | 2252 +++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/common | 8 + tests/qemu-iotests/group | 1 + tests/qemu-iotests/iotests.py | 48 +- tests/test-crypto-afsplit.c | 191 ++++ tests/test-crypto-block.c | 343 +++++++ tests/test-crypto-cipher.c | 215 +++- tests/test-crypto-ivgen.c | 168 +++ tests/test-crypto-pbkdf.c | 378 +++++++ tests/test-crypto-xts.c | 424 ++++++++ util/oslib-posix.c | 66 -- util/oslib-win32.c | 24 - 81 files changed, 10721 insertions(+), 880 deletions(-) create mode 100644 block/crypto.c create mode 100644 crypto/afsplit.c create mode 100644 crypto/block-luks.c create mode 100644 crypto/block-luks.h create mode 100644 crypto/block-qcow.c create mode 100644 crypto/block-qcow.h create mode 100644 crypto/block.c create mode 100644 crypto/blockpriv.h create mode 100644 crypto/ivgen-essiv.c create mode 100644 crypto/ivgen-essiv.h create mode 100644 crypto/ivgen-plain.c create mode 100644 crypto/ivgen-plain.h create mode 100644 crypto/ivgen-plain64.c create mode 100644 crypto/ivgen-plain64.h create mode 100644 crypto/ivgen.c create mode 100644 crypto/ivgenpriv.h create mode 100644 crypto/pbkdf-gcrypt.c create mode 100644 crypto/pbkdf-nettle.c create mode 100644 crypto/pbkdf-stub.c create mode 100644 crypto/pbkdf.c create mode 100644 crypto/random-gcrypt.c create mode 100644 crypto/random-gnutls.c create mode 100644 crypto/random-stub.c create mode 100644 crypto/xts.c create mode 100644 include/crypto/afsplit.h create mode 100644 include/crypto/block.h create mode 100644 include/crypto/ivgen.h create mode 100644 include/crypto/pbkdf.h create mode 100644 include/crypto/random.h create mode 100644 include/crypto/xts.h create mode 100755 tests/qemu-iotests/145 create mode 100644 tests/qemu-iotests/145.out create mode 100644 tests/test-crypto-afsplit.c create mode 100644 tests/test-crypto-block.c create mode 100644 tests/test-crypto-ivgen.c create mode 100644 tests/test-crypto-pbkdf.c create mode 100644 tests/test-crypto-xts.c
Signature · 1 line
2.5.0
PATCH v3 1/26 crypto: add cryptographic random byte source
There are three backend impls provided. The preferred is gnutls, which is backed by nettle in modern distros. The gcrypt impl is provided for cases where QEMU build against gnutls is disabled, but crypto is still desired. No nettle impl is provided, since it is non-trivial to use the nettle APIs for random numbers. Users of nettle should ensure gnutls is enabled for QEMU.
--- Makefile.objs | 2 +- crypto/Makefile.objs | 4 ++++ crypto/random-gcrypt.c | 33 +++++++++++++++++++++++++++++++++ crypto/random-gnutls.c | 43 +++++++++++++++++++++++++++++++++++++++++++ crypto/random-stub.c | 31 +++++++++++++++++++++++++++++++ include/crypto/random.h | 44 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 156 insertions(+), 1 deletion(-) create mode 100644 crypto/random-gcrypt.c create mode 100644 crypto/random-gnutls.c create mode 100644 crypto/random-stub.c create mode 100644 include/crypto/random.h
Makefile.objs +1 -1
@@ -1,6 +1,6 @@ ####################################################################### # 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
crypto/Makefile.objs +4 -0
@@ -8,6 +8,10 @@ crypto-obj-y += tlscredsanon.o crypto-obj-y += tlscredsx509.o crypto-obj-y += tlssession.o crypto-obj-y += secret.o +crypto-obj-$(if $(CONFIG_GNUTLS),n,$(CONFIG_GCRYPT)) += random-gcrypt.o +crypto-obj-$(CONFIG_GNUTLS) += random-gnutls.o # Let the userspace emulators avoid linking gnutls/etc crypto-aes-obj-y = aes.o + +stub-obj-y += random-stub.o
crypto/random-gcrypt.c new file +33 -0
@@ -0,0 +1,33 @@ +/* +* 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 ; +}
crypto/random-gnutls.c new file +43 -0
43 lines not on this page · raw message
crypto/random-stub.c new file +31 -0
@@ -0,0 +1,31 @@ +/* +* 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 <config-host.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 ; +}
include/crypto/random.h new file +44 -0
44 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 2/26 crypto: add support for PBKDF2 algorithm
In reply to Daniel P. BerrangeThe LUKS data format includes use of PBKDF2 (Password-Based Key Derivation Function). The Nettle library can provide an implementation of this, but we don't want code directly depending on a specific crypto library backend. Introduce a new include/crypto/pbkdf.h header which defines a QEMU API for invoking PBKDK2. The initial implementations are backed by nettle & gcrypt, which are commonly available with distros shipping GNUTLS.
The test suite data is taken from the cryptsetup codebase under the LGPLv2.1+ license. This merely aims to verify that whatever backend we provide for this function in QEMU will comply with the spec.
--- crypto/Makefile.objs | 4 + crypto/pbkdf-gcrypt.c | 68 +++++++++ crypto/pbkdf-nettle.c | 65 ++++++++ crypto/pbkdf-stub.c | 41 +++++ crypto/pbkdf.c | 77 ++++++++++ include/crypto/pbkdf.h | 152 +++++++++++++++++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-pbkdf.c | 378 ++++++++++++++++++++++++++++++++++++++++++++++ 9 files changed, 788 insertions(+) create mode 100644 crypto/pbkdf-gcrypt.c create mode 100644 crypto/pbkdf-nettle.c create mode 100644 crypto/pbkdf-stub.c create mode 100644 crypto/pbkdf.c create mode 100644 include/crypto/pbkdf.h create mode 100644 tests/test-crypto-pbkdf.c
crypto/Makefile.objs +4 -0
@@ -10,8 +10,12 @@ crypto-obj-y += tlssession.o crypto-obj-y += secret.o crypto-obj-$(if $(CONFIG_GNUTLS),n,$(CONFIG_GCRYPT)) += random-gcrypt.o crypto-obj-$(CONFIG_GNUTLS) += random-gnutls.o +crypto-obj-y += pbkdf.o +crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o +crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += pbkdf-gcrypt.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
crypto/pbkdf-gcrypt.c new file +68 -0
68 lines not on this page · raw message
crypto/pbkdf-nettle.c new file +65 -0
65 lines not on this page · raw message
crypto/pbkdf-stub.c new file +41 -0
41 lines not on this page · raw message
crypto/pbkdf.c new file +77 -0
77 lines not on this page · raw message
include/crypto/pbkdf.h new file +152 -0
152 lines not on this page · raw message
tests/.gitignore +1 -0
@@ -14,6 +14,7 @@ test-blockjob-txn test-coroutine test-crypto-cipher test-crypto-hash +test-crypto-pbkdf test-crypto-secret test-crypto-tlscredsx509 test-crypto-tlscredsx509-work/
tests/Makefile +2 -0
@@ -92,6 +92,7 @@ check-unit-$(CONFIG_GNUTLS) += tests/test-io-channel-tls$(EXESUF) check-unit-y += tests/test-io-channel-command$(EXESUF) check-unit-y += tests/test-io-channel-buffer$(EXESUF) check-unit-y += tests/test-base64$(EXESUF) +check-unit-y += tests/test-crypto-pbkdf$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -495,6 +496,7 @@ tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \ tests/io-channel-helpers.o $(test-io-obj-y) tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \ tests/io-channel-helpers.o $(test-io-obj-y) +tests/test-crypto-pbkdf$(EXESUF): tests/test-crypto-pbkdf.o $(test-crypto-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-crypto-pbkdf.c new file +378 -0
378 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 3/26 crypto: add support for generating initialization vectors
In reply to Daniel P. BerrangeThere are a number of different algorithms that can be used to generate initialization vectors for disk encryption. This introduces a simple internal QCryptoBlockIV object to provide a consistent internal API to the different algorithms. The initially implemented algorithms are 'plain', 'plain64' and 'essiv', each matching the same named algorithm provided by the Linux kernel dm-crypt driver.
--- crypto/Makefile.objs | 4 + crypto/ivgen-essiv.c | 118 ++++++++++++++++++++++++++ crypto/ivgen-essiv.h | 28 +++++++ crypto/ivgen-plain.c | 59 +++++++++++++ crypto/ivgen-plain.h | 28 +++++++ crypto/ivgen-plain64.c | 59 +++++++++++++ crypto/ivgen-plain64.h | 28 +++++++ crypto/ivgen.c | 99 ++++++++++++++++++++++ crypto/ivgenpriv.h | 49 +++++++++++ include/crypto/ivgen.h | 206 ++++++++++++++++++++++++++++++++++++++++++++++ qapi/crypto.json | 19 +++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-ivgen.c | 168 +++++++++++++++++++++++++++++++++++++ 14 files changed, 868 insertions(+) create mode 100644 crypto/ivgen-essiv.c create mode 100644 crypto/ivgen-essiv.h create mode 100644 crypto/ivgen-plain.c create mode 100644 crypto/ivgen-plain.h create mode 100644 crypto/ivgen-plain64.c create mode 100644 crypto/ivgen-plain64.h create mode 100644 crypto/ivgen.c create mode 100644 crypto/ivgenpriv.h create mode 100644 include/crypto/ivgen.h create mode 100644 tests/test-crypto-ivgen.c
crypto/Makefile.objs +4 -0
@@ -13,6 +13,10 @@ crypto-obj-$(CONFIG_GNUTLS) += random-gnutls.o crypto-obj-y += pbkdf.o crypto-obj-$(CONFIG_NETTLE) += pbkdf-nettle.o crypto-obj-$(if $(CONFIG_NETTLE),n,$(CONFIG_GCRYPT)) += 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 # Let the userspace emulators avoid linking gnutls/etc crypto-aes-obj-y = aes.o
crypto/ivgen-essiv.c new file +118 -0
118 lines not on this page · raw message
crypto/ivgen-essiv.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* 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__ */
crypto/ivgen-plain.c new file +59 -0
59 lines not on this page · raw message
crypto/ivgen-plain.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* 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__ */
crypto/ivgen-plain64.c new file +59 -0
59 lines not on this page · raw message
crypto/ivgen-plain64.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* 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__ */
crypto/ivgen.c new file +99 -0
99 lines not on this page · raw message
crypto/ivgenpriv.h new file +49 -0
49 lines not on this page · raw message
include/crypto/ivgen.h new file +206 -0
206 lines not on this page · raw message
qapi/crypto.json +19 -0
@@ -78,3 +78,22 @@ { 'enum': 'QCryptoCipherMode', 'prefix': 'QCRYPTO_CIPHER_MODE', 'data': ['ecb', 'cbc']} + + +## +# QCryptoIVGenAlgorithm: +# +# The supported algorithms for generating initialization +# vectors for full disk encryption. The 'plain' generator +# should not be used for disks with sector numbers larger +# than 2^32, except where compatibility with pre-existing +# Linux dm-crypt volumes is required. +# +# @plain: 64-bit sector number truncated to 32-bits +# @plain64: 64-bit sector number +# @essiv: 64-bit sector number encrypted with a hash of the encryption key +# Since: 2.6 +## +{ 'enum': 'QCryptoIVGenAlgorithm', + 'prefix': 'QCRYPTO_IVGEN_ALG', + 'data': ['plain', 'plain64', 'essiv']}
tests/.gitignore +1 -0
@@ -14,6 +14,7 @@ test-blockjob-txn test-coroutine test-crypto-cipher test-crypto-hash +test-crypto-ivgen test-crypto-pbkdf test-crypto-secret test-crypto-tlscredsx509
tests/Makefile +2 -0
@@ -93,6 +93,7 @@ check-unit-y += tests/test-io-channel-command$(EXESUF) check-unit-y += tests/test-io-channel-buffer$(EXESUF) check-unit-y += tests/test-base64$(EXESUF) check-unit-y += tests/test-crypto-pbkdf$(EXESUF) +check-unit-y += tests/test-crypto-ivgen$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -497,6 +498,7 @@ tests/test-io-channel-command$(EXESUF): tests/test-io-channel-command.o \ tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \ tests/io-channel-helpers.o $(test-io-obj-y) tests/test-crypto-pbkdf$(EXESUF): tests/test-crypto-pbkdf.o $(test-crypto-obj-y) +tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-crypto-ivgen.c new file +168 -0
168 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 4/26 crypto: add support for anti-forensic split algorithm
In reply to Daniel P. BerrangeThe LUKS format specifies an anti-forensic split algorithm which is used to artificially expand the size of the key material on disk. This is an implementation of that algorithm.
--- crypto/Makefile.objs | 1 + crypto/afsplit.c | 158 ++++++++++++++++++++++++++++++++++++ include/crypto/afsplit.h | 135 +++++++++++++++++++++++++++++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-afsplit.c | 191 ++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 488 insertions(+) create mode 100644 crypto/afsplit.c create mode 100644 include/crypto/afsplit.h create mode 100644 tests/test-crypto-afsplit.c
crypto/Makefile.objs +1 -0
@@ -17,6 +17,7 @@ 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 # Let the userspace emulators avoid linking gnutls/etc crypto-aes-obj-y = aes.o
crypto/afsplit.c new file +158 -0
158 lines not on this page · raw message
include/crypto/afsplit.h new file +135 -0
135 lines not on this page · raw message
tests/.gitignore +1 -0
@@ -12,6 +12,7 @@ test-base64 test-bitops test-blockjob-txn test-coroutine +test-crypto-afsplit test-crypto-cipher test-crypto-hash test-crypto-ivgen
tests/Makefile +2 -0
@@ -94,6 +94,7 @@ check-unit-y += tests/test-io-channel-buffer$(EXESUF) check-unit-y += tests/test-base64$(EXESUF) check-unit-y += tests/test-crypto-pbkdf$(EXESUF) check-unit-y += tests/test-crypto-ivgen$(EXESUF) +check-unit-y += tests/test-crypto-afsplit$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -499,6 +500,7 @@ tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \ tests/io-channel-helpers.o $(test-io-obj-y) tests/test-crypto-pbkdf$(EXESUF): tests/test-crypto-pbkdf.o $(test-crypto-obj-y) tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y) +tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-crypto-afsplit.c new file +191 -0
191 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 5/26 crypto: skip testing of unsupported cipher algorithms
In reply to Daniel P. BerrangeWe don't guarantee that all crypto backends will support all cipher algorithms, so we should skip tests unless the crypto backend indicates support.
--- tests/test-crypto-cipher.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-)
tests/test-crypto-cipher.c +3 -1
@@ -379,7 +379,9 @@ int main(int argc, char **argv)g_assert (qcrypto_init (NULL ) ==0 );for (i =0 ; i <G_N_ELEMENTS (test_data); i++) { -g_test_add_data_func (test_data[i].path, &test_data[i], test_cipher); +if (qcrypto_cipher_supports (test_data[i].alg)) { +g_test_add_data_func (test_data[i].path, &test_data[i], test_cipher); + } } g_test_add_func("/crypto/cipher/null-iv",
Signature · 1 line
2.5.0
PATCH v3 6/26 crypto: add support for the cast5-128 cipher algorithm
In reply to Daniel P. BerrangeA new cipher algorithm 'cast-5-128' is defined for the Cast-5 algorithm with 128 bit key size. Smaller key sizes are supported by Cast-5, but nothing in QEMU should use them, so only 128 bit keys are permitted.
The nettle and gcrypt cipher backends are updated to support the new cipher and a test vector added to the cipher test suite. The new algorithm is enabled in the LUKS block encryption driver.
--- crypto/cipher-gcrypt.c | 18 +++++++++++++++++- crypto/cipher-nettle.c | 26 ++++++++++++++++++++++++++ crypto/cipher.c | 2 ++ qapi/crypto.json | 5 ++++- tests/test-crypto-cipher.c | 9 +++++++++ 5 files changed, 58 insertions(+), 2 deletions(-)
crypto/cipher-gcrypt.c +17 -1
@@ -29,6 +29,7 @@ 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 :return true;default :return false;
@@ -84,6 +85,10 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, gcryalg =GCRY_CIPHER_AES256 ;break ; +case QCRYPTO_CIPHER_ALG_CAST5_128 : + gcryalg =GCRY_CIPHER_CAST5 ; +break ; +default :error_setg (errp,"Unsupported cipher algorithm %d" , alg);return NULL ;
@@ -113,7 +118,18 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, ctx->blocksize =8 ; }else { err =gcry_cipher_setkey (ctx->handle, key, nkey); - ctx->blocksize =16 ; +switch (cipher->alg) { +case QCRYPTO_CIPHER_ALG_AES_128 : +case QCRYPTO_CIPHER_ALG_AES_192 : +case QCRYPTO_CIPHER_ALG_AES_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",
crypto/cipher-nettle.c +26 -0
@@ -23,6 +23,7 @@#include <nettle/aes.h> #include <nettle/des.h> #include <nettle/cbc.h> +#include <nettle/cast128.h> #if CONFIG_NETTLE_VERSION_MAJOR <3 typedef nettle_crypt_func nettle_cipher_func ;
@@ -63,6 +64,18 @@ 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); +} +typedef struct QCryptoCipherNettle QCryptoCipherNettle ;struct QCryptoCipherNettle {void *ctx_encrypt;
@@ -80,6 +93,7 @@ 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 :return true;default :return false;
@@ -143,6 +157,18 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, ctx->blocksize =AES_BLOCK_SIZE ;break ; + +case QCRYPTO_CIPHER_ALG_CAST5_128 : + ctx->ctx_encrypt = g_new0(struct cast128_ctx, 1); + ctx->ctx_decrypt =NULL ;/* 1 ctx can do both */ + +cast5_set_key (ctx->ctx_encrypt, nkey, key); + + ctx->alg_encrypt = cast128_encrypt_wrapper; + ctx->alg_decrypt = cast128_decrypt_wrapper; + + ctx->blocksize =CAST128_BLOCK_SIZE ; +break ;default :error_setg (errp,"Unsupported cipher algorithm %d" , alg); goto error;
crypto/cipher.c +2 -0
@@ -27,6 +27,7 @@ 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, };static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX ] = {
@@ -34,6 +35,7 @@ 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, };static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX ] = {
qapi/crypto.json +4 -1
@@ -59,11 +59,14 @@ # @aes-192: AES with 192 bit / 24 byte keys # @aes-256: AES with 256 bit / 32 byte keys # @des-rfb: RFB specific variant of single DES. Do not use except in VNC. +# @cast5-128: Cast5 with 128 bit / 16 byte keys # Since: 2.6 ## { 'enum': 'QCryptoCipherAlgorithm', 'prefix': 'QCRYPTO_CIPHER_ALG', - 'data': ['aes-128', 'aes-192', 'aes-256', 'des-rfb']} + 'data': ['aes-128', 'aes-192', 'aes-256', + 'des-rfb', + 'cast5-128']} ##
tests/test-crypto-cipher.c +9 -0
@@ -164,6 +164,15 @@ static QCryptoCipherTestData test_data[] = {"ffd29f1bb5596ad94ea2d8e6196b7f09" "30d8ed0bf2773af36dd82a6280c20926", }, + { +/* RFC 2144, Appendix B.1 */ + .path = "/crypto/cipher/cast5-128", + .alg = QCRYPTO_CIPHER_ALG_CAST5_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "0123456712345678234567893456789A", + .plaintext = "0123456789abcdef", + .ciphertext = "238b4fe5847e44b2", + }, };
Signature · 1 line
2.5.0
PATCH v3 7/26 crypto: add support for the serpent cipher algorithm
In reply to Daniel P. BerrangeNew cipher algorithms 'serpent-128', 'serpent-192' and 'serpent-256' are defined for the Serpent algorithm.
The nettle and gcrypt cipher backends are updated to support the new cipher and a test vector added to the cipher test suite. The new algorithm is enabled in the LUKS block encryption driver.
--- crypto/cipher-gcrypt.c | 18 ++++++++++++++++++ crypto/cipher-nettle.c | 31 +++++++++++++++++++++++++++++++ crypto/cipher.c | 6 ++++++ qapi/crypto.json | 6 +++++- tests/test-crypto-cipher.c | 39 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 99 insertions(+), 1 deletion(-)
crypto/cipher-gcrypt.c +18 -0
@@ -30,6 +30,9 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)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 :return true;default :return false;
@@ -89,6 +92,18 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, 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 ; +default :error_setg (errp,"Unsupported cipher algorithm %d" , alg);return NULL ;
@@ -122,6 +137,9 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm 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 : ctx->blocksize =16 ;break ;case QCRYPTO_CIPHER_ALG_CAST5_128 :
crypto/cipher-nettle.c +31 -0
@@ -24,6 +24,7 @@#include <nettle/des.h> #include <nettle/cbc.h> #include <nettle/cast128.h> +#include <nettle/serpent.h> #if CONFIG_NETTLE_VERSION_MAJOR <3 typedef nettle_crypt_func nettle_cipher_func ;
@@ -76,6 +77,18 @@ static void cast128_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,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); +} +typedef struct QCryptoCipherNettle QCryptoCipherNettle ;struct QCryptoCipherNettle {void *ctx_encrypt;
@@ -94,6 +107,9 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)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 :return true;default :return false;
@@ -169,6 +185,21 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, 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_encrypt = g_new0(struct serpent_ctx, 1); + ctx->ctx_decrypt =NULL ;/* 1 ctx can do both */ + +serpent_set_key (ctx->ctx_encrypt, nkey, key); + + ctx->alg_encrypt = serpent_encrypt_wrapper; + ctx->alg_decrypt = serpent_decrypt_wrapper; + + ctx->blocksize =SERPENT_BLOCK_SIZE ; +break ; +default :error_setg (errp,"Unsupported cipher algorithm %d" , alg); goto error;
crypto/cipher.c +6 -0
@@ -28,6 +28,9 @@ static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { [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, };static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX ] = {
@@ -36,6 +39,9 @@ static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { [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, };static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX ] = {
qapi/crypto.json +5 -1
@@ -60,13 +60,17 @@ # @aes-256: AES with 256 bit / 32 byte keys # @des-rfb: RFB specific variant of single DES. Do not use except in VNC. # @cast5-128: Cast5 with 128 bit / 16 byte keys +# @serpent-128: Serpent with 128 bit / 16 byte keys +# @serpent-192: Serpent with 192 bit / 24 byte keys +# @serpent-256: Serpent with 256 bit / 32 byte keys # Since: 2.6 ## { 'enum': 'QCryptoCipherAlgorithm', 'prefix': 'QCRYPTO_CIPHER_ALG', 'data': ['aes-128', 'aes-192', 'aes-256', 'des-rfb', - 'cast5-128']} + 'cast5-128', + 'serpent-128', 'serpent-192', 'serpent-256']} ##
tests/test-crypto-cipher.c +39 -0
@@ -173,6 +173,45 @@ static QCryptoCipherTestData test_data[] = { .plaintext = "0123456789abcdef", .ciphertext = "238b4fe5847e44b2", }, + { +/* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-128", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "00000000000000000000000000000000", + .plaintext = "d29d576fcea3a3a7ed9099f29273d78e", + .ciphertext = "b2288b968ae8b08648d1ce9606fd992d", + }, + { +/* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-192", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key ="00000000000000000000000000000000" + "0000000000000000", + .plaintext = "d29d576fceaba3a7ed9899f2927bd78e", + .ciphertext = "130e353e1037c22405e8faefb2c3c3e9", + }, + { +/* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-256a", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key ="00000000000000000000000000000000" + "00000000000000000000000000000000", + .plaintext = "d095576fcea3e3a7ed98d9f29073d78e", + .ciphertext = "b90ee5862de69168f2bdd5125b45472b", + }, + { +/* libgcrypt serpent.c */ + .path = "/crypto/cipher/serpent-256b", + .alg = QCRYPTO_CIPHER_ALG_SERPENT_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key ="00000000000000000000000000000000" + "00000000000000000000000000000000", + .plaintext = "00000000010000000200000003000000", + .ciphertext = "2061a42782bd52ec691ec383b03ba77c", + }, };
Signature · 1 line
2.5.0
PATCH v3 8/26 crypto: add support for the twofish cipher algorithm
In reply to Daniel P. BerrangeNew cipher algorithms 'twofish-128', 'twofish-192' and 'twofish-256' are defined for the Twofish algorithm. The gcrypt backend does not support 'twofish-192'.
The nettle and gcrypt cipher backends are updated to support the new cipher and a test vector added to the cipher test suite. The new algorithm is enabled in the LUKS block encryption driver.
--- crypto/cipher-gcrypt.c | 12 ++++++++++++ crypto/cipher-nettle.c | 30 ++++++++++++++++++++++++++++++ crypto/cipher.c | 6 ++++++ qapi/crypto.json | 6 +++++- tests/test-crypto-cipher.c | 29 +++++++++++++++++++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-)
crypto/cipher-gcrypt.c +12 -0
@@ -33,6 +33,8 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)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;
@@ -104,6 +106,14 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, 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 ;
@@ -140,6 +150,8 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg,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 :
crypto/cipher-nettle.c +30 -0
@@ -25,6 +25,7 @@#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 ;
@@ -89,6 +90,18 @@ static void serpent_decrypt_wrapper(cipher_ctx_t ctx, cipher_length_t length,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;
@@ -110,6 +123,9 @@ bool qcrypto_cipher_supports(QCryptoCipherAlgorithm alg)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;
@@ -200,6 +216,20 @@ QCryptoCipher *qcrypto_cipher_new(QCryptoCipherAlgorithm alg, 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_encrypt = g_new0(struct twofish_ctx, 1); + ctx->ctx_decrypt =NULL ;/* 1 ctx can do both */ + +twofish_set_key (ctx->ctx_encrypt, 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;
crypto/cipher.c +6 -0
@@ -31,6 +31,9 @@ static size_t alg_key_len[QCRYPTO_CIPHER_ALG__MAX] = { [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 ] = {
@@ -42,6 +45,9 @@ static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = { [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 ] = {
qapi/crypto.json +5 -1
@@ -63,6 +63,9 @@ # @serpent-128: Serpent with 128 bit / 16 byte keys # @serpent-192: Serpent with 192 bit / 24 byte keys # @serpent-256: Serpent with 256 bit / 32 byte keys +# @twofish-128: Twofish with 128 bit / 16 byte keys +# @twofish-192: Twofish with 192 bit / 24 byte keys +# @twofish-256: Twofish with 256 bit / 32 byte keys # Since: 2.6 ## { 'enum': 'QCryptoCipherAlgorithm',
@@ -70,7 +73,8 @@ 'data': ['aes-128', 'aes-192', 'aes-256', 'des-rfb', 'cast5-128', - 'serpent-128', 'serpent-192', 'serpent-256']} + 'serpent-128', 'serpent-192', 'serpent-256', + 'twofish-128', 'twofish-192', 'twofish-256']} ##
tests/test-crypto-cipher.c +29 -0
@@ -212,6 +212,35 @@ static QCryptoCipherTestData test_data[] = { .plaintext = "00000000010000000200000003000000", .ciphertext = "2061a42782bd52ec691ec383b03ba77c", }, + { +/* Twofish paper "Known Answer Test" */ + .path = "/crypto/cipher/twofish-128", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_128, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key = "d491db16e7b1c39e86cb086b789f5419", + .plaintext = "019f9809de1711858faac3a3ba20fbc3", + .ciphertext = "6363977de839486297e661c6c9d668eb", + }, + { +/* Twofish paper "Known Answer Test", I=3 */ + .path = "/crypto/cipher/twofish-192", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_192, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key ="88b2b2706b105e36b446bb6d731a1e88" + "efa71f788965bd44", + .plaintext = "39da69d6ba4997d585b6dc073ca341b2", + .ciphertext = "182b02d81497ea45f9daacdc29193a65", + }, + { +/* Twofish paper "Known Answer Test", I=4 */ + .path = "/crypto/cipher/twofish-256", + .alg = QCRYPTO_CIPHER_ALG_TWOFISH_256, + .mode = QCRYPTO_CIPHER_MODE_ECB, + .key ="d43bb7556ea32e46f2a282b7d45b4e0d" + "57ff739d4dc92c1bd7fc01700cc8216f", + .plaintext = "90afe91bb288544f2c32dc239b2635e6", + .ciphertext = "6cb4561c40bf0a9705931cb6d408e7fa", + }, };
Signature · 1 line
2.5.0
PATCH v3 9/26 crypto: import an implementation of the XTS cipher mode
In reply to Daniel P. BerrangeThe XTS (XEX with tweaked-codebook and ciphertext stealing) cipher mode is commonly used in full disk encryption. There is unfortunately no implementation of it in either libgcrypt or nettle, so we need to provide our own.
The libtomcrypt project provides a repository of crypto algorithms under a choice of either "public domain" or the "what the fuck public license".
So this impl is taken from the libtomcrypt GIT repo and adapted to be compatible with the way we need to call ciphers provided by nettle/gcrypt.
--- crypto/Makefile.objs | 1 + crypto/xts.c | 256 +++++++++++++++++++++++++++++ include/crypto/xts.h | 86 ++++++++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-xts.c | 424 ++++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 770 insertions(+) create mode 100644 crypto/xts.c create mode 100644 include/crypto/xts.h create mode 100644 tests/test-crypto-xts.c
crypto/Makefile.objs +1 -0
@@ -18,6 +18,7 @@ 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 # Let the userspace emulators avoid linking gnutls/etc crypto-aes-obj-y = aes.o
crypto/xts.c new file +256 -0
256 lines not on this page · raw message
include/crypto/xts.h new file +86 -0
86 lines not on this page · raw message
tests/.gitignore +1 -0
@@ -25,6 +25,7 @@ test-crypto-tlssession test-crypto-tlssession-work/ test-crypto-tlssession-client/ test-crypto-tlssession-server/ +test-crypto-xts test-cutils test-hbitmap test-int128
tests/Makefile +2 -0
@@ -95,6 +95,7 @@ check-unit-y += tests/test-base64$(EXESUF) check-unit-y += tests/test-crypto-pbkdf$(EXESUF) check-unit-y += tests/test-crypto-ivgen$(EXESUF) check-unit-y += tests/test-crypto-afsplit$(EXESUF) +check-unit-y += tests/test-crypto-xts$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -474,6 +475,7 @@ tests/test-bitops$(EXESUF): tests/test-bitops.o $(test-util-obj-y) tests/test-crypto-hash$(EXESUF): tests/test-crypto-hash.o $(test-crypto-obj-y) tests/test-crypto-cipher$(EXESUF): tests/test-crypto-cipher.o $(test-crypto-obj-y) tests/test-crypto-secret$(EXESUF): tests/test-crypto-secret.o $(test-crypto-obj-y) +tests/test-crypto-xts$(EXESUF): tests/test-crypto-xts.o $(test-crypto-obj-y) tests/crypto-tls-x509-helpers.o-cflags := $(TASN1_CFLAGS) tests/crypto-tls-x509-helpers.o-libs := $(TASN1_LIBS)
tests/test-crypto-xts.c new file +424 -0
424 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 10/26 crypto: refactor code for dealing with AES cipher
In reply to Daniel P. BerrangeThe built-in and nettle cipher backends for AES maintain two separate AES contexts, one for encryption and one for decryption. This is going to be inconvenient for the future code dealing with XTS, so wrap them up in a single struct so there is just one pointer to pass around for both encryptin and decryption.
--- crypto/cipher-builtin.c | 126 ++++++++++++++++++++++++++++++------------------ crypto/cipher-nettle.c | 57 +++++++++++----------- 2 files changed, 109 insertions(+), 74 deletions(-)
crypto/cipher-builtin.c +80 -46
@@ -22,10 +22,14 @@#include "crypto/aes.h" #include "crypto/desrfb.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;uint8_t iv[AES_BLOCK_SIZE ]; };typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB ;
@@ -67,6 +71,58 @@ 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 int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher, const void *in, void *out,
@@ -75,29 +131,18 @@ 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 ; +default : +g_assert_not_reached (); }return 0 ;
@@ -112,29 +157,18 @@ 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 ; +default : +g_assert_not_reached (); }return 0 ;
@@ -173,12 +207,12 @@ static int qcrypto_cipher_init_aes(QCryptoCipher *cipher, ctxt =g_new0 (QCryptoCipherBuiltin,1 ); -if (AES_set_encrypt_key (key, nkey *8 , &ctxt->state.aes.encrypt_key) !=0 ) { +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.decrypt_key) !=0 ) { +if (AES_set_decrypt_key (key, nkey *8 , &ctxt->state.aes.key.dec) !=0 ) {error_setg (errp,"Failed to set decryption key" ); goto error; }
crypto/cipher-nettle.c +29 -28
141 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 11/26 crypto: wire up XTS mode for cipher APIs
In reply to Daniel P. BerrangeIntroduce 'XTS' as a permitted mode for the cipher APIs. With XTS the key provided must be twice the size of the key normally required for any given algorithm. This is because the key will be split into two pieces for use in XTS mode.
--- crypto/cipher-builtin.c | 85 +++++++++++++++++++++++++--- crypto/cipher-gcrypt.c | 123 ++++++++++++++++++++++++++++++++--------- crypto/cipher-nettle.c | 79 ++++++++++++++++++++++++-- crypto/cipher.c | 27 +++++++-- qapi/crypto.json | 3 +- tests/test-crypto-cipher.c | 134 ++++++++++++++++++++++++++++++++++++++++++++- 6 files changed, 405 insertions(+), 46 deletions(-)
crypto/cipher-builtin.c +76 -9
@@ -21,6 +21,7 @@#include "qemu/osdep.h" #include "crypto/aes.h" #include "crypto/desrfb.h" +#include "crypto/xts.h" typedef struct QCryptoCipherBuiltinAESContext QCryptoCipherBuiltinAESContext ;struct QCryptoCipherBuiltinAESContext {
@@ -30,6 +31,7 @@ struct QCryptoCipherBuiltinAESContext {typedef struct QCryptoCipherBuiltinAES QCryptoCipherBuiltinAES ;struct QCryptoCipherBuiltinAES {QCryptoCipherBuiltinAESContext key; +QCryptoCipherBuiltinAESContext key_tweak;uint8_t iv[AES_BLOCK_SIZE ]; };typedef struct QCryptoCipherBuiltinDESRFB QCryptoCipherBuiltinDESRFB ;
@@ -123,6 +125,30 @@ static void qcrypto_cipher_aes_ecb_decrypt(AES_KEY *key, } +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,
@@ -141,6 +167,14 @@ static int qcrypto_cipher_encrypt_aes(QCryptoCipher *cipher, &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 (); }
@@ -167,6 +201,14 @@ static int qcrypto_cipher_decrypt_aes(QCryptoCipher *cipher, &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 (); }
@@ -200,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.key.enc) !=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.key.dec) !=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 ;
@@ -356,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; }
crypto/cipher-gcrypt.c +98 -25
202 lines not on this page · raw message
crypto/cipher-nettle.c +73 -6
148 lines not on this page · raw message
crypto/cipher.c +23 -4
@@ -53,6 +53,7 @@ static size_t alg_block_len[QCRYPTO_CIPHER_ALG__MAX] = {static bool mode_need_iv[QCRYPTO_CIPHER_MODE__MAX ] = { [QCRYPTO_CIPHER_MODE_ECB ] = false, [QCRYPTO_CIPHER_MODE_CBC ] = true, + [QCRYPTO_CIPHER_MODE_XTS ] = true, };
@@ -93,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) {
@@ -102,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; }
qapi/crypto.json +2 -1
@@ -84,11 +84,12 @@ # # @ecb: Electronic Code Book # @cbc: Cipher Block Chaining +# @xts: XEX with tweaked code book and ciphertext stealing # Since: 2.6 ## { 'enum': 'QCryptoCipherMode', 'prefix': 'QCRYPTO_CIPHER_MODE', - 'data': ['ecb', 'cbc']} + 'data': ['ecb', 'cbc', 'xts']} ##
tests/test-crypto-cipher.c +133 -1
146 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 12/26 crypto: add block encryption framework
In reply to Daniel P. BerrangeAdd a generic framework for support different block encryption formats. Upon instantiating a QCryptoBlock object, it will read the encryption header and extract the encryption keys. It is then possible to call methods to encrypt/decrypt data buffers.
There is also a mode whereby it will create/initialize a new encryption header on a previously unformatted volume.
The initial framework comes with support for the legacy QCow AES based encryption. This enables code in the QCow driver to be consolidated later.
--- crypto/Makefile.objs | 2 + crypto/block-qcow.c | 173 +++++++++++++++++++++++++++++++ crypto/block-qcow.h | 28 +++++ crypto/block.c | 258 ++++++++++++++++++++++++++++++++++++++++++++++ crypto/blockpriv.h | 92 +++++++++++++++++ include/crypto/block.h | 232 +++++++++++++++++++++++++++++++++++++++++ qapi/crypto.json | 67 ++++++++++++ tests/.gitignore | 1 + tests/Makefile | 2 + tests/test-crypto-block.c | 239 ++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 1094 insertions(+) create mode 100644 crypto/block-qcow.c create mode 100644 crypto/block-qcow.h create mode 100644 crypto/block.c create mode 100644 crypto/blockpriv.h create mode 100644 include/crypto/block.h create mode 100644 tests/test-crypto-block.c
crypto/Makefile.objs +2 -0
@@ -19,6 +19,8 @@ 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 # Let the userspace emulators avoid linking gnutls/etc crypto-aes-obj-y = aes.o
crypto/block-qcow.c new file +173 -0
173 lines not on this page · raw message
crypto/block-qcow.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* 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__ */
crypto/block.c new file +258 -0
258 lines not on this page · raw message
crypto/blockpriv.h new file +92 -0
92 lines not on this page · raw message
include/crypto/block.h new file +232 -0
232 lines not on this page · raw message
qapi/crypto.json +67 -0
70 lines not on this page · raw message
tests/.gitignore +1 -0
@@ -13,6 +13,7 @@ test-bitops test-blockjob-txn test-coroutine test-crypto-afsplit +test-crypto-block test-crypto-cipher test-crypto-hash test-crypto-ivgen
tests/Makefile +2 -0
@@ -96,6 +96,7 @@ check-unit-y += tests/test-crypto-pbkdf$(EXESUF) check-unit-y += tests/test-crypto-ivgen$(EXESUF) check-unit-y += tests/test-crypto-afsplit$(EXESUF) check-unit-y += tests/test-crypto-xts$(EXESUF) +check-unit-y += tests/test-crypto-block$(EXESUF) check-block-$(CONFIG_POSIX) += tests/qemu-iotests-quick.sh
@@ -503,6 +504,7 @@ tests/test-io-channel-buffer$(EXESUF): tests/test-io-channel-buffer.o \ tests/test-crypto-pbkdf$(EXESUF): tests/test-crypto-pbkdf.o $(test-crypto-obj-y) tests/test-crypto-ivgen$(EXESUF): tests/test-crypto-ivgen.o $(test-crypto-obj-y) tests/test-crypto-afsplit$(EXESUF): tests/test-crypto-afsplit.o $(test-crypto-obj-y) +tests/test-crypto-block$(EXESUF): tests/test-crypto-block.o $(test-crypto-obj-y) libqos-obj-y = tests/libqos/pci.o tests/libqos/fw_cfg.o tests/libqos/malloc.o libqos-obj-y += tests/libqos/i2c.o tests/libqos/libqos.o
tests/test-crypto-block.c new file +239 -0
239 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 13/26 crypto: implement the LUKS block encryption format
In reply to Daniel P. BerrangeProvide a block encryption implementation that follows the LUKS/dm-crypt specification.
This supports all combinations of hash, cipher algorithm, cipher mode and iv generator that are implemented by the current crypto layer.
The notable missing feature is support for the 'xts' cipher mode, which is commonly used for disk encryption instead of 'cbc'. This is because it is not provided by either nettle or libgcrypt. A suitable implementation will be identified & integrated later.
There is support for opening existing volumes formatted by dm-crypt, and for formatting new volumes. In the latter case it will only use key slot 0.
--- crypto/Makefile.objs | 1 + crypto/block-luks.c | 1313 +++++++++++++++++++++++++++++++++++++++++++++ crypto/block-luks.h | 28 + crypto/block.c | 2 + qapi/crypto.json | 51 +- tests/test-crypto-block.c | 104 ++++ 6 files changed, 1495 insertions(+), 4 deletions(-) create mode 100644 crypto/block-luks.c create mode 100644 crypto/block-luks.h
crypto/Makefile.objs +1 -0
@@ -21,6 +21,7 @@ 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
crypto/block-luks.c new file +1313 -0
1,313 lines not on this page · raw message
crypto/block-luks.h new file +28 -0
@@ -0,0 +1,28 @@ +/* +* 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__ */
crypto/block.c +2 -0
@@ -21,9 +21,11 @@#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, };
qapi/crypto.json +47 -4
83 lines not on this page · raw message
tests/test-crypto-block.c +104 -0
116 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 14/26 block: add flag to indicate that no I/O will be performed
In reply to Daniel P. BerrangeWhen opening an image it is useful to know whether the caller intends to perform I/O on the image or not. In the case of encrypted images this will allow the block driver to avoid having to prompt for decryption keys when we merely want to query header metadata about the image. eg qemu-img info
This flag is enforced at the top level only, since even if we don't want todo I/O on the 'qcow2' file payload, the underlying 'file' driver will still need todo I/O to read the qcow2 header, for example.
--- block.c | 5 +++-- block/io.c | 2 ++ include/block/block.h | 1 + qemu-img.c | 44 ++++++++++++++++++++++---------------------- 4 files changed, 28 insertions(+), 24 deletions(-)
block.c +3 -2
@@ -720,7 +720,8 @@ static void bdrv_inherited_options(int *child_flags, QDict *child_options, flags |=BDRV_O_UNMAP ;/* Clear flags that only apply to the top layer */ - flags &= ~(BDRV_O_SNAPSHOT |BDRV_O_NO_BACKING |BDRV_O_COPY_ON_READ ); + flags &= ~(BDRV_O_SNAPSHOT |BDRV_O_NO_BACKING |BDRV_O_COPY_ON_READ | +BDRV_O_NO_IO ); *child_flags = flags; }
@@ -740,7 +741,7 @@ static void bdrv_inherited_fmt_options(int *child_flags, QDict *child_options, child_file.inherit_options (child_flags, child_options, parent_flags, parent_options); - *child_flags &= ~BDRV_O_PROTOCOL ; + *child_flags &= ~(BDRV_O_PROTOCOL |BDRV_O_NO_IO ); }const BdrvChildRole child_format = {
block/io.c +2 -0
@@ -862,6 +862,7 @@ static int coroutine_fn bdrv_aligned_preadv(BlockDriverState *bs,assert ((offset & (BDRV_SECTOR_SIZE -1 )) ==0 );assert ((bytes & (BDRV_SECTOR_SIZE -1 )) ==0 );assert (!qiov || bytes == qiov->size); +assert ((bs->open_flags &BDRV_O_NO_IO ) ==0 );/* Handle Copy on Read and associated serialisation */ if (flags &BDRV_REQ_COPY_ON_READ ) {
@@ -1148,6 +1149,7 @@ static int coroutine_fn bdrv_aligned_pwritev(BlockDriverState *bs,assert ((offset & (BDRV_SECTOR_SIZE -1 )) ==0 );assert ((bytes & (BDRV_SECTOR_SIZE -1 )) ==0 );assert (!qiov || bytes == qiov->size); +assert ((bs->open_flags &BDRV_O_NO_IO ) ==0 ); waited =wait_serialising_requests (req);assert (!waited || !req->serialising);
include/block/block.h +1 -0
@@ -91,6 +91,7 @@ typedef struct HDGeometry {#define BDRV_O_PROTOCOL 0x8000/* if no block driver is explicitly given: select an appropriate protocol driver, ignoring the format layer */ +#define BDRV_O_NO_IO 0x10000/* don't initialize for I/O */ #define BDRV_O_CACHE_MASK (BDRV_O_NOCACHE | BDRV_O_CACHE_WB | BDRV_O_NO_FLUSH)
qemu-img.c +22 -22
163 lines not on this page · raw message
Signature · 1 line
2.5.0
PATCH v3 15/26 qemu-img/qemu-io: don't prompt for passwords if not required
In reply to Daniel P. BerrangeThe qemu-img/qemu-io tools prompt for disk encryption passwords regardless of whether any are actually required. Adding a check on bdrv_key_required() avoids this prompt for disk formats which have been converted to the QCryptoSecret APIs.
This is just a temporary hack to ensure the block I/O tests continue to work after each patch, since the last patch will completely delete all the password prompting code.
--- qemu-img.c | 3 ++- qemu-io.c | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-)
qemu-img.c +2 -1
@@ -230,7 +230,8 @@ static int img_open_password(BlockBackend *blk, const char *filename,char password[256 ]; bs =blk_bs (blk); -if (bdrv_is_encrypted (bs) && !(flags &BDRV_O_NO_IO )) { +if (bdrv_is_encrypted (bs) &&bdrv_key_required (bs) && + !(flags &BDRV_O_NO_IO )) {qprintf (quiet,"Disk image '%s' is encrypted.\n" , filename);if (qemu_read_password (password,sizeof (password)) <0 ) {error_report ("No password given" );
qemu-io.c +1 -1
@@ -69,7 +69,7 @@ static int openfile(char *name, int flags, QDict *opts) } bs =blk_bs (qemuio_blk); -if (bdrv_is_encrypted (bs)) { +if (bdrv_is_encrypted (bs) &&bdrv_key_required (bs)) {char password[256 ];printf ("Disk image '%s' is encrypted.\n" , name);if (qemu_read_password (password,sizeof (password)) <0 ) {
Signature · 1 line
2.5.0
PATCH v3 16/26 tests: redirect stderr to stdout for iotests
In reply to Daniel P. BerrangeThe python I/O tests helper for running qemu-img/qemu-io setup stdout to be captured to a pipe, but left stderr untouched. As a result, if something failed in qemu-img/ qemu-io, data written to stderr would get output directly and not line up with data on the test stdout due to buffering. If we explicitly redirect stderr to the same pipe as stdout, things are much clearer when they go wrong.
--- tests/qemu-iotests/iotests.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-)
tests/qemu-iotests/iotests.py +5 -2
@@ -71,7 +71,9 @@ def qemu_img_verbose(*args): def qemu_img_pipe(*args): '''Run qemu-img and return its output''' - subp = subprocess.Popen(qemu_img_args + list(args), stdout=subprocess.PIPE) + subp = subprocess.Popen(qemu_img_args + list(args), + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) exitcode = subp.wait() if exitcode < 0: sys.stderr.write('qemu-img received signal %i: %s\n' % (-exitcode, ' '.join(qemu_img_args + list(args))))
@@ -80,7 +82,8 @@ def qemu_img_pipe(*args): def qemu_io(*args): '''Run qemu-io and return the stdout data''' args = qemu_io_args + list(args) - subp = subprocess.Popen(args, stdout=subprocess.PIPE) + subp = subprocess.Popen(args, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) exitcode = subp.wait() if exitcode < 0: sys.stderr.write('qemu-io received signal %i: %s\n' % (-exitcode, ' '.join(args)))
Signature · 1 line
2.5.0
PATCH v3 17/26 tests: refactor python I/O tests helper main method
In reply to Daniel P. BerrangeThe iotests.py helper provides a main() method for running tests via the python unit test framework. Not all tests will want to use this, so refactor it to split the testing of compatible formats and platforms into separate helper methods
--- tests/qemu-iotests/iotests.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-)
tests/qemu-iotests/iotests.py +12 -6
@@ -29,7 +29,8 @@ import qtest import struct __all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io', - 'VM', 'QMPTestCase', 'notrun', 'main'] + 'VM', 'QMPTestCase', 'notrun', 'main', 'verify_image_format', + 'verify_platform'] # This will not work if arguments contain spaces but is necessary if we # want to support the override options that ./check supports.
@@ -394,17 +395,22 @@ def notrun(reason): print '%s not run: %s' % (seq, reason) sys.exit(0) -def main(supported_fmts=[], supported_oses=['linux']): - '''Run tests''' - - debug = '-d' in sys.argv - verbosity = 1 +def verify_image_format(supported_fmts=[]): if supported_fmts and (imgfmt not in supported_fmts): notrun('not suitable for this image format: %s' % imgfmt) +def verify_platform(supported_oses=['linux']): if True not in [sys.platform.startswith(x) for x in supported_oses]: notrun('not suitable for this OS: %s' % sys.platform) +def main(supported_fmts=[], supported_oses=['linux']): + '''Run tests''' + + debug = '-d' in sys.argv + verbosity = 1 + verify_image_format(supported_fmts) + verify_platform(supported_oses) + # We need to filter out the time taken from the output so that qemu-iotest # can reliably diff the results against master output. import StringIO
Signature · 1 line
2.5.0
PATCH v3 18/26 tests: add output filter to python I/O tests helper
In reply to Daniel P. BerrangeAdd a 'log' method to iotests.py which prints messages to stdout, with optional filtering of data. Port over some standard filters for present in the shell common.filter code.
--- tests/qemu-iotests/iotests.py | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-)
tests/qemu-iotests/iotests.py +24 -1
@@ -30,7 +30,8 @@ import struct __all__ = ['imgfmt', 'imgproto', 'test_dir' 'qemu_img', 'qemu_io', 'VM', 'QMPTestCase', 'notrun', 'main', 'verify_image_format', - 'verify_platform'] + 'verify_platform', 'filter_test_dir', 'filter_win32', + 'filter_qemu_io', 'filter_chown', 'log'] # This will not work if arguments contain spaces but is necessary if we # want to support the override options that ./check supports.
@@ -105,6 +106,28 @@ def create_image(name, size): i = i + 512 file.close() +test_dir_re = re.compile(r"%s" % test_dir) +def filter_test_dir(msg): + return test_dir_re.sub("TEST_DIR", msg) + +win32_re = re.compile(r"\r") +def filter_win32(msg): + return win32_re.sub("", msg) + +qemu_io_re = re.compile(r"[0-9]* ops; [0-9\/:. sec]* \([0-9\/.inf]* [EPTGMKiBbytes]*\/sec and [0-9\/.inf]* ops\/sec\)") +def filter_qemu_io(msg): + msg = filter_win32(msg) + return qemu_io_re.sub("X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)", msg) + +chown_re = re.compile(r"chown [0-9]+:[0-9]+") +def filter_chown(msg): + return chown_re.sub("chown UID:GID", msg) + +def log(msg, filters=[]): + for flt in filters: + msg = flt(msg) + print msg + # Test if 'match' is a recursive subset of 'event' def event_match(event, match=None): if match is None:
Signature · 1 line
2.5.0
PATCH v3 19/26 block: add generic full disk encryption driver
In reply to Daniel P. BerrangeAdd a block driver that is capable of supporting any full disk encryption format. This utilizes the previously added block encryption code, and at this time supports the LUKS format.
The driver code is capable of supporting any format supported by the QCryptoBlock module, so it registers one block driver for each format.
At this time, the "luks" driver is registered. New LUKS compatible volume can be formatted using qemu-img with defaults for all settings.
$ qemu-img create --object secret,data=123456,id=sec0 \
-f luks -o key-secret=sec0 demo.luks 10GAlternatively the cryptographic settings can explicitly set
$ qemu-img create --object secret,data=123456,id=sec0 \
-f luks -o key-secret=sec0,cipher-alg=aes-256,\
cipher-mode=cbc,ivgen-alg=plain64,hash-alg=sha256 \
demo.luks 10GAnd query its size
$ qemu-img info demo.img image: demo.img file format: luks virtual size: 10G (10737418240 bytes) disk size: 132K encrypted: yes
Note that it was not neccessary to provide the password when querying info for the volume. The password is only required when performing I/O on the volume
All volumes created by this new 'luks' driver should be capable of being opened by the kernel dm-crypt driver.
The only algorithms listed in the LUKS spec that are not currently supported by this impl are sha512 and ripemd160 hashes and cast6 cipher.
A new I/O test is added which is used to validate the interoperability of the QEMU implementation of LUKS, with the dm-crypt/cryptsetup implementation. Due to the need to run cryptsetup as root, this test requires that the user have password-less sudo configured.
--- block/Makefile.objs | 2 + block/crypto.c | 569 +++++++++++ qapi/block-core.json | 22 +- tests/qemu-iotests/145 | 521 ++++++++++ tests/qemu-iotests/145.out | 2252 ++++++++++++++++++++++++++++++++++++++++++++ tests/qemu-iotests/common | 7 + tests/qemu-iotests/group | 1 + 7 files changed, 3372 insertions(+), 2 deletions(-) create mode 100644 block/crypto.c create mode 100755 tests/qemu-iotests/145 create mode 100644 tests/qemu-iotests/145.out
block/Makefile.objs +2 -0
@@ -23,6 +23,8 @@ block-obj-$(CONFIG_LIBSSH2) += ssh.o block-obj-y += accounting.o block-obj-y += write-threshold.o +block-obj-y += crypto.o + common-obj-y += stream.o common-obj-y += commit.o common-obj-y += backup.o
block/crypto.c new file +569 -0
569 lines not on this page · raw message
qapi/block-core.json +20 -2
@@ -242,11 +242,12 @@ # @drv: the name of the block format used to open the backing device. As of # 0.14.0 this can be: 'blkdebug', 'bochs', 'cloop', 'cow', 'dmg', # 'file', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device', -# 'http', 'https', 'nbd', 'parallels', 'qcow', +# 'http', 'https', 'luks', 'nbd', 'parallels', 'qcow', # 'qcow2', 'raw', 'tftp', 'vdi', 'vmdk', 'vpc', 'vvfat' # 2.2: 'archipelago' added, 'cow' dropped # 2.3: 'host_floppy' deprecated # 2.5: 'host_floppy' dropped +# 2.6: 'luks' added # # @backing_file: #optional the name of the backing file (for copy-on-write) #
@@ -1573,7 +1574,7 @@ { 'enum': 'BlockdevDriver', 'data': [ 'archipelago', 'blkdebug', 'blkverify', 'bochs', 'cloop', 'dmg', 'file', 'ftp', 'ftps', 'host_cdrom', 'host_device', - 'http', 'https', 'null-aio', 'null-co', 'parallels', + 'http', 'https', 'luks', 'null-aio', 'null-co', 'parallels', 'qcow', 'qcow2', 'qed', 'quorum', 'raw', 'tftp', 'vdi', 'vhdx', 'vmdk', 'vpc', 'vvfat' ] }
@@ -1691,6 +1692,22 @@ 'data': { 'file': 'BlockdevRef' } } ## +# @BlockdevOptionsLUKS +# +# Driver specific block device options for LUKS. +# +# @key-secret: #optional the ID of a QCryptoSecret object providing +# the decryption key (since 2.6). Mandatory except when +# doing a metadata-only probe of the image. +# +# Since: 2.6 +## +{ 'struct': 'BlockdevOptionsLUKS', + 'base': 'BlockdevOptionsGenericFormat', + 'data': { '*key-secret': 'str' } } + + +## # @BlockdevOptionsGenericCOWFormat # # Driver specific block device options for image format that have no option
@@ -2027,6 +2044,7 @@ 'http': 'BlockdevOptionsFile', 'https': 'BlockdevOptionsFile', # TODO iscsi: Wait for structured options + 'luks': 'BlockdevOptionsLUKS', # TODO nbd: Should take InetSocketAddress for 'host'? # TODO nfs: Wait for structured options 'null-aio': 'BlockdevOptionsNull',
tests/qemu-iotests/145 new file +521 -0
521 lines not on this page · raw message
tests/qemu-iotests/145.out new file +2252 -0
1,852 lines not on this page · raw message
The rest of this diff is not shown. View the raw message.
Signature · 1 line
2.5.0
Part of the diff is not shown here. View the raw message.
PATCH v3 20/26 qcow2: make qcow2_encrypt_sectors encrypt in place
In reply to Daniel P. BerrangeInstead of requiring separate input/output buffers for encrypting data, change qcow2_encrypt_sectors() to assume use of a single buffer, encrypting in place. The current callers all used the same buffer for input/output already.
--- block/qcow2-cluster.c | 17 +++++------------ block/qcow2.c | 5 ++--- block/qcow2.h | 3 +-- 3 files changed, 8 insertions(+), 17 deletions(-)
block/qcow2-cluster.c +5 -12
@@ -342,12 +342,8 @@ static int count_contiguous_clusters_by_type(int nb_clusters,return i; } -/* The crypt function is compatible with the linux cryptoloop -algorithm for < 4 GB images. NOTE: out_buf == in_buf is -supported */ int qcow2_encrypt_sectors (BDRVQcow2State *s,int64_t sector_num, -uint8_t *out_buf,const uint8_t *in_buf, -int nb_sectors,bool enc, +uint8_t *buf,int nb_sectors,bool enc,Error **errp) {union {
@@ -367,14 +363,12 @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num, }if (enc) { ret =qcrypto_cipher_encrypt (s->cipher, - in_buf, - out_buf, + buf, buf,512 , errp); }else { ret =qcrypto_cipher_decrypt (s->cipher, - in_buf, - out_buf, + buf, buf,512 , errp); }
@@ -382,8 +376,7 @@ int qcow2_encrypt_sectors(BDRVQcow2State *s, int64_t sector_num,return -1 ; } sector_num++; - in_buf +=512 ; - out_buf +=512 ; + buf +=512 ; }return 0 ; }
@@ -431,7 +424,7 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,Error *err =NULL ;assert (s->cipher);if (qcow2_encrypt_sectors (s, start_sect + n_start, - iov.iov_base, iov.iov_base, n, + iov.iov_base, n, true, &err) <0 ) { ret = -EIO ;error_free (err);
block/qcow2.c +2 -3
@@ -1506,7 +1506,7 @@ static coroutine_fn int qcow2_co_readv(BlockDriverState *bs, int64_t sector_num,assert (s->cipher);Error *err =NULL ;if (qcow2_encrypt_sectors (s, sector_num, cluster_data, - cluster_data, cur_nr_sectors, false, + cur_nr_sectors, false, &err) <0 ) {error_free (err); ret = -EIO ;
@@ -1606,8 +1606,7 @@ static coroutine_fn int qcow2_co_writev(BlockDriverState *bs,qemu_iovec_to_buf (&hd_qiov,0 , cluster_data, hd_qiov.size);if (qcow2_encrypt_sectors (s, sector_num, cluster_data, - cluster_data, cur_nr_sectors, - true, &err) <0 ) { + cur_nr_sectors, true, &err) <0 ) {error_free (err); ret = -EIO ; goto fail;
block/qcow2.h +1 -2
@@ -540,8 +540,7 @@ int qcow2_write_l1_entry(BlockDriverState *bs, int l1_index);void qcow2_l2_cache_reset (BlockDriverState *bs);int qcow2_decompress_cluster (BlockDriverState *bs,uint64_t cluster_offset);int qcow2_encrypt_sectors (BDRVQcow2State *s,int64_t sector_num, -uint8_t *out_buf,const uint8_t *in_buf, -int nb_sectors,bool enc,Error **errp); +uint8_t *buf,int nb_sectors,bool enc,Error **errp);int qcow2_get_cluster_offset (BlockDriverState *bs,uint64_t offset,int *num,uint64_t *cluster_offset);
Signature · 1 line
2.5.0
PATCH v3 21/26 qcow2: convert QCow2 to use QCryptoBlock for encryption
In reply to Daniel P. BerrangeThis converts the qcow2 driver to make use of the QCryptoBlock APIs for encrypting image content. As well as continued support for the legacy QCow2 encryption format, the appealing benefit is that it enables support for the LUKS format inside qcow2.
With the LUKS format it is necessary to store the LUKS partition header and key material in the QCow2 file. This data can be many MB in size, so cannot go into the QCow2 header region directly. Thus the spec defines a FDE (Full Disk Encryption) header extension that specifies the offset of a set of clusters to hold the FDE headers, as well as the length of that region. The LUKS header is thus stored in these extra allocated clusters before the main image payload.
With this change it is now required to use the QCryptoSecret object for providing passwords, instead of the current block password APIs / interactive prompting.
$QEMU \
-object secret,id=sec0,filename=/home/berrange/encrypted.pw \
-drive file=/home/berrange/encrypted.qcow2,key-secret=sec0The new LUKS format is set as the new default format when creating encrypted images. ie
# qemu-img create --object secret,data=123456,id=sec0 \
-f qcow2 -o encryption,key-secret=sec0 \
test.qcow2 10Gresults in the creation of an image using the LUKS format.
For compatibility the old qcow2 AES format can still be used via the 'encryption-format' parameter which accepts the values 'luks' or 'qcow'.
# qemu-img create --object secret,data=123456,id=sec0 \
-f qcow2 -o encryption,key-secret=sec0,encryption-format=qcow \
test.qcow2 10G--- block/qcow2-cluster.c | 46 +---- block/qcow2-refcount.c | 10 + block/qcow2.c | 491 ++++++++++++++++++++++++++++++++++++++------- block/qcow2.h | 21 +- docs/specs/qcow2.txt | 77 +++++++ qapi/block-core.json | 7 +- tests/qemu-iotests/049 | 2 +- tests/qemu-iotests/049.out | 7 +- tests/qemu-iotests/082.out | 189 +++++++++++++++++ tests/qemu-iotests/087 | 28 ++- tests/qemu-iotests/087.out | 16 +- tests/qemu-iotests/134 | 18 +- tests/qemu-iotests/134.out | 21 +- tests/qemu-iotests/common | 1 + 14 files changed, 769 insertions(+), 165 deletions(-)
block/qcow2-cluster.c +3 -43
@@ -342,45 +342,6 @@ static int count_contiguous_clusters_by_type(int nb_clusters,return i; } -int qcow2_encrypt_sectors (BDRVQcow2State *s,int64_t sector_num, -uint8_t *buf,int nb_sectors,bool enc, -Error **errp) -{ -union { -uint64_t ll[2 ]; -uint8_t b[16 ]; - } ivec; -int i; -int ret; - -for (i =0 ; i < nb_sectors; i++) { - ivec.ll[0 ] =cpu_to_le64 (sector_num); - ivec.ll[1 ] =0 ; -if (qcrypto_cipher_setiv (s->cipher, - ivec.b,G_N_ELEMENTS (ivec.b), - errp) <0 ) { -return -1 ; - } -if (enc) { - ret =qcrypto_cipher_encrypt (s->cipher, - buf, buf, -512 , - errp); - }else { - ret =qcrypto_cipher_decrypt (s->cipher, - buf, buf, -512 , - errp); - } -if (ret <0 ) { -return -1 ; - } - sector_num++; - buf +=512 ; - } -return 0 ; -} - static int coroutine_fn copy_sectors(BlockDriverState *bs, uint64_t start_sect, uint64_t cluster_offset,
@@ -422,10 +383,9 @@ static int coroutine_fn copy_sectors(BlockDriverState *bs,if (bs->encrypted) {Error *err =NULL ; -assert (s->cipher); -if (qcow2_encrypt_sectors (s, start_sect + n_start, - iov.iov_base, n, - true, &err) <0 ) { +assert (s->crypto); +if (qcrypto_block_encrypt (s->crypto, start_sect + n_start, + iov.iov_base, n, &err) <0 ) { ret = -EIO ;error_free (err); goto out;
block/qcow2-refcount.c +10 -0
@@ -1848,6 +1848,16 @@ static int calculate_refcounts(BlockDriverState *bs, BdrvCheckResult *res,return ret; } +/* encryption */ +if (s->crypto_header.length) { + ret =inc_refcounts (bs, res, refcount_table, nb_clusters, + s->crypto_header.offset, + s->crypto_header.length); +if (ret <0 ) { +return ret; + } + } +return check_refblocks (bs, res, fix, rebuild, refcount_table, nb_clusters); }
block/qcow2.c +416 -75
702 lines not on this page · raw message
block/qcow2.h +19 -2
@@ -25,7 +25,7 @@#ifndef BLOCK_QCOW2_H #define BLOCK_QCOW2_H -#include "crypto/cipher.h" +#include "crypto/block.h" #include "qemu/coroutine.h" //#define DEBUG_ALLOC
@@ -36,6 +36,7 @@#define QCOW_CRYPT_NONE 0#define QCOW_CRYPT_AES 1 +#define QCOW_CRYPT_LUKS 2#define QCOW_MAX_CRYPT_CLUSTERS 32#define QCOW_MAX_SNAPSHOTS 65536
@@ -98,6 +99,15 @@#define QCOW2_OPT_REFCOUNT_CACHE_SIZE "refcount-cache-size"#define QCOW2_OPT_CACHE_CLEAN_INTERVAL "cache-clean-interval" +#define QCOW2_OPT_CRYPTO_FORMAT "encryption-format" +#define QCOW2_OPT_KEY_SECRET "key-secret" +#define QCOW2_OPT_CIPHER_ALG "cipher-alg" +#define QCOW2_OPT_CIPHER_MODE "cipher-mode" +#define QCOW2_OPT_IVGEN_ALG "ivgen-alg" +#define QCOW2_OPT_IVGEN_HASH_ALG "ivgen-hash-alg" +#define QCOW2_OPT_HASH_ALG "hash-alg" + + typedef struct QCowHeader { uint32_t magic; uint32_t version;
@@ -163,6 +173,11 @@ typedef struct QCowSnapshot {struct Qcow2Cache ;typedef struct Qcow2Cache Qcow2Cache ; +typedef struct Qcow2CryptoHeaderExtension { +uint64_t offset; +uint64_t length; +} QEMU_PACKED Qcow2CryptoHeaderExtension; + typedef struct Qcow2UnknownHeaderExtension { uint32_t magic; uint32_t len;
@@ -256,7 +271,9 @@ typedef struct BDRVQcow2State {CoMutex lock; -QCryptoCipher *cipher;/* current cipher, NULL if no key yet */ +Qcow2CryptoHeaderExtension crypto_header;/* QCow2 header extension */ +QCryptoBlockOpenOptions *crypto_opts;/* Disk encryption runtime options */ +QCryptoBlock *crypto;/* Disk encryption format driver */ uint32_t crypt_method_header;uint64_t snapshots_offset;int snapshots_size;
docs/specs/qcow2.txt +77 -0
95 lines not on this page · raw message
qapi/block-core.json +5 -2
@@ -1818,6 +1818,9 @@ # @cache-clean-interval: #optional clean unused entries in the L2 and refcount # caches. The interval is in seconds. The default value # is 0 and it disables this feature (since 2.5) +# @key-secret: #optional the ID of a QCryptoSecret object providing +# the decryption key (since 2.6) Mandatory except when +# doing a metadata-only probe of the image. # # Since: 1.7 ##
@@ -1831,8 +1834,8 @@ '*cache-size': 'int', '*l2-cache-size': 'int', '*refcount-cache-size': 'int', - '*cache-clean-interval': 'int' } } - + '*cache-clean-interval': 'int', + '*key-secret': 'str' } } ## # @BlockdevOptionsArchipelago
tests/qemu-iotests/049 +1 -1
@@ -107,7 +107,7 @@ test_qemu_img create -f $IMGFMT -o preallocation=1234 "$TEST_IMG" 64M echo "== Check encryption option ==" echo test_qemu_img create -f $IMGFMT -o encryption=off "$TEST_IMG" 64M -test_qemu_img create -f $IMGFMT -o encryption=on "$TEST_IMG" 64M +test_qemu_img create -f $IMGFMT --object secret,id=sec0,data=123456 -o encryption=on,key-secret=sec0 "$TEST_IMG" 64M echo "== Check lazy_refcounts option (only with v3) ==" echo
tests/qemu-iotests/049.out +2 -5
@@ -186,14 +186,11 @@ Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_si qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 -qemu-img create -f qcow2 -o encryption=on TEST_DIR/t.qcow2 64M +qemu-img create -f qcow2 --object secret,id=sec0,data=123456 -o encryption=on,key-secret=sec0 TEST_DIR/t.qcow2 64M qemu-img: Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. -Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16 +Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16 key-secret=sec0 == Check lazy_refcounts option (only with v3) ==
tests/qemu-iotests/082.out +189 -0
351 lines not on this page · raw message
tests/qemu-iotests/087 +24 -4
@@ -184,9 +184,18 @@ echo echo === Encrypted image === echo -_make_test_img -o encryption=on $size +_make_test_img --object secret,id=sec0,data=123456 -o encryption=on,key-secret=sec0 $size run_qemu -S <<EOF { "execute": "qmp_capabilities" } +{ "execute": "object-add", + "arguments": { + "qom-type": "secret", + "id": "sec0", + "props": { + "data": "123456" + } + } +} { "execute": "blockdev-add", "arguments": { "options": {
@@ -195,7 +204,8 @@ run_qemu -S <<EOF "file": { "driver": "file", "filename": "$TEST_IMG" - } + }, + "key-secret": "sec0" } } }
@@ -204,6 +214,15 @@ EOF run_qemu <<EOF { "execute": "qmp_capabilities" } +{ "execute": "object-add", + "arguments": { + "qom-type": "secret", + "id": "sec0", + "props": { + "data": "123456" + } + } +} { "execute": "blockdev-add", "arguments": { "options": {
@@ -212,7 +231,8 @@ run_qemu <<EOF "file": { "driver": "file", "filename": "$TEST_IMG" - } + }, + "key-secret": "sec0" } } }
@@ -223,7 +243,7 @@ echo echo === Missing driver === echo -_make_test_img -o encryption=on $size +_make_test_img --object secret,id=sec0,data=123456 -o encryption=on,key-secret=sec0 $size run_qemu -S <<EOF { "execute": "qmp_capabilities" } { "execute": "blockdev-add",
tests/qemu-iotests/087.out +6 -10
@@ -41,27 +41,26 @@ QMP_VERSION qemu-img: Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on key-secret=sec0 Testing: -S QMP_VERSION {"return": {}} +{"return": {}} Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -{"error": {"class": "GenericError", "desc": "blockdev-add doesn't support encrypted devices"}} +{"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"} Testing: QMP_VERSION {"return": {}} +{"return": {}} Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -{"error": {"class": "GenericError", "desc": "Guest must be stopped for opening of encrypted image"}} +{"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
@@ -71,10 +70,7 @@ You can use 'qemu-img convert' to convert your image to an unencrypted one. qemu-img: Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on key-secret=sec0 Testing: -S QMP_VERSION {"return": {}}
tests/qemu-iotests/134 +13 -5
@@ -44,23 +44,31 @@ _supported_os Linux size=128M -IMGOPTS="encryption=on" _make_test_img $size + +SECRET="secret,id=sec0,data=astrochicken" +SECRETALT="secret,id=sec0,data=platypus" + +_make_test_img --object $SECRET -o "encryption=on,key-secret=sec0" $size + +IMGSPEC="driver=$IMGFMT,file.filename=$TEST_IMG,key-secret=sec0" + +QEMU_IO_OPTIONS=$QEMU_IO_OPTIONS_NO_FMT echo echo "== reading whole image ==" -echo "astrochicken" | $QEMU_IO -c "read 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir +$QEMU_IO --object $SECRET -c "read 0 $size" --options $IMGSPEC | _filter_qemu_io | _filter_testdir echo echo "== rewriting whole image ==" -echo "astrochicken" | $QEMU_IO -c "write -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir +$QEMU_IO --object $SECRET -c "write -P 0xa 0 $size" --options $IMGSPEC | _filter_qemu_io | _filter_testdir echo echo "== verify pattern ==" -echo "astrochicken" | $QEMU_IO -c "read -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir +$QEMU_IO --object $SECRET -c "read -P 0xa 0 $size" --options $IMGSPEC | _filter_qemu_io | _filter_testdir echo echo "== verify pattern failure with wrong password ==" -echo "platypus" | $QEMU_IO -c "read -P 0xa 0 $size" "$TEST_IMG" | _filter_qemu_io | _filter_testdir +$QEMU_IO --object $SECRETALT -c "read -P 0xa 0 $size" --options $IMGSPEC | _filter_qemu_io | _filter_testdir # success, all done
tests/qemu-iotests/134.out +3 -18
@@ -2,17 +2,12 @@ QA output created by 134 qemu-img: Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. -Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on +Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on key-secret=sec0 == reading whole image == Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -Disk image 'TEST_DIR/t.qcow2' is encrypted. -password: read 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -20,8 +15,6 @@ read 134217728/134217728 bytes at offset 0 Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -Disk image 'TEST_DIR/t.qcow2' is encrypted. -password: wrote 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
@@ -29,18 +22,10 @@ wrote 134217728/134217728 bytes at offset 0 Encrypted images are deprecated Support for them will be removed in a future release. You can use 'qemu-img convert' to convert your image to an unencrypted one. -Disk image 'TEST_DIR/t.qcow2' is encrypted. -password: read 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == verify pattern failure with wrong password == -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. -Disk image 'TEST_DIR/t.qcow2' is encrypted. -password: -Pattern verification failed at offset 0, 134217728 bytes -read 134217728/134217728 bytes at offset 0 -128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) +can't open: Invalid password, cannot unlock any keyslot +no file open, try 'help open' *** done
tests/qemu-iotests/common +1 -0
@@ -405,6 +405,7 @@ BEGIN { for (t='$start'; t<='$end'; t++) printf "%03d\n",t }' \ done # Set qemu-io cache mode with $CACHEMODE we have +QEMU_IO_OPTIONS_NO_FMT="$QEMU_IO_OPTIONS --cache $CACHEMODE" QEMU_IO_OPTIONS="$QEMU_IO_OPTIONS -f $IMGFMT --cache $CACHEMODE" # Set default options for qemu-img create -o if they were not specified
Signature · 1 line
2.5.0
PATCH v3 22/26 qcow: make encrypt_sectors encrypt in place
In reply to Daniel P. BerrangeInstead of requiring separate input/output buffers for encrypting data, change encrypt_sectors() to assume use of a single buffer, encrypting in place. One current caller all uses the same buffer for input/output already and the other two callers are easily converted todo so.
--- block/qcow.c | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-)
block/qcow.c +10 -26
@@ -294,12 +294,9 @@ static int qcow_set_key(BlockDriverState *bs, const char *key)return 0 ; } -/* The crypt function is compatible with the linux cryptoloop -algorithm for < 4 GB images. NOTE: out_buf == in_buf is -supported */ static int encrypt_sectors (BDRVQcowState *s,int64_t sector_num, -uint8_t *out_buf,const uint8_t *in_buf, -int nb_sectors,bool enc,Error **errp) +uint8_t *buf,int nb_sectors,bool enc, +Error **errp) {union {uint64_t ll[2 ];
@@ -318,14 +315,12 @@ static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num, }if (enc) { ret =qcrypto_cipher_encrypt (s->cipher, - in_buf, - out_buf, + buf, buf,512 , errp); }else { ret =qcrypto_cipher_decrypt (s->cipher, - in_buf, - out_buf, + buf, buf,512 , errp); }
@@ -333,8 +328,7 @@ static int encrypt_sectors(BDRVQcowState *s, int64_t sector_num,return -1 ; } sector_num++; - in_buf +=512 ; - out_buf +=512 ; + buf +=512 ; }return 0 ; }
@@ -454,13 +448,12 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,uint64_t start_sect;assert (s->cipher); start_sect = (offset & ~(s->cluster_size -1 )) >>9 ; -memset (s->cluster_data +512 ,0x00 ,512 ); +memset (s->cluster_data,0x00 ,512 );for (i =0 ; i < s->cluster_sectors; i++) {if (i < n_start || i >= n_end) {Error *err =NULL ;if (encrypt_sectors (s, start_sect + i, - s->cluster_data, - s->cluster_data +512 ,1 , + s->cluster_data,1 , true, &err) <0 ) {error_free (err); errno =EIO ;
@@ -639,7 +632,7 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num, }if (bs->encrypted) {assert (s->cipher); -if (encrypt_sectors (s, sector_num, buf, buf, +if (encrypt_sectors (s, sector_num, buf, n, false, &err) <0 ) { goto fail; }
@@ -674,9 +667,7 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,BDRVQcowState *s = bs->opaque;int index_in_cluster;uint64_t cluster_offset; -const uint8_t *src_buf;int ret =0 , n; -uint8_t *cluster_data =NULL ;struct iovec hd_iov;QEMUIOVector hd_qiov;uint8_t *buf;
@@ -714,21 +705,15 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,if (bs->encrypted) {Error *err =NULL ;assert (s->cipher); -if (!cluster_data) { - cluster_data =g_malloc0 (s->cluster_size); - } -if (encrypt_sectors (s, sector_num, cluster_data, buf, +if (encrypt_sectors (s, sector_num, buf, n, true, &err) <0 ) {error_free (err); ret = -EIO ;break ; } - src_buf = cluster_data; - }else { - src_buf = buf; } - hd_iov.iov_base = (void *)src_buf; + hd_iov.iov_base = (void *)buf; hd_iov.iov_len = n *512 ;qemu_iovec_init_external (&hd_qiov, &hd_iov,1 );qemu_co_mutex_unlock (&s->lock);
@@ -750,7 +735,6 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num,if (qiov->niov >1 ) {qemu_vfree (orig_buf); } -g_free (cluster_data);return ret; }
Signature · 1 line
2.5.0
PATCH v3 23/26 qcow: convert QCow to use QCryptoBlock for encryption
In reply to Daniel P. BerrangeThis converts the qcow2 driver to make use of the QCryptoBlock APIs for encrypting image content. This is only wired up to permit use of the legacy QCow encryption format. Users who wish to have the strong LUKS format should switch to qcow2 instead.
With this change it is now required to use the QCryptoSecret object for providing passwords, instead of the current block password APIs / interactive prompting.
$QEMU \
-object secret,id=sec0,filename=/home/berrange/encrypted.pw \
-drive file=/home/berrange/encrypted.qcow,key-secret=sec0--- block/qcow.c | 172 +++++++++++++++++++++++---------------------------- qapi/block-core.json | 17 ++++- 2 files changed, 92 insertions(+), 97 deletions(-)
block/qcow.c +76 -96
@@ -27,7 +27,9 @@#include "qemu/module.h" #include <zlib.h> #include "qapi/qmp/qerror.h" -#include "crypto/cipher.h" +#include "crypto/block.h" +#include "qapi/opts-visitor.h" +#include "qapi-visit.h" #include "migration/migration.h" /**************************************************************/
@@ -41,6 +43,8 @@#define QCOW_OFLAG_COMPRESSED (1LL << 63) +#define QCOW_OPT_KEY_SECRET "key-secret" + typedef struct QCowHeader { uint32_t magic; uint32_t version;
@@ -73,7 +77,7 @@ typedef struct BDRVQcowState {uint8_t *cluster_cache;uint8_t *cluster_data;uint64_t cluster_cache_offset; -QCryptoCipher *cipher;/* NULL if no key yet */ +QCryptoBlock *crypto;/* Disk encryption format driver */ uint32_t crypt_method_header;CoMutex lock;Error *migration_blocker;
@@ -93,6 +97,19 @@ static int qcow_probe(const uint8_t *buf, int buf_size, const char *filename)return 0 ; } +static QemuOptsList qcow_runtime_opts = { + .name ="qcow" , + .head =QTAILQ_HEAD_INITIALIZER (qcow_runtime_opts.head), + .desc = { + { + .name =QCOW_OPT_KEY_SECRET , + .type =QEMU_OPT_STRING , + .help ="ID of the secret that provides the encryption key" , + }, + {/* end of list */ } + }, +}; +static int qcow_open (BlockDriverState *bs,QDict *options,int flags,Error **errp) {
@@ -100,6 +117,11 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,unsigned int len, i, shift;int ret;QCowHeader header; +QemuOpts *opts =NULL ; +Error *local_err =NULL ; +QCryptoBlockOpenOptions *crypto_opts =NULL ; +unsigned int cflags =0 ; +OptsVisitor *ov; ret =bdrv_pread (bs->file->bs,0 , &header,sizeof (header));if (ret <0 ) {
@@ -148,19 +170,49 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } -if (header.crypt_method >QCOW_CRYPT_AES ) { -error_setg (errp,"invalid encryption method in qcow header" ); - ret = -EINVAL ; - goto fail; - } -if (!qcrypto_cipher_supports (QCRYPTO_CIPHER_ALG_AES_128 )) { -error_setg (errp,"AES cipher not available" ); + opts =qemu_opts_create (&qcow_runtime_opts,NULL ,0 , &error_abort); +qemu_opts_absorb_qdict (opts, options, &local_err); +if (local_err) { +error_propagate (errp, local_err); ret = -EINVAL ; goto fail; } + s->crypt_method_header = header.crypt_method;if (s->crypt_method_header) { +if (s->crypt_method_header ==QCOW_CRYPT_AES ) { + ov =opts_visitor_new (opts); + + crypto_opts =g_new0 (QCryptoBlockOpenOptions,1 ); + crypto_opts->format =Q_CRYPTO_BLOCK_FORMAT_QCOW ; +visit_type_QCryptoBlockOptionsQCow (opts_get_visitor (ov), + &crypto_opts->u.qcow, +"qcow" , &local_err); +if (local_err) { +error_propagate (errp, local_err); +opts_visitor_cleanup (ov); + ret = -EINVAL ; + goto fail; + } +opts_visitor_cleanup (ov); + +if (flags &BDRV_O_NO_IO ) { + cflags |=QCRYPTO_BLOCK_OPEN_NO_IO ; + } + s->crypto =qcrypto_block_open (crypto_opts,NULL ,NULL , + cflags, errp); +if (!s->crypto) { +error_setg (errp,"Could not setup encryption layer" ); + ret = -EINVAL ; + goto fail; + } + }else { +error_setg (errp,"invalid encryption method in qcow header" ); + ret = -EINVAL ; + goto fail; + } bs->encrypted =1 ; + bs->valid_key =1 ; } s->cluster_bits = header.cluster_bits; s->cluster_size =1 << s->cluster_bits;
@@ -239,6 +291,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, bdrv_get_device_or_node_name(bs));migrate_add_blocker (s->migration_blocker); +qapi_free_QCryptoBlockOpenOptions (crypto_opts);qemu_co_mutex_init (&s->lock);return 0 ;
@@ -247,6 +300,7 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags,qemu_vfree (s->l2_cache);g_free (s->cluster_cache);g_free (s->cluster_data); +qapi_free_QCryptoBlockOpenOptions (crypto_opts);return ret; }
@@ -259,79 +313,6 @@ static int qcow_reopen_prepare(BDRVReopenState *state,return 0 ; } -static int qcow_set_key (BlockDriverState *bs,const char *key) -{ -BDRVQcowState *s = bs->opaque; -uint8_t keybuf[16 ]; -int len, i; -Error *err; - -memset (keybuf,0 ,16 ); - len =strlen (key); -if (len >16 ) - len =16 ; -/* XXX: we could compress the chars to 7 bits to increase -entropy */ -for (i =0 ;i < len;i++) { - keybuf[i] = key[i]; - } -assert (bs->encrypted); - -qcrypto_cipher_free (s->cipher); - s->cipher =qcrypto_cipher_new ( -QCRYPTO_CIPHER_ALG_AES_128 , -QCRYPTO_CIPHER_MODE_CBC , - keybuf,G_N_ELEMENTS (keybuf), - &err); - -if (!s->cipher) { -/* XXX would be nice if errors in this method could -* be properly propagate to the caller. Would need -* the bdrv_set_key() API signature to be fixed. */ -error_free (err); -return -1 ; - } -return 0 ; -} - -static int encrypt_sectors (BDRVQcowState *s,int64_t sector_num, -uint8_t *buf,int nb_sectors,bool enc, -Error **errp) -{ -union { -uint64_t ll[2 ]; -uint8_t b[16 ]; - } ivec; -int i; -int ret; - -for (i =0 ; i < nb_sectors; i++) { - ivec.ll[0 ] =cpu_to_le64 (sector_num); - ivec.ll[1 ] =0 ; -if (qcrypto_cipher_setiv (s->cipher, - ivec.b,G_N_ELEMENTS (ivec.b), - errp) <0 ) { -return -1 ; - } -if (enc) { - ret =qcrypto_cipher_encrypt (s->cipher, - buf, buf, -512 , - errp); - }else { - ret =qcrypto_cipher_decrypt (s->cipher, - buf, buf, -512 , - errp); - } -if (ret <0 ) { -return -1 ; - } - sector_num++; - buf +=512 ; - } -return 0 ; -}/* 'allocate' is: *
@@ -446,15 +427,15 @@ static uint64_t get_cluster_offset(BlockDriverState *bs,if (bs->encrypted && (n_end - n_start) < s->cluster_sectors) {uint64_t start_sect; -assert (s->cipher); +assert (s->crypto); start_sect = (offset & ~(s->cluster_size -1 )) >>9 ;memset (s->cluster_data,0x00 ,512 );for (i =0 ; i < s->cluster_sectors; i++) {if (i < n_start || i >= n_end) {Error *err =NULL ; -if (encrypt_sectors (s, start_sect + i, - s->cluster_data,1 , - true, &err) <0 ) { +if (qcrypto_block_encrypt (s->crypto, start_sect + i, + s->cluster_data,1 , + &err) <0 ) {error_free (err); errno =EIO ;return -1 ;
@@ -499,7 +480,7 @@ static int64_t coroutine_fn qcow_co_get_block_status(BlockDriverState *bs,if (!cluster_offset) {return 0 ; } -if ((cluster_offset &QCOW_OFLAG_COMPRESSED ) || s->cipher) { +if ((cluster_offset &QCOW_OFLAG_COMPRESSED ) || s->crypto) {return BDRV_BLOCK_DATA ; } cluster_offset |= (index_in_cluster <<BDRV_SECTOR_BITS );
@@ -631,9 +612,9 @@ static coroutine_fn int qcow_co_readv(BlockDriverState *bs, int64_t sector_num,break ; }if (bs->encrypted) { -assert (s->cipher); -if (encrypt_sectors (s, sector_num, buf, - n, false, &err) <0 ) { +assert (s->crypto); +if (qcrypto_block_decrypt (s->crypto, sector_num, buf, + n, &err) <0 ) { goto fail; } }
@@ -704,9 +685,9 @@ static coroutine_fn int qcow_co_writev(BlockDriverState *bs, int64_t sector_num, }if (bs->encrypted) {Error *err =NULL ; -assert (s->cipher); -if (encrypt_sectors (s, sector_num, buf, - n, true, &err) <0 ) { +assert (s->crypto); +if (qcrypto_block_encrypt (s->crypto, sector_num, buf, + n, &err) <0 ) {error_free (err); ret = -EIO ;break ;
@@ -743,8 +724,8 @@ static void qcow_close(BlockDriverState *bs) {BDRVQcowState *s = bs->opaque; -qcrypto_cipher_free (s->cipher); - s->cipher =NULL ; +qcrypto_block_free (s->crypto); + s->crypto =NULL ;g_free (s->l1_table);qemu_vfree (s->l2_cache);g_free (s->cluster_cache);
@@ -1009,7 +990,6 @@ static BlockDriver bdrv_qcow = { .bdrv_co_writev = qcow_co_writev, .bdrv_co_get_block_status = qcow_co_get_block_status, - .bdrv_set_key = qcow_set_key, .bdrv_make_empty = qcow_make_empty, .bdrv_write_compressed = qcow_write_compressed, .bdrv_get_info = qcow_get_info,
qapi/block-core.json +16 -1
@@ -1785,6 +1785,21 @@ 'mode': 'Qcow2OverlapCheckMode' } } ## +# @BlockdevOptionsQcow +# +# Driver specific block device options for qcow. +# +# @key-secret: #optional ID of the "secret" object providing the +# AES decryption key. +# +# Since: 2.6 +## +{ 'struct': 'BlockdevOptionsQcow', + 'base': 'BlockdevOptionsGenericCOWFormat', + 'data': { '*key-secret': 'str' } } + + +## # @BlockdevOptionsQcow2 # # Driver specific block device options for qcow2.
@@ -2054,7 +2069,7 @@ 'null-co': 'BlockdevOptionsNull', 'parallels': 'BlockdevOptionsGenericFormat', 'qcow2': 'BlockdevOptionsQcow2', - 'qcow': 'BlockdevOptionsGenericCOWFormat', + 'qcow': 'BlockdevOptionsQcow', 'qed': 'BlockdevOptionsGenericCOWFormat', 'quorum': 'BlockdevOptionsQuorum', 'raw': 'BlockdevOptionsGenericFormat',
Signature · 1 line
2.5.0
PATCH v3 24/26 block: rip out all traces of password prompting
In reply to Daniel P. BerrangeNow that qcow & qcow2 are wired up to get encryption keys via the QCryptoSecret object, nothing is relying on the interactive prompting for passwords. All the code related to password prompting can thus be ripped out.
--- hmp.c | 31 --------------------- hw/usb/dev-storage.c | 34 ------------------------ include/monitor/monitor.h | 7 ----- include/qemu/osdep.h | 2 -- monitor.c | 68 ----------------------------------------------- qemu-img.c | 31 --------------------- qemu-io.c | 21 --------------- qmp.c | 10 +------ tests/qemu-iotests/087 | 2 ++ util/oslib-posix.c | 66 --------------------------------------------- util/oslib-win32.c | 24 ----------------- 11 files changed, 3 insertions(+), 293 deletions(-)
hmp.c +0 -31
@@ -969,37 +969,12 @@ void hmp_ringbuf_read(Monitor *mon, const QDict *qdict)g_free (data); } -static void hmp_cont_cb (void *opaque,int err) -{ -if (!err) { -qmp_cont (NULL ); - } -} - -static bool key_is_missing (const BlockInfo *bdev) -{ -return (bdev->inserted && bdev->inserted->encryption_key_missing); -} -void hmp_cont (Monitor *mon,const QDict *qdict) { -BlockInfoList *bdev_list, *bdev;Error *err =NULL ; - bdev_list =qmp_query_block (NULL ); -for (bdev = bdev_list; bdev; bdev = bdev->next) { -if (key_is_missing (bdev->value)) { -monitor_read_block_device_key (mon, bdev->value->device, - hmp_cont_cb,NULL ); - goto out; - } - } -qmp_cont (&err);hmp_handle_error (mon, &err); - -out: -qapi_free_BlockInfoList (bdev_list); }void hmp_system_wakeup (Monitor *mon,const QDict *qdict)
@@ -1380,12 +1355,6 @@ void hmp_change(Monitor *mon, const QDict *qdict)qmp_blockdev_change_medium (device, target, !!arg, arg, !!read_only, read_only_mode, &err); -if (err && -error_get_class (err) ==ERROR_CLASS_DEVICE_ENCRYPTED ) { -error_free (err); -monitor_read_block_device_key (mon, device,NULL ,NULL ); -return ; - } }hmp_handle_error (mon, &err);
hw/usb/dev-storage.c +0 -34
@@ -554,21 +554,6 @@ static void usb_msd_handle_data(USBDevice *dev, USBPacket *p) } } -static void usb_msd_password_cb (void *opaque,int err) -{ -MSDState *s = opaque; -Error *local_err =NULL ; - -if (!err) { -usb_device_attach (&s->dev, &local_err); - } - -if (local_err) { -error_report_err (local_err); -qdev_unplug (&s->dev.qdev,NULL ); - } -} -static void *usb_msd_load_request (QEMUFile *f,SCSIRequest *req) {MSDState *s =DO_UPCAST (MSDState, dev.qdev, req->bus->qbus.parent);
@@ -614,25 +599,6 @@ static void usb_msd_realize_storage(USBDevice *dev, Error **errp)return ; } -if (blk_bs (blk)) { -bdrv_add_key (blk_bs (blk),NULL , &err); -if (err) { -if (monitor_cur_is_qmp ()) { -error_propagate (errp, err); -return ; - } -error_free (err); - err =NULL ; -if (cur_mon) { -monitor_read_bdrv_key_start (cur_mon,blk_bs (blk), - usb_msd_password_cb, s); - s->dev.auto_attach =0 ; - }else { - autostart =0 ; - } - } - } -blkconf_serial (&s->conf, &dev->serial);blkconf_blocksizes (&s->conf);
include/monitor/monitor.h +0 -7
@@ -21,13 +21,6 @@ void monitor_init(CharDriverState *chr, int flags);int monitor_suspend (Monitor *mon);void monitor_resume (Monitor *mon); -int monitor_read_bdrv_key_start (Monitor *mon,BlockDriverState *bs, -BlockCompletionFunc *completion_cb, -void *opaque); -int monitor_read_block_device_key (Monitor *mon,const char *device, -BlockCompletionFunc *completion_cb, -void *opaque); -int monitor_get_fd (Monitor *mon,const char *fdname,Error **errp);int monitor_fd_param (Monitor *mon,const char *fdname,Error **errp);
include/qemu/osdep.h +0 -2
@@ -301,8 +301,6 @@ void qemu_set_tty_echo(int fd, bool echo);void os_mem_prealloc (int fd,char *area,size_t sz); -int qemu_read_password (char *buf,int buf_size); -/** * qemu_fork: *
monitor.c +0 -68
74 lines not on this page · raw message
qemu-img.c +0 -31
@@ -223,29 +223,6 @@ static int print_block_option_help(const char *filename, const char *fmt) } -static int img_open_password (BlockBackend *blk,const char *filename, -int flags,bool quiet) -{ -BlockDriverState *bs; -char password[256 ]; - - bs =blk_bs (blk); -if (bdrv_is_encrypted (bs) &&bdrv_key_required (bs) && - !(flags &BDRV_O_NO_IO )) { -qprintf (quiet,"Disk image '%s' is encrypted.\n" , filename); -if (qemu_read_password (password,sizeof (password)) <0 ) { -error_report ("No password given" ); -return -1 ; - } -if (bdrv_set_key (bs, password) <0 ) { -error_report ("invalid password" ); -return -1 ; - } - } -return 0 ; -} - - static BlockBackend *img_open_opts(const char *id, const char *optstr, QemuOpts *opts, int flags,
@@ -261,10 +238,6 @@ static BlockBackend *img_open_opts(const char *id,return NULL ; } -if (img_open_password (blk, optstr, flags, quiet) <0 ) { -blk_unref (blk); -return NULL ; - }return blk; }
@@ -287,10 +260,6 @@ static BlockBackend *img_open_file(const char *id, const char *filename,return NULL ; } -if (img_open_password (blk, filename, flags, quiet) <0 ) { -blk_unref (blk); -return NULL ; - }return blk; }
qemu-io.c +0 -21
@@ -53,7 +53,6 @@ static const cmdinfo_t close_cmd = {static int openfile (char *name,int flags,QDict *opts) {Error *local_err =NULL ; -BlockDriverState *bs;if (qemuio_blk) {error_report ("file open already, try 'help close'" );
@@ -68,27 +67,7 @@ static int openfile(char *name, int flags, QDict *opts)return 1 ; } - bs =blk_bs (qemuio_blk); -if (bdrv_is_encrypted (bs) &&bdrv_key_required (bs)) { -char password[256 ]; -printf ("Disk image '%s' is encrypted.\n" , name); -if (qemu_read_password (password,sizeof (password)) <0 ) { -error_report ("No password given" ); - goto error; - } -if (bdrv_set_key (bs, password) <0 ) { -error_report ("invalid password" ); - goto error; - } - } - -return 0 ; - - error: -blk_unref (qemuio_blk); - qemuio_blk =NULL ; -return 1 ; }static void open_help (void )
qmp.c +1 -9
@@ -171,9 +171,8 @@ SpiceInfo *qmp_query_spice(Error **errp)void qmp_cont (Error **errp) { -Error *local_err =NULL ;BlockBackend *blk; -BlockDriverState *bs; +Error *local_err =NULL ;if (runstate_needs_reset ()) {error_setg (errp,"Resetting the Virtual Machine is required" );
@@ -185,13 +184,6 @@ void qmp_cont(Error **errp)for (blk =blk_next (NULL ); blk; blk =blk_next (blk)) {blk_iostatus_reset (blk); } -for (bs =bdrv_next (NULL ); bs; bs =bdrv_next (bs)) { -bdrv_add_key (bs,NULL , &local_err); -if (local_err) { -error_propagate (errp, local_err); -return ; - } - }/* Continuing after completed migration. Images have been inactivated to * allow the destination to take control. Need to get control back now. */
tests/qemu-iotests/087 +2 -0
@@ -201,6 +201,7 @@ run_qemu -S <<EOF "options": { "driver": "$IMGFMT", "id": "disk", + "key-secret": "sec0", "file": { "driver": "file", "filename": "$TEST_IMG"
@@ -228,6 +229,7 @@ run_qemu <<EOF "options": { "driver": "$IMGFMT", "id": "disk", + "key-secret": "sec0", "file": { "driver": "file", "filename": "$TEST_IMG"
util/oslib-posix.c +0 -66
72 lines not on this page · raw message
util/oslib-win32.c +0 -24
@@ -473,30 +473,6 @@ void os_mem_prealloc(int fd, char *area, size_t memory) } -/* XXX: put correct support for win32 */ -int qemu_read_password (char *buf,int buf_size) -{ -int c, i; - -printf ("Password: " ); -fflush (stdout); - i =0 ; -for (;;) { - c =getchar (); -if (c <0 ) { - buf[i] ='\0' ; -return -1 ; - }else if (c =='\n' ) { -break ; - }else if (i < (buf_size -1 )) { - buf[i++] = c; - } - } - buf[i] ='\0' ; -return 0 ; -} - -pid_t qemu_fork (Error **errp) { errno =ENOSYS ;
Signature · 1 line
2.5.0
PATCH v3 25/26 block: remove all encryption handling APIs
In reply to Daniel P. BerrangeNow that all encryption keys must be provided upfront via the QCryptoSecret API and associated block driver properties there is no need for any explicit encryption handling APIs in the block layer. Encryption can be handled transparently within the block driver. We only retain an API for querying whether an image is encrypted or not, since that is a potentially useful piece of metadata to report to the user.
--- block.c | 82 ++--------------------------------------------- block/crypto.c | 1 - block/qapi.c | 2 +- block/qcow.c | 1 - block/qcow2.c | 1 - blockdev.c | 41 ++---------------------- include/block/block.h | 4 --- include/block/block_int.h | 1 - 8 files changed, 5 insertions(+), 128 deletions(-)
block.c +2 -80
@@ -1695,17 +1695,8 @@ static int bdrv_open_inherit(BlockDriverState **pbs, const char *filename, goto close_and_fail; } -if (!bdrv_key_required (bs)) { -if (bs->blk) { -blk_dev_change_media_cb (bs->blk, true); - } - }else if (!runstate_check (RUN_STATE_PRELAUNCH ) - && !runstate_check (RUN_STATE_INMIGRATE ) - && !runstate_check (RUN_STATE_PAUSED )) {/* HACK */ -error_setg (errp, -"Guest must be stopped for opening of encrypted image" ); - ret = -EBUSY ; - goto close_and_fail; +if (bs->blk) { +blk_dev_change_media_cb (bs->blk, true); }QDECREF (options);
@@ -2191,7 +2182,6 @@ static void bdrv_close(BlockDriverState *bs) bs->backing_format[0 ] ='\0' ; bs->total_sectors =0 ; bs->encrypted =0 ; - bs->valid_key =0 ; bs->sg =0 ; bs->zero_beyond_eof = false;QDECREF (bs->options);
@@ -2800,74 +2790,6 @@ int bdrv_is_encrypted(BlockDriverState *bs)return bs->encrypted; } -int bdrv_key_required (BlockDriverState *bs) -{ -BdrvChild *backing = bs->backing; - -if (backing && backing->bs->encrypted && !backing->bs->valid_key) { -return 1 ; - } -return (bs->encrypted && !bs->valid_key); -} - -int bdrv_set_key (BlockDriverState *bs,const char *key) -{ -int ret; -if (bs->backing && bs->backing->bs->encrypted) { - ret =bdrv_set_key (bs->backing->bs, key); -if (ret <0 ) -return ret; -if (!bs->encrypted) -return 0 ; - } -if (!bs->encrypted) { -return -EINVAL ; - }else if (!bs->drv || !bs->drv->bdrv_set_key) { -return -ENOMEDIUM ; - } - ret = bs->drv->bdrv_set_key (bs, key); -if (ret <0 ) { - bs->valid_key =0 ; - }else if (!bs->valid_key) { - bs->valid_key =1 ; -if (bs->blk) { -/* call the change callback now, we skipped it on open */ -blk_dev_change_media_cb (bs->blk, true); - } - } -return ret; -} - -/* -* Provide an encryption key for @bs. -* If @key is non-null: -* If @bs is not encrypted, fail. -* Else if the key is invalid, fail. -* Else set @bs's key to @key, replacing the existing key, if any. -* If @key is null: -* If @bs is encrypted and still lacks a key, fail. -* Else do nothing. -* On failure, store an error object through @errp if non-null. -*/ -void bdrv_add_key (BlockDriverState *bs,const char *key,Error **errp) -{ -if (key) { -if (!bdrv_is_encrypted (bs)) { -error_setg (errp,"Node '%s' is not encrypted" , -bdrv_get_device_or_node_name (bs)); - }else if (bdrv_set_key (bs, key) <0 ) { -error_setg (errp,QERR_INVALID_PASSWORD ); - } - }else { -if (bdrv_key_required (bs)) { -error_set (errp,ERROR_CLASS_DEVICE_ENCRYPTED , -"'%s' (%s) is encrypted" , -bdrv_get_device_or_node_name (bs), -bdrv_get_encrypted_filename (bs)); - } - } -} -const char *bdrv_get_format_name (BlockDriverState *bs) {return bs->drv ? bs->drv->format_name :NULL ;
block/crypto.c +0 -1
@@ -303,7 +303,6 @@ static int block_crypto_open_generic(QCryptoBlockFormat format, } bs->encrypted =1 ; - bs->valid_key =1 ;qemu_co_mutex_init (&crypto->lock);
block/qapi.c +1 -1
@@ -43,7 +43,7 @@ BlockDeviceInfo *bdrv_block_device_info(BlockDriverState *bs, Error **errp) info->ro = bs->read_only; info->drv =g_strdup (bs->drv->format_name); info->encrypted = bs->encrypted; - info->encryption_key_missing =bdrv_key_required (bs); + info->encryption_key_missing = false; info->cache =g_new (BlockdevCacheInfo,1 ); *info->cache = (BlockdevCacheInfo ) {
block/qcow.c +0 -1
@@ -212,7 +212,6 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, goto fail; } bs->encrypted =1 ; - bs->valid_key =1 ; } s->cluster_bits = header.cluster_bits; s->cluster_size =1 << s->cluster_bits;
block/qcow2.c +0 -1
@@ -1204,7 +1204,6 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, s->crypt_method_header = header.crypt_method;if (s->crypt_method_header) { bs->encrypted =1 ; - bs->valid_key =1 ; } s->l2_bits = s->cluster_bits -3 ;/* L2 is always one cluster */
blockdev.c +2 -39
65 lines not on this page · raw message
include/block/block.h +0 -4
@@ -417,10 +417,6 @@ bool bdrv_chain_contains(BlockDriverState *top, BlockDriverState *base);BlockDriverState *bdrv_next_node (BlockDriverState *bs);BlockDriverState *bdrv_next (BlockDriverState *bs);int bdrv_is_encrypted (BlockDriverState *bs); -int bdrv_key_required (BlockDriverState *bs); -int bdrv_set_key (BlockDriverState *bs,const char *key); -void bdrv_add_key (BlockDriverState *bs,const char *key,Error **errp); -int bdrv_query_missing_keys (void );void bdrv_iterate_format (void (*it)(void *opaque,const char *name),void *opaque);const char *bdrv_get_node_name (const BlockDriverState *bs);
include/block/block_int.h +0 -1
@@ -376,7 +376,6 @@ struct BlockDriverState {int read_only;/* if true, the media is read only */ int open_flags;/* flags used to open the file, re-used for re-open */ int encrypted;/* if true, the media is encrypted */ -int valid_key;/* if true, a valid encryption key has been set */ int sg;/* if true, the device is a /dev/sg* */ int copy_on_read;/* if true, copy read backing sectors into image note this is a reference count */
Signature · 1 line
2.5.0
PATCH v3 26/26 block: remove support for legecy AES qcow/qcow2 encryption
In reply to Daniel P. BerrangeRefuse to use images with the legacy AES-CBC encryption format in the system emulators. They are still fully supported in the qemu-img, qemu-io & qemu-nbd tools in order to allow data to be liberated and for compatibility with older QEMU versions. Continued support in these tools is not a notable burden with the new FDE framework.
--- block.c | 12 +++++------- block/qcow.c | 8 ++++++++ block/qcow2.c | 8 ++++++++ include/block/block.h | 1 + tests/qemu-iotests/049.out | 3 --- tests/qemu-iotests/087.out | 12 ------------ tests/qemu-iotests/134.out | 12 ------------ 7 files changed, 22 insertions(+), 34 deletions(-)
block.c +5 -7
@@ -313,6 +313,11 @@ static int bdrv_is_whitelisted(BlockDriver *drv, bool read_only)return 0 ; } +bool bdrv_uses_whitelist (void ) +{ +return use_bdrv_whitelist; +} + typedef struct CreateCo { BlockDriver *drv; char *filename;
@@ -1023,13 +1028,6 @@ static int bdrv_open_common(BlockDriverState *bs, BdrvChild *file, goto free_and_fail; } -if (bs->encrypted) { -error_report ("Encrypted images are deprecated" ); -error_printf ("Support for them will be removed in a future release.\n" -"You can use 'qemu-img convert' to convert your image" -" to an unencrypted one.\n" ); - } - ret =refresh_total_sectors (bs, bs->total_sectors);if (ret <0 ) {error_setg_errno (errp, -ret,"Could not refresh total sector count" );
block/qcow.c +8 -0
@@ -181,6 +181,14 @@ static int qcow_open(BlockDriverState *bs, QDict *options, int flags, s->crypt_method_header = header.crypt_method;if (s->crypt_method_header) {if (s->crypt_method_header ==QCOW_CRYPT_AES ) { +if (bdrv_uses_whitelist ()) { +error_setg (errp, +"Use of AES-CBC encrypted qcow images is no longer " +"supported. Please use the qcow2 LUKS format instead." ); + ret = -ENOSYS ; + goto fail; + } + ov =opts_visitor_new (opts); crypto_opts =g_new0 (QCryptoBlockOpenOptions,1 );
block/qcow2.c +8 -0
@@ -1203,6 +1203,14 @@ static int qcow2_open(BlockDriverState *bs, QDict *options, int flags, s->crypt_method_header = header.crypt_method;if (s->crypt_method_header) { +if (bdrv_uses_whitelist () && + s->crypt_method_header ==QCOW_CRYPT_AES ) { +error_setg (errp, +"Use of AES-CBC encrypted qcow2 images is no longer " +"supported. Please use the qcow2 LUKS format instead." ); + ret = -ENOSYS ; + goto fail; + } bs->encrypted =1 ; }
include/block/block.h +1 -0
@@ -191,6 +191,7 @@ void bdrv_io_limits_update_group(BlockDriverState *bs, const char *group);void bdrv_init (void );void bdrv_init_with_whitelist (void ); +bool bdrv_uses_whitelist (void );BlockDriver *bdrv_find_protocol (const char *filename,bool allow_protocol_prefix,Error **errp);
tests/qemu-iotests/049.out +0 -3
@@ -187,9 +187,6 @@ qemu-img create -f qcow2 -o encryption=off TEST_DIR/t.qcow2 64M Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=off cluster_size=65536 lazy_refcounts=off refcount_bits=16 qemu-img create -f qcow2 --object secret,id=sec0,data=123456 -o encryption=on,key-secret=sec0 TEST_DIR/t.qcow2 64M -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Formatting 'TEST_DIR/t.qcow2', fmt=qcow2 size=67108864 encryption=on cluster_size=65536 lazy_refcounts=off refcount_bits=16 key-secret=sec0 == Check lazy_refcounts option (only with v3) ==
tests/qemu-iotests/087.out +0 -12
@@ -38,17 +38,11 @@ QMP_VERSION === Encrypted image === -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on key-secret=sec0 Testing: -S QMP_VERSION {"return": {}} {"return": {}} -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
@@ -57,9 +51,6 @@ Testing: QMP_VERSION {"return": {}} {"return": {}} -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. {"return": {}} {"return": {}} {"timestamp": {"seconds": TIMESTAMP, "microseconds": TIMESTAMP}, "event": "SHUTDOWN"}
@@ -67,9 +58,6 @@ You can use 'qemu-img convert' to convert your image to an unencrypted one. === Missing driver === -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on key-secret=sec0 Testing: -S QMP_VERSION
tests/qemu-iotests/134.out +0 -12
@@ -1,27 +1,15 @@ QA output created by 134 -qemu-img: Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. Formatting 'TEST_DIR/t.IMGFMT', fmt=IMGFMT size=134217728 encryption=on key-secret=sec0 == reading whole image == -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. read 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == rewriting whole image == -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. wrote 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec) == verify pattern == -Encrypted images are deprecated -Support for them will be removed in a future release. -You can use 'qemu-img convert' to convert your image to an unencrypted one. read 134217728/134217728 bytes at offset 0 128 MiB, X ops; XX:XX:XX.X (XXX YYY/sec and XXX ops/sec)
Signature · 1 line
2.5.0