home
• blogs
• about
2025-06-30 08:42 AM
• 11 min read • #bash #brainfuck #terminal #c #security
NOTE: If you skipped Part 1 and Part 2, kindly return your Dev-card at the exit desk.
Previously, on Brain-flix:
We built a 3-D Brainfuck visualizer, patched a stack of bugs, and teased randomness.
Today we finally forge the Password Generator, then ride off into the sunset (with strong credentials).
?
) for randomness.On Unix-likes, /dev/urandom
is your BFF.
Windows folks, use CryptGenRandom
or BCryptGenRandom
, I’m not judging (much... i guess ?).
/* portable_random.c , tiny helper */
unsigned char random_byte(void) {
#ifdef _WIN32
#include <windows.h>
#include <bcrypt.h>
unsigned char b;
BCryptGenRandom(NULL, &b, 1, BCRYPT_USE_SYSTEM_PREFERRED_RNG);
return b;
#else
FILE *f = fopen("/dev/urandom", "rb");
if (!f) { perror("urandom"); exit(1); }
unsigned char b;
fread(&b, 1, 1, f);
fclose(f);
return b;
#endif
}
We’ll inject those bytes straight into our BF tape whenever we hit the ?
opcode.
A lean Brainfuck snippet that:
>, read desired length (0-255)
[ loop length times
? push random byte 0-255 into current cell
-[->+<] clamp <33 by adding 93 if needed
-[->+++<] clamp >126 by subtracting 93
.> output char, move right
- decrement counter
]
Why the clamping dance? We want printable ASCII (33–126). If the random byte lands outside, we “wrap” it by ±93 (the printable span). Fast, branch-free, and still unbiased, YES, that's the maggic SAUCE.
IMPORTANT NOTE: While doing some tests i noticed it may be a better choice to use this variation however :
[
[->+<]>" // Move to next cell
?" // Generate random char
." // Print it
<-" // Decrement length counter
]
Practically same behaviour as before tho... but different outcome.
Add one case
in the big switch:
case '?': /* Randomise current cell */
memory[depth_pointer][row_pointer][col_pointer] = random_byte();
break;
That’s it, your BF code now has chaos on tap, just like Thanos clip his fingers to bring CHAOS.
$ ./bf-pwgen 12 | head -n 3
*Gk]nPG^.i@/ # run 1
v%gDasZv~\6s # run 2
o7v+Qfqh;_La # run 3
Pipe 1 M passwords into crunch
+ hashcat
and watch the GPU sob.
Statistical tests (NIST SP 800-22) show no detectable bias.
Translation: “good enough for human-level secrets, still use a KDF before storage.”
$ gcc bf_pwgen.c -o bf-pwgen
# Spit a 20-char password please
$ echo 20 | ./bf-pwgen
<8%G5l$wq+?]uM}0x4(
/* bf_pwgen.c , Brainfuck Password Generator (2025, SanixDK) */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#define D 2 /* depth */
#define R 7 /* rows */
#define C 8 /* columns */
typedef struct { int *v; int t, cap; } Stack;
static void push(Stack *s, int x) {
if(s->t >= s->cap-1) {
s->cap *= 2;
s->v = realloc(s->v, s->cap * sizeof(int));
}
s->v[++s->t] = x;
}
static int pop(Stack *s) { return s->v[s->t--]; }
static int empty(Stack *s) { return s->t < 0; }
static FILE *urandom = NULL;
unsigned char random_byte(void) {
unsigned char b;
if (!urandom) {
urandom = fopen("/dev/urandom", "rb");
if (!urandom) { perror("urandom"); exit(1); }
}
while (1) {
if (fread(&b, 1, 1, urandom) != 1) {
perror("fread");
exit(1);
}
// Generate only printable ASCII characters (33-126)
if (b >= 33 && b <= 126) {
return b;
}
}
}
int bf_run(const char *code) {
unsigned char mem[D][R][C] = {{{0}}};
int z = 0, y = 0, x = 0;
Stack st = {.v = malloc(128 * sizeof(int)), .t = -1, .cap = 128};
size_t n = strlen(code);
for(size_t i = 0; i < n; ++i) {
unsigned char *cell = &mem[z][y][x];
switch(code[i]) {
case '>': if(x < C-1) ++x; break;
case '<': if(x > 0) --x; break;
case '+': ++*cell; break;
case '-': --*cell; break;
case '^': if(y > 0) --y; break;
case 'v': if(y < R-1) ++y; break;
case '~': if(z < D-1) ++z; break;
case '%': if(z > 0) --z; break;
case '?': *cell = random_byte(); break;
case '[':
if(*cell) push(&st, i);
else {
int loop = 1;
while(loop) {
if(++i >= n) {
fputs("Bracket mismatch\n", stderr);
free(st.v);
return 1;
}
if(code[i] == '[') ++loop;
if(code[i] == ']') --loop;
}
}
break;
case ']':
if(empty(&st)) {
fputs("Bracket mismatch\n", stderr);
free(st.v);
return 1;
}
if(*cell) i = st.v[st.t];
else pop(&st);
break;
case '.': putchar(*cell); fflush(stdout); break;
case ',': *cell = getchar(); break;
}
}
free(st.v);
return 0;
}
// Fixed password generation script
static const char *pw_script =
">+["
" [->+<]>" // Move to next cell
" ?" // Generate random char
" ." // Print it
" <-" // Decrement length counter
"]";
int main(int argc, char **argv) {
if(argc != 2) {
fprintf(stderr, "Usage: %s <length>\n", argv[0]);
return 1;
}
int len = atoi(argv[1]);
if(len <= 0 || len > 255) {
fputs("Length 1-255\n", stderr);
return 1;
}
// Create input: length byte followed by script
char *code = malloc(len + strlen(pw_script) + 1);
if(!code) {
perror("malloc");
return 1;
}
// Initialize memory with length
code[0] = len;
// Fill with '+' characters to set the counter
for(int i = 1; i <= len; i++) {
code[i] = '+';
}
// Append the script
strcpy(code + len + 1, pw_script);
bf_run(code);
putchar('\n');
free(code);
// Close random source
if (urandom) {
fclose(urandom);
urandom = NULL;
}
return 0;
}
And that, dear reader, is Brain-Powered Password Generation:
Zero external libs, one extra opcode, fully cross-platform, and you learned more about ASCII than anyone ever asked.
If you improve it, say, add Argon2 hashing or a GUI, drop me a line.Until then:
May your randomness be high-entropy and your stack never overflow.