enchantrix

package
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Feb 22, 2026 License: EUPL-1.2 Imports: 20 Imported by: 0

Documentation

Overview

Package enchantrix provides the Sigil transformation framework for composable, reversible data transformations. See RFC-0003 for the formal specification.

Sigils are the core abstraction - each sigil implements a specific transformation (encoding, compression, hashing, encryption) with a uniform interface. Sigils can be chained together to create transformation pipelines.

Example usage:

hexSigil, _ := enchantrix.NewSigil("hex")
base64Sigil, _ := enchantrix.NewSigil("base64")
result, _ := enchantrix.Transmute(data, []enchantrix.Sigil{hexSigil, base64Sigil})

Index

Examples

Constants

This section is empty.

Variables

View Source
var (
	// ErrInvalidKey is returned when the encryption key is invalid.
	ErrInvalidKey = errors.New("enchantrix: invalid key size, must be 32 bytes")
	// ErrCiphertextTooShort is returned when the ciphertext is too short to decrypt.
	ErrCiphertextTooShort = errors.New("enchantrix: ciphertext too short")
	// ErrDecryptionFailed is returned when decryption or authentication fails.
	ErrDecryptionFailed = errors.New("enchantrix: decryption failed")
	// ErrNoKeyConfigured is returned when no encryption key has been set.
	ErrNoKeyConfigured = errors.New("enchantrix: no encryption key configured")
)

Functions

func GetNonceFromCiphertext

func GetNonceFromCiphertext(ciphertext []byte) ([]byte, error)

GetNonceFromCiphertext extracts the nonce from encrypted output. This is provided for debugging/logging purposes only. The nonce should NOT be stored separately in headers.

func Transmute

func Transmute(data []byte, sigils []Sigil) ([]byte, error)

Transmute applies a series of sigils to data in sequence.

Each sigil's In method is called in order, with the output of one sigil becoming the input of the next. If any sigil returns an error, Transmute stops immediately and returns nil with that error.

To reverse a transmutation, call each sigil's Out method in reverse order.

Example
package main

import (
	"fmt"
	"log"

	"forge.lthn.ai/Snider/Enchantrix/pkg/enchantrix"
)

func main() {
	data := []byte("Hello, World!")
	sigils := []enchantrix.Sigil{
		&enchantrix.ReverseSigil{},
		&enchantrix.HexSigil{},
	}
	transformed, err := enchantrix.Transmute(data, sigils)
	if err != nil {
		log.Fatalf("Transmute failed: %v", err)
	}
	fmt.Printf("Transformed data: %s\n", transformed)
}
Output:

Transformed data: 21646c726f57202c6f6c6c6548

Types

type Base64Sigil

type Base64Sigil struct{}

Base64Sigil is a Sigil that encodes/decodes data to/from base64. The In method encodes the data, and the Out method decodes it.

func (*Base64Sigil) In

func (s *Base64Sigil) In(data []byte) ([]byte, error)

In encodes the data to base64.

func (*Base64Sigil) Out

func (s *Base64Sigil) Out(data []byte) ([]byte, error)

Out decodes the data from base64.

type ChaChaPolySigil

type ChaChaPolySigil struct {
	Key        []byte
	Obfuscator PreObfuscator
	// contains filtered or unexported fields
}

ChaChaPolySigil is a Sigil that encrypts/decrypts data using ChaCha20-Poly1305. It applies pre-obfuscation before encryption to ensure raw plaintext never goes directly to CPU encryption routines.

The output format is: [24-byte nonce][encrypted(obfuscated(plaintext))]

Unlike demo implementations, the nonce is ONLY embedded in the ciphertext, not exposed separately in headers.

func NewChaChaPolySigil

func NewChaChaPolySigil(key []byte) (*ChaChaPolySigil, error)

NewChaChaPolySigil creates a new encryption sigil with the given key. The key must be exactly 32 bytes.

func NewChaChaPolySigilWithObfuscator

func NewChaChaPolySigilWithObfuscator(key []byte, obfuscator PreObfuscator) (*ChaChaPolySigil, error)

NewChaChaPolySigilWithObfuscator creates a new encryption sigil with custom obfuscator.

func (*ChaChaPolySigil) In

func (s *ChaChaPolySigil) In(data []byte) ([]byte, error)

In encrypts the data with pre-obfuscation. The flow is: plaintext -> obfuscate -> encrypt

func (*ChaChaPolySigil) Out

func (s *ChaChaPolySigil) Out(data []byte) ([]byte, error)

Out decrypts the data and reverses obfuscation. The flow is: decrypt -> deobfuscate -> plaintext

type Enchantrix

type Enchantrix interface {
	Transmute(data []byte, sigils []Sigil) ([]byte, error)
}

Enchantrix defines the interface for acceptance testing.

type GzipSigil

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

GzipSigil is a Sigil that compresses/decompresses data using gzip. The In method compresses the data, and the Out method decompresses it.

func (*GzipSigil) In

func (s *GzipSigil) In(data []byte) ([]byte, error)

In compresses the data using gzip.

func (*GzipSigil) Out

func (s *GzipSigil) Out(data []byte) ([]byte, error)

Out decompresses the data using gzip.

type HashSigil

type HashSigil struct {
	Hash crypto.Hash
}

HashSigil is a Sigil that hashes the data using a specified algorithm. The In method hashes the data, and the Out method is a no-op.

func NewHashSigil

func NewHashSigil(h crypto.Hash) *HashSigil

NewHashSigil creates a new HashSigil.

func (*HashSigil) In

func (s *HashSigil) In(data []byte) ([]byte, error)

In hashes the data.

func (*HashSigil) Out

func (s *HashSigil) Out(data []byte) ([]byte, error)

Out is a no-op for HashSigil.

type HexSigil

type HexSigil struct{}

HexSigil is a Sigil that encodes/decodes data to/from hexadecimal. The In method encodes the data, and the Out method decodes it.

func (*HexSigil) In

func (s *HexSigil) In(data []byte) ([]byte, error)

In encodes the data to hexadecimal.

func (*HexSigil) Out

func (s *HexSigil) Out(data []byte) ([]byte, error)

Out decodes the data from hexadecimal.

type JSONSigil

type JSONSigil struct{ Indent bool }

JSONSigil is a Sigil that compacts or indents JSON data. The Out method is a no-op.

func (*JSONSigil) In

func (s *JSONSigil) In(data []byte) ([]byte, error)

In compacts or indents the JSON data.

func (*JSONSigil) Out

func (s *JSONSigil) Out(data []byte) ([]byte, error)

Out is a no-op for JSONSigil.

type PreObfuscator

type PreObfuscator interface {
	// Obfuscate transforms plaintext before encryption using the provided entropy.
	// The entropy is typically the encryption nonce, ensuring the transformation
	// is unique per-encryption without additional random generation.
	Obfuscate(data []byte, entropy []byte) []byte

	// Deobfuscate reverses the transformation after decryption.
	// Must be called with the same entropy used during Obfuscate.
	Deobfuscate(data []byte, entropy []byte) []byte
}

PreObfuscator applies a reversible transformation to data before encryption. This ensures that raw plaintext patterns are never sent directly to CPU encryption routines, providing defense against side-channel attacks.

Implementations must be deterministic: given the same entropy, the transformation must be perfectly reversible: Deobfuscate(Obfuscate(x, e), e) == x

type ReverseSigil

type ReverseSigil struct{}

ReverseSigil is a Sigil that reverses the bytes of the payload. It is a symmetrical Sigil, meaning that the In and Out methods perform the same operation.

func (*ReverseSigil) In

func (s *ReverseSigil) In(data []byte) ([]byte, error)

In reverses the bytes of the data.

func (*ReverseSigil) Out

