securerandom

package module
v0.0.0-...-cb5f782 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 7 Imported by: 0

README

go-ruby-securerandom/securerandom

securerandom — go-ruby-securerandom

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's SecureRandom — the formatting layer MRI 4.0.5 builds over a cryptographically secure entropy source. Hex, Base64, UrlsafeBase64, RandomBytes, Uuid / UuidV7, Alphanumeric, and RandomNumber all produce byte-identical output to MRI, without any Ruby runtime.

It is the SecureRandom backend for go-embedded-ruby, but is a standalone, reusable module with no dependency on the Ruby runtime — a sibling of go-ruby-regexp, go-ruby-erb, and go-ruby-yaml.

Randomness vs. formatting. The randomness comes from an injectable RandSource (the default is crypto/rand); everything else — hex/base64/URL-safe encoding, the UUID bit-layout, the #choose string builder behind Alphanumeric, and the random_number distribution — is deterministic, interpreter-independent formatting and lives here as pure Go. The hex path runs on go-simd/hex and the base64 paths on go-simd/base64, both byte-identical SIMD drop-ins for the standard library.

Features

Faithful port of SecureRandom, validated against the ruby binary on every supported platform:

  • Hex(n) / Base64(n) / UrlsafeBase64(n, padding) / RandomBytes(n) — the encoders, with MRI's defaults (n = 16; URL-safe base64 unpadded unless asked). Hex is SIMD-accelerated via go-simd/hex; the base64 family via go-simd/base64.
  • Uuid() — an RFC 4122 version-4 UUID with the exact xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx layout (version nibble 4, variant 8/9/a/b).
  • UuidV7() — MRI 4.0's version-7 UUID: a 48-bit big-endian Unix-milliseconds prefix (from an injectable clock) plus random bits, version nibble 7.
  • Alphanumeric(n, chars…) — MRI's A-Z a-z 0-9 default (or a supplied character set), built with the same Random::Formatter#choose base-size digit construction so the distribution matches.
  • RandomNumber(n) — no/zero/non-positive argument → a Float in [0, 1); a positive integer → an Integer in [0, n); a positive float → a Float in [0, n). RandomInt / RandomFloat are typed conveniences.

CGO-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x) and three operating systems (Linux, macOS, Windows).

Install

go get github.com/go-ruby-securerandom/securerandom

Usage

package main

import (
	"fmt"

	securerandom "github.com/go-ruby-securerandom/securerandom"
)

func main() {
	fmt.Println(securerandom.Hex())              // 32 hex chars
	fmt.Println(securerandom.Base64())           // 24 chars, padded
	fmt.Println(securerandom.UrlsafeBase64(16, false)) // 22 chars, no padding
	fmt.Println(securerandom.Uuid())             // 4-version, 8/9/a/b-variant
	fmt.Println(securerandom.Alphanumeric(16))   // A-Za-z0-9

	fmt.Println(securerandom.RandomInt(100))     // Integer in [0,100)
	fmt.Println(securerandom.RandomFloat())      // Float   in [0,1)
}

The RandSource seam

Every method ultimately draws from a RandSource; the default is crypto/rand. Injecting a fixed source makes the formatters deterministic — exactly how the test suite asserts exact bytes:

type RandSource interface {
	Read(p []byte) (int, error)
}

// Bind a generator to a specific source (nil -> crypto/rand).
s := securerandom.New(myFixedSource)
s.Hex(4) // deterministic given myFixedSource

// Or swap the package-level default (restore it after):
securerandom.DefaultSource = myFixedSource

The top-level Hex, Base64, … functions read securerandom.DefaultSource; the methods on *SecureRandom are preferred for concurrent or test use. A broken source (a Read error) panics, mirroring MRI raising on a failed CSPRNG — a condition that does not occur on the platforms we target.

API

func New(src RandSource) *SecureRandom // nil src -> crypto/rand

