diff mbox

[1/6] crypto: make PBKDF iterations configurable for LUKS format

Message ID 1473352047-908-2-git-send-email-berrange@redhat.com (mailing list archive)
State New, archived
Headers show

Commit Message

Daniel P. Berrangé Sept. 8, 2016, 4:27 p.m. UTC
As protection against bruteforcing passphrases, the PBKDF
algorithm is tuned by counting the number of iterations
needed to produce 1 second of running time. If the machine
that the image will be used on is much faster than the
machine where the image is created, it can be desirable
to raise the number of limits. This adds a new 'iter-time'
property that allows the user to choose the iteration
wallclock time.

Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
---
 block/crypto.c      |  6 ++++++
 crypto/block-luks.c | 32 +++++++++++++++++++++++---------
 qapi/crypto.json    |  6 +++++-
 3 files changed, 34 insertions(+), 10 deletions(-)

Comments

Eric Blake Sept. 8, 2016, 5:44 p.m. UTC | #1
On 09/08/2016 11:27 AM, Daniel P. Berrange wrote:
> As protection against bruteforcing passphrases, the PBKDF
> algorithm is tuned by counting the number of iterations
> needed to produce 1 second of running time. If the machine
> that the image will be used on is much faster than the
> machine where the image is created, it can be desirable
> to raise the number of limits. This adds a new 'iter-time'

s/limits/iterations/ ?

> property that allows the user to choose the iteration
> wallclock time.
> 
> Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> ---
>  block/crypto.c      |  6 ++++++
>  crypto/block-luks.c | 32 +++++++++++++++++++++++---------
>  qapi/crypto.json    |  6 +++++-
>  3 files changed, 34 insertions(+), 10 deletions(-)
> 

> +++ b/crypto/block-luks.c
> @@ -917,8 +917,12 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>      const char *hash_alg;
>      char *cipher_mode_spec = NULL;
>      QCryptoCipherAlgorithm ivcipheralg = 0;
> +    uint64_t iters;
>  
>      memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
> +    if (!luks_opts.has_iter_time) {
> +        luks_opts.iter_time = 1000;
> +    }
>      if (!luks_opts.has_cipher_alg) {
>          luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
>      }
> @@ -1064,7 +1068,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>      /* Determine how many iterations we need to hash the master
>       * key, in order to have 1 second of compute time used
>       */
> -    luks->header.master_key_iterations =
> +    iters = luks_opts.iter_time *
>          qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,

luks_opts.iter_time is a user-provided 64-bit value, so this
multiplication can overflow...

