[Bug 1703691] Re: Using iter-time doesn't give the desired timeout and security

asi 1703691 at bugs.launchpad.net
Fri Aug 18 12:34:47 UTC 2017


Volume (master) key digest is there only to verify validity of the key.
The digest iteration count is not relevant for the security of LUKS in normal situation. 

This iteration (slowdown) for digest will only help if the volume key
was generated by a flawed RNG, where brute-force is possible. (For
proper RNG it is impossible to brute force key even without iterations.)

Moreover, if you know some plaintext on device, attacker will use
different trick (I think it is described in linked paper): You try to
decrypt device and check that plaintext (for example filesytem magic
string). This bypasses digest completely and cost is onle one cipher
decryption step per try (much cheaper than digest calculation).

IOW: the digest iteration count is not important, only the iteration
count in keyslot is, this one slows down password dictionary and brute-
force attacks.

-- 
You received this bug notification because you are a member of Ubuntu
Foundations Bugs, which is subscribed to cryptsetup in Ubuntu.
https://bugs.launchpad.net/bugs/1703691

Title:
  Using iter-time doesn't give the desired timeout and security

Status in cryptsetup:
  New
Status in cryptsetup package in Ubuntu:
  Confirmed

Bug description:
  I have formatted using cryptsetup with option iter-time for use longer
  timeout than the default 2000ms. When I write 60000 I get 18 seconds
  and 10000 is 2 seconds. Something is terrible wrong.

  To get a timeout closer to what I want I wrote this script, which is
  an except:

    benchmarkiterations=$(cryptsetup benchmark | grep -e
  '^PBKDF2-sha256' | awk '{print $2}')

    cryptsetup -q --key-file ${keyfileluks} luksFormat -i $(($cryptsetuptimeout)) -c aes -s 256 -h sha256 --uuid=${uuidluks} --use-random $loopluks
    timerstart=$(date +%s.%N)
    cryptsetup -q --key-file ${keyfileluks} luksOpen $loopluks ${uuidluks}_${mapper}crypt
    timerend=$(date +%s.%N)
    cryptsetup -q luksClose ${uuidluks}_${mapper}crypt

    timerdiff=$(bc -l <<< "($timerend-$timerstart)*1000")
    timerfactor=$(bc -l <<< "$cryptsetuptimeout/$timerdiff")
    timeoutnew=$(bc -l <<< "scale=0; ($cryptsetuptimeout*$timerfactor*1.2)/1")

    cryptsetup -q --key-file ${keyfileluks} luksFormat -i $timeoutnew -c aes -s 256 -h sha256 --uuid=${uuidluks} --use-random $loopluks
    iterations=$(cryptsetup luksDump $loopluks | grep -e '[[:space:]]Iterations:' | awk '{print $2}')
    iterationspermsec=$(($iterations/$cryptsetuptimeout))
    if [ "$iterationspermsec" -lt "500" ]; then
     echo "Error too few iterations: $iterationspermsec"
     exit 1
    fi

  First I benchmark the computer with rounds per second and then test
  the desired timeout. Then I compare the benchmark with the rounds per
  seconds got while testing and then calculates a new approximate value.
  Then I finally format the device. This gives better values.

  At lines around 700 in keymanage.c master key digest is set to the 1/8
  of the expected value:

   /* Compute master key digest */
   iteration_time_ms /= 8;
   header->mkDigestIterations = at_least((uint32_t)(*PBKDF2_per_sec/1024) * iteration_time_ms,
             LUKS_MKD_ITERATIONS_MIN);

  At lines around 800 in keymanage.c about half of the timeout goes
  away:

   /*
    * Avoid floating point operation
    * Final iteration count is at least LUKS_SLOT_ITERATIONS_MIN
    */
   PBKDF2_temp = (*PBKDF2_per_sec / 2) * (uint64_t)iteration_time_ms;
   PBKDF2_temp /= 1024;
   if (PBKDF2_temp > UINT32_MAX)
    PBKDF2_temp = UINT32_MAX;
   hdr->keyblock[keyIndex].passwordIterations = at_least((uint32_t)PBKDF2_temp,
               LUKS_SLOT_ITERATIONS_MIN);

  Moreover one second are 1000 ms, not 1024.

  Benchmarking of PBKDF always gives to low speed: (from pbkdf_check.c
  from line 54)

  int crypt_pbkdf_check(const char *kdf, const char *hash,
          const char *password, size_t password_size,
          const char *salt, size_t salt_size,
          uint64_t *iter_secs)
  {
   struct rusage rstart, rend;
   int r = 0, step = 0;
   long ms = 0;
   char buf;
   unsigned int iterations;

   if (!kdf || !hash)
    return -EINVAL;

   iterations = 1 << 15;
   while (ms < 500) {
    if (getrusage(RUSAGE_SELF, &rstart) < 0)
     return -EINVAL;

    r = crypt_pbkdf(kdf, hash, password, password_size, salt,
      salt_size, &buf, 1, iterations);
    if (r < 0)
     return r;

    if (getrusage(RUSAGE_SELF, &rend) < 0)
     return -EINVAL;

    ms = time_ms(&rstart, &rend);
    if (ms > 500)
     break;

    if (ms <= 62)
     iterations <<= 4;
    else if (ms <= 125)
     iterations <<= 3;
    else if (ms <= 250)
     iterations <<= 2;
    else
     iterations <<= 1;

    if (++step > 10 || !iterations)
     return -EINVAL;
   }

   if (iter_secs)
    *iter_secs = (iterations * 1000) / ms;
   return r;
  }

  It is not as secure as you expect. You can decrypt the device with a
  master key.

  https://unix.stackexchange.com/questions/119803/how-to-decrypt-luks-
  with-the-known-master-key

To manage notifications about this bug go to:
https://bugs.launchpad.net/cryptsetup/+bug/1703691/+subscriptions



More information about the foundations-bugs mailing list