Comment 0 for bug 1703691

Revision history for this message
Patrik Nilsson (nipatriknilsson) wrote :

I have formatted 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