>                                     masterkey, luks->header.key_bytes,
>                                     luks->header.master_key_salt,
> @@ -1074,15 +1078,19 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>          error_propagate(errp, local_err);
>          goto error;
>      }
> -
> +    /* iter_time was in millis, but count_iters reported for secs */
> +    iters /= 1000;
>      /* Why /= 8 ?  That matches cryptsetup, but there's no
>       * explanation why they chose /= 8... Probably so that
>       * if all 8 keyslots are active we only spend 1 second
>       * in total time to check all keys */
> -    luks->header.master_key_iterations /= 8;
> +    iters /= 8;
> +    if (iters > UINT32_MAX) {
> +        error_setg(errp, "Too many PBKDF iterations for LUKS format");

...and your check here is too late to catch it.

> @@ -1131,7 +1139,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
>      /* Again we determine how many iterations are required to
>       * hash the user password while consuming 1 second of compute
>       * time */
> -    luks->header.key_slots[0].iterations =
> +    iters = luks_opts.iter_time *
>          qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,

And again.

> +++ b/qapi/crypto.json
> @@ -185,6 +185,9 @@
>  #                  Currently defaults to 'sha256'
>  # @hash-alg: #optional the master key hash algorithm
>  #            Currently defaults to 'sha256'
> +# @iter-time: #optional number of milliseconds to spend in
> +#             PBKDF passphrase processing. Currently defaults
> +#             to 1000. Since 2.8

I think this is usually written '(since 2.8)' rather than 'Since 2.8'.
Marc-Andre may have more input on exactly what format he is expecting
when his patches enable doc generation from .json source.
Daniel P. Berrangé Sept. 9, 2016, 9:32 a.m. UTC | #2
On Thu, Sep 08, 2016 at 12:44:55PM -0500, Eric Blake wrote:
> On 09/08/2016 11:27 AM, Daniel P. Berrange wrote:
> > As protection against bruteforcing passphrases, the PBKDF
> > algorithm is tuned by counting the number of iterations
> > needed to produce 1 second of running time. If the machine
> > that the image will be used on is much faster than the
> > machine where the image is created, it can be desirable
> > to raise the number of limits. This adds a new 'iter-time'
> 
> s/limits/iterations/ ?
> 
> > property that allows the user to choose the iteration
> > wallclock time.
> > 
> > Signed-off-by: Daniel P. Berrange <berrange@redhat.com>
> > ---
> >  block/crypto.c      |  6 ++++++
> >  crypto/block-luks.c | 32 +++++++++++++++++++++++---------
> >  qapi/crypto.json    |  6 +++++-
> >  3 files changed, 34 insertions(+), 10 deletions(-)
> > 
> 
> > +++ b/crypto/block-luks.c
> > @@ -917,8 +917,12 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >      const char *hash_alg;
> >      char *cipher_mode_spec = NULL;
> >      QCryptoCipherAlgorithm ivcipheralg = 0;
> > +    uint64_t iters;
> >  
> >      memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
> > +    if (!luks_opts.has_iter_time) {
> > +        luks_opts.iter_time = 1000;
> > +    }
> >      if (!luks_opts.has_cipher_alg) {
> >          luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
> >      }
> > @@ -1064,7 +1068,7 @@ qcrypto_block_luks_create(QCryptoBlock *block,
> >      /* Determine how many iterations we need to hash the master
> >       * key, in order to have 1 second of compute time used
> >       */
> > -    luks->header.master_key_iterations =
> > +    iters = luks_opts.iter_time *
> >          qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
> 
> luks_opts.iter_time is a user-provided 64-bit value, so this
> multiplication can overflow...

Oh doh, there I was thinkig it was just a 32bit int...

Regards,
Daniel
diff mbox

Patch

diff --git a/block/crypto.c b/block/crypto.c
index 7f61e12..7aa7eb5 100644
--- a/block/crypto.c
+++ b/block/crypto.c
@@ -33,6 +33,7 @@ 
 #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_ALG "ivgen-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_IVGEN_HASH_ALG "ivgen-hash-alg"
 #define BLOCK_CRYPTO_OPT_LUKS_HASH_ALG "hash-alg"
+#define BLOCK_CRYPTO_OPT_LUKS_ITER_TIME "iter-time"
 
 typedef struct BlockCrypto BlockCrypto;
 
@@ -183,6 +184,11 @@  static QemuOptsList block_crypto_create_opts_luks = {
             .type = QEMU_OPT_STRING,
             .help = "Name of encryption hash algorithm",
         },
+        {
+            .name = BLOCK_CRYPTO_OPT_LUKS_ITER_TIME,
+            .type = QEMU_OPT_NUMBER,
+            .help = "Time to spend in PBKDF in milliseconds",
+        },
         { /* end of list */ }
     },
 };
