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.
#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:
cc -std=c17 -Wall -Wextra -Wpedantic password-generator.c \
$(pkg-config --cflags --libs libsodium) -o password-generator./password-generator
./password-generator 32The 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 withoutGRND_RANDOMfor normal cryptographic use. - Windows: Microsoft recommends
BCryptGenRandom()withBCRYPT_USE_SYSTEM_PREFERRED_RNG. Check the returnedNTSTATUSand linkBcrypt.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.
./password-generator 4
./password-generator 20
./password-generator 128
./password-generator 3 # must fail
./password-generator abc # must failFor 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))andrand(): these functions are predictable and unsuitable for credentials. - Applying raw modulo:
random_value % alphabet_sizeis 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.
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.