securerandom

package module
v1.0.1 Latest Latest
Warning

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

Go to latest
Published: Jun 30, 2026 License: MIT Imports: 10 Imported by: 0

README

securerandom

Pure-Go implementation of Java's SecureRandom SHA1PRNG, for reproducing Java-compatible deterministic sequences. It is not cryptographically secure — use crypto/rand when you need real randomness.

Install

go get github.com/0verkilll/securerandom

Sponsor

If this project is useful to you, please consider supporting its development:

Sponsor @0verkilll on GitHub

License

MIT

Documentation

Index

Constants

View Source
const (
	// MaxAllocationSize is the maximum number of bytes that can be generated
	// in a single call to NextBytesSafe. This prevents memory exhaustion attacks
	// and denial-of-service through excessive allocation requests.
	//
	// Default: 20MB (20,971,520 bytes)
	//
	// Applications can check this limit before requesting bytes:
	//
	//	if requestedSize > securerandom.MaxAllocationSize {
	//	    // Split into multiple requests or return error
	//	}
	//
	// For larger data generation, make multiple calls:
	//
	//	for remaining > 0 {
	//	    chunk := min(remaining, MaxAllocationSize)
	//	    bytes, err := sr.NextBytesSafe(chunk)
	//	    // ... process chunk
	//	    remaining -= chunk
	//	}
	MaxAllocationSize = 20 * 1024 * 1024 // 20 MB

	// MinimumSeedEntropyBytes is the minimum recommended seed length in bytes
	// for security-conscious applications (not enforced by default).
	//
	// Recommendation: 32 bytes (256 bits) minimum
	//
	// While this library is NOT cryptographically secure, using high-entropy
	// seeds of sufficient length makes output harder to predict. For security-
	// critical applications, use crypto/rand to generate seeds:
	//
	//	seed := make([]byte, securerandom.MinimumSeedEntropyBytes)
	//	if _, err := rand.Read(seed); err != nil {
	//	    panic(err)
	//	}
	//	sr.Seed(seed)
	MinimumSeedEntropyBytes = 32
)

Variables

View Source
var ErrAllocationTooLarge = &AllocationError{}

ErrAllocationTooLarge is returned when NextBytesSafe is called with a size exceeding MaxAllocationSize. This prevents memory exhaustion attacks.

To handle large data generation, split requests into smaller chunks:

totalSize := 50 * 1024 * 1024 // 50MB
for offset := 0; offset < totalSize; offset += MaxAllocationSize {
    size := min(MaxAllocationSize, totalSize-offset)
    chunk, err := sr.NextBytesSafe(size)
    if err != nil {
        return err
    }
    // ... process chunk
}

Functions

func DetectLocale

func DetectLocale() string

DetectLocale automatically detects the system locale from environment variables.

It checks the following environment variables in order:

  • LC_ALL
  • LC_MESSAGES
  • LANG

The detected locale is normalized to the format used by this package (e.g., "en-US"). Common system formats like "en_US.UTF-8" are converted to "en-US".

If the detected locale is not in the list of supported locales returned by GetSupportedLocales(), or if no locale can be detected, it falls back to "en-US".

Example:

// Auto-detect and create translator
locale := securerandom.DetectLocale()
translator, err := securerandom.NewTranslator(locale)
if err != nil {
    log.Fatal(err)
}
securerandom.SetTranslator(translator)

Example - Check what was detected:

locale := securerandom.DetectLocale()
fmt.Printf("Detected locale: %s\n", locale)

Returns:

  • string: The detected locale code (e.g., "en-US", "es-ES") or "en-US" as fallback

func GetLogger

func GetLogger() logger.Logger

GetLogger returns the package logger, or NopLogger if not set.

func GetSupportedLocales

func GetSupportedLocales() []string

GetSupportedLocales returns the list of locales supported by this package.

It reads the list of embedded locale files and returns their locale codes. The returned array is sorted alphabetically for consistency.

If the embedded filesystem cannot be read (which should never happen in normal operation), this function returns a fallback array containing only "en-US" to ensure graceful degradation.

Example:

locales := securerandom.GetSupportedLocales()
for _, locale := range locales {
    fmt.Printf("- %s\n", locale)
}

Returns:

  • []string: Sorted array of supported locale codes

func NewTranslator

func NewTranslator(locale ...string) (*i18n.Translator, error)

NewTranslator creates an i18n translator with embedded locale translations.

It loads translation files that are embedded in the package binary, providing a batteries-included translation experience.

The created translator:

  • Loads from embedded locale files (no external files needed)
  • Uses "en-US" as the default/fallback locale
  • Sets the requested locale as the current locale
  • Supports all locales returned by GetSupportedLocales()

After creating the translator, pass it to SetTranslator() to enable automatic translation of all error messages in this package.

If the requested locale is not supported, an error is returned. Use GetSupportedLocales() to see the list of available locales.

Auto-Detection:

Call without arguments or with an empty string to auto-detect the locale from system environment variables (LC_ALL, LC_MESSAGES, LANG). If detection fails or the detected locale is not supported, it falls back to "en-US".

Example - Auto-Detect Locale (Recommended):

// Auto-detect locale from system environment
translator, err := securerandom.NewTranslator()
if err != nil {
    log.Fatal(err)
}
securerandom.SetTranslator(translator)

Example - Specific Language:

// Enable Spanish translations
translator, err := securerandom.NewTranslator("es-ES")
if err != nil {
    log.Fatal(err)
}
securerandom.SetTranslator(translator)

// Now all error messages will be in Spanish
sr := NewSecureRandom(hasher)
_, err = sr.NextBytesSafe(100 * 1024 * 1024) // Exceeds limit
fmt.Println(err) // Error in Spanish!

Example - Runtime Language Switching:

translator, _ := securerandom.NewTranslator("en-US")
securerandom.SetTranslator(translator)

// Later, switch to French
translator.SetLocale("fr-FR")
// Errors now appear in French

Example - Checking Available Locales:

locales := securerandom.GetSupportedLocales()
fmt.Println("Supported languages:", locales)

// Create translator for first available locale
translator, err := securerandom.NewTranslator(locales[0])

Binary Size Impact:

The embedded locale files add approximately 190KB to the binary size.

Parameters:

  • locale: Optional locale code (e.g., "en-US", "es-ES", "fr-FR"). Omit or pass empty string for auto-detect

Returns:

  • *i18n.Translator: Configured translator instance
  • error: Error if locale is not supported or initialization fails

Errors:

  • Returns error if the requested locale file doesn't exist
  • Returns error if i18n.New() fails to create the translator
  • Returns error if the embedded filesystem cannot be accessed

func SetLogger

func SetLogger(l logger.Logger)

SetLogger sets the logger for the securerandom package. Pass nil to disable logging and reset to the default NopLogger. The logger is shared across all goroutines and is thread-safe.

Example:

// Using a custom logger implementation
securerandom.SetLogger(myLogger)

Example - Disable logging:

securerandom.SetLogger(nil)

func SetTranslator

func SetTranslator(translator TranslatorProvider)

SetTranslator sets the global translator for this package. This allows the application to provide translations for error messages and other user-facing strings.

Pass nil to disable translations and use English defaults.

This function is thread-safe and can be called from multiple goroutines.

Example:

translator, _ := i18n.New(
    i18n.WithFileSystemLoader("locales"),
    i18n.WithDefaultLocale("en-US"),
)
securerandom.SetTranslator(translator)

Types

type AllocationError

type AllocationError struct{}

AllocationError is returned when the requested allocation size exceeds the maximum allowed.

func (*AllocationError) Error

func (e *AllocationError) Error() string

Error implements the error interface with translation support.

type Hasher

type Hasher interface {
	// Sum computes and returns the hash of the provided data.
	// The returned slice is the final hash value (e.g., 20 bytes for SHA-1).
	// Multiple calls to Sum with the same data must return identical results.
	//
	// Parameters:
	//   data - The input bytes to hash
	//
	// Returns:
	//   hash - The computed hash as a byte slice
	//   error - Error if hash computation fails, nil otherwise
	Sum(data []byte) ([]byte, error)

	// Reset clears the internal state of the hasher, allowing it to be reused
	// for a new hash computation. This is more efficient than creating a new
	// Hasher instance for each operation.
	Reset()

	// BlockSize returns the hash's underlying block size in bytes.
	// For SHA-1, this is 64 bytes. For SHA-256, this is also 64 bytes.
	//
	// This is useful for certain cryptographic operations that need to know
	// the block size, such as HMAC implementations.
	//
	// Returns:
	//   The block size in bytes
	BlockSize() int

	// Size returns the hash's output size in bytes.
	// For SHA-1, this is 20 bytes. For SHA-256, this is 32 bytes.
	//
	// This allows generic code to work with different hash algorithms
	// without hardcoding the output size.
	//
	// Returns:
	//   The hash output size in bytes
	Size() int
}

Hasher defines the interface for cryptographic hash functions. This abstraction follows the Single Responsibility Principle by focusing solely on hashing operations, and the Dependency Inversion Principle by allowing different hash implementations (SHA-1, SHA-256, etc.) to be used interchangeably.

Implementations must be stateful and maintain internal hash state between calls. The Reset method allows reuse of the same instance for multiple hash operations.

type HasherWithSumInto

type HasherWithSumInto interface {
	// SumInto computes the hash of data and writes it into dst.
	// dst must be at least Size() bytes (20 for SHA-1).
	SumInto(dst, data []byte) error
}

HasherWithSumInto is an optional extension of Hasher that supports zero-allocation hashing by writing the output into a caller-provided buffer.

When a Hasher also implements HasherWithSumInto, SecureRandom's state-update hot path uses SumInto instead of Sum, eliminating a 20-byte allocation per hash call. In brute-force scenarios (100K+ PRNG calls per candidate), this removes ~2MB of allocation per candidate.

The sha1 package's *sha1.SHA1 type satisfies this interface out of the box.

type MisuseWarning

type MisuseWarning struct {
	// Message is the human-readable warning text explaining the concern
	Message string

	// Severity indicates the warning level: "warning" or "error"
	Severity string
}

MisuseWarning represents a warning about potential cryptographic misuse. This struct is returned when users request byte sizes that match common cryptographic key or token sizes, indicating they may be attempting to use this non-cryptographic PRNG for security purposes.

Fields:

  • Message: Human-readable warning message explaining the concern
  • Severity: Severity level ("warning" or "error")

Example usage:

bytes, warnings := sr.NextBytesWithWarnings(32)
for _, w := range warnings {
    log.Printf("[%s] %s", w.Severity, w.Message)
}

func DetectPotentialMisuse

func DetectPotentialMisuse(n int) []MisuseWarning

DetectPotentialMisuse analyzes a byte size request and returns warnings if the size matches common cryptographic key or token sizes. This helps prevent users from accidentally using this non-cryptographic PRNG for security purposes.

Detected sizes:

  • 16 bytes (128 bits) - AES-128 keys, MD5 hashes
  • 24 bytes (192 bits) - AES-192 keys
  • 32 bytes (256 bits) - AES-256 keys, SHA-256 hashes
  • 64 bytes (512 bits) - SHA-512 hashes

Parameters:

n - The number of bytes being requested

Returns:

Array of MisuseWarning structs (empty if no concerns detected)

Example:

warnings := DetectPotentialMisuse(32)
if len(warnings) > 0 {
    for _, w := range warnings {
        log.Printf("WARNING: %s", w.Message)
    }
    return errors.New("cannot generate crypto-sized data with non-secure PRNG")
}

NOTE: This is a best-effort detection mechanism. It cannot prevent all misuse, only warn about the most common patterns.

type RandomSource

type RandomSource interface {
	// Seed initializes or re-initializes the random source with the given seed.
	// For deterministic PRNGs, the same seed must produce the same sequence
	// of random values across all calls.
	//
	// Parameters:
	//   seed - The seed bytes for initializing the random state
	//
	// Returns:
	//   error - Error if seeding fails (e.g. hash computation error), nil otherwise.
	//   Empty seeds are accepted and produce a zero initial state.
	Seed(seed []byte) error

	// SeedWithValidation initializes the random source and returns the
	// estimated strength of the provided seed. This helps users understand
	// seed quality before relying on generated output.
	//
	// The method analyzes the seed for patterns and entropy, then seeds
	// the generator. The strength rating does not affect cryptographic
	// security - even "Strong" seeds do not make this a secure PRNG.
	//
	// Parameters:
	//   seed - The seed bytes to analyze and use for initialization
	//
	// Returns:
	//   SeedStrength rating (Weak, Fair, Good, or Strong)
	SeedWithValidation(seed []byte) SeedStrength

	// NextBytes generates and returns n random bytes.
	// The bytes are generated from the internal PRNG state and advance
	// the state for subsequent calls.
	//
	// Parameters:
	//   n - The number of random bytes to generate
	//
	// Returns:
	//   A slice containing n pseudo-random bytes
	NextBytes(n int) []byte

	// NextInt generates and returns the next random 32-bit integer.
	// For Java compatibility, this should return a signed int32 value
	// in the range [-2147483648, 2147483647].
	//
	// Returns:
	//   A pseudo-random int32 value
	NextInt() int32

	// NextBytesSafe generates n pseudorandom bytes with resource exhaustion protection.
	// This is the recommended method for generating random bytes as it enforces
	// allocation limits to prevent denial-of-service attacks.
	//
	// Parameters:
	//   n - The number of random bytes to generate (maximum: MaxAllocationSize)
	//
	// Returns:
	//   Generated bytes and nil error on success, or nil and error if limits exceeded
	//
	// Security: Returns ErrAllocationTooLarge if n > MaxAllocationSize
	NextBytesSafe(n int) ([]byte, error)

	// NextBytesWithWarnings generates n pseudorandom bytes and returns warnings
	// if the requested size matches common cryptographic key or token sizes.
	// This helps detect potential misuse of the non-cryptographic PRNG.
	//
	// The method always generates the requested bytes but also returns warnings
	// if the size matches common crypto patterns (16, 24, 32, 64 bytes).
	//
	// Parameters:
	//   n - The number of random bytes to generate
	//
	// Returns:
	//   - Generated bytes (always returned, even if warnings present)
	//   - Array of MisuseWarning structs (empty if no concerns detected)
	//
	// Note: Callers should check warnings and decide whether to use the bytes
	NextBytesWithWarnings(n int) ([]byte, []MisuseWarning)

	// Clear securely zeros all internal state to prevent memory disclosure.
	// This method MUST be called when done using the RandomSource, especially
	// if seeded with sensitive data (passwords, keys, etc.).
	//
	// After calling Clear(), the instance is no longer usable and must be
	// reseeded before generating new random data.
	//
	// Best practice: Use defer to ensure Clear() is called:
	//   rs := NewRandomSource(...)
	//   defer rs.Clear()
	//   rs.Seed(sensitiveData)
	//   // ... use rs
	Clear()
}

RandomSource defines the interface for pseudo-random number generation. This abstraction enables different PRNG implementations while maintaining the Interface Segregation Principle by providing only essential methods.

The interface is designed to match Java's SecureRandom behavior for compatibility with PixelKnot's F5 steganography algorithm, but any deterministic PRNG can implement this interface.

SECURITY NOTE: Implementations of this interface are NOT cryptographically secure. Do not use for cryptographic key generation, session tokens, or other security-critical purposes. Use crypto/rand for secure randomness.

func NewSecureRandom

func NewSecureRandom(hasher Hasher) RandomSource

NewSecureRandom creates a new SecureRandom instance with the provided hasher. The hasher should implement SHA-1 for Java compatibility.

type RandomSourceWithBytesInto

type RandomSourceWithBytesInto interface {
	// NextBytesInto fills dst with pseudo-random bytes. Returns nil on success,
	// or the underlying error if the hasher fails.
	NextBytesInto(dst []byte) error
}

RandomSourceWithBytesInto is an optional extension of RandomSource that supports zero-allocation byte generation by writing into a caller-provided buffer. Consumers extracting bytes in inner loops should type-assert for this interface and use NextBytesInto when available.

The byte stream produced by NextBytesInto is identical to NextBytes(len(dst)).

type SecureRandom

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

SecureRandom implements Java's SecureRandom with SHA1PRNG algorithm. This is a pure Go implementation that produces identical output to java.security.SecureRandom when initialized with the same seed.

The algorithm uses SHA-1 hashing to generate pseudorandom bytes and maintains an internal state that gets updated with each generation. This implementation is designed for portability to other languages (TypeScript, Rust, WASM) and uses dependency injection for the hasher.

THREAD SAFETY: SecureRandom is NOT thread-safe. Do not share instances between goroutines. Either:

  1. Create separate instances per goroutine, or
  2. Use external synchronization (sync.Mutex)

Example (separate instances):

func worker(id int, hasher Hasher, seed []byte) {
    sr := NewSecureRandom(hasher) // Each goroutine gets its own
    defer sr.Clear()               // Clean up sensitive data
    sr.Seed(seed)
    bytes := sr.NextBytes(100)
    // ... use bytes
}

Example (with mutex):

var (
    sr = NewSecureRandom(hasher)
    mu sync.Mutex
)

func generate() []byte {
    mu.Lock()
    defer mu.Unlock()
    return sr.NextBytes(100)
}

SECURITY WARNING: This implementation is NOT cryptographically secure. SHA-1 is broken and should not be used for security purposes. Use crypto/rand for secure random generation. This implementation is suitable for:

  • F5 steganography decoding (PixelKnot, F5.jar)
  • Legacy Java application compatibility
  • Deterministic PRNG for testing/replay
  • Educational and research purposes

MEMORY PROTECTION: Always call Clear() when done to zero sensitive data:

sr := NewSecureRandom(hasher)
defer sr.Clear() // Prevents memory disclosure
sr.Seed([]byte(password))
// ... use sr

func (*SecureRandom) Clear

func (sr *SecureRandom) Clear()

Clear securely zeros all internal state to prevent memory disclosure attacks. This method MUST be called when done using SecureRandom, especially if seeded with sensitive data (passwords, keys, secrets, etc.).

Clear() performs the following operations:

  • Zeros the 20-byte state array
  • Zeros the 20-byte remainder buffer
  • Zeros the 4-byte intBuf array
  • Resets remCount to 0
  • Hints to the garbage collector for immediate memory reclamation

Note: Clear() does NOT reset the logger field. The logger remains available for any post-Clear() logging if needed.

After calling Clear(), the SecureRandom instance is no longer usable and must be reseeded with Seed() before generating new random data.

Security Best Practice:

sr := NewSecureRandom(hasher)
defer sr.Clear() // Ensure cleanup even if panic occurs
sr.Seed([]byte(sensitivePassword))
// ... use sr
// Clear() called automatically on function exit

Multiple calls to Clear() are safe (idempotent - subsequent calls are no-ops).

Thread Safety: This method is NOT thread-safe. Do not call Clear() concurrently with other SecureRandom methods on the same instance.

func (*SecureRandom) LastError

func (sr *SecureRandom) LastError() error

LastError returns the last error that occurred on NextBytes/NextInt/ NextBytesInto/Seed calls. Callers that need to surface PRNG errors (rare in practice) can check this after a call; the Java-compatible public signatures cannot return errors directly.

func (*SecureRandom) NextBytes

func (sr *SecureRandom) NextBytes(n int) []byte

NextBytes returns n pseudorandom bytes. This implements Java's engineNextBytes method.

func (*SecureRandom) NextBytesInto

func (sr *SecureRandom) NextBytesInto(dst []byte) error

NextBytesInto fills dst with pseudo-random bytes without allocating. The byte stream is identical to NextBytes(len(dst)) for the same seed, so callers can mix the two freely while preserving Java-compatible output. Returns nil on success or the underlying hasher error otherwise.

func (*SecureRandom) NextBytesSafe

func (sr *SecureRandom) NextBytesSafe(n int) ([]byte, error)

NextBytesSafe returns n pseudorandom bytes with resource exhaustion protection. This is the recommended method for generating random bytes as it enforces allocation limits to prevent denial-of-service attacks.

Security Considerations:

  • Maximum allocation: 20MB (MaxAllocationSize constant)
  • Returns empty slice for n <= 0 (no error)
  • Returns ErrAllocationTooLarge if n > MaxAllocationSize
  • Not thread-safe: use separate instances per goroutine
  • Not cryptographically secure: do not use for security-critical operations

For secure random generation, use crypto/rand instead.

Example:

bytes, err := sr.NextBytesSafe(1000)
if err != nil {
    if errors.Is(err, ErrAllocationTooLarge) {
        // Handle size limit error
    }
    return err
}

For large data generation, split into chunks:

totalSize := 50 * 1024 * 1024 // 50MB
result := make([]byte, 0, totalSize)
for len(result) < totalSize {
    chunk, err := sr.NextBytesSafe(MaxAllocationSize)
    if err != nil {
        return nil, err
    }
    result = append(result, chunk...)
}

Returns:

  • Generated bytes and nil error on success
  • nil and ErrAllocationTooLarge if n exceeds limit

func (*SecureRandom) NextBytesWithWarnings

func (sr *SecureRandom) NextBytesWithWarnings(n int) ([]byte, []MisuseWarning)

NextBytesWithWarnings generates n pseudorandom bytes and returns warnings if the requested size matches common cryptographic key or token sizes. This method helps detect potential misuse of this non-cryptographic PRNG.

The method performs two operations:

  1. Generates random bytes using NextBytes()
  2. Checks the size against DetectPotentialMisuse() and returns warnings

Logging: When a logger is configured and warnings are detected, this method logs each warning with its severity and message.

Parameters:

n - The number of random bytes to generate

Returns:

  • Generated bytes (always returned, even if warnings present)
  • Array of MisuseWarning structs (empty if no concerns detected)

Example usage:

sr := NewSecureRandom(hasher)
defer sr.Clear()
sr.Seed([]byte("seed"))

bytes, warnings := sr.NextBytesWithWarnings(32)
if len(warnings) > 0 {
    for _, w := range warnings {
        log.Printf("[%s] %s", w.Severity, w.Message)
    }
    // Decide whether to proceed or abort based on warnings
    return errors.New("refusing to generate crypto-sized data with non-secure PRNG")
}

// Use the bytes (warnings were checked)
processData(bytes)

NOTE: This method does NOT prevent generation of the bytes. It only provides warnings. It's the caller's responsibility to check warnings and decide whether to use the generated bytes.

func (*SecureRandom) NextInt

func (sr *SecureRandom) NextInt() int32

NextInt returns a pseudorandom int32. This implements Java's nextInt() method which returns a 32-bit signed integer.

IMPORTANT: This gets 4 bytes using an internal buffer to avoid allocations, while maintaining the exact byte consumption order and signed integer behavior used by Java's SecureRandom and the F5 algorithm.

The byte order matches GetNextValue() from the working implementation:

byte0 | (byte1 << 8) | (byte2 << 16) | (byte3 << 24)

func (*SecureRandom) Seed

func (sr *SecureRandom) Seed(seed []byte) error

Seed initializes the random number generator with the provided seed. This implements Java's engineSetSeed method.

Logging: When a logger is configured (instance or global), this method logs a debug message with the seed length. The seed content is NOT logged for security reasons.

func (*SecureRandom) SeedWithValidation

func (sr *SecureRandom) SeedWithValidation(seed []byte) SeedStrength

SeedWithValidation initializes the random number generator and returns the estimated strength of the provided seed. This method helps users understand the quality of their seed before relying on the generated output.

This method performs two operations:

  1. Estimates seed strength using pattern detection and entropy analysis
  2. Seeds the generator using the standard Seed() method

Logging: When a logger is configured, this method logs:

  • Debug: seed strength assessment result with strength level and seed length
  • Warning: if seed strength is Weak or Fair (potential security concern)

Parameters:

seed - The seed bytes to analyze and use for initialization

Returns:

SeedStrength rating (Weak, Fair, Good, or Strong)

Example usage:

sr := NewSecureRandom(hasher)
defer sr.Clear()

seed := []byte("user-provided-password")
strength := sr.SeedWithValidation(seed)

switch strength {
case SeedStrengthWeak:
    log.Fatal("Seed is too weak - use crypto/rand to generate a strong seed")
case SeedStrengthFair:
    log.Warn("Seed has low entropy - consider using a stronger seed")
case SeedStrengthGood:
    log.Info("Seed has moderate entropy - acceptable for non-security use")
case SeedStrengthStrong:
    log.Info("Seed has high entropy - best available for this library")
}

// Generator is now seeded and ready to use
bytes := sr.NextBytes(100)

NOTE: Even a "Strong" seed does not make this library cryptographically secure. For security-critical applications, always use crypto/rand.

func (*SecureRandom) SetLogger

func (sr *SecureRandom) SetLogger(l logger.Logger)

SetLogger sets the instance-level logger for this SecureRandom instance. When an instance logger is set, it overrides the package logger for all logging operations on this specific instance.

Pass nil to disable instance-level logging and fall back to the package logger.

This method allows different SecureRandom instances to have different loggers, which is useful for:

  • Isolating log output in tests
  • Routing different instances' logs to different destinations
  • Disabling logging for specific instances while keeping package logging enabled

Example:

sr := NewSecureRandom(hasher).(*SecureRandom)
sr.SetLogger(myLogger) // Use custom logger for this instance

Example - Disable instance logging:

sr.SetLogger(nil) // Fall back to package logger

type SeedStrength

type SeedStrength int

SeedStrength represents the estimated quality of a seed based on entropy analysis. This helps users understand if their seed is predictable or has sufficient randomness.

Strength Levels:

  • Weak: Empty, all-zero, or same-byte patterns (security risk)
  • Fair: Sequential patterns or low entropy (< 25% unique bytes)
  • Good: Moderate entropy (25-50% unique bytes)
  • Strong: High entropy (> 50% unique bytes, well-distributed)

Example usage:

seed := []byte("password123")
strength := EstimateSeedStrength(seed)
if strength < SeedStrengthGood {
    log.Printf("Warning: Weak seed detected (%v). Consider using crypto/rand", strength)
}
const (
	// SeedStrengthWeak indicates a seed with obvious patterns or no entropy.
	// Examples: empty, all-zeros, all same byte, single byte
	// Security Impact: Trivially predictable, DO NOT USE for any security purpose
	SeedStrengthWeak SeedStrength = iota

	// SeedStrengthFair indicates a seed with sequential patterns or low entropy.
	// Examples: 0,1,2,3..., repeating patterns, < 25% unique bytes
	// Security Impact: Easily guessable, acceptable only for testing/development
	SeedStrengthFair

	// SeedStrengthGood indicates a seed with moderate entropy and variety.
	// Examples: text passwords, mixed patterns, 25-50% unique bytes
	// Security Impact: Better than Fair, but still not cryptographically strong
	SeedStrengthGood

	// SeedStrengthStrong indicates a seed with high entropy and good distribution.
	// Examples: output from crypto/rand, > 50% unique bytes, well-distributed
	// Security Impact: Best option for this library (though still not crypto-secure)
	SeedStrengthStrong
)

func EstimateSeedStrength

func EstimateSeedStrength(seed []byte) SeedStrength

EstimateSeedStrength analyzes a seed and returns an estimated strength rating. This function helps users identify weak seeds that may compromise the randomness of generated output.

Analysis includes:

  • Pattern detection (empty, all-zeros, same-byte, sequential)
  • Entropy calculation based on unique byte distribution
  • Byte variety analysis

Parameters:

seed - The seed bytes to analyze

Returns:

SeedStrength rating (Weak, Fair, Good, or Strong)

Example:

seed := []byte("password123")
strength := EstimateSeedStrength(seed)
switch strength {
case SeedStrengthWeak:
    log.Fatal("Seed is too weak - use crypto/rand")
case SeedStrengthFair:
    log.Warn("Seed has low entropy - consider stronger seed")
case SeedStrengthGood:
    log.Info("Seed has moderate entropy")
case SeedStrengthStrong:
    log.Info("Seed has high entropy")
}

NOTE: Even "Strong" seeds do not make this library cryptographically secure. For security-critical applications, always use crypto/rand.

func (SeedStrength) String

func (s SeedStrength) String() string

String returns the string representation of the seed strength level.

type TranslatorProvider

type TranslatorProvider interface {
	// Translate looks up a translation key in the current locale.
	// If the key is not found, it tries the fallback chain.
	// Returns the key itself if not found in any locale.
	Translate(key string) string

	// TranslateWithArgs looks up a translation key and formats it with arguments.
	// Uses fmt.Sprintf formatting. If the key is not found, returns the key itself.
	TranslateWithArgs(key string, args ...interface{}) string

	// HasKey checks if a translation key exists in the current locale or fallback chain.
	HasKey(key string) bool

	// SetLocale changes the current locale for translation lookups.
	SetLocale(locale string)

	// GetLocale returns the current locale being used for translations.
	GetLocale() string
}

TranslatorProvider allows optional translation support. This interface matches github.com/0verkilll/i18n.TranslatorProvider but is defined here to avoid a hard dependency on the i18n package.

Packages using this pattern allow application developers to optionally provide translations without forcing the i18n package on all users.

Example usage:

import "github.com/0verkilll/i18n"

translator, _ := i18n.New(
    i18n.WithFileSystemLoader("locales"),
    i18n.WithDefaultLocale("en-US"),
)
securerandom.SetTranslator(translator)

Now all securerandom error messages will be translated according to the current locale setting in the translator.

func GetTranslator

func GetTranslator() TranslatorProvider

GetTranslator returns the currently configured translator, or nil if none is set.

This function is thread-safe and can be called from multiple goroutines.

Jump to

Keyboard shortcuts

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