rigid

package module
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Aug 22, 2025 License: MIT Imports: 10 Imported by: 0

README

rigid-go

Go Reference Go Report Card CI

Cryptographically secured ULIDs with built-in integrity verification - Go port of the Python rigid library.

Table of Contents

Overview

Rigid is a Go library that generates cryptographically secure, unique identifiers based on ULIDs (Universally Unique Lexicographically Sortable Identifiers) with HMAC-based integrity verification. It provides tamper detection, metadata binding, and ensures that IDs cannot be forged without the secret key.

Features

  • Cryptographically Secure: Uses HMAC-SHA256 for integrity verification
  • Time-Ordered: Based on ULIDs, naturally sorted by creation time
  • Tamper-Proof: Any modification to an ID will be detected during verification
  • Metadata Support: Optional metadata can be cryptographically bound to IDs
  • Configurable Signatures: Adjustable signature length (4-32 bytes)
  • Thread-Safe: Safe for concurrent use across multiple goroutines
  • Compatible: Multi-instance compatible when using the same secret key

Installation

go get github.com/bahadrix/rigid-go

Quick Start

package main

import (
    "fmt"
    "log"
    "github.com/bahadrix/rigid-go"
)

func main() {
    // Your secret key - keep this secure!
    secretKey := []byte("your-secret-key-here")
    
    // Create a new Rigid instance
    r, err := rigid.NewRigid(secretKey)
    if err != nil {
        log.Fatal(err)
    }
    
    // Generate a new Rigid ULID
    rigidID, err := r.Generate()
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Generated ID: %s\n", rigidID)
    
    // Generate with metadata
    rigidWithMetadata, err := r.Generate("user:alice:role:admin")
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("With metadata: %s\n", rigidWithMetadata)
    
    // Verify the ID
    result, err := r.Verify(rigidWithMetadata)
    if err != nil {
        log.Fatal(err)
    }
    
    fmt.Printf("Valid: %t, ULID: %s, Metadata: %s\n", 
        result.Valid, result.ULID, result.Metadata)
}

API Reference

Creating a Rigid Instance
// Create with default signature length (8 bytes)
r, err := rigid.NewRigid(secretKey)

// Create with custom signature length (4-32 bytes)
r, err := rigid.NewRigid(secretKey, 16)
Generating IDs
// Generate without metadata
rigidID, err := r.Generate()

// Generate with metadata
rigidID, err := r.Generate("metadata-string")
Verification
// Verify returns a VerifyResult struct
result, err := r.Verify(rigidID)

// VerifyResult contains:
// - Valid (bool): whether the ID is valid
// - ULID (string): the extracted ULID
// - Metadata (string): the extracted metadata (if any)
Utility Methods
// Extract the ULID object
ulidObj, err := r.ExtractULID(rigidID)

// Extract the timestamp
timestamp, err := r.ExtractTimestamp(rigidID)
Error Types
  • ErrInvalidFormat: Invalid Rigid ID format
  • ErrInvalidULID: Invalid ULID component
  • ErrIntegrityFailure: ID failed integrity verification
  • ErrEmptySecretKey: Empty or nil secret key
  • ErrInvalidSigLength: Invalid signature length

ID Format

A Rigid ID has the format: ULID-SIGNATURE or ULID-SIGNATURE-METADATA

  • ULID: 26-character standard ULID (timestamp + randomness)
  • SIGNATURE: Base32-encoded HMAC signature (configurable length)
  • METADATA: Optional metadata string (can contain hyphens)

Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV-MFRGG2BA-user:alice:role:admin

Security Considerations

  1. Key Management: Keep your secret key secure and rotate it periodically
  2. Key Sharing: Use the same key across all systems that need to verify IDs
  3. Signature Length: Longer signatures provide more security but increase ID length
  4. Constant-Time Verification: Uses crypto/subtle for timing-attack resistance

Examples

Basic Usage
secretKey := []byte("your-secret-key")
r, _ := rigid.NewRigid(secretKey)

// Simple generation
id, _ := r.Generate()
fmt.Println(id) // 01ARZ3NDEKTSV4RRFFQ69G5FAV-MFRGG2BA

// With metadata
id, _ = r.Generate("session:12345")
fmt.Println(id) // 01ARZ3NDEKTSV4RRFFQ69G5FAV-MFRGG2BA-session:12345
Advanced Usage

See examples/advanced/main.go for comprehensive examples including:

  • User management systems
  • Session management
  • Multi-instance compatibility
  • Different signature lengths
  • Tamper detection

Benchmarks

Run benchmarks with:

go test -bench=. -benchmem

Performance on Apple M1 Pro (darwin/arm64):

  • Generation: 1,885,310 ops/sec (631.3 ns/op, 624 B/op, 10 allocs/op)
  • Verification: 2,172,638 ops/sec (555.4 ns/op, 592 B/op, 9 allocs/op)
  • Generation with metadata: 1,750,885 ops/sec (689.7 ns/op, 712 B/op, 12 allocs/op)

Compatibility

This Go implementation is compatible with the Python rigid library when using the same:

  • Secret key
  • Signature length
  • HMAC algorithm (SHA-256)

Testing

Run the full test suite:

go test -v                # Run all tests
go test -race -v          # Test for race conditions
go test -cover            # Generate coverage report

Migration from v0.x

The new API is completely different from v0.x versions. Key changes:

  • Use NewRigid() instead of New()
  • Use Generate() method instead of direct function
  • Use Verify() method that returns a VerifyResult struct
  • IDs are now string-based with ULID format instead of binary
  • Metadata support is now built-in
  • Configurable signature lengths

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

  1. Fork the repository
  2. Create a feature branch (git checkout -b feature/amazing-feature)
  3. Add tests for new functionality
  4. Ensure all tests pass (go test -v)
  5. Commit your changes (git commit -m 'Add some amazing feature')
  6. Push to the branch (git push origin feature/amazing-feature)
  7. Open a Pull Request
Development Setup
git clone https://github.com/bahadrix/rigid-go.git
cd rigid-go
go mod download
go test -v
Issues and Support

License

MIT License - see LICENSE file for details.

Changelog

v1.0.1
  • Documentation: Update README with actual benchmark results from Apple M1 Pro
  • Documentation: Add comprehensive Table of Contents with navigation links
  • Code Quality: Fix concurrent generation race conditions and improve thread safety
  • Code Quality: Fix various linting errors (gofmt, errcheck, fmt redundancy)
  • CI/CD: Improve CI workflow and update golangci-lint configuration
  • Code Quality: Code formatting improvements across examples and tests
  • Bug Fix: Fix Go version compatibility issues
v1.0.0
  • Complete rewrite to match Python rigid library API
  • ULID-based implementation using github.com/oklog/ulid/v2
  • Configurable signature lengths (4-32 bytes)
  • Metadata support with cryptographic binding
  • Constant-time signature verification
  • Thread-safe concurrent generation
  • Comprehensive test suite and examples

Documentation

Overview

Package rigid provides cryptographically secured ULIDs (Universally Unique Lexicographically Sortable Identifiers) with built-in HMAC-based integrity verification.

Rigid generates tamper-proof, time-ordered unique identifiers that include cryptographic signatures to ensure integrity and prevent forgery. It supports optional metadata binding and configurable signature lengths for different security requirements.

Basic Usage

secretKey := []byte("your-secret-key")
r, err := rigid.NewRigid(secretKey)
if err != nil {
	log.Fatal(err)
}

// Generate a secure ULID
rigidID, err := r.Generate()
if err != nil {
	log.Fatal(err)
}

// Generate with metadata
rigidWithMetadata, err := r.Generate("user:alice:role:admin")
if err != nil {
	log.Fatal(err)
}

// Verify integrity
result, err := r.Verify(rigidWithMetadata)
if err != nil {
	log.Fatal(err)
}
fmt.Printf("Valid: %t, Metadata: %s\n", result.Valid, result.Metadata)

