Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 18 May 2016 16:44:31 +0300
From:      Andriy Gapon <avg@FreeBSD.org>
To:        freebsd-fs <freebsd-fs@FreeBSD.org>
Subject:   Fwd: ZFS Encryption Implementation for Review
Message-ID:  <1ea0d65f-fc7d-f472-ce0a-f3c74bf08d77@FreeBSD.org>
In-Reply-To: <CAF2oFs_os_JxMuuFm=mxhB_-O%2Bg1TLYTh0FDySQZbX%2BN1yDPmQ@mail.gmail.com>
References:  <CAF2oFs_os_JxMuuFm=mxhB_-O%2Bg1TLYTh0FDySQZbX%2BN1yDPmQ@mail.gmail.com>

next in thread | previous in thread | raw e-mail | index | archive | help

Just in case people overlooked this information in another thread here.

-------- Forwarded Message --------
Subject: [developer] ZFS Encryption Implementation for Review
Date: Tue, 17 May 2016 17:17:53 -0400
From: Thomas Caputi <tcaputi@datto.com>
To: developer@open-zfs.org

I have created an implementation for native encryption in ZFS. This
implementation is currently available as a PR against ZoL
(https://github.com/zfsonlinux/zfs/pull/4329). I would appreciate it
if this PR could receive a review for consideration. For convenience,
I have pasted the PR's description below.

Thanks,
Tom Caputi

Native encryption in zfsonlinux (See issue #494)

The change incorporates 3 major pieces:

The first is a port of the Illumos Crypto Framework to a Linux kernel
module (found in module/icp). This is needed to do the actual
encryption work. We cannot use the Linux kernel's built in crypto api
because it is only exported to GPL-licensed modules. Having the ICP
also means the crypto code can run on any of the other kernels under
OpenZFS. I ended up porting over most of the internals of the
framework, which means that porting over other API calls (if we need
them) should be fairly easy. Specifically, I have ported over the API
functions related to encryption, digests, macs, and crypto templates.
The ICP is able to use assembly-accelerated encryption on amd64
machines and AES-NI instructions on Intel chips that support it. There
are place-holder directories for similar assembly optimizations for
other architectures (although they have not been written).

The second feature is a keystore that manages wrapping and encryption
keys for encrypted datasets. It has feature parity with Solaris, but
should be more predictable and consistent. It is fully integrated with
the existing zfs create functions and zfs clone functions. It also
exposes a new set of commands via zfs key for managing the keystore.
For more info on the inconsistencies in Solaris see my comment
(https://github.com/zfsonlinux/zfs/issues/494#issuecomment-178853634)
on the issue page. The keystore operates on a few rules:

All wrapping keys are 32 bytes (256 bits), even for 128 and 192 bit
encryption types.

Encryption must be specified at dataset creation time.

Specifying a keysource while creating a dataset causes the dataset to
become the root of an encryption tree.

All members of an encryption tree share the same wrapping key.

Each dataset can have up to 1 keychain (if it is encrypted) that is
not shared with anybody.


The last feature is the actual data and metadata encryption. All data
in an encrypted dataset is stored encrypted on-disk. User-provided
metadata is also encrypted, but metadata structures have been left
plain so that scrubbing and resilvering still works without the keys
loaded. Most of the design comes from this article
(https://blogs.oracle.com/darren/entry/zfs_encryption_what_is_on).
There are a few important distinctions, however. For instance, I store
the encryption IV in the padding of blkptr_t instead of in its third
DVA. I also have L2ARC encryption implemented, which Oracle did not
have at the time.


Implementation details that should be looked at

I created a new DMU_OT_* for keychain objects instead of using the
DMU_OTN() macro. I did this mostly for the ability to to register a
name which helped with debugging. The Keychain objects also seem like
a core enough structure to warrant a new dedicated object type.

The crypto framework has some code bloat to it, particularly in the
form of function stubs in header files that are never actually
implemented. I figured it would be best to leave these in, in case
more functions needed to be ported over.

The in-memory keystore is not the most efficient structure, since it
zeros and frees encryption keys whenever they are not in use. This is
intended as a security measure so that unwrapped keys do not exist in
memory longer than they are needed.

Encrypting data going to disk requires creating a keychain_record_t
during dsl_dataset_tryown(). I added a flag to this function for code
that wishes to own the dataset, but that does not require encrypted
data, such as the scrub functions. I did my best to confirm that all
owners set this flag correctly, but someone should confirm them, just
to be sure.

zfs send and zfs recv do not currently do anything special with
regards to encryption. The format of the send file has not changed and
zfs send requires the keys to be loaded in order to work. At some
point there should probably be a way to do encrypted sends.

I altered the prototype of lzc_create() and lzc_clone(). I understand
that the purpose of libzfs_core is to have a stable api interacting
with the ZFS ioctls. However, these functions need to accept wrapping
keys separately from the rest of their parameters because they need to
use the (new) hidden_args framework to support hiding arguments from
the logs. Without this, the wrapping keys would get printed to the
zpool history.

There is an extra local label that I needed to add to the top of of
the "global" function rijndael_key_setup_enc_intel() in
module/icp/aes_intel.S. For some reason, I had to use the local label
or the module would fail to link. If any assembly experts can tell me
why this is required or a better way to fix it, I would appreciate it.

As part of the L2ARC changes, I added a 8 byte MAC field to
l2arc_buf_hdr_t. I understand that there are a lot of reasons to keep
this struct small since many of them may be allocated at once, but I
do not seem to have another reasonable option here.

The icp is a kernel module that has a directory structure (unlike the
other modules in zfs). There are a few reasons for this. First, the
ICP has assembly code for different CPU architectures and I wanted to
match the structure of libspl. The ICP also has headers that did not
really need to belong in the global zfs headers and so it made sense
to make an includes directory for them locally. The directory
structure also approximately mimics the the structure of the Illumos
Crypto Framework, which will be important for maintainability. As a
result, I had to adjust the build systems to avoid flattening module
directories. This shouldn't matter much, since the other modules were
already flat.


-------------------------------------------
openzfs-developer
Archives: https://www.listbox.com/member/archive/274414/=now
RSS Feed: https://www.listbox.com/member/archive/rss/274414/28133750-22ed9730
Modify Your Subscription:
https://www.listbox.com/member/?member_id=28133750&id_secret=28133750-6c0d6209
Powered by Listbox: http://www.listbox.com



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?1ea0d65f-fc7d-f472-ce0a-f3c74bf08d77>