f5matrix

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: 12 Imported by: 0

README

f5matrix

Matrix-encoding math for F5 steganography — the (1, n, k) code-word hashing and change-position logic at the heart of the algorithm.

Install

go get github.com/0verkilll/f5matrix

Sponsor

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

Sponsor @0verkilll on GitHub

License

MIT

Documentation

Overview

Package f5matrix provides matrix encoding operations for F5 steganography. These operations are shared across f5messageextract, f5messageembed, and f5imagerecover.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrEmptyCodeWord indicates that an empty code word was provided.
	ErrEmptyCodeWord = errors.New("empty code word")

	// ErrInvalidKParameter indicates that the k parameter is out of valid range (1-8).
	ErrInvalidKParameter = errors.New("invalid k parameter: must be 1-8")

	// ErrCodeWordSizeMismatch indicates that the code word size doesn't match n = 2^k - 1.
	ErrCodeWordSizeMismatch = errors.New("code word size mismatch")

	// ErrMessageBitsExceedK indicates that message bits exceed the maximum for k bits.
	ErrMessageBitsExceedK = errors.New("message bits exceed k-bit maximum")
)

Matrix encoding errors

Functions

func ApplyMatrixChange

func ApplyMatrixChange(coefficients []int16, indices []int, position int) int16

ApplyMatrixChange applies the coefficient modification at the calculated position.

This function modifies the coefficient array in-place at the position determined by matrix encoding. Position 0 means no change is needed. Position 1 to n indicates which coefficient (1-indexed) should be modified.

The modification follows F4/F5 rules using ModifyCoefficient. This may cause shrinkage if the coefficient is 1 or -1, resulting in a 0. The caller is responsible for detecting and handling shrinkage.

Parameters:

  • coefficients: The coefficient array to modify (modified in-place)
  • indices: The actual indices into coefficients for each code word position
  • position: The 1-indexed position to change (0 = no change)

Returns:

  • The new coefficient value at the modified position (0 if position is 0)

Example:

coeffs := []int16{5, 3, -2}
indices := []int{0, 1, 2}
newVal := ApplyMatrixChange(coeffs, indices, 2) // Modifies coeffs[1], returns 2

func CodeWordLength

func CodeWordLength(k int) int

CodeWordLength returns the code word length n for a given k parameter.

In F5's (1, n, k) matrix encoding, n = 2^k - 1.

Parameters:

  • k: The encoding parameter (typically 1-8)

Returns:

  • n: The code word length

Example:

CodeWordLength(1) // Returns 1
CodeWordLength(2) // Returns 3
CodeWordLength(3) // Returns 7
CodeWordLength(4) // Returns 15
CodeWordLength(8) // Returns 255

func ComputeCodeWordHash

func ComputeCodeWordHash(codeWord []int) int

ComputeCodeWordHash computes the hash function for matrix encoding.

The hash function is defined as: f(a) = XOR(i=1 to n) of (a_i * i) where a_i is the steganographic bit value at position i.

This function is central to the (1, n, k) matrix encoding used in F5. The hash produces a k-bit value that represents the current state of the code word from the perspective of steganographic data.

Parameters:

  • codeWord: A slice of steganographic bit values (0 or 1), length n

Returns:

  • The k-bit hash value computed by XORing indices where a_i = 1

Example:

// For k=2, n=3:
ComputeCodeWordHash([]int{1, 0, 1}) // Returns 1 XOR 3 = 2
ComputeCodeWordHash([]int{1, 1, 1}) // Returns 1 XOR 2 XOR 3 = 0
ComputeCodeWordHash([]int{0, 0, 0}) // Returns 0

func ComputeCodeWordHashPacked

func ComputeCodeWordHashPacked(packed []uint64, k int) int

ComputeCodeWordHashPacked computes the k-bit Hamming syndrome of a packed codeword in O(k · ceil(n/64)) instead of O(n) operations.

