Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Insecure Password Generation #338

@zx2c4

Description

@zx2c4

The current way of generating passwords is insecure. All passwords that have been generated with QtPass in the past must be regenerated and changed.

Here is the current password generation function:

      for (int i = 0; i < length; ++i) {
        int index = Util::rand() % charset.length();
        QChar nextChar = charset.at(index);
        passwd.append(nextChar);
      }

The problem here is that modulo will not uniformly distribute that set. The proper way to do things is to just throw away values that are out of bounds. You could try to do the calculation correctly to uniformly stretch or compress, but it's hard to get right, so it's best to just discard numbers outside the set and try again.

Secondly, and more critically, here is the implementation of Util::rand:

...
  qsrand(static_cast<uint>(QTime::currentTime().msec()));
...
int Util::rand() {
#ifdef Q_OS_WIN
  quint32 ret = 0;
  if (FAILED(BCryptGenRandom(NULL, (PUCHAR)&ret, sizeof(ret),
                             BCRYPT_USE_SYSTEM_PREFERRED_RNG)))
    return qrand();
  return ret % RAND_MAX;
#else
  return qrand();
#endif
}

Unfortunately, using a non-cryptographically secure random number generator like libc's rand() is problematic -- future outputs can be derived from knowing only a handful of past outputs -- and seeding that deterministic rng with currentTime()->msec() is even more dangerous. Not only is the current time a guessable/bruteforcable parameter, but the documentation for QTime::msec actually indicates that this is merely the "the millisecond part (0 to 999) of the time", which means there are only 1000 possibilities of generated sequences of passwords.

This is as bad as it gets, in terms of password manager password generation.

The proper fix is to use Qt 5.10's QRandomGenerator::system(). If 5.10 is not available to you, use /dev/urandom on Mac/Linux and RtlGenRandom on Windows.


The password generator inside pass(1) itself is implemented like this:

read -r -n $length pass < <(LC_ALL=C tr -dc "$characters" < /dev/urandom)
  • uses /dev/urandom
  • discards characters that aren't part of the set

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions