Skip to content

Instantly share code, notes, and snippets.

@dopey
Forked from denisbrodbeck/main.go
Last active June 26, 2024 13:38
Show Gist options
  • Save dopey/c69559607800d2f2f90b1b1ed4e550fb to your computer and use it in GitHub Desktop.
Save dopey/c69559607800d2f2f90b1b1ed4e550fb to your computer and use it in GitHub Desktop.
How to generate secure random strings in golang with crypto/rand.
package main
import (
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"math/big"
)
// Adapted from https://elithrar.github.io/article/generating-secure-random-numbers-crypto-rand/
func init() {
assertAvailablePRNG()
}
func assertAvailablePRNG() {
// Assert that a cryptographically secure PRNG is available.
// Panic otherwise.
buf := make([]byte, 1)
_, err := io.ReadFull(rand.Reader, buf)
if err != nil {
panic(fmt.Sprintf("crypto/rand is unavailable: Read() failed with %#v", err))
}
}
// GenerateRandomBytes returns securely generated random bytes.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomBytes(n int) ([]byte, error) {
b := make([]byte, n)
_, err := rand.Read(b)
// Note that err == nil only if we read len(b) bytes.
if err != nil {
return nil, err
}
return b, nil
}
// GenerateRandomString returns a securely generated random string.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomString(n int) (string, error) {
const letters = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
ret := make([]byte, n)
for i := 0; i < n; i++ {
num, err := rand.Int(rand.Reader, big.NewInt(int64(len(letters))))
if err != nil {
return "", err
}
ret[i] = letters[num.Int64()]
}
return string(ret), nil
}
// GenerateRandomStringURLSafe returns a URL-safe, base64 encoded
// securely generated random string.
// It will return an error if the system's secure random
// number generator fails to function correctly, in which
// case the caller should not continue.
func GenerateRandomStringURLSafe(n int) (string, error) {
b, err := GenerateRandomBytes(n)
return base64.URLEncoding.EncodeToString(b), err
}
func main() {
// Example: this will give us a 44 byte, base64 encoded output
token, err := GenerateRandomStringURLSafe(32)
if err != nil {
// Serve an appropriately vague error to the
// user, but log the details internally.
panic(err)
}
fmt.Println(token)
// Example: this will give us a 32 byte output
token, err = GenerateRandomString(32)
if err != nil {
// Serve an appropriately vague error to the
// user, but log the details internally.
panic(err)
}
fmt.Println(token)
}
@denisbrodbeck
Copy link

@dopey, @aaomidi, @shadez95

License is MIT.
My gist was just a note to myself, never thought of adding a license to it. Feel free to use it, just use the revised version which does not suffer from modulo bias 😄

@dopey
Copy link
Author

dopey commented Jan 23, 2021

Thanks @denisbrodbeck. Cheers!

@stonymahony
Copy link

Thanks for the useful snippets!

For me, the supposedly URLsafe string generated with GenerateRandomStringURLSafe() unfortunately contained "=".
I now use base64.RawURLEncoding instead of base64.URLEncoding , so the strings are really URLsafe ;)

Cheers

@dopey
Copy link
Author

dopey commented Sep 2, 2021

@stonymahony is that something I should update the gist with?

@kyle-aoki
Copy link

Had to add "math/big" to imports to get this code to work FYI

@dopey
Copy link
Author

dopey commented Sep 24, 2021

@kyle-aoki added. thanks!

@imusmanmalik
Copy link

Maybe give this a shot and improve it for others to use as well ? I had similar usecase sometime ago https://pkg.go.dev/github.com/imusmanmalik/randomizer

@unlessbamboo
Copy link

thanks

@hexaza
Copy link

hexaza commented May 18, 2024

Can't you just do rand.read and get random bytes, then encode it to base64?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment