New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ZFS Encryption #4329

Closed
wants to merge 10 commits into
base: master
from

Conversation

Projects
None yet
@tcaputi
Contributor

tcaputi commented Feb 11, 2016

Native encryption in zfsonlinux (See issue #494)

The change incorporates 2 major pieces:

The first feature is a keystore that manages wrapping and encryption keys for encrypted datasets. The commands are similar to that of Solaris but with a few key enhancements to make it more predictable, more consistent, and require less manual maintenance. 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 issues with the Solaris implementation see my comments here and here. 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 second feature is the actual data and metadata encryption. All user 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. The design was originallly inspired by this article but has been changed fairly significantly since.

Implementation details that should be looked at

  • Encrypting data going to disk requires creating a key_mapping_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 raw sends.
  • I altered the prototype of lzc_create() and lzc_clone() to support hidden arguments. 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.

EDIT 5/4/16: Updated to reflect the current state of the PR
EDIT 1/3/17: Updated to reflect the current state of the PR

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 12, 2016

Contributor

I see my builds all failed because I left in a few bad asserts and I didn't have debugging enabled so they didn't cause an issue on my end. Oops. I will fix this tonight.

Contributor

tcaputi commented Feb 12, 2016

I see my builds all failed because I left in a few bad asserts and I didn't have debugging enabled so they didn't cause an issue on my end. Oops. I will fix this tonight.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 12, 2016

Contributor

I also see that the style issues are causing the builds to fail. I will fix that tonight as well.

Contributor

tcaputi commented Feb 12, 2016

I also see that the style issues are causing the builds to fail. I will fix that tonight as well.

@ilovezfs

This comment has been minimized.

Show comment
Hide comment
@ilovezfs

ilovezfs Feb 12, 2016

Contributor

Does none of this belong in spl?

Contributor

ilovezfs commented Feb 12, 2016

Does none of this belong in spl?

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 12, 2016

Contributor

All of the code in module/icp (with the exception of the algs directory which has specific cipher implementations) acts as a single frameowrk and should be kept together. Personally, I view it as a standalone module that relies on the spl which is why I put it where it is. To me it seemed like it was along the same lines as the nvpair module. I can move it if the community feels there is a better place for it.

Contributor

tcaputi commented Feb 12, 2016

All of the code in module/icp (with the exception of the algs directory which has specific cipher implementations) acts as a single frameowrk and should be kept together. Personally, I view it as a standalone module that relies on the spl which is why I put it where it is. To me it seemed like it was along the same lines as the nvpair module. I can move it if the community feels there is a better place for it.

@ryao

This comment has been minimized.

Show comment
Hide comment
@ryao

ryao Feb 13, 2016

Member

@ilovezfs Putting any of this into the SPL would require implementing new encryption routines because the Linux kernel GPL symbol exports its encryption routines and the SPL is not allowed to touch that.

Member

ryao commented Feb 13, 2016

@ilovezfs Putting any of this into the SPL would require implementing new encryption routines because the Linux kernel GPL symbol exports its encryption routines and the SPL is not allowed to touch that.

@ilovezfs

This comment has been minimized.

Show comment
Hide comment
@ilovezfs

ilovezfs Feb 13, 2016

Contributor

@ryao Then perhaps SPL needs to be renamed LPL "License Porting Layer" since a piece of Solaris porting cannot go in the SPL. Ahem.

Contributor

ilovezfs commented Feb 13, 2016

@ryao Then perhaps SPL needs to be renamed LPL "License Porting Layer" since a piece of Solaris porting cannot go in the SPL. Ahem.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 13, 2016

Contributor

@ryao Thank you for mentioning that. I had forgotten about the licensing aspect.

Contributor

tcaputi commented Feb 13, 2016

@ryao Thank you for mentioning that. I had forgotten about the licensing aspect.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 16, 2016

Contributor

does anyone know how to re-trigger the automated build tests? The issues from above should have been fixed by my reformatting commit, but I'd like to be sure.

Contributor

tcaputi commented Feb 16, 2016

does anyone know how to re-trigger the automated build tests? The issues from above should have been fixed by my reformatting commit, but I'd like to be sure.

@behlendorf

This comment has been minimized.

Show comment
Hide comment
@behlendorf

behlendorf Feb 17, 2016

Member

@tcaputi if you force update your branch on Github it will rerun all the builds and tests. Usually, I just rebase my work on master and then force update it.

As for putting this work in the SPL I'd prefer to keep in the ZFS source tree since it's all CDDL and originated from illumos. The SPL itself will in due course get moved in to a directory of the ZFS source tree for convenience so let's try and avoid adding more to it if we don't have too.

Member

behlendorf commented Feb 17, 2016

@tcaputi if you force update your branch on Github it will rerun all the builds and tests. Usually, I just rebase my work on master and then force update it.

As for putting this work in the SPL I'd prefer to keep in the ZFS source tree since it's all CDDL and originated from illumos. The SPL itself will in due course get moved in to a directory of the ZFS source tree for convenience so let's try and avoid adding more to it if we don't have too.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 18, 2016

Contributor

@behlendorf It doesn't look like the tests ran. I did a merge against upstream/master and then git push -f origin master to push to my master branch. Is this correct?

Contributor

tcaputi commented Feb 18, 2016

@behlendorf It doesn't look like the tests ran. I did a merge against upstream/master and then git push -f origin master to push to my master branch. Is this correct?

@l1k

This comment has been minimized.

Show comment
Hide comment
@l1k

l1k Feb 18, 2016

Contributor

@tcaputi: You need to rebase instead of merge, so assuming your index and working tree are clean:

git reset --hard f702c83
git fetch upstream
git rebase -i upstream/master
git push -f origin master
Contributor

l1k commented Feb 18, 2016

@tcaputi: You need to rebase instead of merge, so assuming your index and working tree are clean:

git reset --hard f702c83
git fetch upstream
git rebase -i upstream/master
git push -f origin master
@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 18, 2016

Contributor

I looked at the logs here. The builds are failing now because zfs requires the (few) changes I made to the SPL. I will make a PR for that too, but I suppose this won't work on the automated builds until then (which will probably take a little while)

Contributor

tcaputi commented Feb 18, 2016

I looked at the logs here. The builds are failing now because zfs requires the (few) changes I made to the SPL. I will make a PR for that too, but I suppose this won't work on the automated builds until then (which will probably take a little while)

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 18, 2016

Contributor

I made a (small) corresponding PR against the spl: zfsonlinux/spl#533

Contributor

tcaputi commented Feb 18, 2016

I made a (small) corresponding PR against the spl: zfsonlinux/spl#533

@behlendorf

This comment has been minimized.

Show comment
Hide comment
@behlendorf

behlendorf Feb 19, 2016

Member

@tcaputi if you add the line Requires-spl: refs/pull/533/head to commit message of the top patch in this stack then the builtbot will use that PR instead of master when checking out the spl for testing.

Member

behlendorf commented Feb 19, 2016

@tcaputi if you add the line Requires-spl: refs/pull/533/head to commit message of the top patch in this stack then the builtbot will use that PR instead of master when checking out the spl for testing.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 19, 2016

Contributor

@behlendorf I will do that and fix the SPL PR tonight. Thanks for the advice and patience.

Contributor

tcaputi commented Feb 19, 2016

@behlendorf I will do that and fix the SPL PR tonight. Thanks for the advice and patience.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 21, 2016

Contributor

I see that there are a few problems on non x86_64 architectures. I will fix these soon, but I don't have any local machines to test against. I hope its ok if I end up hitting the build system a few more times for testing.

Contributor

tcaputi commented Feb 21, 2016

I see that there are a few problems on non x86_64 architectures. I will fix these soon, but I don't have any local machines to test against. I hope its ok if I end up hitting the build system a few more times for testing.

@behlendorf

This comment has been minimized.

Show comment
Hide comment
@behlendorf

behlendorf Feb 25, 2016

Member

@tcaputi by all means keep submitting things until the buildbot is happy. That's what it's there for.

Member

behlendorf commented Feb 25, 2016

@tcaputi by all means keep submitting things until the buildbot is happy. That's what it's there for.

Show outdated Hide outdated include/sys/zio_crypt.h
@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 26, 2016

Contributor

@ryao Ok. Thanks. I just copies the license from an existing file. I'lll make the change.

Contributor

tcaputi commented Feb 26, 2016

@ryao Ok. Thanks. I just copies the license from an existing file. I'lll make the change.

@grahamperrin grahamperrin referenced this pull request Feb 26, 2016

Closed

ZFS Crypto support #494

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Mar 7, 2016

Contributor

@ryao fixed licensing in the latest commit.

Contributor

tcaputi commented Mar 7, 2016

@ryao fixed licensing in the latest commit.

@tcaputi tcaputi changed the title from [WIP][comments] ZFS Encryption Keystore to ZFS Encryption Keystore May 4, 2016

@tcaputi tcaputi changed the title from ZFS Encryption Keystore to ZFS Encryption May 4, 2016

@sammcj

This comment has been minimized.

Show comment
Hide comment
@sammcj

sammcj May 15, 2016

Unconstructive comment: I cannot express how exciting this is!

sammcj commented May 15, 2016

Unconstructive comment: I cannot express how exciting this is!

@FransUrbo

This comment has been minimized.

Show comment
Hide comment
@FransUrbo

FransUrbo May 16, 2016

Member

@tcaputi This have been out for a few months now, how does it look? Does it work, is there any got-ya's, risks etc?

I think that all boils down to is: Would you trust it with production data?

Member

FransUrbo commented May 16, 2016

@tcaputi This have been out for a few months now, how does it look? Does it work, is there any got-ya's, risks etc?

I think that all boils down to is: Would you trust it with production data?

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi May 16, 2016

Contributor

@FransUrbo

This have been out for a few months now, how does it look?

Well, this PR was a WIP for a few months now. I just changed it to a real PR a few days ago. That said, I have not been able to produce a scenario where I have lost data in quite some time. I have a lot of confidence in the encryption code because I stole all of it from Illumos, and after that the problem of not corrupting data is actually pretty simple, since really you just need to make sure you're not losing track of any of the encryption parameters.

I have looked into the 2 buildbot failures that are here (I don't know why github is showing 2 runs as incomplete when they succeeded). I was able to reproduce all of them using ZoL master. Of course, this makes me a little nervous about my code, since the tests are still failing and I'm not sure if I have introduced any new bugs.

The only real gotcha is that performance with many files may be a bit reduced because I force spill zfs to use spill blocks instead of bonus buffers for DMU_OT_PLAIN_FILE_CONTENTS. That should be it, as far as I'm aware.

I think that all boils down to is: Would you trust it with production data?

All of that said, for a project as widely used as zfs, I would not trust ANY code with production data until it has at least been reviewed by somebody else from the project, so I would hold off on using it for anything but testing until then. Today I plan to finish integrating my tests into the zfs-test framework and then I will post a message to the mailing list asking the wider OpenZFS community for review.

Contributor

tcaputi commented May 16, 2016

@FransUrbo

This have been out for a few months now, how does it look?

Well, this PR was a WIP for a few months now. I just changed it to a real PR a few days ago. That said, I have not been able to produce a scenario where I have lost data in quite some time. I have a lot of confidence in the encryption code because I stole all of it from Illumos, and after that the problem of not corrupting data is actually pretty simple, since really you just need to make sure you're not losing track of any of the encryption parameters.

I have looked into the 2 buildbot failures that are here (I don't know why github is showing 2 runs as incomplete when they succeeded). I was able to reproduce all of them using ZoL master. Of course, this makes me a little nervous about my code, since the tests are still failing and I'm not sure if I have introduced any new bugs.

The only real gotcha is that performance with many files may be a bit reduced because I force spill zfs to use spill blocks instead of bonus buffers for DMU_OT_PLAIN_FILE_CONTENTS. That should be it, as far as I'm aware.

I think that all boils down to is: Would you trust it with production data?

All of that said, for a project as widely used as zfs, I would not trust ANY code with production data until it has at least been reviewed by somebody else from the project, so I would hold off on using it for anything but testing until then. Today I plan to finish integrating my tests into the zfs-test framework and then I will post a message to the mailing list asking the wider OpenZFS community for review.

@FransUrbo

This comment has been minimized.

Show comment
Hide comment
@FransUrbo

FransUrbo May 16, 2016

Member

@tcaputi Sounds good, thanx. I'll setup some test machine with this then.

Member

FransUrbo commented May 16, 2016

@tcaputi Sounds good, thanx. I'll setup some test machine with this then.

@FransUrbo

This comment has been minimized.

Show comment
Hide comment
@FransUrbo

FransUrbo May 16, 2016

Member

It did occur to me that if it's at all possible, could you do TWO PRs instead of one big one?

As in, PR1 which adds all the fundamentals (commit1 with the direct port of the Illumos code, without any changes, commit2 with the porting stuff) and then PR2 which enables the whole thing?

That way it's easier to review and if it's easier, there's a bigger likelihood to get it accepted [this year :)]. And since it doesn't DO anything, it's safe to add into master.

Then everyone that want to test it, just apply PR2 to enable the whole thing. Also, since PR2 depends on PR1, PR2 will contain the commit(s) of PR1, so before it's accepted, one could just apply PR2 to test..

Member

FransUrbo commented May 16, 2016

It did occur to me that if it's at all possible, could you do TWO PRs instead of one big one?

As in, PR1 which adds all the fundamentals (commit1 with the direct port of the Illumos code, without any changes, commit2 with the porting stuff) and then PR2 which enables the whole thing?

That way it's easier to review and if it's easier, there's a bigger likelihood to get it accepted [this year :)]. And since it doesn't DO anything, it's safe to add into master.

Then everyone that want to test it, just apply PR2 to enable the whole thing. Also, since PR2 depends on PR1, PR2 will contain the commit(s) of PR1, so before it's accepted, one could just apply PR2 to test..

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi May 16, 2016

Contributor

@FransUrbo I did the PR as 2 commits at the request of @behlendorf. I would be fine splitting it if that is what everyone wants. Just to clarify, you would just want me to split the PR so that commit 1 is one PR, and commit 2 is another?

Contributor

tcaputi commented May 16, 2016

@FransUrbo I did the PR as 2 commits at the request of @behlendorf. I would be fine splitting it if that is what everyone wants. Just to clarify, you would just want me to split the PR so that commit 1 is one PR, and commit 2 is another?

@FransUrbo

This comment has been minimized.

Show comment
Hide comment
@FransUrbo

FransUrbo May 16, 2016

Member

@tcaputi I was thinking THREE commits:

  1. Full Illumos code, no changes
  2. Porting of the Illumos code
  3. Enabling of native crypto (code to use it in zfs create etc).

Commit one and two in one PR and commit three in a second. But maybe get @behlendorf opinion on that? @behlendorf, wouldn't that help making your life easier as well? If we could get the core in faster, wouldn't that be better for everyone?

We all know that 10k+ lines of PR isn't going to be looked at any time soon :)

Member

FransUrbo commented May 16, 2016

@tcaputi I was thinking THREE commits:

  1. Full Illumos code, no changes
  2. Porting of the Illumos code
  3. Enabling of native crypto (code to use it in zfs create etc).

Commit one and two in one PR and commit three in a second. But maybe get @behlendorf opinion on that? @behlendorf, wouldn't that help making your life easier as well? If we could get the core in faster, wouldn't that be better for everyone?

We all know that 10k+ lines of PR isn't going to be looked at any time soon :)

@FransUrbo

This comment has been minimized.

Show comment
Hide comment
@FransUrbo

FransUrbo May 16, 2016

Member

PS. The whole point in doing it this way is that the majority of the code can be accepted without any risk - it's never used/accessed! So technically, @behlendorf only need to review point/commit two this time and once it's been in there "X number of months/weeks", he/we can start looking at commit three without feeling stressed.

At work they're talking about "feature flags" (although that would probably clash with Z filesystem flags :) - write as much of new stuff as possible, but keep it disabled. Then enable it slowly on selected machines.

Member

FransUrbo commented May 16, 2016

PS. The whole point in doing it this way is that the majority of the code can be accepted without any risk - it's never used/accessed! So technically, @behlendorf only need to review point/commit two this time and once it's been in there "X number of months/weeks", he/we can start looking at commit three without feeling stressed.

At work they're talking about "feature flags" (although that would probably clash with Z filesystem flags :) - write as much of new stuff as possible, but keep it disabled. Then enable it slowly on selected machines.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi May 16, 2016

Contributor

@FransUrbo Unfortunately, when I created the ICP I did not start with the entirety of the Illumos Crypto code since it is pretty tightly coupled to the Illumos kernel. Most of what I did involved copying in the API functions I needed, getting them to compile (with linking errors) and then resolving dependencies. As a result, many files are almost perfectly identical to Illumos, but there are quite a few that are missing significant pieces since they were not needed by the functions I ported.

In order to make PR 1 look as you described, I would need to go back and do a lot of extra work to re-port everything starting with the full ICP. I'm also not sure it would be nicer to look at since there would be literally thousands of compilation errors in the first commit.

I agree, though, that it might make sense to have a separate PR for the ICP since it is functionally complete, but doesn't do anything on its own.

Contributor

tcaputi commented May 16, 2016

@FransUrbo Unfortunately, when I created the ICP I did not start with the entirety of the Illumos Crypto code since it is pretty tightly coupled to the Illumos kernel. Most of what I did involved copying in the API functions I needed, getting them to compile (with linking errors) and then resolving dependencies. As a result, many files are almost perfectly identical to Illumos, but there are quite a few that are missing significant pieces since they were not needed by the functions I ported.

In order to make PR 1 look as you described, I would need to go back and do a lot of extra work to re-port everything starting with the full ICP. I'm also not sure it would be nicer to look at since there would be literally thousands of compilation errors in the first commit.

I agree, though, that it might make sense to have a separate PR for the ICP since it is functionally complete, but doesn't do anything on its own.

@FransUrbo

This comment has been minimized.

Show comment
Hide comment
@FransUrbo

FransUrbo May 16, 2016

Member

To explain what I mean, taking a little part of one of the files in one of the commits:

diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 7525afc..37dab84 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -103,6 +103,7 @@ static int zfs_do_holds(int argc, char **argv);  <- This you add to PR1/commit1
 static int zfs_do_release(int argc, char **argv);
 static int zfs_do_diff(int argc, char **argv);
 static int zfs_do_bookmark(int argc, char **argv);
+static int zfs_do_crypto(int argc, char **argv);

 /*
  * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
@@ -150,6 +151,7 @@ typedef enum {  <- This you add to PR2/commit1
        HELP_RELEASE,
        HELP_DIFF,
        HELP_BOOKMARK,
+       HELP_CRYPTO,
 } zfs_help_t;

 typedef struct zfs_command {
@@ -203,6 +205,7 @@ static zfs_command_t command_table[] = {  <- This you add to PR2/commit1
        { "holds",      zfs_do_holds,           HELP_HOLDS              },
        { "release",    zfs_do_release,         HELP_RELEASE            },
        { "diff",       zfs_do_diff,            HELP_DIFF               },
+       { "key",        zfs_do_crypto,          HELP_CRYPTO             },
 };

 #define        NCOMMAND        (sizeof (command_table) / sizeof (command_table[0]))
@@ -319,6 +322,9 @@ get_usage(zfs_help_t idx)  <- This you add to PR2/commit1
                    "[snapshot|filesystem]\n"));
        case HELP_BOOKMARK:
                return (gettext("\tbookmark <snapshot> <bookmark>\n"));
+       case HELP_CRYPTO:
+               return (gettext("\tkey [-luK] <filesystem|volume>\n"
+                   "\tkey -c [-o keysource=value] <filesystem|volume>\n"));
        }

        abort();
@@ -640,7 +646,7 @@ static int  <- This you add to PR1/commit1
 zfs_do_clone(int argc, char **argv)
 {
        zfs_handle_t *zhp = NULL;
-       boolean_t parents = B_FALSE;
+       boolean_t parents = B_FALSE, add_key = B_FALSE;
        nvlist_t *props;
        int ret = 0;
        int c;
@@ -649,7 +655,7 @@ zfs_do_clone(int argc, char **argv)  <- This you add to PR2/commit1
                nomem();

        /* check options */
-       while ((c = getopt(argc, argv, "o:p")) != -1) {
+       while ((c = getopt(argc, argv, "o:pK")) != -1) {
                switch (c) {
                case 'o':
                        if (parseprop(props, optarg) != 0)
@@ -658,6 +664,9 @@ zfs_do_clone(int argc, char **argv)  <- This you add to PR1/commit1
                case 'p':
                        parents = B_TRUE;
                        break;
+               case 'K':
+                       add_key = B_TRUE;
+                       break;
                case '?':
                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                            optopt);
@@ -703,7 +712,7 @@ zfs_do_clone(int argc, char **argv)  <- This you add to PR1/commit1
        }

        /* pass to libzfs */
-       ret = zfs_clone(zhp, argv[1], props);
+       ret = zfs_clone(zhp, argv[1], props, add_key);

        /* create the mountpoint if necessary */
        if (ret == 0) {

You see? That way, PR1/commit1 can be added/accepted without any/little review (because it's already been reviewed by Illumos) and PR2/commit1 can be reviewed at "a later date".

Member

FransUrbo commented May 16, 2016

To explain what I mean, taking a little part of one of the files in one of the commits:

diff --git a/cmd/zfs/zfs_main.c b/cmd/zfs/zfs_main.c
index 7525afc..37dab84 100644
--- a/cmd/zfs/zfs_main.c
+++ b/cmd/zfs/zfs_main.c
@@ -103,6 +103,7 @@ static int zfs_do_holds(int argc, char **argv);  <- This you add to PR1/commit1
 static int zfs_do_release(int argc, char **argv);
 static int zfs_do_diff(int argc, char **argv);
 static int zfs_do_bookmark(int argc, char **argv);
+static int zfs_do_crypto(int argc, char **argv);

 /*
  * Enable a reasonable set of defaults for libumem debugging on DEBUG builds.
@@ -150,6 +151,7 @@ typedef enum {  <- This you add to PR2/commit1
        HELP_RELEASE,
        HELP_DIFF,
        HELP_BOOKMARK,
+       HELP_CRYPTO,
 } zfs_help_t;

 typedef struct zfs_command {
@@ -203,6 +205,7 @@ static zfs_command_t command_table[] = {  <- This you add to PR2/commit1
        { "holds",      zfs_do_holds,           HELP_HOLDS              },
        { "release",    zfs_do_release,         HELP_RELEASE            },
        { "diff",       zfs_do_diff,            HELP_DIFF               },
+       { "key",        zfs_do_crypto,          HELP_CRYPTO             },
 };

 #define        NCOMMAND        (sizeof (command_table) / sizeof (command_table[0]))
@@ -319,6 +322,9 @@ get_usage(zfs_help_t idx)  <- This you add to PR2/commit1
                    "[snapshot|filesystem]\n"));
        case HELP_BOOKMARK:
                return (gettext("\tbookmark <snapshot> <bookmark>\n"));
+       case HELP_CRYPTO:
+               return (gettext("\tkey [-luK] <filesystem|volume>\n"
+                   "\tkey -c [-o keysource=value] <filesystem|volume>\n"));
        }

        abort();
@@ -640,7 +646,7 @@ static int  <- This you add to PR1/commit1
 zfs_do_clone(int argc, char **argv)
 {
        zfs_handle_t *zhp = NULL;
-       boolean_t parents = B_FALSE;
+       boolean_t parents = B_FALSE, add_key = B_FALSE;
        nvlist_t *props;
        int ret = 0;
        int c;
@@ -649,7 +655,7 @@ zfs_do_clone(int argc, char **argv)  <- This you add to PR2/commit1
                nomem();

        /* check options */
-       while ((c = getopt(argc, argv, "o:p")) != -1) {
+       while ((c = getopt(argc, argv, "o:pK")) != -1) {
                switch (c) {
                case 'o':
                        if (parseprop(props, optarg) != 0)
@@ -658,6 +664,9 @@ zfs_do_clone(int argc, char **argv)  <- This you add to PR1/commit1
                case 'p':
                        parents = B_TRUE;
                        break;
+               case 'K':
+                       add_key = B_TRUE;
+                       break;
                case '?':
                        (void) fprintf(stderr, gettext("invalid option '%c'\n"),
                            optopt);
@@ -703,7 +712,7 @@ zfs_do_clone(int argc, char **argv)  <- This you add to PR1/commit1
        }

        /* pass to libzfs */
-       ret = zfs_clone(zhp, argv[1], props);
+       ret = zfs_clone(zhp, argv[1], props, add_key);

        /* create the mountpoint if necessary */
        if (ret == 0) {

You see? That way, PR1/commit1 can be added/accepted without any/little review (because it's already been reviewed by Illumos) and PR2/commit1 can be reviewed at "a later date".

@FransUrbo

This comment has been minimized.

Show comment
Hide comment
@FransUrbo

FransUrbo May 16, 2016

Member

@tcaputi Personally, I think PR1/commit1 and all it's compile problems is not a problem. That code have already been reviewed and should be ok generally. It's PR2/commit2 that's "The Magic (™)" and that's what really needs to be reviewed. Yes, they both need to be accepted "in bulk", but reviewing only the stuff that makes it compile should be less than 10k lines :).

And I know that would be a lot of work, but it should be faster second time around :). And it's only if you feel like spending a day or so doing it. Maybe Brian won't have a problem with it as-is, in which case my opinion is moot.

The really "hard core" thing is PR2/commit1. That's when all the really cool things starts to happen for users :D. But I rather have all the functionality in there, but disabled/unusable, for a couple of months to see if we run into some problem than have the whole shebang, 10k lines of code, accepted in one go..

Member

FransUrbo commented May 16, 2016

@tcaputi Personally, I think PR1/commit1 and all it's compile problems is not a problem. That code have already been reviewed and should be ok generally. It's PR2/commit2 that's "The Magic (™)" and that's what really needs to be reviewed. Yes, they both need to be accepted "in bulk", but reviewing only the stuff that makes it compile should be less than 10k lines :).

And I know that would be a lot of work, but it should be faster second time around :). And it's only if you feel like spending a day or so doing it. Maybe Brian won't have a problem with it as-is, in which case my opinion is moot.

The really "hard core" thing is PR2/commit1. That's when all the really cool things starts to happen for users :D. But I rather have all the functionality in there, but disabled/unusable, for a couple of months to see if we run into some problem than have the whole shebang, 10k lines of code, accepted in one go..

@FransUrbo

This comment has been minimized.

Show comment
Hide comment
@FransUrbo

FransUrbo May 16, 2016

Member

Also, doing the Illumos + porting in two commits helps with "traceability" - what part is ours and what part is theirs..

Member

FransUrbo commented May 16, 2016

Also, doing the Illumos + porting in two commits helps with "traceability" - what part is ours and what part is theirs..

@behlendorf

This comment has been minimized.

Show comment
Hide comment
@behlendorf

behlendorf May 16, 2016

Member

@tcaputi thanks for splitting this in to two commits, that'll make it easier to review and merge in chunks. While splitting it in to three as @FransUrbo suggested would make it easier to review, in this case I don't think it's worth it for a couple of reasons.

  1. Based on past experience, when bringing over large chunks of Illumos code you must have made a ton of trivial changes just to get it to compile. Those changes should all be evident and easily understandable when diff'ing against the original Illumos implementation. This is something I'm going to have to do anyway during the review so splitting them out likely isn't worth the effort.

  2. Merging just the original Illumos code would result in a ZoL commit which doesn't build and can't be tested. I'd prefer to avoid that and always keep the tree in a buildable state. My story here would be different if we were starting with a new git tree where nothing currently worked.

I do think it would be worthwhile to include in the commit for the crypto framework a comment describing in general what kinds of changes needed to be made. Where the changes all trivial C90 fixes? Were there changes to address gcc warnings? Did you identify and fix any real bugs? What large code chucks were dropped? etc.

I agree there's a good chance we can merge the crypto framework first without the additional key store implementation. As for splitting it in to different PRs I'd actually prefer to keep it in one. This way once test cases are added and automated testing runs it will actually exercise that new code. Splitting it in to its own PR logically makes sense but we'd give up the automated testing.

@tcaputi I've resubmitted those 4 failed/pending builders for another test run. I suspect those two never logged success because for some reason those two status updates were dropped when getting posted to Github. It seems to happen on occasion.

Member

behlendorf commented May 16, 2016

@tcaputi thanks for splitting this in to two commits, that'll make it easier to review and merge in chunks. While splitting it in to three as @FransUrbo suggested would make it easier to review, in this case I don't think it's worth it for a couple of reasons.

  1. Based on past experience, when bringing over large chunks of Illumos code you must have made a ton of trivial changes just to get it to compile. Those changes should all be evident and easily understandable when diff'ing against the original Illumos implementation. This is something I'm going to have to do anyway during the review so splitting them out likely isn't worth the effort.

  2. Merging just the original Illumos code would result in a ZoL commit which doesn't build and can't be tested. I'd prefer to avoid that and always keep the tree in a buildable state. My story here would be different if we were starting with a new git tree where nothing currently worked.

I do think it would be worthwhile to include in the commit for the crypto framework a comment describing in general what kinds of changes needed to be made. Where the changes all trivial C90 fixes? Were there changes to address gcc warnings? Did you identify and fix any real bugs? What large code chucks were dropped? etc.

I agree there's a good chance we can merge the crypto framework first without the additional key store implementation. As for splitting it in to different PRs I'd actually prefer to keep it in one. This way once test cases are added and automated testing runs it will actually exercise that new code. Splitting it in to its own PR logically makes sense but we'd give up the automated testing.

@tcaputi I've resubmitted those 4 failed/pending builders for another test run. I suspect those two never logged success because for some reason those two status updates were dropped when getting posted to Github. It seems to happen on occasion.

@lundman

This comment has been minimized.

Show comment
Hide comment
@lundman

lundman May 17, 2016

Contributor

Rats, lost the commit history with rebase, guess I have to report the code again :)

Contributor

lundman commented May 17, 2016

Rats, lost the commit history with rebase, guess I have to report the code again :)

@sempervictus

This comment has been minimized.

Show comment
Hide comment
@sempervictus

sempervictus Dec 26, 2016

Contributor
Contributor

sempervictus commented Dec 26, 2016

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Dec 26, 2016

Contributor

encrypted send without loaded keys is already in place, so is scrub.

To clear up a bit of confusion, raw sends with unloaded keys is NOT actually currently implemented yet. I wanted to make this PR as small as functionally possible to keep it as reviewable as possible. That said, the code has been completely engineered with raw sends in mind and it should not take long to implement (probably a week or 2). I've been a bit busy with some other projects and was waiting for a few initial reviews to come in from the ZFS maintainers (even before cryptogaphic review) before I got started.

Scrubs and resilvers are implemented as these are essential to the way ZFS currently functions so this PR would be incomplete without them.

My understanding is we are currently held up by the mostly insurmountable (for open source endeavors) question of whether the on disk/sent structures expose something which may reduce cryptographic strength in offline attacks (today or tomorrow).

That is certainly the biggest hurdle to overcome. However it is my understanding that review of this patch was also slated to be looked at only after #3656 was merged, so I've just been maintaining it until then.

@tcaputi: could you possibly share the validation plan/actors involved? It may be worth while as a community to scrape together some funds for a proper audit by a well known vendor (or ask them to do it for community PR/exposure - thats a marketable accomplishment) along with some tooling for the test suite to ensure we don't start leaking sensitive content from future additions made by others alongside your code.

I'm not sure how much I'm allowed to say about this due to NDAs I have signed. I believe I can say, however, that Datto would definitely be interested in pooling together some funds to get this properly reviewed. I would also encourage, as you said, anyone who has any contacts at a crypto security shop to see if they might be interested in helping out.

Contributor

tcaputi commented Dec 26, 2016

encrypted send without loaded keys is already in place, so is scrub.

To clear up a bit of confusion, raw sends with unloaded keys is NOT actually currently implemented yet. I wanted to make this PR as small as functionally possible to keep it as reviewable as possible. That said, the code has been completely engineered with raw sends in mind and it should not take long to implement (probably a week or 2). I've been a bit busy with some other projects and was waiting for a few initial reviews to come in from the ZFS maintainers (even before cryptogaphic review) before I got started.

Scrubs and resilvers are implemented as these are essential to the way ZFS currently functions so this PR would be incomplete without them.

My understanding is we are currently held up by the mostly insurmountable (for open source endeavors) question of whether the on disk/sent structures expose something which may reduce cryptographic strength in offline attacks (today or tomorrow).

That is certainly the biggest hurdle to overcome. However it is my understanding that review of this patch was also slated to be looked at only after #3656 was merged, so I've just been maintaining it until then.

@tcaputi: could you possibly share the validation plan/actors involved? It may be worth while as a community to scrape together some funds for a proper audit by a well known vendor (or ask them to do it for community PR/exposure - thats a marketable accomplishment) along with some tooling for the test suite to ensure we don't start leaking sensitive content from future additions made by others alongside your code.

I'm not sure how much I'm allowed to say about this due to NDAs I have signed. I believe I can say, however, that Datto would definitely be interested in pooling together some funds to get this properly reviewed. I would also encourage, as you said, anyone who has any contacts at a crypto security shop to see if they might be interested in helping out.

@sempervictus

This comment has been minimized.

Show comment
Hide comment
@sempervictus

sempervictus Dec 29, 2016

Contributor
Contributor

sempervictus commented Dec 29, 2016

@storkone

This comment has been minimized.

Show comment
Hide comment
@storkone

storkone Jan 3, 2017

Contributor

Is there a way to make the loaded keys volatile? So that after a reboot the keys must be entered again.

Contributor

storkone commented Jan 3, 2017

Is there a way to make the loaded keys volatile? So that after a reboot the keys must be entered again.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Jan 3, 2017

Contributor

@storkone

Is there a way to make the loaded keys volatile? So that after a reboot the keys must be entered again.

That is exactly how it currently works.

Contributor

tcaputi commented Jan 3, 2017

@storkone

Is there a way to make the loaded keys volatile? So that after a reboot the keys must be entered again.

That is exactly how it currently works.

@Kokokokoka

This comment has been minimized.

Show comment
Hide comment
@Kokokokoka

Kokokokoka Jan 3, 2017

Is there a way to use encryption with tpm? So one could use this with a coreboot linux payload, like heads (https://github.com/osresearch/heads/commits/master) does. As I want to use buildroot in order to get small
and rather easy to deploy on bios-chip environment.

Kokokokoka commented Jan 3, 2017

Is there a way to use encryption with tpm? So one could use this with a coreboot linux payload, like heads (https://github.com/osresearch/heads/commits/master) does. As I want to use buildroot in order to get small
and rather easy to deploy on bios-chip environment.

@storkone

This comment has been minimized.

Show comment
Hide comment
@storkone

storkone Jan 3, 2017

Contributor

@tcaputi, sorry about the noise. Didn't actually looked at the code nor build it. But the last paragraph in your comment gave me the impression that the keys were non volatile.

Contributor

storkone commented Jan 3, 2017

@tcaputi, sorry about the noise. Didn't actually looked at the code nor build it. But the last paragraph in your comment gave me the impression that the keys were non volatile.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Jan 3, 2017

Contributor

@Kokokokoka

Is there a way to use encryption with tpm?

As of now, no. The ICP does not currently work with TPMs and we cannot use the Linux crypto frameworks due to licensing problems.

Contributor

tcaputi commented Jan 3, 2017

@Kokokokoka

Is there a way to use encryption with tpm?

As of now, no. The ICP does not currently work with TPMs and we cannot use the Linux crypto frameworks due to licensing problems.

Show outdated Hide outdated module/zfs/zio_crypt.c
Show outdated Hide outdated module/zfs/zio_crypt.c
Show outdated Hide outdated module/zfs/zio_crypt.c
Show outdated Hide outdated module/zfs/zio_crypt.c
Show outdated Hide outdated module/zfs/zio_crypt.c
Show outdated Hide outdated module/zfs/zio_crypt.c
Show outdated Hide outdated module/zfs/dsl_crypt.c
Show outdated Hide outdated module/zfs/dsl_crypt.c
Show outdated Hide outdated module/zfs/dsl_crypt.c
Show outdated Hide outdated module/zfs/dsl_crypt.c
@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Jan 6, 2017

Contributor

@ahrens I've addressed your comments (other than the one about the mapping tree, which I think we need to discuss more) and repushed.

Contributor

tcaputi commented Jan 6, 2017

@ahrens I've addressed your comments (other than the one about the mapping tree, which I think we need to discuss more) and repushed.

Show outdated Hide outdated module/zfs/dsl_dataset.c
Show outdated Hide outdated module/zfs/dsl_dataset.c
Show outdated Hide outdated module/zfs/dmu_objset.c
Show outdated Hide outdated cmd/zinject/translate.c
Show outdated Hide outdated cmd/zinject/translate.c
.ad
.sp .6
.RS 4n
Indicates that the zpool command will request encryption keys for all encrypted datasets it attempts to mount as it is bringing the pool online. This is equivalent to running \fBzfs mount\fR on each encrypted dataset immediately after the pool is imported. If any datasets have a \fBprompt\fR keysource this command will block waiting for the key to be entered. Otherwise, encrypted datasets will be left unavailable until the keys are loaded.

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

equivalent to running \fBzfs mount\fR on each encrypted dataset

So, it mounts them? That seems counterintuitive. Seems like this should be equivalent to zfs key -l on all encrypted filesystems.

waiting for the key

I think this should be keys (plural).

Otherwise,

I think you mean, If \fB-l\fR is not specified,, not "If no datasets have a prompt keysource"

@ahrens

ahrens Jan 14, 2017

Contributor

equivalent to running \fBzfs mount\fR on each encrypted dataset

So, it mounts them? That seems counterintuitive. Seems like this should be equivalent to zfs key -l on all encrypted filesystems.

waiting for the key

I think this should be keys (plural).

Otherwise,

I think you mean, If \fB-l\fR is not specified,, not "If no datasets have a prompt keysource"

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

Correct on both points, will fix.

@tcaputi

tcaputi Jan 14, 2017

Contributor

Correct on both points, will fix.

This comment has been minimized.

@ahrens

ahrens Jan 15, 2017

Contributor

Thinking about this some more, on the surface it seems like -l does approximately nothing, because we already mount filesystems when the pool is imported, and presumably that will load keys for the filesystems we mount. I think the exception is filesystems with mountpoint=legacy or canmount=off | noauto. If that's right, I think it would be worth mentioning here - that -l is used to ensure that all keys are loaded, even for filesystems that are not mounted because they have mountpoint=legacy or canmount=off | noauto.

@ahrens

ahrens Jan 15, 2017

Contributor

Thinking about this some more, on the surface it seems like -l does approximately nothing, because we already mount filesystems when the pool is imported, and presumably that will load keys for the filesystems we mount. I think the exception is filesystems with mountpoint=legacy or canmount=off | noauto. If that's right, I think it would be worth mentioning here - that -l is used to ensure that all keys are loaded, even for filesystems that are not mounted because they have mountpoint=legacy or canmount=off | noauto.

This comment has been minimized.

@tcaputi

tcaputi Jan 15, 2017

Contributor

zfs key -l and zpool import -l also will result in encrypted zvols appearing in /dev/, by the way. To me, these commands allow the user to access the dataset normally doing whatever without worrying about encryption anymore.

That said, I can verify the behavior of this command and make the comment more specific accordingly.

@tcaputi

tcaputi Jan 15, 2017

Contributor

zfs key -l and zpool import -l also will result in encrypted zvols appearing in /dev/, by the way. To me, these commands allow the user to access the dataset normally doing whatever without worrying about encryption anymore.

That said, I can verify the behavior of this command and make the comment more specific accordingly.

This comment has been minimized.

@tcaputi

tcaputi Jan 17, 2017

Contributor

Some more context about the reason for zpool import -l after looking back at the code:

Thinking about this some more, on the surface it seems like -l does approximately nothing, because we already mount filesystems when the pool is imported, and presumably that will load keys for the filesystems we mount.

Right now, zpool import (without -l) will actually leave encrypted datasets unmounted. The reason for this was compatibility. I was afraid that someone might add an encrypted dataset to a pool that is mounted via an automated script. When the pool is re-imported, the script would hit the prompt for the encrypted dataset and hang indefinitely. As a result, I wanted people to have to opt into the automatic key loading.

@tcaputi

tcaputi Jan 17, 2017

Contributor

Some more context about the reason for zpool import -l after looking back at the code:

Thinking about this some more, on the surface it seems like -l does approximately nothing, because we already mount filesystems when the pool is imported, and presumably that will load keys for the filesystems we mount.

Right now, zpool import (without -l) will actually leave encrypted datasets unmounted. The reason for this was compatibility. I was afraid that someone might add an encrypted dataset to a pool that is mounted via an automated script. When the pool is re-imported, the script would hit the prompt for the encrypted dataset and hang indefinitely. As a result, I wanted people to have to opt into the automatic key loading.

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

Right now, zpool import (without -l) will actually leave encrypted datasets unmounted.

OK, let's document that in the manpage.

@ahrens

ahrens Jan 31, 2017

Contributor

Right now, zpool import (without -l) will actually leave encrypted datasets unmounted.

OK, let's document that in the manpage.

hierarchy, file size, file holes, and dedup tables. Key rotation is managed
internally by the ZFS kernel module and changing the user's key does not
require re-encrypting the entire dataset. Datasets can be scrubbed, resilvered,
moved, and deleted without the encryption keys being loaded (see the zfs key

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

moved

renamed?

zfs key

\fBzfs key\fR

@ahrens

ahrens Jan 14, 2017

Contributor

moved

renamed?

zfs key

\fBzfs key\fR

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 14, 2017

Contributor

will fix

provides additional protection against maliciously altered data. Deduplication
is still possible with encryption enabled but for security, datasets will only
dedup against themselves, their snapshots, and their clones. Encrypted data
cannot be embedded via the \fIembedded_data\fR feature.

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

This paragraph belongs in the zfs.8 manpage.

@ahrens

ahrens Jan 14, 2017

Contributor

This paragraph belongs in the zfs.8 manpage.

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 14, 2017

Contributor

will fix

encryption datasets may be vulnerable to a CRIME-like attack if applications
accessing the data allow for it. Deduplication with encryption will leak
information about which blocks are equivalent in a dataset and will incur an
extra CPU cost per block written.

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

This paragraph belongs in the zfs.8 manpage.

@ahrens

ahrens Jan 14, 2017

Contributor

This paragraph belongs in the zfs.8 manpage.

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 14, 2017

Contributor

will fix

internally by the ZFS kernel module and changing the user's key does not
require re-encrypting the entire dataset. Datasets can be scrubbed, resilvered,
moved, and deleted without the encryption keys being loaded (see the zfs key
subcommand for more info).

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

All the information in here should also be in the zfs.8 manpage, perhaps in a new subsection of the DESCRIPTION (peer to Clones, Mount Points, etc). The zpool-features manpage should primarily document why you would want to enable this feature. See for example the documentation of the bookmarks feature. In this case:

Enabling this property allows setting the \fBencryption\fR property to values other than \fBoff\fR.

@ahrens

ahrens Jan 14, 2017

Contributor

All the information in here should also be in the zfs.8 manpage, perhaps in a new subsection of the DESCRIPTION (peer to Clones, Mount Points, etc). The zpool-features manpage should primarily document why you would want to enable this feature. See for example the documentation of the bookmarks feature. In this case:

Enabling this property allows setting the \fBencryption\fR property to values other than \fBoff\fR.

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

Will fix. Would you want me to move some of this information or make a copy of it in zfs.8 (perhaps with different wording)?

@tcaputi

tcaputi Jan 14, 2017

Contributor

Will fix. Would you want me to move some of this information or make a copy of it in zfs.8 (perhaps with different wording)?

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

I think move it, and leave just the minimal description of what this property does (allows setting encryption), and when it becomes active and enabled.

@ahrens

ahrens Jan 14, 2017

Contributor

I think move it, and leave just the minimal description of what this property does (allows setting encryption), and when it becomes active and enabled.

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

Sounds good. Will fix.

@tcaputi

tcaputi Jan 14, 2017

Contributor

Sounds good. Will fix.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Jan 14, 2017

Contributor

@ahrens Thanks for helping with the review. I should be able to address all of the comments and make another push early next week.

Contributor

tcaputi commented Jan 14, 2017

@ahrens Thanks for helping with the review. I should be able to address all of the comments and make another push early next week.

.LP
.nf
\fB\fBzfs key -l\fR \fIfilesystem\fR | \fIvolume\fR\fR

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

I think it would be easier to read (e.g. in a script or an email) if we also have long opts for the "verbs" here:

zfs key --load
zfs key --unload
zfs key --change
@ahrens

ahrens Jan 14, 2017

Contributor

I think it would be easier to read (e.g. in a script or an email) if we also have long opts for the "verbs" here:

zfs key --load
zfs key --unload
zfs key --change

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

ZoL actually doesn't have any longopts handling at all. I can add it though, if it will help.

This (the wording of this command) is actually the biggest complaint I've received about the patch so far. I've heard from several people that they don't like that key isn't a verb like create or destroy. That said, I have been unable to come up with any alternatives that dont seem tedious (like zfs loadkey, zfs unloadkey,zfs changekey`. If you have any thoughts here now would probably be a good time while I'm cleaning up all of this other stuff.

@tcaputi

tcaputi Jan 14, 2017

Contributor

ZoL actually doesn't have any longopts handling at all. I can add it though, if it will help.

This (the wording of this command) is actually the biggest complaint I've received about the patch so far. I've heard from several people that they don't like that key isn't a verb like create or destroy. That said, I have been unable to come up with any alternatives that dont seem tedious (like zfs loadkey, zfs unloadkey,zfs changekey`. If you have any thoughts here now would probably be a good time while I'm cleaning up all of this other stuff.

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

I had the same thoughts... nothing better jumps to mind but I'll keep pondering...

FWIW, I think that the tedious zfs loadkey etc are in keeping with the existing interface, which does have some collections of verbs that all operate on the same concepts (e.g. zfs allow, zfs unallow; and zfs hold, zfs release, zfs holds).

@ahrens

ahrens Jan 14, 2017

Contributor

I had the same thoughts... nothing better jumps to mind but I'll keep pondering...

FWIW, I think that the tedious zfs loadkey etc are in keeping with the existing interface, which does have some collections of verbs that all operate on the same concepts (e.g. zfs allow, zfs unallow; and zfs hold, zfs release, zfs holds).

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

I can change this if you'd like (its a bit tedius to make every one of these a separate funnction in both kernel and userspace. My only other qualm here is that people have asked for a few other key-related subcommands like zfs verifykey which would tell you if a key is correct (even if its already loaded).

Another one I was going to add while doing all these fixups is zfs linkkey which would tell a dataset to inherit the wrapping keys of a parent. Currently, you can break wrapping key inheritance with zfs key -c but there's no way to relink them later so this would add that functionality. So at that point there are now 5 key commands which seems like a lot of bloat in both zfs_cmd.c and zfs_ioctl.c.

I'm fine doing it either way. Let me know what you think is best. Maybe @behlendorf could weigh in here too.

@tcaputi

tcaputi Jan 14, 2017

Contributor

I can change this if you'd like (its a bit tedius to make every one of these a separate funnction in both kernel and userspace. My only other qualm here is that people have asked for a few other key-related subcommands like zfs verifykey which would tell you if a key is correct (even if its already loaded).

Another one I was going to add while doing all these fixups is zfs linkkey which would tell a dataset to inherit the wrapping keys of a parent. Currently, you can break wrapping key inheritance with zfs key -c but there's no way to relink them later so this would add that functionality. So at that point there are now 5 key commands which seems like a lot of bloat in both zfs_cmd.c and zfs_ioctl.c.

I'm fine doing it either way. Let me know what you think is best. Maybe @behlendorf could weigh in here too.

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

If you do create lots of subcommands, I don't think that makes it necessary to create lots of ioctls; you can keep using one ioctl if that's cleaner/simpler.

zfs linkkey

Too bad key change doesn't really fit in with the property scheme, otherwise zfs inherit would be the obvious choice. I'd suggest that if we want this functionality, it may make sense to borrow the "inherit" terminology here, e.g. zfs inheritkey or zfs key --inherit.

@ahrens

ahrens Jan 14, 2017

Contributor

If you do create lots of subcommands, I don't think that makes it necessary to create lots of ioctls; you can keep using one ioctl if that's cleaner/simpler.

zfs linkkey

Too bad key change doesn't really fit in with the property scheme, otherwise zfs inherit would be the obvious choice. I'd suggest that if we want this functionality, it may make sense to borrow the "inherit" terminology here, e.g. zfs inheritkey or zfs key --inherit.

.ne 2
.mk
.na
\fB\fBkeysource\fR=<\fBraw\fR | \fBhex\fR | \fBpassphrase\fR>,<\fBprompt\fR | \fBfile://\fR\fI<absolute file path\fR>>

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

I get wanting to use a URI in case we later add extensions, like to contact some kind of keyserver. But it seems like it would be handy if we could specify an absolute path as a shortcut for file://<path>. This seems non-ambiguous since AFAIK a URI can't start with a /.

@ahrens

ahrens Jan 14, 2017

Contributor

I get wanting to use a URI in case we later add extensions, like to contact some kind of keyserver. But it seems like it would be handy if we could specify an absolute path as a shortcut for file://<path>. This seems non-ambiguous since AFAIK a URI can't start with a /.

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

This is another leftover from trying to stick to the Solaris UI. I actually kind of like the file URI, but that's just me. Maybe @behlendorf could be a tie-breaker here?

@tcaputi

tcaputi Jan 14, 2017

Contributor

This is another leftover from trying to stick to the Solaris UI. I actually kind of like the file URI, but that's just me. Maybe @behlendorf could be a tie-breaker here?

.ne 2
.mk
.na
\fB\fBkeysource\fR=<\fBraw\fR | \fBhex\fR | \fBpassphrase\fR>,<\fBprompt\fR | \fBfile://\fR\fI<absolute file path\fR>>

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

If you do keysource=raw,prompt, is it really possible to type/paste the raw data (including nul characters) into the terminal? If not, I assume this is still a valid setting because you can pipe raw bytes into zfs key -l? That might be worth mentioning in the "STDIN" section of zfs key -l docs.

@ahrens

ahrens Jan 14, 2017

Contributor

If you do keysource=raw,prompt, is it really possible to type/paste the raw data (including nul characters) into the terminal? If not, I assume this is still a valid setting because you can pipe raw bytes into zfs key -l? That might be worth mentioning in the "STDIN" section of zfs key -l docs.

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

It actually is possible to type paste raw characters in (at least in my terminal maybe not all). But yes, this is still valid for the use-case you described. I can add a sentence or 2 to the man page.

@tcaputi

tcaputi Jan 14, 2017

Contributor

It actually is possible to type paste raw characters in (at least in my terminal maybe not all). But yes, this is still valid for the use-case you described. I can add a sentence or 2 to the man page.

@grahamperrin

This comment has been minimized.

Show comment
Hide comment
@grahamperrin

grahamperrin Jan 14, 2017

Back to basics for a moment, and apologies if I read these points out of context:

… can't create an unencrypted fs under an encrypted …

– and from related https://github.com/tcaputi/zfs/blob/2f7c9cb5e27c4feef62971d0c2f802735d60a4e8/man/man8/zfs.8#L1021:

… Regardless, all children of an encrypted dataset must also be encrypted.

Children and creation of children aside, for a moment.

What, if anything, exists – or should exist – to prevent simple mount of a non-encrypted filesystem at a point within an encrypted filesystem?

(Probably treat that as a question about documentation. Not an expression of paranoia.)

grahamperrin commented Jan 14, 2017

Back to basics for a moment, and apologies if I read these points out of context:

… can't create an unencrypted fs under an encrypted …

– and from related https://github.com/tcaputi/zfs/blob/2f7c9cb5e27c4feef62971d0c2f802735d60a4e8/man/man8/zfs.8#L1021:

… Regardless, all children of an encrypted dataset must also be encrypted.

Children and creation of children aside, for a moment.

What, if anything, exists – or should exist – to prevent simple mount of a non-encrypted filesystem at a point within an encrypted filesystem?

(Probably treat that as a question about documentation. Not an expression of paranoia.)

.ad
.sp .6
.RS 4n
Unloads a key from ZFS, removing the ability to access the dataset and all of its children that inherit the \fBencryption\fR property. This requires that the dataset is not currently open or mounted. When a key is unloaded the \fBkeystatus\fR property will be set to \fBunavailable\fR.

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

how about \fBkeystatus\fR property is \fBunavailable\fR. It seems strange to say that it's "set" because it isn't a settable property.

@ahrens

ahrens Jan 14, 2017

Contributor

how about \fBkeystatus\fR property is \fBunavailable\fR. It seems strange to say that it's "set" because it isn't a settable property.

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 14, 2017

Contributor

will fix

.ad
.sp .6
.RS 4n
Allows a user to change the encryption key used to access a dataset. This command requires that the existing key for the dataset is already loaded into ZFS. This command may also be used to change the \fBpbkdf2iters\fR and / or \fBkeysource\fR properties as needed. If the dataset was previously inheriting the \fBencryption\fR property when this command is run it will now be locally set, indicating that this dataset must have its key loaded separately from the parent.

This comment has been minimized.

@ahrens

ahrens Jan 14, 2017

Contributor

What's the rationale behind requiring the key already be loaded for zfs key -c, compared with zfs mount which will load the key for you? I think you mentioned in another comment that there was a concern about what state you're left in (loaded or not) when zfs key -c completes. Maybe it should be the same as if you do zfs mount; zfs unmount, which I think would leave the key loaded.

The philosophy around key loaded-ness seems to be that in general we attempt to load when the keys are needed, and unload only when explicitly requested (zfs key -u). In the current implementation, I'm imaging user interaction:

$ zfs key -c ...
Sorry, you can't change keys because they aren't loaded.  Run "zfs key -l ..." to load them.
# OK fine I'll copy/paste what you said to do, but if you know what I have to do, why didn't you do it for me?
$ zfs key -l ...
$ zfs key -c ...

Seems like we should have a good reason for annoying them in this case.

@ahrens

ahrens Jan 14, 2017

Contributor

What's the rationale behind requiring the key already be loaded for zfs key -c, compared with zfs mount which will load the key for you? I think you mentioned in another comment that there was a concern about what state you're left in (loaded or not) when zfs key -c completes. Maybe it should be the same as if you do zfs mount; zfs unmount, which I think would leave the key loaded.

The philosophy around key loaded-ness seems to be that in general we attempt to load when the keys are needed, and unload only when explicitly requested (zfs key -u). In the current implementation, I'm imaging user interaction:

$ zfs key -c ...
Sorry, you can't change keys because they aren't loaded.  Run "zfs key -l ..." to load them.
# OK fine I'll copy/paste what you said to do, but if you know what I have to do, why didn't you do it for me?
$ zfs key -l ...
$ zfs key -c ...

Seems like we should have a good reason for annoying them in this case.

This comment has been minimized.

@tcaputi

tcaputi Jan 14, 2017

Contributor

Maybe a good middle ground is that the keys will remain in whatever state they were in before the zfs key -c command was run? Is that reasonable?

@tcaputi

tcaputi Jan 14, 2017

Contributor

Maybe a good middle ground is that the keys will remain in whatever state they were in before the zfs key -c command was run? Is that reasonable?

This comment has been minimized.

@tcaputi

tcaputi Jan 17, 2017

Contributor

See this comment I made above.

@tcaputi

tcaputi Jan 17, 2017

Contributor

See this comment I made above.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Jan 14, 2017

Contributor

@grahamperrin

What, if anything, exists – or should exist – to prevent simple mount of a non-encrypted filesystem at a point within an encrypted filesystem?

Nothing prevents a mount like that from being done right now, but as far as I am aware that will not automount when the parent is mounted where as child datasets are.

I can look into adding a sentence or 2 to the docs about this

Contributor

tcaputi commented Jan 14, 2017

@grahamperrin

What, if anything, exists – or should exist – to prevent simple mount of a non-encrypted filesystem at a point within an encrypted filesystem?

Nothing prevents a mount like that from being done right now, but as far as I am aware that will not automount when the parent is mounted where as child datasets are.

I can look into adding a sentence or 2 to the docs about this

@mailinglists35

This comment has been minimized.

Show comment
Hide comment
@mailinglists35

mailinglists35 Jan 17, 2017

if this PR good for testing by unpatient end-users? is the on-disk format finished?

mailinglists35 commented Jan 17, 2017

if this PR good for testing by unpatient end-users? is the on-disk format finished?

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Jan 17, 2017

Contributor

@mailinglists35 and everyone else:

if this PR good for testing by unpatient end-users? is the on-disk format finished?

I would say patch 89b4e7a is good enough to test. This week I will be addressing @ahrens comments and I will be using buildbot here for testing, so I wouldn't use any of these commits until theyre back to being stable (I will squash them all at that time).

The on-disk format has not been verified yet. We are working to get there.

Contributor

tcaputi commented Jan 17, 2017

@mailinglists35 and everyone else:

if this PR good for testing by unpatient end-users? is the on-disk format finished?

I would say patch 89b4e7a is good enough to test. This week I will be addressing @ahrens comments and I will be using buildbot here for testing, so I wouldn't use any of these commits until theyre back to being stable (I will squash them all at that time).

The on-disk format has not been verified yet. We are working to get there.

@lundman

This comment has been minimized.

Show comment
Hide comment
@lundman

lundman Jan 18, 2017

Contributor

I'm still getting patches on a silver-platter once the dust settles, right? :)

Contributor

lundman commented Jan 18, 2017

I'm still getting patches on a silver-platter once the dust settles, right? :)

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Jan 18, 2017

Contributor

@lundman Yes. Let me get all of these changes stable first (hopefully sometime this week) and then I'll squash them and I'll send you a nice and tidy patch

Contributor

tcaputi commented Jan 18, 2017

@lundman Yes. Let me get all of these changes stable first (hopefully sometime this week) and then I'll squash them and I'll send you a nice and tidy patch

tcaputi added some commits Jan 18, 2017

first attempt at splitting keysource into keyformat and keylocation. …
…needs testing. man pages / tests not updated
split "zfs key" command into subcommands. fixed tests to work with ne…
…w api. fixed a few bugs as they appeared. man page still not updated.
/*
* With the advent of encrypted data in the ARC it is now possible for
* legitimate errors to arise while transforming data into its desired format.

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

In general, the style/tone of comments should be to describe the code as it is. Consider someone reading this in 5 years time - the "advent" making it "now possible" will seem misplaced. Instead, consider something like:
Because the ARC can store encrypted data, errors (not due to bugs) may arise while transforming data into its desired format - specifically, when decrypting, the key may not be present, or the HMAC may not be correct, which signifies deliberate tampering with the on-disk state (assuming that the checksum was correct). The "error" parameter will be nonzero in this case, even if there is no associated zio.
Not sure if the technical details above are correct, but hopefully the style makes sense.

@ahrens

ahrens Jan 29, 2017

Contributor

In general, the style/tone of comments should be to describe the code as it is. Consider someone reading this in 5 years time - the "advent" making it "now possible" will seem misplaced. Instead, consider something like:
Because the ARC can store encrypted data, errors (not due to bugs) may arise while transforming data into its desired format - specifically, when decrypting, the key may not be present, or the HMAC may not be correct, which signifies deliberate tampering with the on-disk state (assuming that the checksum was correct). The "error" parameter will be nonzero in this case, even if there is no associated zio.
Not sure if the technical details above are correct, but hopefully the style makes sense.

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

That is correct. I will also fix the comment.

@tcaputi

tcaputi Jan 29, 2017

Contributor

That is correct. I will also fix the comment.

@@ -112,20 +121,21 @@ typedef enum arc_flags
ARC_FLAG_L2_WRITING = 1 << 11, /* write in progress */
ARC_FLAG_L2_EVICTED = 1 << 12, /* evicted during I/O */
ARC_FLAG_L2_WRITE_HEAD = 1 << 13, /* head of write list */
ARC_FLAG_ENCRYPT = 1 << 14, /* encrypted on disk */

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

You might add something like may or may not be encrypted in memory, to emphasize this fact (lest anyone wonder if encrypted on disk is what you precisely meant).

@ahrens

ahrens Jan 29, 2017

Contributor

You might add something like may or may not be encrypted in memory, to emphasize this fact (lest anyone wonder if encrypted on disk is what you precisely meant).

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

* disk as they appear in the main pool. In order for this to work we
* need to pass around the encryption parameters so they can be used
* to write data to the L2ARC. This struct is only defined in the
* arc_buf_hdr_t if the L1 header is defined and the has the

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

typo: remove extraneous the.

@ahrens

ahrens Jan 29, 2017

Contributor

typo: remove extraneous the.

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@@ -144,7 +154,8 @@ typedef enum arc_flags
typedef enum arc_buf_flags {
ARC_BUF_FLAG_SHARED = 1 << 0,
ARC_BUF_FLAG_COMPRESSED = 1 << 1
ARC_BUF_FLAG_COMPRESSED = 1 << 1,
ARC_BUF_FLAG_ENCRYPTED = 1 << 2

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

Add a comment here contrasting this with ARC_FLAG_ENCRYPTED.

@ahrens

ahrens Jan 29, 2017

Contributor

Add a comment here contrasting this with ARC_FLAG_ENCRYPTED.

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@@ -1331,13 +1391,30 @@ arc_buf_lsize(arc_buf_t *buf)
return (HDR_GET_LSIZE(buf->b_hdr));
}
boolean_t
arc_is_encrypted(arc_buf_t *buf)

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

Add a comment explaining what this means. In particular, this tells if the buf is stored encrypted in the ARC, and therefore ... [can't be read unless X flag passed to arc_read()? unless key loaded via foobar()?]. It returns false if the data is encrypted on disk but decrypted in memory.

(The same should type of comment should probably have been added above arc_get_compression.)

@ahrens

ahrens Jan 29, 2017

Contributor

Add a comment explaining what this means. In particular, this tells if the buf is stored encrypted in the ARC, and therefore ... [can't be read unless X flag passed to arc_read()? unless key loaded via foobar()?]. It returns false if the data is encrypted on disk but decrypted in memory.

(The same should type of comment should probably have been added above arc_get_compression.)

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix. should i add the one for compression as well while I'm here? Or should that be left to a different PR?

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix. should i add the one for compression as well while I'm here? Or should that be left to a different PR?

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

I'm fine with adding the compression comment while you're here.

@ahrens

ahrens Jan 31, 2017

Contributor

I'm fine with adding the compression comment while you're here.

@@ -208,7 +209,7 @@ typedef struct zio_cksum_salt {
* G gang block indicator
* B byteorder (endianness)
* D dedup
* X encryption (on version 30, which is not supported)
* X encryption

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

I think this should say set to zero; see diagram below for encrypted blocks, and then there should be a separate diagram that shows the layout for encrypted blocks, similar to how we explain embedded BP's (IIRC, checksum[2-3], fill, and dva[2] have different meanings when the X bit is set). The legend of the new diagram might omit fields that are already described here.

@ahrens

ahrens Jan 29, 2017

Contributor

I think this should say set to zero; see diagram below for encrypted blocks, and then there should be a separate diagram that shows the layout for encrypted blocks, similar to how we explain embedded BP's (IIRC, checksum[2-3], fill, and dva[2] have different meanings when the X bit is set). The legend of the new diagram might omit fields that are already described here.

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

Since I think the in-memory representations of the IV and salt (and MAC?) are treated as byte arrays, be sure to explain how those are encoded into the 64-bit words (e.g. low 8 bits of the word are the low byte of the array, etc). As you probably know, we can't simply treat the byte array as a uint64_t*, because of endianness concerns.

@ahrens

ahrens Jan 31, 2017

Contributor

Since I think the in-memory representations of the IV and salt (and MAC?) are treated as byte arrays, be sure to explain how those are encoded into the 64-bit words (e.g. low 8 bits of the word are the low byte of the array, etc). As you probably know, we can't simply treat the byte array as a uint64_t*, because of endianness concerns.

}
#define BP_GET_IV2(bp) BF64_GET((bp)->blk_fill, 32, 32)
#define BP_SET_IV2(bp, iv2) BF64_SET((bp)->blk_fill, 32, 32, iv2);

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

These should assert that the BP is encrypted (you can use the comma operator to do the assertion in the GET, see BPE_GET_ETYPE() for an example).

@ahrens

ahrens Jan 29, 2017

Contributor

These should assert that the BP is encrypted (you can use the comma operator to do the assertion in the GET, see BPE_GET_ETYPE() for an example).

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

}
#define BP_GET_IV2(bp) BF64_GET((bp)->blk_fill, 32, 32)
#define BP_SET_IV2(bp, iv2) BF64_SET((bp)->blk_fill, 32, 32, iv2);

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

It might be helpful (for readability) to add accessor macros for the other encryption-specific fields (even if they are entire words). Those macros could also assert that they are only used on encrypted BP's.

@ahrens

ahrens Jan 29, 2017

Contributor

It might be helpful (for readability) to add accessor macros for the other encryption-specific fields (even if they are entire words). Those macros could also assert that they are only used on encrypted BP's.

This comment has been minimized.

@tcaputi

tcaputi Feb 3, 2017

Contributor

I actually have full functions for these (see zio_crypt_encode_params_bp() in zio_crypt.c for instance). Let me know if you think they should be changed to macros and brought to spa.h

@tcaputi

tcaputi Feb 3, 2017

Contributor

I actually have full functions for these (see zio_crypt_encode_params_bp() in zio_crypt.c for instance). Let me know if you think they should be changed to macros and brought to spa.h

@@ -269,6 +270,7 @@ struct spa {
spa_avz_action_t spa_avz_action; /* destroy/rebuild AVZ? */
uint64_t spa_errata; /* errata issues detected */
spa_stats_t spa_stats; /* assorted spa statistics */
spa_keystore_t spa_keystore; /* loaded crypto keys */

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

align the comment with the adjacent ones.

@ahrens

ahrens Jan 29, 2017

Contributor

align the comment with the adjacent ones.

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@@ -86,7 +87,7 @@ typedef struct zil_header {
* number passed in the blk_cksum field of the blkptr_t
*/
typedef struct zil_chain {
uint64_t zc_pad;
uint64_t zc_mac; /* mac for encryption */

This comment has been minimized.

@ahrens

ahrens Jan 29, 2017

Contributor

align the comment with the adjacent ones

@ahrens

ahrens Jan 29, 2017

Contributor

align the comment with the adjacent ones

This comment has been minimized.

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

@tcaputi

tcaputi Jan 29, 2017

Contributor

will fix

.ad
.sp .6
.RS 4n
Indicates that the zpool command will request encryption keys for all encrypted datasets it attempts to mount as it is bringing the pool online. This is equivalent to running \fBzfs mount\fR on each encrypted dataset immediately after the pool is imported. If any datasets have a \fBprompt\fR keysource this command will block waiting for the key to be entered. Otherwise, encrypted datasets will be left unavailable until the keys are loaded.

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

Right now, zpool import (without -l) will actually leave encrypted datasets unmounted.

OK, let's document that in the manpage.

@ahrens

ahrens Jan 31, 2017

Contributor

Right now, zpool import (without -l) will actually leave encrypted datasets unmounted.

OK, let's document that in the manpage.

@@ -1331,13 +1391,30 @@ arc_buf_lsize(arc_buf_t *buf)
return (HDR_GET_LSIZE(buf->b_hdr));
}
boolean_t
arc_is_encrypted(arc_buf_t *buf)

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

I'm fine with adding the compression comment while you're here.

@ahrens

ahrens Jan 31, 2017

Contributor

I'm fine with adding the compression comment while you're here.

/*
* After encrypting many blocks with the same salt we may start to run
* up against the theoretical limits of how much data can securely be
* encrypted a single key using the supported encryption modes. To

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

typo: encrypted *with* a single key

@ahrens

ahrens Jan 31, 2017

Contributor

typo: encrypted *with* a single key

* counteract this we generate a new salt after writing
* ZIO_CRYPT_MAX_SALT_USAGE blocks of data, tracked by zk_salt_count.
* The current value was chosen because it is approximately the number
* of blocks that would have to be written in order to acheive a

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

spelling: achieve

@ahrens

ahrens Jan 31, 2017

Contributor

spelling: achieve

* 1 / 1 trillion chance of having an IV collision. Developers looking to
* change this number should make sure they take into account the
* birthday problem in regards to IV generation and the limits of what the
* underlying mode can actually handle.

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

I think this comment should lay out the math behind this number. That would make it clear that the birthday problem is accounted for. Also we should simply say that we account for the birthday problem rather than condescend to future readers. E.g. This protects against a birthday attack. With n = 400 million blocks encrypted with the same key and salt, and d = the number of IV's = 2^96, the probability of two blocks using the same IV is: 1 - 1 * (1 - 1 / d ) * ( 1 - 2 / d ) * … * (1 - (n - 1) / d) which is approximated by ... (according to [citation]), which is 10^-12.

@ahrens

ahrens Jan 31, 2017

Contributor

I think this comment should lay out the math behind this number. That would make it clear that the birthday problem is accounted for. Also we should simply say that we account for the birthday problem rather than condescend to future readers. E.g. This protects against a birthday attack. With n = 400 million blocks encrypted with the same key and salt, and d = the number of IV's = 2^96, the probability of two blocks using the same IV is: 1 - 1 * (1 - 1 / d ) * ( 1 - 2 / d ) * … * (1 - (n - 1) / d) which is approximated by ... (according to [citation]), which is 10^-12.

/* utility macros */
#define BITS_TO_BYTES(x) (((x) + 7) >> 3)
#define BYTES_TO_BITS(x) (x << 3)

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

I think that the compiler can do the optimization for / 8. How about x * NBBY and (x + NBBY - 1) / NBBY.

@ahrens

ahrens Jan 31, 2017

Contributor

I think that the compiler can do the optimization for / 8. How about x * NBBY and (x + NBBY - 1) / NBBY.

#define DSL_CRYPTO_KEY_IV "DSL_CRYPTO_IV"
#define DSL_CRYPTO_KEY_MAC "DSL_CRYPTO_MAC"
#define DSL_CRYPTO_KEY_MASTER_BUF "DSL_CRYPTO_MASTER"
#define DSL_CRYPTO_KEY_HMAC_KEY_BUF "DSL_CRYPTO_HMAC_KEY"

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

Any reason for the macro names and values to not be consistent, at least DSL_CRYPTO_KEY_XXX -> "DSL_CRYPTO_XXX" (this applies to the last 2 which differ in the _BUF suffix)

@ahrens

ahrens Jan 31, 2017

Contributor

Any reason for the macro names and values to not be consistent, at least DSL_CRYPTO_KEY_XXX -> "DSL_CRYPTO_XXX" (this applies to the last 2 which differ in the _BUF suffix)

extern zio_crypt_info_t zio_crypt_table[ZIO_CRYPT_FUNCTIONS];
/* ZAP entry keys for DSL Encryption Keys stored on disk */
#define DSL_CRYPTO_KEY_CRYPT "DSL_CRYPTO_CRYPT"

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

"crypto crypt" is not the most descriptive name. Maybe CRYPTO_FUNC?

@ahrens

ahrens Jan 31, 2017

Contributor

"crypto crypt" is not the most descriptive name. Maybe CRYPTO_FUNC?

@@ -208,7 +209,7 @@ typedef struct zio_cksum_salt {
* G gang block indicator
* B byteorder (endianness)
* D dedup
* X encryption (on version 30, which is not supported)
* X encryption

This comment has been minimized.

@ahrens

ahrens Jan 31, 2017

Contributor

Since I think the in-memory representations of the IV and salt (and MAC?) are treated as byte arrays, be sure to explain how those are encoded into the 64-bit words (e.g. low 8 bits of the word are the low byte of the array, etc). As you probably know, we can't simply treat the byte array as a uint64_t*, because of endianness concerns.

@ahrens

ahrens Jan 31, 2017

Contributor

Since I think the in-memory representations of the IV and salt (and MAC?) are treated as byte arrays, be sure to explain how those are encoded into the 64-bit words (e.g. low 8 bits of the word are the low byte of the array, etc). As you probably know, we can't simply treat the byte array as a uint64_t*, because of endianness concerns.

* encrypted, this stage determines how the encryption metadata is stored in
* the bp. Decryption is performed during ZIO_STAGE_READ_BP_INIT as a transform
* callback. Encryption is also mutually exclusive with nopwrite, because
* encrypted blocks with the same plaintext will not have matching ciphertexts.

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

how about because blocks with the same plaintext will be encrypted with different salts and therefore different IV's (if dedup is off), and therefore have different ciphertexts.

@ahrens

ahrens Feb 4, 2017

Contributor

how about because blocks with the same plaintext will be encrypted with different salts and therefore different IV's (if dedup is off), and therefore have different ciphertexts.

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

will fix

@tcaputi

tcaputi Feb 4, 2017

Contributor

will fix

zfeature_register(SPA_FEATURE_ENCRYPTION,
"com.datto:encryption", "encryption",
"Support for dataset level encryption",
0, encryption_deps);

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

The encryption feature is is deactivated when it's no longer used, but it isn't PER_DATASET? I'll look for where this is implemented... it may be simpler to use the PER_DATASET flag here.

@ahrens

ahrens Feb 4, 2017

Contributor

The encryption feature is is deactivated when it's no longer used, but it isn't PER_DATASET? I'll look for where this is implemented... it may be simpler to use the PER_DATASET flag here.

This comment has been minimized.

@tcaputi

tcaputi Feb 5, 2017

Contributor

From the comments around this flag it looks like this might be the case. I'm not familiar with this flag but it looks like all I need to do is set ds->ds_feature_activation_needed[feature] = B_TRUE when the dataset starts using the feature. Is this correct? Currently, the implementation increments the feature count when it creates a key object in the spa and decrements it when that object is destroyed.

@tcaputi

tcaputi Feb 5, 2017

Contributor

From the comments around this flag it looks like this might be the case. I'm not familiar with this flag but it looks like all I need to do is set ds->ds_feature_activation_needed[feature] = B_TRUE when the dataset starts using the feature. Is this correct? Currently, the implementation increments the feature count when it creates a key object in the spa and decrements it when that object is destroyed.

ZIO_CRYPT_DEFAULT, PROP_ONETIME, ZFS_TYPE_DATASET,
"on | off | aes-128-ccm | aes-192-ccm | aes-256-ccm | "
"aes-128-gcm | aes-192-gcm | aes-256-gcm", "ENCRYPTION",
crypto_table);

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

I guess you could argue that this property is inherited, but from the prop management infrastructure, it's ONETIME, so it should probably be registered in the "set once index properties" section

@ahrens

ahrens Feb 4, 2017

Contributor

I guess you could argue that this property is inherited, but from the prop management infrastructure, it's ONETIME, so it should probably be registered in the "set once index properties" section

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

will fix

@tcaputi

tcaputi Feb 4, 2017

Contributor

will fix

boolean_t
zfs_prop_valid_keylocation(const char *str)
{
if (strlen(str) == 6 && strncmp("prompt", str, 6) == 0)

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

This is equivalent to strcmp("prompt", str) == 0, but more brittle

@ahrens

ahrens Feb 4, 2017

Contributor

This is equivalent to strcmp("prompt", str) == 0, but more brittle

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

will fix

@tcaputi

tcaputi Feb 4, 2017

Contributor

will fix

{
if (strlen(str) == 6 && strncmp("prompt", str, 6) == 0)
return (B_TRUE);
else if (strlen(str) > 8 && strncmp("file:///", str, 8) == 0)

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

Rather than having the literal 8's, how about something like:

#define FILE_URI_PREFIX "file:///"
#define FILE_URI_PREFIX_LEN strlen(FILE_URI_PREFIX)

Also I'm not sure how valuable the "len>8" check is, since file:/// is just as wrong as file:///etc, but we aren't checking for that here.

@ahrens

ahrens Feb 4, 2017

Contributor

Rather than having the literal 8's, how about something like:

#define FILE_URI_PREFIX "file:///"
#define FILE_URI_PREFIX_LEN strlen(FILE_URI_PREFIX)

Also I'm not sure how valuable the "len>8" check is, since file:/// is just as wrong as file:///etc, but we aren't checking for that here.

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

This function is something I was trying to think of ways to improve or possibly remove. Realistically, zfs_prop_valid_keylocation() is just a sanity check. The create, clone, and change-key code all will actually need to load the key from the given keylocation before they can even attempt to talk to the kernel. This is all done in userspace, however, so the kernel can't really be sure if the keylocation it receives is valid. As a result, I added this function as a quick smoke test for the kernel. Can you think of a better way to check this?

@tcaputi

tcaputi Feb 4, 2017

Contributor

This function is something I was trying to think of ways to improve or possibly remove. Realistically, zfs_prop_valid_keylocation() is just a sanity check. The create, clone, and change-key code all will actually need to load the key from the given keylocation before they can even attempt to talk to the kernel. This is all done in userspace, however, so the kernel can't really be sure if the keylocation it receives is valid. As a result, I added this function as a quick smoke test for the kernel. Can you think of a better way to check this?

@@ -1527,13 +1536,14 @@ dmu_recv_begin_sync(void *arg, dmu_tx_t *tx)
const char *tofs = drba->drba_cookie->drc_tofs;
dsl_dataset_t *ds, *newds;
uint64_t dsobj;
int flags = DS_HOLD_FLAG_DECRYPT;

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

maybe name this dsflags for consistency with dmu_recv_begin_check()?

@ahrens

ahrens Feb 4, 2017

Contributor

maybe name this dsflags for consistency with dmu_recv_begin_check()?

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

I only called it dsflags there because flags was already being used. Would you like me to change this everywhere?

@tcaputi

tcaputi Feb 4, 2017

Contributor

I only called it dsflags there because flags was already being used. Would you like me to change this everywhere?

@@ -2454,14 +2466,16 @@ receive_free(struct receive_writer_arg *rwa, struct drr_free *drrf)
static void
dmu_recv_cleanup_ds(dmu_recv_cookie_t *drc)
{
int flags = DS_HOLD_FLAG_DECRYPT;

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

dsflags?

@ahrens

ahrens Feb 4, 2017

Contributor

dsflags?

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

see comment above

@tcaputi

tcaputi Feb 4, 2017

Contributor

see comment above

*/
dsl_dataset_disown(drc->drc_ds, dmu_recv_tag);
(void) spa_keystore_remove_mapping(dmu_tx_pool(tx)->dp_spa,
drc->drc_ds, drc->drc_ds);

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

this is so that zfs receive unloads the key?

@ahrens

ahrens Feb 4, 2017

Contributor

this is so that zfs receive unloads the key?

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

So that it releases the key mapping hold it created in dmu_recv_begin(). Loading and unloading is for wrapping keys, technically.

@tcaputi

tcaputi Feb 4, 2017

Contributor

So that it releases the key mapping hold it created in dmu_recv_begin(). Loading and unloading is for wrapping keys, technically.

@@ -196,8 +197,11 @@ traverse_prefetch_metadata(traverse_data_t *td,
if (BP_GET_LEVEL(bp) == 0 && BP_GET_TYPE(bp) != DMU_OT_DNODE)
return;
if ((td->td_flags & TRAVERSE_NO_DECRYPT) && BP_IS_ENCRYPTED(bp))
zio_flags |= ZIO_FLAG_RAW;

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

It might be worth a comment somewhere that this is all about dnode blocks (since traversal doesn't read any other encrypted blocks).

@ahrens

ahrens Feb 4, 2017

Contributor

It might be worth a comment somewhere that this is all about dnode blocks (since traversal doesn't read any other encrypted blocks).

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

will fix

@tcaputi

tcaputi Feb 4, 2017

Contributor

will fix

int32_t i;
int32_t epb = BP_GET_LSIZE(bp) >> DNODE_SHIFT;
dnode_phys_t *child_dnp;
/*
* dnode blocks might have their bonus buffers encrypted, so
* we must be careful to honor TRAVERSE_NO_DECRYPT

This comment has been minimized.

@ahrens

ahrens Feb 4, 2017

Contributor

should we assert that other cases here are not encrypted (and therefore it's OK to ignore NO_DECRYPT)?

@ahrens

ahrens Feb 4, 2017

Contributor

should we assert that other cases here are not encrypted (and therefore it's OK to ignore NO_DECRYPT)?

This comment has been minimized.

@tcaputi

tcaputi Feb 4, 2017

Contributor

sure. will fix.

@tcaputi

tcaputi Feb 4, 2017

Contributor

sure. will fix.

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 9, 2017

Contributor

Thank you very much to everyone who has looked at and commented on this PR. As some of you may have noticed, this page is getting a bit too big for github to handle effectively so I will be moving it to a new PR in a few minutes. I will link it here when it has been made.

Contributor

tcaputi commented Feb 9, 2017

Thank you very much to everyone who has looked at and commented on this PR. As some of you may have noticed, this page is getting a bit too big for github to handle effectively so I will be moving it to a new PR in a few minutes. I will link it here when it has been made.

@tcaputi tcaputi closed this Feb 9, 2017

@tcaputi tcaputi referenced this pull request Feb 9, 2017

Merged

ZFS Encryption #5769

@tcaputi

This comment has been minimized.

Show comment
Hide comment
@tcaputi

tcaputi Feb 9, 2017

Contributor

The new PR has been opened at #5769. Please leave any future comments and reviews there

Contributor

tcaputi commented Feb 9, 2017

The new PR has been opened at #5769. Please leave any future comments and reviews there

@behlendorf behlendorf removed this from In Progress in 0.7.0-rc4 Mar 20, 2017

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment