C17 code guide

C Password Generator

Build a secure random password generator in C with libsodium, uniform character selection, input validation, and explicit policy checks.

Complete password generator C program

This C17 program generates a 20-character password by default. It accepts lengths from 4 through 128 and requires lowercase, uppercase, numeric, and symbol characters.

password-generator.c
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sodium.h>

#define DEFAULT_LENGTH 20
#define MIN_LENGTH 4
#define MAX_LENGTH 128

static const char *GROUPS[] = {
    "abcdefghijklmnopqrstuvwxyz",
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
    "0123456789",
    "!@#$%^&*()-_=+"
};

static const char ALPHABET[] =
    "abcdefghijklmnopqrstuvwxyz"
    "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
    "0123456789"
    "!@#$%^&*()-_=+";

static bool contains_group(const char *password, size_t length,
                           const char *group) {
    for (size_t index = 0; index < length; index++) {
        if (strchr(group, password[index]) != NULL) {
            return true;
        }
    }
    return false;
}

static bool satisfies_policy(const char *password, size_t length) {
    const size_t group_count = sizeof GROUPS / sizeof GROUPS[0];

    for (size_t group = 0; group < group_count; group++) {
        if (!contains_group(password, length, GROUPS[group])) {
            return false;
        }
    }
    return true;
}

static int generate_password(char *output, size_t length) {
    if (output == NULL || length < MIN_LENGTH || length > MAX_LENGTH) {
        return -1;
    }

    const uint32_t alphabet_size = (uint32_t)(sizeof ALPHABET - 1);

    do {
        for (size_t index = 0; index < length; index++) {
            output[index] = ALPHABET[randombytes_uniform(alphabet_size)];
        }
    } while (!satisfies_policy(output, length));

    output[length] = '\0';
    return 0;
}

static int parse_length(const char *text, size_t *length) {
    char *end = NULL;
    errno = 0;
    const long value = strtol(text, &end, 10);

    if (errno != 0 || end == text || *end != '\0' ||
        value < MIN_LENGTH || value > MAX_LENGTH) {
        return -1;
    }

    *length = (size_t)value;
    return 0;
}

int main(int argc, char **argv) {
    size_t length = DEFAULT_LENGTH;

    if (argc > 2 ||
        (argc == 2 && parse_length(argv[1], &length) != 0)) {
        fprintf(stderr, "Usage: %s [length from %d to %d]\n",
                argv[0], MIN_LENGTH, MAX_LENGTH);
        return EXIT_FAILURE;
    }

    if (sodium_init() < 0) {
        fputs("Unable to initialize libsodium.\n", stderr);
        return EXIT_FAILURE;
    }

    char password[MAX_LENGTH + 1];
    if (generate_password(password, length) != 0) {
        fputs("Unable to generate the password.\n", stderr);
        return EXIT_FAILURE;
    }

    puts(password);
    sodium_memzero(password, sizeof password);
    return EXIT_SUCCESS;
}

Download the same C source file. The visible and downloadable versions are checked for exact agreement by the site test suite.

Install libsodium, compile, and run

The ISO C standard library does not provide a portable cryptographic random-number API. This example uses libsodium so one code path can obtain secure operating-system randomness on Linux, macOS, Windows, and supported Unix-like systems.

Install the libsodium development package through your operating system or project dependency manager. On systems with pkg-config, compile with:

Compile with C17 warnings enabled
cc -std=c17 -Wall -Wextra -Wpedantic password-generator.c \
  $(pkg-config --cflags --libs libsodium) -o password-generator
Generate the default or a custom length
./password-generator
./password-generator 32

The program prints one password followed by a newline. Avoid running it in a recorded shell session or a CI job whose standard output is retained.

Why randombytes_uniform is the important call

randombytes_uniform(upper_bound) returns an unpredictable integer from zero through upper_bound - 1. Libsodium documents that it avoids the bias introduced by randombytes_random() % upper_bound when the random range is not evenly divisible by the alphabet size.

The function delegates to the operating system's secure random source. Current libsodium documentation lists getrandom() on recent Linux and FreeBSD kernels, arc4random() on OpenBSD, and an operating-system random API on Windows.

How the policy check stays unbiased

Every position is first selected independently from the complete alphabet. If the result does not contain all four requested groups, the program discards the complete candidate and tries again. This rejection step means every accepted string from the policy-compliant set begins with the same probability.

Do not force one uppercase letter into the first position, one digit into the second, and one symbol into the third. Fixed placement makes the format predictable. Generating each required character separately and then shuffling can work, but whole-candidate rejection is easier to reason about for this small policy.

Customize the alphabet safely

Edit both GROUPS and ALPHABET together. The groups define the minimum policy; the combined alphabet defines every selectable character. If the destination rejects punctuation, remove that group, lower MIN_LENGTH only if the number of remaining required groups permits it, and increase the default length when practical.

The example uses single-byte ASCII characters. If you add multibyte UTF-8 text, array indexing selects bytes rather than user-perceived characters. Use an explicit Unicode representation and normalization policy instead of treating arbitrary UTF-8 bytes as a C character array.

Native alternatives for a platform-specific C program

  • Linux: getrandom() can return cryptographic bytes without opening a pathname. Check its return value and handle short reads or errors. The Linux manual recommends the urandom source without GRND_RANDOM for normal cryptographic use.
  • Windows: Microsoft recommends BCryptGenRandom() with BCRYPT_USE_SYSTEM_PREFERRED_RNG. Check the returned NTSTATUS and link Bcrypt.lib.
  • OpenBSD and compatible systems: arc4random_uniform() provides bounded uniform selection where the operating system exposes it.

Do not silently fall back to rand() if a secure API fails. Stop and report the error.

Test the C random password generator

Compile with warnings enabled and run representative boundaries. The published source was compiled with Clang in C17 mode using -Wall -Wextra -Wpedantic. The 4- and 20-character cases succeeded, and length 3 was rejected.

Boundary and policy checks
./password-generator 4
./password-generator 20
./password-generator 128
./password-generator 3   # must fail
./password-generator abc # must fail

For automated tests, verify length, allowed characters, every required group, and invalid arguments across many independently generated samples. Do not expect one exact password and do not treat a small duplicate check as a statistical certification of the random source.

Common password generator C mistakes

  • Calling srand(time(NULL)) and rand(): these functions are predictable and unsuitable for credentials.
  • Applying raw modulo: random_value % alphabet_size is biased unless the ranges divide evenly.
  • Ignoring errors: initialization and operating-system random calls can fail and must not produce a password from uninitialized memory.
  • Missing the terminator: reserve one extra byte and add '\0' before using the password as a C string.
  • Accepting an unchecked length: validate parsed input before writing into a fixed-size buffer.
  • Logging generated credentials: secure randomness cannot protect a secret copied into telemetry, CI logs, or shell history.
Password storage is a separate problem. A generator creates a new secret. An authentication service should store an appropriate salted password hash rather than plaintext or a reversible encoding.

No C project required

If you need a password rather than source code, use the browser password generator. For adjacent programming-language examples, see the Python, Java, and JavaScript password generator guides. The password-generating algorithm guide explains rejection sampling and policy constraints in more depth.

Sources and tested scope

The program and boundary cases were compiled and executed with Clang and libsodium 1.0.20 on October 6, 2026. Platform behavior was checked against current primary documentation.