From: Eric Blake <eblake@redhat.com>
To: "Daniel P. Berrange" <berrange@redhat.com>, qemu-devel@nongnu.org
Cc: Kevin Wolf <kwolf@redhat.com>, Fam Zheng <famz@redhat.com>,
qemu-block@nongnu.org
Subject: Re: [Qemu-devel] [PATCH v2 06/17] crypto: add block encryption framework
Date: Thu, 4 Feb 2016 17:23:32 -0700 [thread overview]
Message-ID: <56B3EB84.1000100@redhat.com> (raw)
In-Reply-To: <1453311539-1193-7-git-send-email-berrange@redhat.com>
[-- Attachment #1: Type: text/plain, Size: 12615 bytes --]
On 01/20/2016 10:38 AM, Daniel P. Berrange wrote:
> Add 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.
>
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
> crypto/Makefile.objs | 2 +
> crypto/block-qcow.c | 167 +++++++++++++++++++++++++++++
> crypto/block-qcow.h | 28 +++++
> crypto/block.c | 263 ++++++++++++++++++++++++++++++++++++++++++++++
> crypto/blockpriv.h | 90 ++++++++++++++++
> include/crypto/block.h | 233 ++++++++++++++++++++++++++++++++++++++++
> qapi/crypto.json | 67 ++++++++++++
> tests/.gitignore | 1 +
> tests/Makefile | 2 +
> tests/test-crypto-block.c | 239 +++++++++++++++++++++++++++++++++++++++++
> 10 files changed, 1092 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
>
> +++ b/crypto/block-qcow.c
> @@ -0,0 +1,167 @@
> +/*
> + * 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/>.
> + *
> + */
Maybe worth a big comment stating that this file exists for backwards
compatibility, and no one in their right mind should copy the code
and/or encrypt new files with it.
>
> +static gboolean
> +qcrypto_block_qcow_has_format(const uint8_t *buf G_GNUC_UNUSED,
> + size_t buf_size G_GNUC_UNUSED)
> +{
> + return false;
> +}
When I see gboolean, I think TRUE/FALSE. Yes, C99 'false' happens to
promote to the correct value for whatever integer type gboolean is, but
this would read nicer if it returned 'bool'.
> +
> +static int
> +qcrypto_block_qcow_init(QCryptoBlock *block,
> + const char *keysecret,
> + Error **errp)
> +{
> + char *password;
> + int ret;
> + uint8_t keybuf[16];
> + int len, i;
> +
> + memset(keybuf, 0, 16);
> +
> + password = qcrypto_secret_lookup_as_utf8(keysecret, errp);
> + if (!password) {
> + return -1;
> + }
> +
> + len = strlen(password);
> + if (len > 16) {
> + len = 16;
> + }
> + for (i = 0; i < len; i++) {
> + keybuf[i] = password[i];
> + }
What - we really throw away anything longer than 16 bytes?
Would a memcpy() be any nicer than an open-coded loop?
> +
> +static int
> +qcrypto_block_qcow_create(QCryptoBlock *block,
> + QCryptoBlockCreateOptions *options,
> + QCryptoBlockInitFunc initfunc G_GNUC_UNUSED,
> + QCryptoBlockWriteFunc writefunc G_GNUC_UNUSED,
> + void *opaque G_GNUC_UNUSED,
> + Error **errp)
> +{
> + if (!options->u.qcow->key_secret) {
> + error_setg(errp, "Parameter 'key-secret' is required for cipher");
> + return -1;
> + }
> + /* QCow2 has no special header, since everything is hardwired */
> + return qcrypto_block_qcow_init(block, options->u.qcow->key_secret, errp);
> +}
> +
> +
> +static void
> +qcrypto_block_qcow_cleanup(QCryptoBlock *block)
> +{
> +}
Nothing to free? qcrypto_block_qcow_init cleaned up block->cipher and
block->ivgen on error, so shouldn't this do likewise (and then the init
could call this function instead of open-coding the cleanup)?...
> diff --git a/crypto/block.c b/crypto/block.c
> new file mode 100644
> index 0000000..757e28a
> --- /dev/null
> +++ b/crypto/block.c
> @@ -0,0 +1,263 @@
> +uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block)
> +{
> + return block->payload_offset;
> +}
> +
> +
> +void qcrypto_block_free(QCryptoBlock *block)
> +{
> + if (!block) {
> + return;
> + }
> +
> + block->driver->cleanup(block);
> +
> + qcrypto_cipher_free(block->cipher);
> + qcrypto_ivgen_free(block->ivgen);
> + g_free(block);
...oh, you centralized that part of the cleanup.
> +++ b/crypto/blockpriv.h
> +
> +typedef struct QCryptoBlockDriver QCryptoBlockDriver;
> +
> +struct QCryptoBlock {
> + QCryptoBlockFormat format;
> +
> + const QCryptoBlockDriver *driver;
> + void *opaque;
> +
> + QCryptoCipher *cipher;
> + QCryptoIVGen *ivgen;
> + QCryptoHashAlgorithm kdfhash;
> + size_t niv;
> + uint64_t payload_offset; /* In 512 byte sectors */
Someday, we may want to support 4k sectors natively. I don't envy the
person making the conversion, but we could at least make their life
easier by representing this in bytes instead of units of 512-byte sectors.
> +};
> +
> +struct QCryptoBlockDriver {
> + int (*open)(QCryptoBlock *block,
> + QCryptoBlockOpenOptions *options,
> + QCryptoBlockReadFunc readfunc,
> + void *opaque,
> + unsigned int flags,
> + Error **errp);
No documentation on any of these contracts?
> +
> + gboolean (*has_format)(const uint8_t *buf,
> + size_t buflen);
Why gboolean instead of bool?
> +++ b/include/crypto/block.h
> +/**
> + * qcrypto_block_has_format:
> + * @format: the encryption format
> + * @buf: the data from head of the volume
> + * @len: the length of @buf in bytes
> + *
> + * Given @len bytes of data from the head of a storage volume
> + * in @buf, probe to determine if the volume has the encryption
> + * format specified in @format.
> + *
> + * Returns: true if the data in @buf matches @format
> + */
> +gboolean qcrypto_block_has_format(QCryptoBlockFormat format,
Again, this should probably be 'bool', not gboolean.
> + const uint8_t *buf,
> + size_t buflen);
> +
> +typedef enum {
> + QCRYPTO_BLOCK_OPEN_NO_IO = (1 << 0),
> +} QCryptoBlockOpenFlags;
> +
> +/**
> + * qcrypto_block_open:
> + * @options: the encryption options
> + * @readfunc: callback for reading data from the volume
> + * @opaque: data to pass to @readfunc
> + * @flags: bitmask of QCryptoBlockOpenFlags values
> + * @errp: pointer to a NULL-initialized error object
> + *
> + * Create a new block encryption object for an existing
> + * storage volume encrypted with format identified by
> + * the parameters in @options.
> + *
> + * This will use @readfunc to initialize the encryption
> + * context based on the volume header(s), extracting the
> + * master key(s) as required.
> + *
> + * If @flags contains QCRYPTO_BLOCK_OPEN_NO_IO then
> + * the open process will be optimized to skip any parts
> + * that are only required to perform I/O. In particular
> + * this would usually avoid the need to decrypt any
> + * master keys. The only thing that can be done with
> + * the resulting QCryptoBlock object would be to query
> + * metadata such as the payload offset. There will be
> + * no cipher or ivgen objects available.
> + *
> + * If any part of initializing the encryption context
> + * fails an error will be returned. This could be due
> + * to the volume being in the wrong format, an cipher
> + * or IV generator algorithm that is not supoported,
s/supoported/supported/
> + * or incorrect passphrases.
> + *
> + * Returns: a block encryption format, or NULL on error
> + */
> +QCryptoBlock *qcrypto_block_open(QCryptoBlockOpenOptions *options,
> + QCryptoBlockReadFunc readfunc,
> + void *opaque,
> + unsigned int flags,
> + Error **errp);
> +
> +/**
> + * qcrypto_block_create:
> + * @format: the encryption format
> + * @initfunc: callback for initializing volume header
> + * @writefunc: callback for writing data to the volume header
> + * @opaque: data to pass to @initfunc & @writefunc
I'd spell it out s/&/and/
> + * @errp: pointer to a NULL-initialized error object
> + *
> + * Create a new block encryption object for initializing
> + * a storage volume to be encrypted with format identified
> + * by the parameters in @options.
> + *
> + * This method will allocate space for a new volume header
> + * using @initfunc and then write header data using @writefunc,
> + * generating new master keys, etc as required. Any existing
> + * data present on the volume will be irrevokably destroyed.
s/irrevokably/irrevocably/
> + *
> + * If any part of initializing the encryption context
> + * fails an error will be returned. This could be due
> + * to the volume being in the wrong format, an cipher
> + * or IV generator algorithm that is not supoported,
s/supoported/supported/
> + * or incorrect passphrases.
> + *
> + * Returns: a block encryption format, or NULL on error
> + */
> +QCryptoBlock *qcrypto_block_create(QCryptoBlockCreateOptions *options,
> + QCryptoBlockInitFunc initfunc,
> + QCryptoBlockWriteFunc writefunc,
> + void *opaque,
> + Error **errp);
> +
> +/**
> + * @qcrypto_block_decrypt:
> + * @block: the block encryption object
> + * @startsector: the sector from which @buf was read
From the guest's point of view, right?
> + * @buf: the buffer to decrypt
> + * @len: the length of @buf in bytes
> + * @errp: pointer to a NULL-initialized error object
> + *
> + * Decrypt @len bytes of cipher text in @buf, writing
> + * plain text back into @buf
> + *
> + * Returns 0 on success, -1 on failure
> + */
> +int qcrypto_block_decrypt(QCryptoBlock *block,
> + uint64_t startsector,
> + uint8_t *buf,
> + size_t len,
> + Error **errp);
> +
> +/**
> + * @qcrypto_block_encrypt:
> + * @block: the block encryption object
> + * @startsector: the sector to which @buf will be written
Likewise, from the guest's point of view, right?
> + * @buf: the buffer to decrypt
> + * @len: the length of @buf in bytes
> + * @errp: pointer to a NULL-initialized error object
> + *
> + * Encrypt @len bytes of plain text in @buf, writing
> + * cipher text back into @buf
> + *
> + * Returns 0 on success, -1 on failure
> + */
> +int qcrypto_block_encrypt(QCryptoBlock *block,
> + uint64_t startsector,
> + uint8_t *buf,
> + size_t len,
> + Error **errp);
> +
> +/**
> + * qcrypto_block_get_payload_offset:
> + * @block: the block encryption object
> + *
> + * Get the offset to the payload indicated by the
> + * encryption header. The offset is measured in
> + * 512 byte sectors
> + *
> + * Returns: the payload offset in sectors.
> + */
> +uint64_t qcrypto_block_get_payload_offset(QCryptoBlock *block);
Please, let's use bytes here, not sectors.
> +++ b/tests/test-crypto-block.c
Nice tests. Overall looks like we are fairly close to having this ready
to commit.
--
Eric Blake eblake redhat com +1-919-301-3266
Libvirt virtualization library http://libvirt.org
[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 604 bytes --]
next prev parent reply other threads:[~2016-02-05 0:23 UTC|newest]
Thread overview: 69+ messages / expand[flat|nested] mbox.gz Atom feed top
2016-01-20 17:38 [Qemu-devel] [PATCH v2 00/17] Support LUKS encryption in block devices Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 01/17] crypto: ensure qcrypto_hash_digest_len is always defined Daniel P. Berrange
2016-01-21 6:12 ` Fam Zheng
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 02/17] crypto: add cryptographic random byte source Daniel P. Berrange
2016-01-21 6:12 ` Fam Zheng
2016-01-21 8:59 ` Daniel P. Berrange
2016-02-04 17:44 ` Eric Blake
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 03/17] crypto: add support for PBKDF2 algorithm Daniel P. Berrange
2016-01-21 6:59 ` Fam Zheng
2016-01-21 10:59 ` Daniel P. Berrange
2016-02-04 22:14 ` Eric Blake
2016-02-05 9:23 ` Daniel P. Berrange
2016-02-05 10:13 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 04/17] crypto: add support for generating initialization vectors Daniel P. Berrange
2016-01-21 7:51 ` Fam Zheng
2016-01-21 11:00 ` Daniel P. Berrange
2016-02-04 22:57 ` Eric Blake
2016-02-05 10:23 ` Daniel P. Berrange
2016-02-05 13:23 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 05/17] crypto: add support for anti-forensic split algorithm Daniel P. Berrange
2016-01-21 8:37 ` Fam Zheng
2016-01-21 11:01 ` Daniel P. Berrange
2016-02-04 23:26 ` Eric Blake
2016-02-05 12:37 ` Daniel P. Berrange
2016-02-05 12:39 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 06/17] crypto: add block encryption framework Daniel P. Berrange
2016-02-05 0:23 ` Eric Blake [this message]
2016-02-05 12:43 ` Daniel P. Berrange
2016-02-05 18:48 ` Eric Blake
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 07/17] crypto: implement the LUKS block encryption format Daniel P. Berrange
2016-02-05 17:38 ` Eric Blake
2016-02-08 16:03 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 08/17] block: add flag to indicate that no I/O will be performed Daniel P. Berrange
2016-02-05 19:08 ` Eric Blake
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 09/17] qemu-img/qemu-io: don't prompt for passwords if not required Daniel P. Berrange
2016-02-05 19:52 ` Eric Blake
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 10/17] block: add generic full disk encryption driver Daniel P. Berrange
2016-01-21 9:12 ` Fam Zheng
2016-01-21 11:02 ` Daniel P. Berrange
2016-01-21 13:01 ` Fam Zheng
2016-01-21 13:12 ` Daniel P. Berrange
2016-02-05 22:20 ` Eric Blake
2016-02-08 16:28 ` Daniel P. Berrange
2016-02-08 20:23 ` Eric Blake
2016-02-09 9:55 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 11/17] qcow2: make qcow2_encrypt_sectors encrypt in place Daniel P. Berrange
2016-01-21 9:13 ` Fam Zheng
2016-02-05 23:22 ` Eric Blake
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 12/17] qcow2: convert QCow2 to use QCryptoBlock for encryption Daniel P. Berrange
2016-01-21 9:54 ` Fam Zheng
2016-01-21 10:50 ` Daniel P. Berrange
2016-01-21 13:56 ` Fam Zheng
2016-01-21 14:03 ` Daniel P. Berrange
2016-02-08 18:12 ` Eric Blake
2016-02-09 12:32 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 13/17] qcow: make encrypt_sectors encrypt in place Daniel P. Berrange
2016-02-08 20:30 ` Eric Blake
2016-02-09 12:33 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 14/17] qcow: convert QCow to use QCryptoBlock for encryption Daniel P. Berrange
2016-02-08 20:57 ` Eric Blake
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 15/17] block: rip out all traces of password prompting Daniel P. Berrange
2016-01-21 13:02 ` Fam Zheng
2016-01-21 13:11 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 16/17] block: remove all encryption handling APIs Daniel P. Berrange
2016-02-08 21:23 ` Eric Blake
2016-02-09 12:34 ` Daniel P. Berrange
2016-01-20 17:38 ` [Qemu-devel] [PATCH v2 17/17] block: remove support for legecy AES qcow/qcow2 encryption Daniel P. Berrange
2016-02-08 21:26 ` Eric Blake
2016-02-09 12:35 ` Daniel P. Berrange
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=56B3EB84.1000100@redhat.com \
--to=eblake@redhat.com \
--cc=berrange@redhat.com \
--cc=famz@redhat.com \
--cc=kwolf@redhat.com \
--cc=qemu-block@nongnu.org \
--cc=qemu-devel@nongnu.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.