homeblogs about

Sukuna dramatically pointing at /dev/urandom like it just insulted his mother.

HOW TO MAKE A PASSWORD GENERATOR USING BRAINFUCK (PART 3 / 3)

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).


OBJECTIVES (a.k.a. “Final Boss”)

  1. Feed true entropy into Brainfuck.
  2. Design a minimalist BF script that churns passwords.
  3. Extend the interpreter with one extra opcode (?) for randomness.
  4. Benchmark output strength (and crack a joke while we’re at it).
  5. Drop a full ready-to-compile source file + usage guide.

1. SUMMONING REAL ENTROPY

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.


2. THE PASSWORD-FORGE SCRIPT

A lean Brainfuck snippet that:

  1. Fills N cells with random printable ASCII.
  2. Prints them, newline, done.
>,                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.


3. EXTENDING THE INTERPRETER

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.


MICRO-BENCHMARK (optional nerd flex, because i can... OKAY ?)

$ ./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.”


4. PUTTING IT TOGETHER

Building

$ gcc bf_pwgen.c -o bf-pwgen

Usage

# Spit a 20-char password please
$ echo 20 | ./bf-pwgen
<8%G5l$wq+?]uM}0x4(

5. FULL SOURCE (Appendix)

Click to expand 400 lines of boring C code (probably memory unsafe).
/* 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;
}

WRAP-UP

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.


<< blogs


githubtelegramemail