crandomlibsodiumpassword-generatorsodium

Random password generator + algorithm


I'm currently developing a CLI password generator and I've been trying to come up with ways of randomizing characters between a defined set of chars. I know the srand(time(NULL)) method but as far as I know, it's a bit incosistent and not so safe to generate random passwords. I also know there is a way of randomizing numbers using the libsodium library for C (according to this topic), but I'm not sure how to use it. Should I install all the dependencies in my project? Is a relative small project to have such a huge library. Although I plan expanding it as time goes by, I don't know if it's worth having a huge library and not use most of its functions. On top of that, are there specific algorithms to generate passwords other than just randomizing characters? Should I also randomize the array within itself following another algorithm for better consistency like the Fisher Yates Shuffle? Thanks in advance!


Solution

  • There are lots of issues to resolve, including:

    1. Is the requirement for a function or a program?
    2. If it is a function, can it use the same random number generator (seed) as other parts of the programs it is used in, or should its random numbers be independent of other sequences created by the same program?
    3. How do you create a good random seed?
    4. Assuming you want a function, what should the interface to the function be?
      • For example: extern void gen_random_password(size_t length, char buffer[length]);
      • The length specifies the bytes available in the array.
      • The password will therefore have one character less than the specified length in it, to allow for the null terminator.
    5. Which random number generators are available from your o/s:
      • rand() and srand() will be available 'everywhere'
      • POSIX nrand48() - no hidden seed
      • arc4random() — no seed permitted (BSD, macOS)
      • random() and srandom() (BSD, macOS)
    6. What characters are allowed in a password?

    I like using nrand48() because it allows the random password generator to run a series of random numbers independently of any other sequence because it takes the seed — an array of 3 unsigned short integers — as arguments.

    Generating a good random seed is tricky. I have code which can be configured to use any of these mechanisms:

    1. Value from reading /dev/random
    2. Value from reading /dev/urandom
    3. Value from arc4random()
    4. Value from mixing clock_gettime() and getpid() and 16-bit CRC
    5. Value from mixing gettimeofday() and getpid() and 16-bit CRC
    6. Value from mixing time() and getpid() and 16-bit CRC

    The first two are preferable — there may or may not be a significant difference between /dev/random and /dev/urandom.

    Once you've got these important but tedious issues out of the way, the core algorithm for generating a random password is very simple:

    grpwd43.h

    #ifndef JLSS_ID_GRPWD43_H
    #define JLSS_ID_GRPWD43_H
    
    #include <stddef.h>
    
    extern void gen_random_passwd(size_t length, char buffer[length]);
    
    #endif /* JLSS_ID_GRPWD43_H */
    

    grpwd43.c

    #include "grpwd43.h"         /* SSC: Self-sufficiency check */
    #include <assert.h>
    #include "randseed.h"
    #include "prng48.h"
    
    /*
    ** Tweak this list of alphanumerics plus punctuation to suit.
    ** For example, list the alphabet twice (or more) to make letters more
    ** likely than numbers or punctuation.
    */
    static const char password[] =
        "abcdefghijklmnopqrstuvwxyz"
        "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
        "0123456789"
        "!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~"
        ;
    enum { NUM_PASSWORD = sizeof(password) - 1 };
    
    static int initialized = 0;
    
    void gen_random_passwd(size_t length, char buffer[length])
    {
        assert(buffer != NULL && length != 0);
        if (buffer == NULL || length == 0)
            return;
    
        if (initialized == 0)
        {
            unsigned short seed[3];
            random_seed_bytes(sizeof(seed), seed);
            prng48_seed(seed);
            initialized = 1;
        }
    
        for (size_t i = 0; i < length - 1; i++)
        {
            buffer[i] = password[prng48_rand(0, NUM_PASSWORD - 1)];
        }
        buffer[length - 1] = '\0';
    }
    
    #ifdef TEST
    
    #include <stdio.h>
    
    int main(int argc, char **argv)
    {
        for (int i = 11; i < 31; i++)
        {
            char passwd[i];
            gen_random_passwd(i, passwd);
            printf("%d: %s\n", i - 1, passwd);
        }
    
        return 0;
    }
    
    #endif /* TEST */
    

    The other source files needed can be found in my SOQ (Stack Overflow Questions) repository on GitHub in the src/so-7594-6155 sub-directory or in the src/libsoq sub-directory.