Your Random is Static

Introduction Random numbers are widely used in computing, from cryptography to simulations and gaming. They can be classified into two types: true random numbers and pseudorandom numbers. True Random Numbers True random numbers are generated using physical phenomena, such as coin tossing, dice rolling, spinning wheels, electronic noise, nuclear fission, and more. Random number generators based on these methods are called physical random number generators. Pseudorandom Numbers Pseudorandomness refers to a process that appears random but is not. For example, pseudorandom numbers are calculated using deterministic algorithms to produce sequences that seem random. Functions used to calculate pseudorandom numbers are called random functions, and algorithms using random functions to generate random numbers are referred to as random number generators. Some random functions are periodic. While non-periodic functions are generally better, periodic random functions are often faster. Some periodic functions have adjustable coefficients, allowing them to achieve very large periods, making them nearly as effective as non-periodic functions. Let’s take a look at the pseudorandom implementation in Golang. Random Numbers in Early Go Versions // Go 1.18 import ( "fmt" "math/rand" ) func main() { fmt.Println(rand.Intn(100)) } When this code is executed, the result is the same no matter how many times you run it. Why? Don’t panic—let’s explore the implementation. By inspecting the source code, we can see that the math/rand library uses a default source with a seed value of 1 to generate random numbers. As mentioned earlier, since pseudorandom numbers are generated by deterministic algorithms, they are not truly random. If the seed in NewSource remains unchanged, the sequence of random numbers will always be the same upon restarting the program. Source code: rand.go (Go 1.18) // ... /* * Top-level convenience functions */ var globalRand = New(&lockedSource{src: NewSource(1).(*rngSource)}) // ... To ensure the seed is different on each run, you can use a timestamp as the seed. This ensures the random sequence will change with every program execution. // Go 1.18 package main import ( "fmt" "math/rand" "time" ) func main() { rand.Seed(time.Now().UnixNano()) fmt.Println(rand.Intn(100)) // The result will differ each time you run the program } Fortunately, Google eventually addressed this issue in Go 1.20. Starting from Go 1.20, the random seed for the global random number generator is automatically initialized, rather than being fixed at 1. Unless the environment variable GODEBUG=randautoseed=0 is set, the random sequence will change with each program execution. Cryptographically Secure Random Numbers The crypto/rand library implements a more secure random number generator. According to the code comments, on Linux-based platforms, it prioritizes the getrandom(2) system call. If unavailable, it falls back to /dev/urandom. getrandom is a system call that encapsulates reading from the /dev/urandom character device file to obtain high-quality random numbers. /dev/urandom uses /dev/random as its seed reference, and /dev/random derives its values from hardware-generated noise, which has very high randomness quality. package rand import "io" // Reader is a global, shared instance of a cryptographically // secure random number generator. // // On Linux, FreeBSD, Dragonfly, and Solaris, Reader uses getrandom(2) if // available, /dev/urandom otherwise. // On OpenBSD and macOS, Reader uses getentropy(2). // On other Unix-like systems, Reader reads from /dev/urandom. // On Windows systems, Reader uses the RtlGenRandom API. // On Wasm, Reader uses the Web Crypto API. var Reader io.Reader // Read is a helper function that calls Reader.Read using io.ReadFull. // On return, n == len(b) if and only if err == nil. func Read(b []byte) (n int, err error) { return io.ReadFull(Reader, b) } Summary Random numbers generated by programs are pseudorandom. In Go versions prior to 1.20, the math/rand package used a fixed seed (seed=1) for the default shared source, resulting in identical random number sequences for every program execution. Starting from Go 1.20, the seed is automatically initialized, ensuring varying sequences unless explicitly overridden. The crypto/rand package provides a cryptographically secure random number generator. While crypto/rand is more secure than math/rand, it comes with a performance cost. Choose the appropriate library based on your use case. For cryptographic purposes, always use the crypto/rand package. In JavaScript, crypto.getRandomValues is a cryptographically secure random number generator, making it safer than Math.random. We are Leapcell, your top choice for deploying Go projects to the cloud. Leapcell is the Next-Gen Serverless Platform for

Jan 18, 2025 - 06:10
Your Random is Static

Cover

Introduction

Random numbers are widely used in computing, from cryptography to simulations and gaming. They can be classified into two types: true random numbers and pseudorandom numbers.

True Random Numbers

True random numbers are generated using physical phenomena, such as coin tossing, dice rolling, spinning wheels, electronic noise, nuclear fission, and more. Random number generators based on these methods are called physical random number generators.

Pseudorandom Numbers

Pseudorandomness refers to a process that appears random but is not. For example, pseudorandom numbers are calculated using deterministic algorithms to produce sequences that seem random.

