iron

package module
v1.1.0 Latest Latest
Warning

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

Go to latest
Published: Jul 22, 2025 License: MIT Imports: 15 Imported by: 1

README

iron

Package iron provides functions for encrypting and signing Iron tokens using PBKDF2 with HMAC-SHA256 for key derivation,AES-256-CBC for data encryption, and HMAC-SHA256 for integrity checks.

Documentation

Overview

Package iron provides functions for encrypting and signing Iron tokens using

  • PBKDF2 with HMAC-SHA256 for key derivation,
  • AES-256-CBC for data encryption,
  • and HMAC-SHA256 for integrity checks.

Token Format

Iron tokens are strings in the following format (all parts are base64url encoded without padding):

Fe26.1*{PASSWORD_ID}*{SALT}*{IV}*{ENCRYPTED_DATA}*{EXPIRATION}*{MAC_SALT}*{MAC}

where:

  • Fe26.1 is a literal prefix indicating the token version (1),
  • {PASSWORD_ID} is the unique ID of the password used to derive the encryption and HMAC keys,
  • {SALT} is the salt used for the encryption key,
  • {IV} is the initialization vector used for AES encryption,
  • {ENCRYPTED_DATA} is the encrypted data itself,
  • {EXPIRATION} is the expiration time of the token in milliseconds since the Unix epoch (January 1, 1970),
  • {MAC_SALT} is the salt used for the HMAC key,
  • {MAC} is the HMAC of the token data (everything before MAC_SALT).

Password Management

iron allows for password rotation. Passwords are identified by a unique ID. Each token is associated with a password that is used to derive the encryption and HMAC keys and has an expiration time that is less than the password's expiration time. New passwords can be added at any time, and the last password (sorted lexicographically by ID) is used for token generation. Old passwords that have expired can be removed because all the tokens they were used to generate must also be expired.

Simple applications that don't require password rotation can use NewIronWithPassword to create an Iron instance with a single password and no expiration.

Example
package main

import (
	"fmt"
	"time"

	"github.com/calico32/iron"
)

type MyData struct {
	UserId      int      `json:"user_id"`
	Permissions []string `json:"permissions"`
}

func main() {
	i := iron.NewIronWithPassword("secretpasswordatleast32byteslong")

	data := MyData{
		UserId:      12345,
		Permissions: []string{"read", "write", "delete"},
	}
	token := i.Seal(data, time.Now().Add(time.Second))

	var decrypted MyData
	_, err := i.Unseal(token, &decrypted)
	if err != nil {
		fmt.Println("Error unsealing token:", err)
		return
	}
	fmt.Printf("User ID: %d\n", decrypted.UserId)
	fmt.Printf("Permissions: %v\n", decrypted.Permissions)
}
Output:

User ID: 12345
Permissions: [read write delete]

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	ErrBadPassword      = fmt.Errorf("password must be at least %d bytes long", minPasswordLength)
	ErrNoValidPasswords = fmt.Errorf("iron: no valid passwords available")
	ErrInvalidToken     = fmt.Errorf("iron: invalid token format or data")
	ErrInvalidSignature = fmt.Errorf("iron: invalid token signature")
	ErrExpiredToken     = fmt.Errorf("iron: token has expired")
	ErrPasswordExpired  = fmt.Errorf("iron: password has expired")
)

Functions

This section is empty.

Types

type Iron

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

An Iron holds the passwords for token generation.

func NewIron

func NewIron() Iron

NewIron returns a new empty Iron instance. Passwords must be added before the instance can be used to generate tokens.

func NewIronWithPassword

func NewIronWithPassword(password string) Iron

NewIronWithPassword returns a new Iron instance with the provided password and no expiration. The password must be at least 32 bytes long.

func (*Iron) AddPassword

func (i *Iron) AddPassword(id string, password string, expiresAt time.Time)

AddPassword adds a password to the Iron instance. The password must be at least 32 bytes long, and the ID must be unique. It panics if these requirements are not met.

If the password's expiration time is in the past, it will not be added.

func (Iron) Seal

func (i Iron) Seal(v any, expiresAt time.Time) string

Seal encrypts the provided data and returns a token that contains the encrypted data and the cryptographic information needed to verify its integrity and decrypt it later.

The provided expiration time will be capped to the expiration time of the currently active password, if it is later than that.

It panics v is not JSON-marshalable, no passwords are available and non-expired, or the expiration time is in the past.

func (Iron) Unseal

func (i Iron) Unseal(data string, v any) (time.Time, error)

Unseal decrypts the provided token and unmarshals the data into v. v must be a pointer to a type that can be unmarshaled from JSON; otherwise, Unseal panics. It returns the expiration time of the token, ErrInvalidToken if the token is invalid, ErrExpiredToken if the token has expired, or another error if the token cannot be decrypted or unmarshaled.

Jump to

Keyboard shortcuts

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