Parameters:

  • packed: bit-packed codeword (use PackCodeWord, or pack inline)
  • k: matrix-encoding parameter, 1..8 (must match the codeword's intended n = 2^k - 1 length)

Returns the same value as ComputeCodeWordHash on an unpacked codeword of the same content. Pass an unsupported k (k < 1 or k > 8) and the function returns 0 — the validating wrapper MatrixEncode handles that case for callers; this function is the speed-only fast path.

Performance: at k=8 this is ~5–6× faster than the branching loop in ComputeCodeWordHash (measured on M4 Max, Go 1.25). The relative win shrinks at smaller k (n=1, 3, 7 are too small for popcount overhead to amortise) — for k ∈ {1, 2, 3} the branching loop is comparable or faster, so callers in tight loops at small k may stick with the classic API.

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 := f5matrix.DetectLocale()
translator, err := f5matrix.NewTranslator(locale)
if err != nil {
    log.Fatal(err)
}
f5matrix.SetTranslator(translator)

Returns:

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

func DetermineChangePosition

func DetermineChangePosition(codeWord []int, messageBits, k int) (changePosition int, err error)

DetermineChangePosition determines which coefficient position was changed during embedding to encode the given message bits.

This is the inverse operation for recovery analysis. Given the current state of a code word (after embedding) and the message bits that were embedded, this function calculates which position was modified.

The function is mathematically identical to MatrixEncode because the XOR operation is symmetric: if we know the result and one operand, we can find the other.

Parameters:

  • codeWord: Steganographic bit values of the n coefficients (length must be 2^k - 1)
  • messageBits: The k-bit message chunk that was embedded (0 to 2^k - 1)
  • k: The encoding parameter (1-8), determines code word length n = 2^k - 1

Returns:

  • changePosition: 0 if no change was made, or 1 to n indicating which position was modified
  • err: Error if parameters are invalid

func ExtractStegoBits

func ExtractStegoBits(coefficients []int16) []int

ExtractStegoBits extracts the steganographic bit values from a slice of coefficients.

This is a convenience wrapper around f5coefficient.ExtractStegoBits. It allocates a fresh []int on every call; in matrix-encoding inner loops (once per codeword, thousands of codewords per JPEG) those allocations add up. For allocation- sensitive callers, prefer ExtractStegoBitsInto, which writes into a caller- supplied scratch slice and allocates only when the scratch capacity is insufficient.

Parameters:

  • coefficients: The coefficient values

Returns:

  • A slice of steganographic bits (0 or 1) for each coefficient

func ExtractStegoBitsInto

func ExtractStegoBitsInto(dst []int, coefficients []int16) []int

ExtractStegoBitsInto is the zero-allocation variant of ExtractStegoBits. It writes one stego bit per coefficient into dst[:len(coefficients)]. Caller must supply a slice with cap >= len(coefficients); the returned slice shares the backing array. Use this in matrix-encoding inner loops where allocating on every codeword is expensive.

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 := f5matrix.GetSupportedLocales()
fmt.Println(locales)
// Output: [en-US]

Returns:

  • []string: Sorted array of supported locale codes

func MatrixEncode

func MatrixEncode(codeWord []int, messageBits, k int) (changePosition int, err error)

MatrixEncode performs (1, n, k) matrix encoding to determine which coefficient to modify in order to embed k message bits.

F5 implements matrix encoding with d_max = 1, meaning at most one coefficient is changed per code word. The code word length is n = 2^k - 1.

The algorithm:

  1. Compute the hash of the current code word: h = f(a)
  2. Calculate the position to change: s = messageBits XOR h
  3. If s = 0, no change is needed (message already matches)
  4. If s > 0, change the coefficient at position s (1-indexed)

Parameters:

  • codeWord: Steganographic bit values of the n coefficients (length must be 2^k - 1)
  • messageBits: The k-bit message chunk to embed (0 to 2^k - 1)
  • k: The encoding parameter (1-8), determines code word length n = 2^k - 1

Returns:

  • changePosition: 0 if no change needed, or 1 to n indicating which position to modify
  • err: Error if parameters are invalid

Example:

// For k=2, n=3, embedding message bits 2 into code word [1,0,0]:
// hash = 1, s = 2 XOR 1 = 3, so change position 3
pos, _ := MatrixEncode([]int{1, 0, 0}, 2, 2) // Returns 3

// If message already matches hash, no change needed:
pos, _ := MatrixEncode([]int{1, 0, 0}, 1, 2) // Returns 0 (hash=1, s=1 XOR 1=0)

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 := f5matrix.NewTranslator()
if err != nil {
    log.Fatal(err)
}
f5matrix.SetTranslator(translator)

Example - Specific Language:

// Enable English translations
translator, err := f5matrix.NewTranslator("en-US")
if err != nil {
    log.Fatal(err)
}
f5matrix.SetTranslator(translator)

Parameters:

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

Returns:

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

func PackCodeWord

func PackCodeWord(buf []uint64, codeWord []int) []uint64

PackCodeWord packs a 0/1 codeword slice into a packed-bit representation. The output buffer is reused if it has enough capacity; otherwise a new slice is allocated. Returns the buffer (possibly grown).

Bit i of the codeword is stored at bit (i mod 64) of word (i / 64).

Callers may pack incrementally as they collect codeword bits — the embed and extract paths can do this for free during the coefficient walk:

bitIdx := len(indices)
if GetStegoBit(coeff) != 0 {
    packed[bitIdx/64] |= uint64(1) << (bitIdx % 64)
}
indices = append(indices, zigzag)

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"),
)
f5matrix.SetTranslator(translator)

Types

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"),
)
f5matrix.SetTranslator(translator)

Now all f5matrix 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