1 The Problem
We want a tool that creates a strong password: a random mix of letters, numbers, and symbols, of a length the user chooses. It teaches building a string from random choices — and an important lesson about which randomness is safe for security.
2 How to Think About It
Think about how a strong password is built, before any code:
3 The Build — explained part by part
Here is the complete generator. Each part is explained below.
package main
import (
"bufio"
"crypto/rand"
"fmt"
"math/big"
"os"
"strconv"
"strings"
)
const (
letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
digits = "0123456789"
symbols = "!@#$%^&*"
pool = letters + digits + symbols
)
// generate builds a password of the given length from the character pool,
// using crypto/rand — the secure choice for anything security-related.
func generate(length int) string {
password := make([]byte, length)
for i := range password {
// rand.Int never returns an error when reading from crypto/rand.Reader.
n, _ := rand.Int(rand.Reader, big.NewInt(int64(len(pool))))
password[i] = pool[n.Int64()]
}
return string(password)
}
func main() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Password length: ")
line, _ := reader.ReadString('\n')
length, err := strconv.Atoi(strings.TrimSpace(line))
if err != nil || length <= 0 {
fmt.Println("Please type a whole number greater than 0.")
return
}
fmt.Printf("Your password: %s\n", generate(length))
}
secrets vs random exactly. Go has two random packages: math/rand is fast but predictable (fine for games), and crypto/rand reads from the operating system’s secure entropy source, which an attacker cannot predict. Anything security-related — passwords, tokens, session IDs — must use crypto/rand.const pool = letters + digits + symbols — Go string constants can be built from other constants at compile time, so the whole allowed-character pool is computed once, before the program even runs.
rand.Int(rand.Reader, big.NewInt(n)) —
crypto/rand’s API is lower-level than Python’s secrets.choice: it returns a secure random big integer from 0 up to (but not including) n, which is exactly what we need to pick a random index into the pool. math/big is required because cryptographic randomness in Go is built around arbitrary-precision integers.make([]byte, length) then filling it in a loop — build the password one secure character at a time, the same repeat-
length-times idea as Python’s generator expression, just spelled as an explicit loop.
math/rand instead of crypto/rand — ordinary random is seeded predictably and unsafe for anything security-related.crypto/rand for passwords, tokens, or keys.rand.Int in real production code — here it practically never fails, but silently discarding errors is a habit worth catching early.pool constant above does.4 Test & Prove Each Part
We cannot predict a random password, but we can prove the rules it must always follow.
package main
import (
"strings"
"testing"
)
func TestCorrectLength(t *testing.T) {
if got := len(generate(12)); got != 12 {
t.Errorf("len(generate(12)) = %d; want 12", got)
}
}
func TestCharsFromPool(t *testing.T) {
pw := generate(50)
for _, ch := range pw {
if !strings.ContainsRune(pool, ch) {
t.Errorf("character %q in password is not in the allowed pool", ch)
}
}
}
func TestPasswordsDiffer(t *testing.T) {
if generate(16) == generate(16) {
t.Errorf("two 16-character passwords were identical — extremely unlikely, check the randomness source")
}
}
Run with go test -v ./.... We test the guarantees: correct length, only allowed characters, and uniqueness — we cannot test the exact output, which is the whole point of randomness. strings.ContainsRune checks each generated character is one we actually allowed.
5 The Interface
What it expects
Password length: 16What it returns
Your password: Rxp7QarCT*DvpdFA6 Run It & Automate It
Save the code as password.go and run it with go run password.go — Go compiles and executes in one step, no separate build needed while you are experimenting.
go run password.goEnter a length and get a strong, secure password.
A CI tool like Jenkins runs go test automatically whenever the code changes — every line below has a plain explanation.
Password length: 16
Your password: Rxp7QarCT*DvpdFA16.math/rand without noticing, or reusing a fixed seed somewhere. Use crypto/rand as shown above — it never needs seeding.// Jenkinsfile — runs the tests automatically every time the code changes.
pipeline {
agent any // run on any available machine
stages {
stage('Get the code') {
steps { checkout scm } // download the latest code
}
stage('Set up Go') {
steps {
sh 'go version' // confirm Go is installed
sh 'test -f go.mod || go mod init password_generator' // create a module if none exists
}
}
stage('Run the tests') {
steps {
sh 'go vet ./...' // catch obvious mistakes before running
sh 'go test -v ./...' // run every test, show each result
}
}
}
post {
success { echo 'All tests passed.' }
failure { echo 'A test failed — look above.' }
}
}
- Guarantee variety. Ensure at least one digit and one symbol by checking after generating (retry if missing). (Teaches: checking conditions.)
- Strength meter. Rate the password weak/strong by length with a
switch. (Teaches: branching on rules.) - Flag-based length. Use the
flagpackage so you can run./password -length 20without a prompt. (Teaches: command-line flags.)
crypto/rand (never math/rand) for anything security-related. Related: Standard Library, Error Handling & the Standard Library.