Understanding `math/rand` and `crypto/rand` in Go

Posted on Dec 12, 2024

In Go, random number generation can be achieved using two packages: math/rand and crypto/rand. Each serves different purposes and has distinct characteristics, making them suitable for various use cases.

math/rand

The math/rand package provides pseudorandom number generation. It is fast and suitable for non-cryptographic use cases, such as simulations, games, and other applications where the predictability of the random sequence is not a critical issue.

Key points about math/rand:

  • Deterministic: Given the same seed, it will produce the same sequence of numbers.
  • Speed: It is optimized for performance.
  • Use Cases: Ideal for scenarios where performance is critical, and security is not a concern.

Example usage:

package main

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

func main() {
    rand.Seed(time.Now().UnixNano())
    fmt.Println(rand.Intn(100)) // Random number between 0 and 99
}

crypto/rand

The crypto/rand package, on the other hand, provides cryptographically secure random number generation. It is suitable for use cases where security is paramount, such as generating encryption keys, tokens, or any scenario requiring high entropy.

Key points about crypto/rand:

  • Non-deterministic: It provides cryptographically secure random numbers.
  • Security: Ensures high entropy and is suitable for cryptographic applications.
  • Use Cases: Ideal for security-sensitive scenarios, such as generating keys or tokens.

Example usage:

package main

import (
    "crypto/rand"
    "fmt"
    "math/big"
)

func main() {
    n, err := rand.Int(rand.Reader, big.NewInt(100))
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(n) // Secure random number between 0 and 99
}

Recent Changes and PR Overview

Recently, I submitted a Pull Request (PR) to the Prysm repository to address the deprecation of the Seed function in the math/rand package. The changes involved replacing instances of math/rand with crypto/rand in certain test files to enhance security and adhere to best practices.

PR Details

PR Link: Prysm PR #14747

Commit Messages:

  1. math/rand to crypto/rand
  2. Update CHANGELOG.md
  3. Update some test files to use crypto/rand instead of math/rand

Changes Summary:

  • Replaced math/rand with crypto/rand in test files to ensure cryptographically secure random number generation.
  • Updated the CHANGELOG.md to reflect these changes.

Example Changes:

- import (
-     "math/rand"
- )
+ import (
+     "crypto/rand"
+     mathRand "math/rand"
+ )

- rand.Seed(time.Now().UnixNano())
- rand.Shuffle(len(allValidators), func(i, j int) { allValidators[i], allValidators[j] = allValidators[j], allValidators[i] })
+ mathRand.New(mathRand.NewSource(time.Now().UnixNano()))
+ mathRand.Shuffle(len(allValidators), func(i, j int) { allValidators[i], allValidators[j] = allValidators[j], allValidators[i] })

Conclusion

Switching from math/rand to crypto/rand in the Prysm repository enhances the security of the random number generation process, particularly in test files. This change ensures that the generated values are cryptographically secure and align with best practices for security.

By understanding the differences between math/rand and crypto/rand, developers can make informed decisions about which package to use based on their specific requirements, balancing performance and security as needed.

For more details, you can view the full PR here.