This is CVE-2013-4440, and unlike other CVEs issued for pwgen it's an
actually severe issue.  The problem here is that when !isatty() the default
surprisingly changes to something insecure.

Using the attached program, I empirically estimated the entropy:
length  8, -0A:  22.63 bits
length  8, -nc:  27.86 bits
length  8, -snc: ≈47 bits
length 10, -0A:  26.97 bits
length 10, -nc:  34.05 bits
length 12, -0A:  32.02 bits
length 12, -nc:  40.36 bits

The problem here is, an inconspicuous act of redirecting the output makes
generated passwords really insecure.  It _is_ documented, but a typical
person doesn't read the docs for something that seems obvious.  And dropping
the password quality to something as low as 22 bits of entropy warrants
a RC bug.

-- 
// If you believe in so-called "intellectual property", please immediately
// cease using counterfeit alphabets.  Instead, contact the nearest temple
// of Amon, whose priests will provide you with scribal services for all
// your writing needs, for Reasonable and Non-Discriminatory prices.
// Usage: feed this program a long stream of generated passwords on stdin,
// one per line -- like, pwgen -N 1000000000|./se
// To estimate entropy in mid-40s you need a billion samples and several hours
// of generation, much less for weaker passwords.

#include <math.h>
#include <stdio.h>
#include <string.h>
#include <set>
#include <string>

static std::set<std::string> seen;

int main()
{
    char line[128];
    long in = 0, cnt = 0;
    double log2 = log(2);

    do // First, grab 2^20 unique strings.
    {
        if (!fgets(line, sizeof(line), stdin))
            return 0;
        if (char* p = strchr(line, '\n'))
            *p = 0;
        seen.insert(line);
    } while (seen.size() < 1048576);

    while (1) // then, see what percentage of subsequently generated ones
    {         // fall into the above set.
        if (!fgets(line, sizeof(line), stdin))
            return 0;
        if (char* p = strchr(line, '\n'))
            *p = 0;

        cnt++;
        if (seen.count(line))
            in++;

        if (!(cnt % 65536) && in)
        {
            double pop = 1048576.0*(double)cnt/(double)in;
            printf("%ld/%ld: pop. est. %g, %g bits\n",
                   in, cnt, pop, log(pop)/log2);
        }
    }
}

Reply via email to