Functions used to calculate pseudorandom numbers are called random functions, and algorithms using random functions to generate random numbers are referred to as random number generators. Some random functions are periodic. While non-periodic functions are generally better, periodic random functions are often faster. Some periodic functions have adjustable coefficients, allowing them to achieve very large periods, making them nearly as effective as non-periodic functions.

Let’s take a look at the pseudorandom implementation in Golang.

Random Numbers in Early Go Versions

// Go 1.18

import (
    "fmt"
    "math/rand"
)

func main() {
    fmt.Println(rand.Intn(100))
}

When this code is executed, the result is the same no matter how many times you run it. Why? Don’t panic—let’s explore the implementation. By inspecting the source code, we can see that the math/rand library uses a default source with a seed value of 1 to generate random numbers. As mentioned earlier, since pseudorandom numbers are generated by deterministic algorithms, they are not truly random. If the seed in NewSource remains unchanged, the sequence of random numbers will always be the same upon restarting the program.

Source code: rand.go (Go 1.18)

// ...

/*
 * Top-level convenience functions
 */

var globalRand = New(&lockedSource{src: NewSource(1).(*rngSource)})

// ...

To ensure the seed is different on each run, you can use a timestamp as the seed. This ensures the random sequence will change with every program execution.

// Go 1.18

package main

import (
    "fmt"
    "math/rand"
    "time"
)

func main() {
    rand.Seed(time.Now().UnixNano())
    fmt.Println(rand.Intn(100)) // The result will differ each time you run the program
}

Fortunately, Google eventually addressed this issue in Go 1.20. Starting from Go 1.20, the random seed for the global random number generator is automatically initialized, rather than being fixed at 1. Unless the environment variable GODEBUG=randautoseed=0 is set, the random sequence will change with each program execution.

Cryptographically Secure Random Numbers

The crypto/rand library implements a more secure random number generator. According to the code comments, on Linux-based platforms, it prioritizes the getrandom(2) system call. If unavailable, it falls back to /dev/urandom.

getrandom is a system call that encapsulates reading from the /dev/urandom character device file to obtain high-quality random numbers. /dev/urandom uses /dev/random as its seed reference, and /dev/random derives its values from hardware-generated noise, which has very high randomness quality.

package rand

import "io"

// Reader is a global, shared instance of a cryptographically
// secure random number generator.
//
// On Linux, FreeBSD, Dragonfly, and Solaris, Reader uses getrandom(2) if
// available, /dev/urandom otherwise.
// On OpenBSD and macOS, Reader uses getentropy(2).
// On other Unix-like systems, Reader reads from /dev/urandom.
// On Windows systems, Reader uses the RtlGenRandom API.
// On Wasm, Reader uses the Web Crypto API.
var Reader io.Reader

// Read is a helper function that calls Reader.Read using io.ReadFull.
// On return, n == len(b) if and only if err == nil.
func Read(b []byte) (n int, err error) {
    return io.ReadFull(Reader, b)
}

Summary

  1. Random numbers generated by programs are pseudorandom.
  2. In Go versions prior to 1.20, the math/rand package used a fixed seed (seed=1) for the default shared source, resulting in identical random number sequences for every program execution. Starting from Go 1.20, the seed is automatically initialized, ensuring varying sequences unless explicitly overridden.
  3. The crypto/rand package provides a cryptographically secure random number generator.
  4. While crypto/rand is more secure than math/rand, it comes with a performance cost. Choose the appropriate library based on your use case. For cryptographic purposes, always use the crypto/rand package.
  5. In JavaScript, crypto.getRandomValues is a cryptographically secure random number generator, making it safer than Math.random.

We are Leapcell, your top choice for deploying Go projects to the cloud.

Leapcell

Leapcell is the Next-Gen Serverless Platform for Web Hosting, Async Tasks, and Redis:

Multi-Language Support

  • Develop with Node.js, Python, Go, or Rust.

Deploy unlimited projects for free

  • pay only for usage — no requests, no charges.

Unbeatable Cost Efficiency

  • Pay-as-you-go with no idle charges.
  • Example: $25 supports 6.94M requests at a 60ms average response time.

Streamlined Developer Experience

  • Intuitive UI for effortless setup.
  • Fully automated CI/CD pipelines and GitOps integration.
  • Real-time metrics and logging for actionable insights.

Effortless Scalability and High Performance

  • Auto-scaling to handle high concurrency with ease.
  • Zero operational overhead — just focus on building.

Explore more in the Documentation!

Try Leapcell

Follow us on X: @LeapcellHQ

Read on our blog