diff --git a/crypto/block-luks.c b/crypto/block-luks.c
index aba4455..a5d9ebc 100644
--- a/crypto/block-luks.c
+++ b/crypto/block-luks.c
@@ -917,8 +917,12 @@  qcrypto_block_luks_create(QCryptoBlock *block,
     const char *hash_alg;
     char *cipher_mode_spec = NULL;
     QCryptoCipherAlgorithm ivcipheralg = 0;
+    uint64_t iters;
 
     memcpy(&luks_opts, &options->u.luks, sizeof(luks_opts));
+    if (!luks_opts.has_iter_time) {
+        luks_opts.iter_time = 1000;
+    }
     if (!luks_opts.has_cipher_alg) {
         luks_opts.cipher_alg = QCRYPTO_CIPHER_ALG_AES_256;
     }
@@ -1064,7 +1068,7 @@  qcrypto_block_luks_create(QCryptoBlock *block,
     /* Determine how many iterations we need to hash the master
      * key, in order to have 1 second of compute time used
      */
-    luks->header.master_key_iterations =
+    iters = luks_opts.iter_time *
         qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
                                    masterkey, luks->header.key_bytes,
                                    luks->header.master_key_salt,
@@ -1074,15 +1078,19 @@  qcrypto_block_luks_create(QCryptoBlock *block,
         error_propagate(errp, local_err);
         goto error;
     }
-
+    /* iter_time was in millis, but count_iters reported for secs */
+    iters /= 1000;
     /* Why /= 8 ?  That matches cryptsetup, but there's no
      * explanation why they chose /= 8... Probably so that
      * if all 8 keyslots are active we only spend 1 second
      * in total time to check all keys */
-    luks->header.master_key_iterations /= 8;
+    iters /= 8;
+    if (iters > UINT32_MAX) {
+        error_setg(errp, "Too many PBKDF iterations for LUKS format");
+        goto error;
+    }
     luks->header.master_key_iterations = MAX(
-        luks->header.master_key_iterations,
-        QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS);
+        iters, QCRYPTO_BLOCK_LUKS_MIN_MASTER_KEY_ITERS);
 
 
     /* Hash the master key, saving the result in the LUKS
@@ -1131,7 +1139,7 @@  qcrypto_block_luks_create(QCryptoBlock *block,
     /* Again we determine how many iterations are required to
      * hash the user password while consuming 1 second of compute
      * time */
-    luks->header.key_slots[0].iterations =
+    iters = luks_opts.iter_time *
         qcrypto_pbkdf2_count_iters(luks_opts.hash_alg,
                                    (uint8_t *)password, strlen(password),
                                    luks->header.key_slots[0].salt,
@@ -1141,12 +1149,18 @@  qcrypto_block_luks_create(QCryptoBlock *block,
         error_propagate(errp, local_err);
         goto error;
     }
+
+    /* iter_time was in millis, but count_iters reported for secs */
+    iters /= 1000;
     /* Why /= 2 ?  That matches cryptsetup, but there's no
      * explanation why they chose /= 2... */
-    luks->header.key_slots[0].iterations /= 2;
+    iters /= 2;
+    if (iters > UINT32_MAX) {
+        error_setg(errp, "Too many PBKDF iterations for LUKS format");
+        goto error;
+    }
     luks->header.key_slots[0].iterations = MAX(
-        luks->header.key_slots[0].iterations,
-        QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
+        iters, QCRYPTO_BLOCK_LUKS_MIN_SLOT_KEY_ITERS);
 
 
     /* Generate a key that we'll use to encrypt the master
diff --git a/qapi/crypto.json b/qapi/crypto.json
index 34d2583..1527f4b 100644
--- a/qapi/crypto.json
+++ b/qapi/crypto.json
@@ -185,6 +185,9 @@ 
 #                  Currently defaults to 'sha256'
 # @hash-alg: #optional the master key hash algorithm
 #            Currently defaults to 'sha256'
+# @iter-time: #optional number of milliseconds to spend in
+#             PBKDF passphrase processing. Currently defaults
+#             to 1000. Since 2.8
 # Since: 2.6
 ##
 { 'struct': 'QCryptoBlockCreateOptionsLUKS',
@@ -193,7 +196,8 @@ 
             '*cipher-mode': 'QCryptoCipherMode',
             '*ivgen-alg': 'QCryptoIVGenAlgorithm',
             '*ivgen-hash-alg': 'QCryptoHashAlgorithm',
-            '*hash-alg': 'QCryptoHashAlgorithm'}}
+            '*hash-alg': 'QCryptoHashAlgorithm',
+            '*iter-time': 'int'}}
 
 
 ##