func (s *ReverseSigil) Out(data []byte) ([]byte, error)

Out reverses the bytes of the data.

type ShuffleMaskObfuscator

type ShuffleMaskObfuscator struct{}

ShuffleMaskObfuscator provides stronger obfuscation through byte shuffling and masking.

The obfuscation process:

  1. Generate a mask from entropy using SHA-256 in counter mode
  2. XOR the data with the mask
  3. Generate a deterministic permutation using Fisher-Yates shuffle
  4. Reorder bytes according to the permutation

This provides both value transformation (XOR mask) and position transformation (shuffle), making pattern analysis more difficult than XOR alone.

func (*ShuffleMaskObfuscator) Deobfuscate

func (s *ShuffleMaskObfuscator) Deobfuscate(data []byte, entropy []byte) []byte

Deobfuscate reverses the shuffle and mask operations.

func (*ShuffleMaskObfuscator) Obfuscate

func (s *ShuffleMaskObfuscator) Obfuscate(data []byte, entropy []byte) []byte

Obfuscate shuffles bytes and applies a mask derived from entropy.

type Sigil

type Sigil interface {
	// In applies the forward transformation to the data.
	// For encoding sigils, this encodes the data.
	// For compression sigils, this compresses the data.
	// For hash sigils, this computes the digest.
	In(data []byte) ([]byte, error)

	// Out applies the reverse transformation to the data.
	// For reversible sigils, this recovers the original data.
	// For irreversible sigils (e.g., hashing), this returns the input unchanged.
	Out(data []byte) ([]byte, error)
}

Sigil defines the interface for a data transformer.

A Sigil represents a single transformation unit that can be applied to byte data. Sigils may be reversible (encoding, compression, encryption) or irreversible (hashing).

For reversible sigils: Out(In(x)) == x for all valid x For irreversible sigils: Out returns the input unchanged For symmetric sigils: In(x) == Out(x)

Implementations must handle nil input by returning nil without error, and empty input by returning an empty slice without error.

func NewSigil

func NewSigil(name string) (Sigil, error)

NewSigil is a factory function that returns a Sigil based on a string name. It is the primary way to create Sigil instances.

Example
package main

import (
	"fmt"
	"log"

	"forge.lthn.ai/Snider/Enchantrix/pkg/enchantrix"
)

func main() {
	sigil, err := enchantrix.NewSigil("base64")
	if err != nil {
		log.Fatalf("Failed to create sigil: %v", err)
	}
	data := []byte("Hello, World!")
	encoded, err := sigil.In(data)
	if err != nil {
		log.Fatalf("Sigil In failed: %v", err)
	}
	fmt.Printf("Encoded data: %s\n", encoded)
	decoded, err := sigil.Out(encoded)
	if err != nil {
		log.Fatalf("Sigil Out failed: %v", err)
	}
	fmt.Printf("Decoded data: %s\n", decoded)
}
Output:

Encoded data: SGVsbG8sIFdvcmxkIQ==
Decoded data: Hello, World!

type XORObfuscator

type XORObfuscator struct{}

XORObfuscator performs XOR-based obfuscation using an entropy-derived key stream.

The key stream is generated using SHA-256 in counter mode:

keyStream[i*32:(i+1)*32] = SHA256(entropy || BigEndian64(i))

This provides a cryptographically uniform key stream that decorrelates plaintext patterns from the data seen by the encryption routine. XOR is symmetric, so obfuscation and deobfuscation use the same operation.

func (*XORObfuscator) Deobfuscate

func (x *XORObfuscator) Deobfuscate(data []byte, entropy []byte) []byte

Deobfuscate reverses the XOR transformation (XOR is symmetric).

func (*XORObfuscator) Obfuscate

func (x *XORObfuscator) Obfuscate(data []byte, entropy []byte) []byte

Obfuscate XORs the data with a key stream derived from the entropy.

Jump to

Keyboard shortcuts

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