Security Features

- HMAC-SHA256 cryptographic signatures prevent tampering and forgery - Constant-time verification resists timing attacks - Configurable signature lengths (4-32 bytes) for security/size trade-offs - Thread-safe concurrent generation with monotonic entropy

ID Format

Rigid IDs follow the format: ULID-SIGNATURE or ULID-SIGNATURE-METADATA

Example: 01ARZ3NDEKTSV4RRFFQ69G5FAV-MFRGG2BA-user:session:12345

Compatibility

This implementation is compatible with the Python rigid library when using the same secret key, signature length, and HMAC algorithm.

Index

Constants

View Source
const (
	// DefaultSignatureLength is the default HMAC signature length in bytes.
	DefaultSignatureLength = 8
	// MinSignatureLength is the minimum allowed signature length in bytes.
	MinSignatureLength = 4
	// MaxSignatureLength is the maximum allowed signature length in bytes.
	MaxSignatureLength = 32
)

Constants defining signature length constraints.

Variables

View Source
var (
	// ErrInvalidFormat indicates the rigid ID format is invalid.
	ErrInvalidFormat = errors.New("invalid rigid format")
	// ErrInvalidULID indicates the ULID component is malformed.
	ErrInvalidULID = errors.New("invalid ULID")
	// ErrIntegrityFailure indicates the signature verification failed.
	ErrIntegrityFailure = errors.New("integrity verification failed")
	// ErrEmptySecretKey indicates the provided secret key is empty or nil.
	ErrEmptySecretKey = errors.New("secret key cannot be empty")
	// ErrInvalidSigLength indicates the signature length is outside valid range.
	ErrInvalidSigLength = errors.New("signature length must be positive")
)

Error variables returned by rigid operations.

Functions

This section is empty.

Types

type Rigid

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

Rigid is the main structure for generating and verifying cryptographically secured ULIDs. It maintains the secret key, signature configuration, and entropy source for ULID generation. All methods are thread-safe for concurrent use.

func NewRigid

func NewRigid(secretKey []byte, signatureLength ...int) (*Rigid, error)

NewRigid creates a new Rigid instance with the provided secret key. The optional signatureLength parameter sets the HMAC signature length in bytes (4-32). If not provided, DefaultSignatureLength (8 bytes) is used. Returns an error if the secret key is empty or signature length is invalid.

func (*Rigid) ExtractTimestamp

func (r *Rigid) ExtractTimestamp(secureULID string) (time.Time, error)

ExtractTimestamp extracts the timestamp from the ULID component of a rigid ID. Returns the embedded timestamp or an error if extraction fails.

func (*Rigid) ExtractULID

func (r *Rigid) ExtractULID(secureULID string) (ulid.ULID, error)

ExtractULID extracts and parses the ULID component from a rigid ID. Returns the parsed ULID object or an error if extraction fails.

func (*Rigid) Generate

func (r *Rigid) Generate(metadata ...string) (string, error)

Generate creates a new cryptographically secured ULID with optional metadata. The optional metadata parameter will be cryptographically bound to the ID. Only the first metadata parameter is used if multiple are provided. Returns the generated rigid ID string or an error if generation fails.

func (*Rigid) Verify

func (r *Rigid) Verify(secureULID string) (VerifyResult, error)

Verify checks the integrity and authenticity of a rigid ID. Returns a VerifyResult containing validation status, extracted ULID, and metadata. Returns an error if the ID format is invalid or verification fails.

type VerifyResult

type VerifyResult struct {
	// Valid indicates whether the rigid ID passed integrity verification.
	Valid bool
	// ULID contains the extracted ULID string.
	ULID string
	// Metadata contains the extracted metadata string, if any.
	Metadata string
}

VerifyResult contains the results of a rigid ID verification operation.

Directories

Path Synopsis
examples
advanced command
basic command

Jump to

Keyboard shortcuts

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