Skip to content

Instantly share code, notes, and snippets.

@hgaiser
Created December 23, 2025 18:23
Show Gist options
  • Select an option

  • Save hgaiser/49b0f8150f932e2955500671dd017ef4 to your computer and use it in GitHub Desktop.

Select an option

Save hgaiser/49b0f8150f932e2955500671dd017ef4 to your computer and use it in GitHub Desktop.
use std::io::{self, Read};
use std::env;
// Define the character set to use for the password.
// You can customize this string to include or exclude specific characters.
const CHARSET: &[u8] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZ\
abcdefghijklmnopqrstuvwxyz\
0123456789\
!@#$%^&*()_+-=[]{}|;:,.<>?";
/// Fills the provided buffer with cryptographically secure random bytes.
#[cfg(unix)]
fn get_random_bytes(buffer: &mut [u8]) -> io::Result<()> {
// On Unix-like systems, we read from /dev/urandom.
// /dev/urandom is non-blocking and suitable for generating cryptographic keys.
let mut file = std::fs::File::open("/dev/urandom")?;
file.read_exact(buffer)?;
Ok(())
}
/// Fills the provided buffer with cryptographically secure random bytes.
#[cfg(windows)]
mod win_api {
use std::os::raw::c_void;
// Define the constant for the system-preferred RNG.
// This allows us to pass NULL as the algorithm handle.
pub const BCRYPT_USE_SYSTEM_PREFERRED_RNG: u32 = 0x00000002;
// Link to the bcrypt.dll system library.
#[link(name = "bcrypt")]
extern "system" {
// BCryptGenRandom function signature.
// Documentation: https://docs.microsoft.com/en-us/windows/win32/api/bcrypt/nf-bcrypt-bcryptgenrandom
fn BCryptGenRandom(
halgorithm: *mut c_void,
pbbuffer: *mut u8,
cbbuffer: u32,
dwflags: u32,
) -> i32; // NTSTATUS (0 indicates success)
}
}
#[cfg(windows)]
fn get_random_bytes(buffer: &mut [u8]) -> io::Result<()> {
// Safety: We are calling an external Windows API.
// We verify that the buffer length fits into a u32.
let buffer_len = buffer.len();
if buffer_len > u32::MAX as usize {
return Err(io::Error::new(io::ErrorKind::InvalidInput, "Buffer too large"));
}
let status = unsafe {
win_api::BCryptGenRandom(
std::ptr::null_mut(), // hAlgorithm (NULL implies system preferred RNG)
buffer.as_mut_ptr(), // pBuffer
buffer_len as u32, // cbBuffer
win_api::BCRYPT_USE_SYSTEM_PREFERRED_RNG,
)
};
if status == 0 {
Ok(())
} else {
Err(io::Error::last_os_error())
}
}
/// Helper structure to buffer random bytes, reducing system call overhead.
struct RandomBuffer {
buffer: [u8; 64],
position: usize,
}
impl RandomBuffer {
fn new() -> Self {
Self {
buffer: [0u8; 64],
position: 64, // Start with "empty" buffer to force fill on first use
}
}
fn get_byte(&mut self) -> u8 {
if self.position >= self.buffer.len() {
// Refill buffer if empty
get_random_bytes(&mut self.buffer).expect("Fatal: Failed to generate secure random bytes");
self.position = 0;
}
let byte = self.buffer[self.position];
self.position += 1;
byte
}
}
/// Generates a secure password of the specified length.
fn generate_password(length: usize) -> String {
let charset_len = CHARSET.len();
let mut rng = RandomBuffer::new();
let mut password = String::with_capacity(length);
// To ensure a uniform distribution, we cannot simply do `random_byte % charset_len`.
// Because 256 is not perfectly divisible by most charset lengths, using modulo
// introduces "modulo bias" (some characters would appear slightly more often).
//
// We calculate the largest multiple of charset_len that fits in a u8 (0..256).
// We reject random bytes that are greater than or equal to this value.
let rejection_threshold = 256 - (256 % charset_len);
while password.len() < length {
let random_byte = rng.get_byte() as usize;
if random_byte < rejection_threshold {
// The byte is safe to map to a character index
let index = random_byte % charset_len;
let char = CHARSET[index] as char;
password.push(char);
}
// If random_byte >= rejection_threshold, we discard it and try again (Rejection Sampling)
}
password
}
fn main() {
let args: Vec<String> = env::args().collect();
// Default length if no argument is provided
let length: usize = if args.len() > 1 {
match args[1].parse() {
Ok(l) if l > 0 => l,
_ => {
eprintln!("Please provide a valid positive integer for the password length.");
return;
}
}
} else {
16
};
let password = generate_password(length);
println!("{}", password);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment