crypto, hash, rand, and regexp

Updated

September 8, 2026

crypto, hash, rand, and regexp

Overview

Last stdlib essentials for integrity, randomness, and text matching.

Package Use
crypto/sha256 (etc.) Content hashes, integrity
crypto/hmac Message authentication
crypto/rand Secure random bytes
math/rand/v2 Non-secure randomness / sims
crypto/subtle Constant-time compare
crypto/mldsa Post-quantum ML-DSA signatures (Go 1.27+; also TLS/x509)
uuid Generate/parse UUIDs (Go 1.27+; RFC 9562)
regexp Regular expressions
net/url URL parse (with HTTP chapter)

TLS deep dive: Security hardening.

Hashing

sum := sha256.Sum256([]byte("hello"))
fmt.Printf("%x\n", sum)

h := sha256.New()
io.Copy(h, r)
digest := h.Sum(nil)

Streaming hash large inputs — do not ReadAll multi-GB files first.

HMAC

mac := hmac.New(sha256.New, key)
mac.Write(msg)
sig := mac.Sum(nil)
if !hmac.Equal(sig, expected) {
    return errUnauthorized
}

Use hmac.Equal / subtle.ConstantTimeCompare — not == on strings for secrets.

Randomness

// Secure tokens / keys
b := make([]byte, 16)
if _, err := rand.Read(b); err != nil { // crypto/rand
    return err
}
token := hex.EncodeToString(b)

// Simulations, randomized tests (not secrets)
n := rand.IntN(100) // math/rand/v2

Never use math/rand for session IDs, API keys, or CSRF tokens.

uuid (Go 1.27+)

Stdlib uuid covers the common cases that used to need github.com/google/uuid:

import "uuid"

id := uuid.New()       // currently v4
id = uuid.NewV7()      // time-ordered; good for DB keys
s := id.String()
parsed, err := uuid.Parse(s)

New() is the default; use NewV7() when insertion order / index locality matters. Random bits come from a cryptographically secure generator.

Go 1.27 also adds crypto/mldsa (FIPS 204) with crypto/x509 and crypto/tls integration for post-quantum signatures. Treat it as an opt-in for new PKI work, not a drop-in replacement for existing ECDSA certs.

regexp

re := regexp.MustCompile(`(?i)^[a-z0-9._%+\-]+@[a-z0-9.\-]+\.[a-z]{2,}$`)
ok := re.MatchString(email)
parts := re.FindStringSubmatch(s)

Performance tips

  1. Compile once (var re = regexp.MustCompile(...) at package scope).
  2. Prefer strings / bytes for fixed-prefix checks.
  3. Beware catastrophic backtracking on untrusted patterns/input — keep patterns simple.
  4. Use regexp.Compile when pattern comes from config; handle errors.
const max = 1024
if len(s) > max {
    return false
}
return re.MatchString(s)

Password hashing note

Stdlib does not include bcrypt/argon2. For passwords use golang.org/x/crypto/bcrypt or argon2 — do not roll SHA256(password).

Runnable example

go mod init example
go run .
package main

import (
    "crypto/hmac"
    "crypto/rand"
    "crypto/sha256"
    "crypto/subtle"
    "encoding/hex"
    "fmt"
    mrand "math/rand/v2"
    "regexp"
)

func main() {
    sum := sha256.Sum256([]byte("hello"))
    fmt.Println("sha256", hex.EncodeToString(sum[:])[:16]+"...")

    key := []byte("secret-key")
    msg := []byte("payload")
    mac := hmac.New(sha256.New, key)
    mac.Write(msg)
    sig := mac.Sum(nil)

    mac2 := hmac.New(sha256.New, key)
    mac2.Write(msg)
    fmt.Println("hmac ok?", hmac.Equal(sig, mac2.Sum(nil)))

    // constant-time compare demo
    a := []byte("abcd")
    b := []byte("abce")
    fmt.Println("subtle", subtle.ConstantTimeCompare(a, b) == 1)

    tok := make([]byte, 8)
    _, _ = rand.Read(tok)
    fmt.Println("token", hex.EncodeToString(tok))
    fmt.Println("sim roll", mrand.IntN(6)+1)

    re := regexp.MustCompile(`^item-(\d+)$`)
    m := re.FindStringSubmatch("item-42")
    fmt.Println("submatch", m)
}

Expected output (token varies):

sha256 2cf24dba5fb0a30e...
hmac ok? true
subtle false
token a1b2c3d4e5f67890
sim roll 4
submatch [item-42 42]

What to notice: - HMAC equality is constant-time via hmac.Equal. - crypto/rand for tokens; math/rand/v2 for games/sims. - Submatch index 0 is the full match; 1+ are groups.

Try next: Stream-hash a file with sha256.New + io.Copy and compare to shasum -a 256 on the command line.

Stdlib tour wrap-up

You now have a map of the packages that appear in nearly every Go binary. Next steps:

  1. Rebuild a small CLI using only parts 980–992.
  2. Rebuild a tiny JSON API with timeouts and slog.
  3. Jump to specialized parts only when stdlib friction is real.

Return to the stdlib overview checklist and mark what you can do without docs.