func (s *SecureRandom) RandomBytes(n ...int) []byte                 // default 16
func (s *SecureRandom) Hex(n ...int) string                        // 2*n chars
func (s *SecureRandom) Base64(n ...int) string                     // StdEncoding
func (s *SecureRandom) UrlsafeBase64(n int, padding bool) string   // URL alphabet
func (s *SecureRandom) Uuid() string                               // v4
func (s *SecureRandom) UuidV7() string                             // v7
func (s *SecureRandom) Alphanumeric(n int, chars ...string) string // #choose
func (s *SecureRandom) RandomNumber(n ...float64) (i int64, f float64, isInt bool)
func (s *SecureRandom) RandomInt(n int64) int64                    // [0,n)
func (s *SecureRandom) RandomFloat() float64                       // [0,1)

// Package-level equivalents read DefaultSource:
func Hex(n ...int) string
func Base64(n ...int) string
func UrlsafeBase64(n int, padding bool) string
func RandomBytes(n ...int) []byte
func Uuid() string
func UuidV7() string
func Alphanumeric(n int, chars ...string) string
func RandomNumber(n ...float64) (int64, float64, bool)
func RandomInt(n int64) int64
func RandomFloat() float64

var DefaultSource RandSource // defaults to crypto/rand

Notes on MRI fidelity

  • random_number with a non-positive bound returns a Float in [0, 1), matching MRI (which never raises there).
  • Alphanumeric with a single-element or empty character set: MRI's #choose loops forever (no power of size exceeds the batch threshold). This library returns the only reachable string instead — n copies of the element, or "" for an empty set — rather than hanging.

Tests & coverage

The suite pairs deterministic, ruby-free tests — which alone hold coverage at 100%, so the qemu cross-arch and Windows lanes pass the gate — with a differential MRI oracle: each method is run under the system ruby and the output is checked for the same format, length, charset, and UUID bit-layout (never exact bytes, since both sides draw from independent CSPRNGs). The oracle scripts $stdout.binmode so a text-mode runner never corrupts the bytes, and skip themselves on Windows and where ruby is absent.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-securerandom/securerandom authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package securerandom is a pure-Go (no cgo) reimplementation of Ruby's SecureRandom module — the formatting layer MRI 4.0.5 layers over a cryptographically secure entropy source.

The randomness comes from an injectable RandSource (the default is crypto/rand); everything else — hex, base64, URL-safe base64, UUIDs, the alphanumeric/choose string builder, and the random_number distribution — is deterministic, interpreter-independent formatting, so it lives here as pure Go. The hex path is accelerated with github.com/go-simd/hex and the base64 paths with github.com/go-simd/base64; both are byte-identical drop-ins for the standard library, so the output matches MRI exactly.

Because the default source is cryptographically random, callers (and the tests) assert on the FORMAT, length, charset, and UUID bit-layout, not on exact bytes. Feed a fixed RandSource when a deterministic result is needed.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Alphanumeric

func Alphanumeric(n int, chars ...string) string

Alphanumeric calls SecureRandom.Alphanumeric on the default source.

func Base64

func Base64(n ...int) string

Base64 calls SecureRandom.Base64 on the default source.

func Hex

func Hex(n ...int) string

Hex calls SecureRandom.Hex on the default source.

func RandomBytes

func RandomBytes(n ...int) []byte

RandomBytes calls SecureRandom.RandomBytes on the default source.

func RandomFloat

func RandomFloat() float64

RandomFloat calls SecureRandom.RandomFloat on the default source.

func RandomInt

func RandomInt(n int64) int64

RandomInt calls SecureRandom.RandomInt on the default source.

func RandomNumber

func RandomNumber(n ...float64) (int64, float64, bool)

RandomNumber calls SecureRandom.RandomNumber on the default source.

func UrlsafeBase64

func UrlsafeBase64(n int, padding bool) string

UrlsafeBase64 calls SecureRandom.UrlsafeBase64 on the default source.

func Uuid

func Uuid() string

Uuid calls SecureRandom.Uuid on the default source.

func UuidV7

func UuidV7() string

UuidV7 calls SecureRandom.UuidV7 on the default source.

Types

type RandSource

type RandSource interface {
	Read(p []byte) (int, error)
}

RandSource is the entropy seam SecureRandom draws from. The default is crypto/rand (via DefaultSource); a fixed implementation makes the formatters deterministic for testing. Read fills p with len(p) random bytes and returns an error only on a broken environment.

var DefaultSource RandSource = cryptoSource{}

DefaultSource is the package-level entropy source used by the top-level functions. It defaults to crypto/rand. Replace it (and restore it) to make the formatters deterministic; the per-call API on SecureRandom is preferred for that, as it is concurrency-safe.

type SecureRandom

type SecureRandom struct {
	// contains filtered or unexported fields
}

SecureRandom is a SecureRandom generator bound to a specific entropy source. The zero value is not usable; build one with New. Every method mirrors the matching MRI 4.0.5 SecureRandom method.

func New

func New(src RandSource) *SecureRandom

New returns a SecureRandom drawing from src. A nil src uses crypto/rand.

func (*SecureRandom) Alphanumeric

func (s *SecureRandom) Alphanumeric(n int, chars ...string) string

Alphanumeric returns an n-character (default 16) random string. With no chars it draws from MRI's default A-Z a-z 0-9 alphabet; pass an explicit chars set to draw from it instead. This is MRI's SecureRandom.alphanumeric, and it uses the same #choose construction so the character distribution matches.

func (*SecureRandom) Base64

func (s *SecureRandom) Base64(n ...int) string

Base64 returns the standard padded base64 of n random bytes (default 16). This is MRI's SecureRandom.base64; the encoding is the SIMD drop-in for base64.StdEncoding.

func (*SecureRandom) Hex

func (s *SecureRandom) Hex(n ...int) string

Hex returns 2*n hexadecimal characters built from n random bytes (default 16, so 32 chars). This is MRI's SecureRandom.hex; the encoding is the SIMD drop-in for encoding/hex.

func (*SecureRandom) RandomBytes

func (s *SecureRandom) RandomBytes(n ...int) []byte

RandomBytes returns n random bytes (default 16) as a []byte. This is MRI's SecureRandom.random_bytes; in Ruby the result is an ASCII-8BIT (binary) String of bytesize n, which maps to a Go byte slice.

func (*SecureRandom) RandomFloat

func (s *SecureRandom) RandomFloat() float64

RandomFloat returns a uniform random float64 in [0, 1), built from 53 random bits exactly as MRI's Random#random_number with no bound. This is the zero-argument branch of SecureRandom.random_number.

func (*SecureRandom) RandomInt

func (s *SecureRandom) RandomInt(n int64) int64

RandomInt returns a uniform random integer in [0, n) for n > 0, and 0 for n <= 0. This is the Integer branch of MRI's SecureRandom.random_number(n).

func (*SecureRandom) RandomNumber

func (s *SecureRandom) RandomNumber(n ...float64) (i int64, f float64, isInt bool)

RandomNumber mirrors MRI's SecureRandom.random_number. With no argument, or a non-positive one, it returns a Float in [0, 1) (as the second return). With a positive integer it returns an Integer in [0, n) (first return, isInt true). With a positive float it returns a Float in [0, n). Callers that know the argument type may prefer SecureRandom.RandomFloat / SecureRandom.RandomInt.

func (*SecureRandom) UrlsafeBase64

func (s *SecureRandom) UrlsafeBase64(n int, padding bool) string

UrlsafeBase64 returns the URL- and filename-safe base64 of n random bytes (default 16). MRI omits the padding by default; pass padding=true to keep it. This is MRI's SecureRandom.urlsafe_base64.

func (*SecureRandom) Uuid

func (s *SecureRandom) Uuid() string

Uuid returns a random RFC 4122 version-4 UUID, the xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx form where the version nibble is 4 and the variant nibble y is one of 8, 9, a, b. This is MRI's SecureRandom.uuid.

func (*SecureRandom) UuidV7

func (s *SecureRandom) UuidV7() string

UuidV7 returns a random UUID version 7: a 48-bit big-endian Unix-milliseconds timestamp followed by random bits, with the version nibble 7 and the 10x variant. This is MRI 4.0's SecureRandom.uuid_v7. The timestamp comes from the injected clock (default: time.Now) so it is deterministic under test.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL