Documentation
¶
Overview ¶
Package f5imagerecover recovers original DCT coefficients from F5-embedded JPEG images.
This package REVERSES the F5 embedding process by incrementing coefficient values that were decremented during embedding. It does NOT extract hidden messages - use f5extract for message extraction.
Purpose ¶
When the F5 algorithm embeds a hidden message, it modifies JPEG DCT coefficients by decrementing their absolute values. This package recovers the ORIGINAL coefficients by reversing those modifications.
The typical workflow is:
- Use f5extract to extract the hidden message from a stego image
- Use f5imagerecover with that message to recover original coefficients
- Compare original vs stego coefficients for forensic analysis
Recovery Algorithm ¶
The recovery process retraces the embedding path:
- Initialize PRNG with password (same as f5embed)
- Generate coefficient permutation (same PRNG consumption as f5embed)
- XOR header bytes with PRNG (same sequence as f5embed)
- Parse k parameter from header
- Walk coefficients in permuted order, matching embedding path exactly
- For each code word, determine if/which coefficient was modified
- Reverse the modification (increment absolute value: 4->5, -4->-5)
- Handle shrinkage recovery (restore 0s to original +/-1 values)
F5 Algorithm Overview ¶
The F5 algorithm uses several key techniques that this recovery reverses:
F4 Encoding: Positive coefficients encode even=0, odd=1; negative coefficients encode even=1, odd=0. Embedding DECREMENTS absolute values; recovery INCREMENTS.
Matrix Encoding: Uses (1, n, k) codes where n = 2^k - 1. Recovery uses the same hash function f(a) = XOR of (a_i * i) to determine which coefficient was modified at each code word.
Permutative Straddling: Shuffles coefficient processing order using a password-seeded PRNG. Recovery generates the same permutation.
Shrinkage: When embedding decrements +/-1 to 0, the bit must be re-embedded. Recovery detects these zero coefficients and restores them to +/-1.
Thread Safety ¶
The package functions RecoverCoefficients and RecoverCoefficientsInPlace are safe for concurrent use. Each call creates fresh PRNG instances, ensuring no shared mutable state between concurrent operations.
Basic Usage ¶
Recover original coefficients after extracting the message with f5extract:
import (
"github.com/0verkilll/f5imagerecover"
"github.com/0verkilll/f5extract"
)
// Step 1: Extract the hidden message using f5extract
extractor := f5extract.NewExtractor(hasher, permutator, randomFactory)
result, err := extractor.Extract(stegoCoefficients, password)
if err != nil {
log.Fatalf("extraction failed: %v", err)
}
// Step 2: Recover original coefficients using f5imagerecover
originalCoeffs, err := f5imagerecover.RecoverCoefficients(
stegoCoefficients,
password,
result.Data, // The extracted message
)
if err != nil {
log.Fatalf("recovery failed: %v", err)
}
// originalCoeffs now contains the coefficients BEFORE embedding
Boolean Convenience ¶
For simple success/failure checking without error details:
originalCoeffs, ok := f5imagerecover.TryRecoverCoefficients(
stegoCoefficients,
password,
extractedMessage,
)
if !ok {
// Recovery failed (wrong password, corrupted data, etc.)
return
}
// Use recovered coefficients
In-Place Recovery ¶
For memory efficiency when you own the coefficient array:
// Modifies stegoCoefficients directly
err := f5imagerecover.RecoverCoefficientsInPlace(
stegoCoefficients,
password,
extractedMessage,
)
Configuration Options ¶
Use functional options to customize recovery behavior:
// Limit maximum file size (default: 10MB)
originalCoeffs, err := f5imagerecover.RecoverCoefficients(
stegoCoefficients, password, message,
f5imagerecover.WithMaxFileSize(1_000_000), // 1MB limit
)
// Enable strict mode (fail on any inconsistency)
originalCoeffs, err := f5imagerecover.RecoverCoefficients(
stegoCoefficients, password, message,
f5imagerecover.WithStrictMode(true),
)
// Disable shrinkage recovery
originalCoeffs, err := f5imagerecover.RecoverCoefficients(
stegoCoefficients, password, message,
f5imagerecover.WithShrinkageRecovery(false),
)
// Enable debug logging for diagnostics
originalCoeffs, err := f5imagerecover.RecoverCoefficients(
stegoCoefficients, password, message,
f5imagerecover.WithLogger(myLogger),
)
Relationship to Other Packages ¶
This package is part of the F5 steganography toolset:
- f5embed: Embeds messages into JPEG coefficients (forward operation)
- f5extract: Extracts embedded messages from coefficients
- f5imagerecover: Recovers original coefficients (reverse of f5embed)
The recovery workflow requires f5extract's output as input:
JPEG Image -> f5extract -> Hidden Message
|
v
JPEG Image -> f5imagerecover -> Original Coefficients
(with message)
Reference ¶
This implementation follows the algorithm described in:
Westfeld, A. (2001). "F5 - A Steganographic Algorithm: High Capacity Despite Better Steganalysis." Information Hiding, LNCS 2137, pp. 289-302.
For Java compatibility, the PRNG matches java.security.SecureRandom's SHA1PRNG algorithm, enabling interoperability with PixelKnot and F5.jar.
Index ¶
- Constants
- Variables
- func ApplyDeZigZag(shuffled int) int
- func ApplyHeaderXOR(headerBits int, random RandomSource) int
- func CodeWordLength(k int) int
- func ComputeCodeWordHash(codeWord []int) int
- func DetectShrinkage(coefficient int16) bool
- func DetectShrinkageCandidate(currentCoeff int16, expectedBit int) bool
- func DetermineChangePosition(codeWord []int, messageBits, k int) (changePosition int, err error)
- func ErrEmptyCoefficients() error
- func ErrEmptyExtractedMessage() error
- func ErrEmptyPassword() error
- func ErrFileSizeZeroOrNegative(fileSize int) error
- func ErrInsufficientCoefficientsHeader(bitsExtracted int) error
- func ErrInsufficientCoefficientsMessage(bytesExtracted, expectedBytes int) error
- func ErrInvalidExtractedMessage(detail string) error
- func ErrInvalidFileSize(fileSize, maxFileSize int) error
- func ErrInvalidK(k int) error
- func ErrPermutationMismatch() error
- func ErrRecoveryFailed(reason string) error
- func ErrShrinkageRecoveryFailed(position int) error
- func ExtractBit(coefficient int16) int
- func ExtractFileSize(headerBits int) int
- func ExtractHeaderBits(coefficients []int16, permutation []int) (headerBits, nextIndex int, err error)
- func ExtractKParameter(headerBits int) int
- func ExtractStegoBits(coefficients []int16) []int
- func GetLogger() logger.Logger
- func GetStegoBit(coefficient int16) int
- func GetSupportedLocales() []string
- func IsRecoveryError(err error) bool
- func IsUsableCoefficient(shuffled, zigzag int, coefficient int16) bool
- func IsUsableCoefficientSimple(index int, coefficient int16) bool
- func NewF5PRNGFactory() f5prng.PRNGFactory
- func NextSignedByte(random RandomSource) int
- func RecoverCoefficients(stegoCoeffs []int16, password string, extractedMessage []byte, opts ...Option) ([]int16, error)
- func RecoverCoefficientsInPlace(stegoCoeffs []int16, password string, extractedMessage []byte, opts ...Option) error
- func RestoreShrunkCoefficient(expectedBit int) int16
- func ReverseModification(coefficient int16) int16
- func SetLogger(l logger.Logger)
- func SetTranslator(translator TranslatorProvider)
- func TryRecoverCoefficients(stegoCoeffs []int16, password string, extractedMessage []byte, opts ...Option) ([]int16, bool)
- func TryRecoverCoefficientsInPlace(stegoCoeffs []int16, password string, extractedMessage []byte, opts ...Option) bool
- func ValidateFileSize(fileSize, maxFileSize int) error
- func ValidateKParameter(k int) error
- func WrapError(err error, message string) error
- type Config
- type DefaultRandomSourceFactory
- type Hasher
- type HeaderResult
- type Option
- type PRNGFactory
- type Permutator
- type RandomSourcedeprecated
- type RandomSourceFactory
- type Recoverer
- type TranslatorProvider
Constants ¶
const ( // DefaultMaxFileSize is the maximum allowed size for recovered data in bytes. // See f5core.DefaultMaxFileSize for documentation. DefaultMaxFileSize = f5core.DefaultMaxFileSize // MaxKParameter is the maximum valid k parameter value for matrix encoding. // See f5core.MaxKParameter for documentation. MaxKParameter = f5core.MaxKParameter // MinKParameter is the minimum valid k parameter value for matrix encoding. // See f5core.MinKParameter for documentation. MinKParameter = f5core.MinKParameter )
Re-export constants from f5core for backward compatibility.
Variables ¶
var ( // ErrRecoveryFailedBase is the base error for recovery failures. ErrRecoveryFailedBase = errors.New("recovery failed") // ErrInvalidExtractedMessageBase is the base error for invalid extracted message. ErrInvalidExtractedMessageBase = errors.New("invalid extracted message") // ErrPermutationMismatchBase is the base error for permutation mismatch. ErrPermutationMismatchBase = errors.New("permutation mismatch") // ErrShrinkageRecoveryFailedBase is the base error for shrinkage recovery failure. ErrShrinkageRecoveryFailedBase = errors.New("shrinkage recovery failed") )
Sentinel errors for error checking with errors.Is(). These provide a way to check error types without string matching.
var ( ErrEmptyCodeWord = f5matrix.ErrEmptyCodeWord ErrInvalidKParameter = f5matrix.ErrInvalidKParameter ErrCodeWordSizeMismatch = f5matrix.ErrCodeWordSizeMismatch ErrMessageBitsExceedK = f5matrix.ErrMessageBitsExceedK )
Re-export matrix encoding errors from f5matrix for backward compatibility.
var DeZigZagTable = f5core.DeZigZag[:]
DeZigZagTable is an alias to f5core.DeZigZag for backward compatibility. See f5core.DeZigZag for full documentation.
Functions ¶
func ApplyDeZigZag ¶
ApplyDeZigZag converts a shuffled coefficient index to its de-zigzagged position. This is an alias to f5core.ApplyDeZigZag for backward compatibility. See f5core.ApplyDeZigZag for full documentation.
func ApplyHeaderXOR ¶
func ApplyHeaderXOR(headerBits int, random RandomSource) int
ApplyHeaderXOR decrypts the header bits by XORing with 4 PRNG bytes.
The XOR bytes are consumed from the PRNG AFTER the permutation has been generated. This ordering is critical for Java F5 compatibility.
The XOR is applied in little-endian byte order:
- Byte 0: XOR with bits 0-7 (no shift)
- Byte 1: XOR with bits 8-15 (shift left 8)
- Byte 2: XOR with bits 16-23 (shift left 16)
- Byte 3: XOR with bits 24-31 (shift left 24)
Note: Each byte is converted to a signed value before XOR, which causes sign-extension for values >= 128. This matches Java's behavior.
Parameters:
headerBits - The raw header bits extracted from coefficients random - The RandomSource to read XOR bytes from
Returns:
The decrypted header value after XOR
Example:
// After extracting header bits and continuing from permutation decryptedHeader := ApplyHeaderXOR(headerBits, random) k := ExtractKParameter(decryptedHeader) fileSize := ExtractFileSize(decryptedHeader)
func CodeWordLength ¶
CodeWordLength returns the code word length n for a given k parameter. This is an alias to f5matrix.CodeWordLength for backward compatibility. See f5matrix.CodeWordLength for full documentation.
func ComputeCodeWordHash ¶
ComputeCodeWordHash computes the hash function for matrix encoding. This is an alias to f5matrix.ComputeCodeWordHash for backward compatibility. See f5matrix.ComputeCodeWordHash for full documentation.
func DetectShrinkage ¶
DetectShrinkage determines if modifying a coefficient will cause shrinkage.
This is a helper function that matches f5embed.DetectShrinkage for consistency. Shrinkage occurs when decrementing the absolute value of a coefficient produces zero. This happens only when the coefficient is 1 or -1.
Note: This function is used during the FORWARD direction (embedding). For recovery, use DetectShrinkageCandidate instead.
Parameters:
- coefficient: A JPEG DCT coefficient value
Returns:
- true if the coefficient is 1 or -1 (will shrink to 0 when modified)
- false otherwise
Example:
DetectShrinkage(1) // Returns true DetectShrinkage(-1) // Returns true DetectShrinkage(2) // Returns false DetectShrinkage(0) // Returns false
func DetectShrinkageCandidate ¶
DetectShrinkageCandidate determines if a coefficient is a shrinkage candidate.
During F5 embedding, shrinkage occurs when a +1 or -1 coefficient is modified, becoming 0. Since 0 cannot encode steganographic data, the bit must be re-embedded using the next coefficient. During recovery, we need to identify which 0 coefficients were originally +/-1.
A coefficient is a shrinkage candidate if:
- It is currently 0
The expectedBit parameter is included for interface consistency and potential future validation, but the primary detection is based on the coefficient being 0. When we're walking the recovery path and encounter a 0 at a position that was supposed to be modified, we know it's a shrinkage case.
Parameters:
- currentCoeff: The current coefficient value (after embedding)
- expectedBit: The bit that was supposed to be encoded (0 or 1)
Returns:
- true if the coefficient is 0 (potential shrinkage candidate)
- false otherwise
Example:
DetectShrinkageCandidate(0, 1) // Returns true (was +1 before shrinkage) DetectShrinkageCandidate(0, 0) // Returns true (was -1 before shrinkage) DetectShrinkageCandidate(4, 0) // Returns false (not a shrinkage case) DetectShrinkageCandidate(-4, 1) // Returns false (not a shrinkage case)
func DetermineChangePosition ¶
DetermineChangePosition determines which coefficient position was changed during embedding to encode the given message bits. This is an alias to f5matrix.DetermineChangePosition for backward compatibility. See f5matrix.DetermineChangePosition for full documentation.
func ErrEmptyCoefficients ¶
func ErrEmptyCoefficients() error
ErrEmptyCoefficients returns an error indicating that no coefficients were provided.
func ErrEmptyExtractedMessage ¶
func ErrEmptyExtractedMessage() error
ErrEmptyExtractedMessage returns an error indicating that the extracted message is empty.
Recovery requires the extracted message to determine which coefficients were modified. An empty message means there's nothing to recover.
func ErrEmptyPassword ¶
func ErrEmptyPassword() error
ErrEmptyPassword returns an error indicating that the password was empty.
func ErrFileSizeZeroOrNegative ¶
ErrFileSizeZeroOrNegative returns an error indicating that the file size is zero or negative.
func ErrInsufficientCoefficientsHeader ¶
ErrInsufficientCoefficientsHeader returns an error indicating insufficient coefficients for header extraction.
func ErrInsufficientCoefficientsMessage ¶
ErrInsufficientCoefficientsMessage returns an error indicating insufficient coefficients for message extraction.
func ErrInvalidExtractedMessage ¶
ErrInvalidExtractedMessage returns an error indicating that the extracted message doesn't match the coefficient state, suggesting wrong password or corrupted data.
This error occurs when the message bits XORed with the code word hash produce invalid change positions that don't align with the actual coefficient modifications.
Parameters:
- detail: Specific detail about the mismatch
Example:
err := ErrInvalidExtractedMessage("change position exceeds code word length")
func ErrInvalidFileSize ¶
ErrInvalidFileSize returns an error indicating that the file size exceeds the maximum allowed.
func ErrInvalidK ¶
ErrInvalidK returns an error indicating that the k parameter is invalid.
func ErrPermutationMismatch ¶
func ErrPermutationMismatch() error
ErrPermutationMismatch returns an error indicating that the PRNG-generated permutation doesn't align with the coefficient state.
This typically indicates:
- Wrong password was used
- Coefficients were modified after embedding
- The image was not embedded with F5 algorithm
Example:
err := ErrPermutationMismatch()
if errors.Is(err, ErrPermutationMismatchBase) { ... }
func ErrRecoveryFailed ¶
ErrRecoveryFailed returns an error indicating that recovery failed with a specific reason.
Use this for general recovery failures that don't fit more specific error types. The error wraps ErrRecoveryFailedBase for use with errors.Is().
Parameters:
- reason: A description of why recovery failed
Example:
err := ErrRecoveryFailed("header validation failed")
if errors.Is(err, ErrRecoveryFailedBase) { ... }
func ErrShrinkageRecoveryFailed ¶
ErrShrinkageRecoveryFailed returns an error indicating that shrinkage recovery could not determine the original coefficient value.
During shrinkage recovery, we need to determine whether a zero coefficient was originally +1 or -1. This error occurs when that determination fails, usually due to insufficient context or corrupted data.
Parameters:
- position: The position in the coefficient array where recovery failed
Example:
err := ErrShrinkageRecoveryFailed(1234)
func ExtractBit ¶
ExtractBit extracts a single bit from a DCT coefficient using F4 encoding. This is an alias to f5coefficient.GetStegoBit for backward compatibility. See f5coefficient.GetStegoBit for full documentation.
func ExtractFileSize ¶
ExtractFileSize extracts the file size from the decrypted header.
The file size is stored in bits 0-22 of the header (23 bits). Maximum theoretical value: 0x7FFFFF = 8,388,607 bytes (~8MB).
Parameters:
headerBits - The decrypted header value (after XOR)
Returns:
The file size in bytes (may be invalid, use ValidateFileSize)
Example:
fileSize := ExtractFileSize(decryptedHeader)
if err := ValidateFileSize(fileSize, cfg.MaxFileSize); err != nil {
return nil, err
}
func ExtractHeaderBits ¶
func ExtractHeaderBits(coefficients []int16, permutation []int) (headerBits, nextIndex int, err error)
ExtractHeaderBits extracts 32 bits from the first 32 usable coefficients.
This function implements the F5 header extraction algorithm exactly as specified in the Java reference implementation. It processes coefficients in permuted order, skipping DC coefficients and zeros, and extracts one bit from each usable coefficient.
The extraction follows F4 encoding rules:
- Positive coefficients: even=0, odd=1
- Negative coefficients: even=1, odd=0
Parameters:
coefficients - The quantized DCT coefficients from the JPEG image permutation - The Fisher-Yates permutation determining coefficient order
Returns:
headerBits - The 32-bit header value (before XOR decryption) nextIndex - The permutation index to continue extraction from err - Error if insufficient usable coefficients for 32 bits
Example:
headerBits, nextIdx, err := ExtractHeaderBits(coefficients, permutation)
if err != nil {
return nil, fmt.Errorf("header extraction failed: %w", err)
}
// Continue with XOR decryption and parameter extraction
func ExtractKParameter ¶
ExtractKParameter extracts the k parameter from the decrypted header.
The k parameter is stored in bits 24-31 of the header. After extraction, it is taken modulo 32 with negative adjustment for Java compatibility.
The k parameter determines the matrix encoding strength:
- n = 2^k - 1 (coefficients needed per extraction group)
- Higher k = better embedding efficiency but needs more coefficients
- k=1: Simple 1:1 mapping (1 coefficient per bit)
- k=8: 255 coefficients extract 8 bits
Parameters:
headerBits - The decrypted header value (after XOR)
Returns:
The k parameter value (may be outside valid range 1-8, use ValidateKParameter)
Example:
k := ExtractKParameter(decryptedHeader)
if err := ValidateKParameter(k); err != nil {
return nil, err
}
func ExtractStegoBits ¶
ExtractStegoBits extracts the steganographic bit values from a slice of coefficients. This is an alias to f5matrix.ExtractStegoBits for backward compatibility. See f5matrix.ExtractStegoBits for full documentation.
func GetLogger ¶
GetLogger returns the package logger, or NopLogger if not set. This is useful for inspecting the current logger or for testing.
func GetStegoBit ¶
GetStegoBit extracts the steganographic bit value from a DCT coefficient. This is an alias to f5coefficient.GetStegoBit for backward compatibility. See f5coefficient.GetStegoBit for full documentation.
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.
func IsRecoveryError ¶
IsRecoveryError checks if an error is a recovery-related error. This is a convenience function for checking multiple recovery error types.
func IsUsableCoefficient ¶
IsUsableCoefficient determines if a coefficient can be used for bit extraction.
This wrapper maintains backward compatibility with the 3-parameter signature. The zigzag parameter is unused but retained for API compatibility.
See f5coefficient.IsUsableCoefficient for full documentation.
func IsUsableCoefficientSimple ¶
IsUsableCoefficientSimple determines if a coefficient can be used for steganographic data. This is an alias to f5coefficient.IsUsableCoefficient for backward compatibility. See f5coefficient.IsUsableCoefficient for full documentation.
func NewF5PRNGFactory ¶
func NewF5PRNGFactory() f5prng.PRNGFactory
NewF5PRNGFactory creates a new f5prng.PRNGFactory for direct use with f5prng types. This is the recommended way to create PRNG instances for new code.
Example:
factory := NewF5PRNGFactory()
prng := factory.NewPRNG()
prng.Seed([]byte("password"))
defer prng.Clear()
func NextSignedByte ¶
func NextSignedByte(random RandomSource) int
NextSignedByte gets the next byte from the PRNG as a Java-compatible signed integer.
In Java, bytes are signed (-128 to 127). When used for XOR operations, the signed byte is sign-extended to an int. This function replicates that behavior exactly for compatibility with Java F5 implementations.
The conversion process:
- Read one unsigned byte from PRNG (0-255)
- Interpret as Java signed byte (-128 to 127)
- Sign-extend to Go int for XOR operations
Parameters:
random - The RandomSource providing PRNG bytes
Returns:
A signed integer in range [-128, 127], sign-extended for XOR
Example:
// PRNG byte 0xFF becomes -1 (sign-extended to 0xFFFFFFFF for 32-bit XOR) // PRNG byte 0x80 becomes -128 (sign-extended to 0xFFFFFF80) // PRNG byte 0x7F becomes 127 (no sign extension needed) signedByte := NextSignedByte(random)
func RecoverCoefficients ¶
func RecoverCoefficients(stegoCoeffs []int16, password string, extractedMessage []byte, opts ...Option) ([]int16, error)
RecoverCoefficients recovers the original DCT coefficients from steganographic coefficients.
This function reverses the F5 embedding process by:
- Initializing PRNG with password (same as f5embed)
- Generating permutation (same PRNG consumption as f5embed)
- Processing header coefficients (may have been modified)
- Walking coefficients in permuted order, matching embedding path exactly
- Reversing modifications by incrementing absolute values
- Handling shrinkage recovery by restoring 0s to +/-1
The function creates a COPY of coefficients and does not modify the original.
Parameters:
- stegoCoeffs: The steganographic DCT coefficients to recover from
- password: The password used during embedding
- extractedMessage: The message extracted using f5extract
- opts: Optional configuration options (WithMaxFileSize, WithLogger, WithStrictMode, WithShrinkageRecovery)
Returns:
- []int16: The recovered original coefficients
- error: If recovery fails
func RecoverCoefficientsInPlace ¶
func RecoverCoefficientsInPlace(stegoCoeffs []int16, password string, extractedMessage []byte, opts ...Option) error
RecoverCoefficientsInPlace recovers the original DCT coefficients in place.
This is the memory-efficient variant of RecoverCoefficients that modifies the input slice directly. Use this when you own the coefficient array and want to avoid an extra copy.
Parameters:
- stegoCoeffs: The steganographic DCT coefficients to recover (modified in place)
- password: The password used during embedding
- extractedMessage: The message extracted using f5extract
- opts: Optional configuration options
Returns:
- error: If recovery fails
func RestoreShrunkCoefficient ¶
RestoreShrunkCoefficient restores a shrunk coefficient to its original +/-1 value.
When a coefficient becomes 0 due to shrinkage during embedding, we can determine its original value based on what bit it was supposed to encode:
- If expectedBit is 1: original was +1 (positive odd encodes 1 in F4)
- If expectedBit is 0: original was -1 (negative odd encodes 0 in F4)
This follows from the F4 encoding rules:
- Positive odd coefficients encode 1: e.g., +1 encodes 1
- Negative odd coefficients encode 0: e.g., -1 encodes 0
The reasoning is: before shrinkage, the coefficient was +1 or -1. When modified by F5 embedding (decrement absolute value), it became 0. We can infer the original sign from the bit that was being encoded at that position.
Parameters:
- expectedBit: The bit that was encoded at this position (0 or 1)
Returns:
- +1 if expectedBit is 1 (positive odd encodes 1)
- -1 if expectedBit is 0 (negative odd encodes 0)
Example:
RestoreShrunkCoefficient(1) // Returns +1 RestoreShrunkCoefficient(0) // Returns -1
func ReverseModification ¶
ReverseModification reverses an F5 embedding modification by incrementing the absolute value of a coefficient. This is an alias to f5coefficient.ReverseModification for backward compatibility. See f5coefficient.ReverseModification for full documentation.
func SetLogger ¶
SetLogger sets the logger for the f5imagerecover package. Pass nil to disable logging and reset to the default NopLogger. The logger is shared across all goroutines and is thread-safe.
When a logger is configured, the package will log diagnostic information at various stages of the extraction process:
- Permutation generation (size, timing)
- Header extraction (k parameter, file size)
- Matrix encoding progress (coefficients processed, bytes extracted)
- Error conditions with context
Example:
// Enable debug logging f5imagerecover.SetLogger(myDebugLogger) // Disable logging f5imagerecover.SetLogger(nil)
func SetTranslator ¶
func SetTranslator(translator TranslatorProvider)
SetTranslator sets the global translator for this package. Pass nil to disable translations and use default English messages. The translator is shared across all goroutines and is thread-safe.
func TryRecoverCoefficients ¶
func TryRecoverCoefficients(stegoCoeffs []int16, password string, extractedMessage []byte, opts ...Option) ([]int16, bool)
TryRecoverCoefficients attempts to recover coefficients, returning false instead of an error.
This is a convenience wrapper that returns (nil, false) on any error.
func TryRecoverCoefficientsInPlace ¶
func TryRecoverCoefficientsInPlace(stegoCoeffs []int16, password string, extractedMessage []byte, opts ...Option) bool
TryRecoverCoefficientsInPlace attempts to recover coefficients in place, returning false instead of an error.
This is a convenience wrapper that returns false on any error.
func ValidateFileSize ¶
ValidateFileSize checks if the file size is valid and within limits.
The file size must be:
- Greater than 0 (empty files are invalid)
- Less than or equal to maxFileSize (to prevent memory exhaustion)
Parameters:
fileSize - The file size extracted from the header maxFileSize - The configured maximum file size limit
Returns:
nil if valid, error describing the validation failure otherwise
Example:
if err := ValidateFileSize(fileSize, cfg.MaxFileSize); err != nil {
return nil, fmt.Errorf("invalid F5 header: %w", err)
}
func ValidateKParameter ¶
ValidateKParameter checks if the k parameter is within the valid range.
For F5 matrix encoding, k must be in the range [1, 8]:
- k < 1: Invalid (no meaningful extraction)
- k > 8: Invalid (would require excessive coefficients)
Parameters:
k - The k parameter extracted from the header
Returns:
nil if valid, error describing the validation failure otherwise
Example:
if err := ValidateKParameter(k); err != nil {
return nil, fmt.Errorf("invalid F5 header: %w", err)
}
Types ¶
type Config ¶
type Config struct {
// Logger is an optional logger for debug output during recovery.
// When nil, no logging occurs (silent operation).
//
// Default: nil (no logging)
Logger logger.Logger
// MaxFileSize is the maximum allowed size for recovered data in bytes.
// If the extracted header indicates a larger file, Recover returns an error.
//
// Default: DefaultMaxFileSize (10MB)
MaxFileSize int
// StrictMode controls how inconsistencies are handled during recovery.
// When true, any mismatch between expected and actual coefficient state
// causes the recovery to fail with an error.
// When false, recovery continues on a best-effort basis, attempting to
// recover as many coefficients as possible despite inconsistencies.
//
// Default: false (best-effort recovery)
StrictMode bool
// ShrinkageRecovery controls whether shrinkage recovery is attempted.
// When true, the recovery process attempts to identify coefficients that
// were +1 or -1 before embedding and became 0 (shrinkage).
// When false, zero coefficients are left as-is.
//
// Default: true (enable shrinkage recovery)
ShrinkageRecovery bool
}
Config holds the resolved configuration for a Recover operation. This struct is populated by applying Option functions to default values.
Fields are exported to allow inspection in tests, but should generally be accessed through the functional options pattern.
func ApplyOptions ¶
ApplyOptions applies the given options to a default Config and returns the result. This is an internal helper used by Recover and TryRecover.
func DefaultConfig ¶
func DefaultConfig() *Config
DefaultConfig returns a Config with default values. This is used as the starting point before applying options.
Default values:
- MaxFileSize: DefaultMaxFileSize (10MB)
- Logger: nil (no logging)
- StrictMode: false (best-effort recovery)
- ShrinkageRecovery: true (enabled)
type DefaultRandomSourceFactory ¶
type DefaultRandomSourceFactory struct{}
DefaultRandomSourceFactory creates the default RandomSource implementation. This factory uses f5prng internally to create SecureRandom instances compatible with Java's SHA1PRNG.
func NewDefaultRandomSourceFactory ¶
func NewDefaultRandomSourceFactory() *DefaultRandomSourceFactory
NewDefaultRandomSourceFactory creates a new DefaultRandomSourceFactory.
func (*DefaultRandomSourceFactory) New ¶
func (f *DefaultRandomSourceFactory) New(hasher Hasher) RandomSource
New creates a new RandomSource instance. The hasher parameter is accepted for interface compatibility but the factory uses f5prng internally which creates its own SHA-1 hasher.
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:
// The computed hash as a byte slice
Sum(data []byte) []byte
}
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.
For F5 steganography compatibility, implementations should use SHA-1 to match Java's SecureRandom SHA1PRNG algorithm.
Note: This interface differs from f5prng.Hasher in method signature. f5prng.Hasher.Sum returns ([]byte, error) while this Hasher.Sum returns []byte. This local interface is maintained for backward compatibility with existing f5imagerecover code that depends on the simpler signature.
Thread Safety: Implementations need not be thread-safe. Create separate instances for concurrent use.
type HeaderResult ¶
type HeaderResult struct {
// K is the matrix encoding parameter (1-8).
K int
// FileSize is the expected message size in bytes.
FileSize int
// NextIndex is the permutation index to continue extraction from.
NextIndex int
}
HeaderResult contains the extracted and validated header information. This struct encapsulates the result of successful header extraction and validation, providing all values needed for message extraction.
func ExtractHeader ¶
func ExtractHeader( coefficients []int16, permutation []int, random RandomSource, maxFileSize int, ) (*HeaderResult, error)
ExtractHeader performs complete header extraction, XOR decryption, and validation.
This is the high-level function that combines all header extraction steps:
- Extract 32 bits from first 32 usable coefficients
- Apply XOR decryption with 4 PRNG bytes
- Extract k parameter and file size
- Validate both parameters
Parameters:
coefficients - The quantized DCT coefficients from the JPEG image permutation - The Fisher-Yates permutation determining coefficient order random - The RandomSource (state should be after permutation generation) maxFileSize - The maximum allowed file size for validation
Returns:
*HeaderResult containing k, fileSize, and nextIndex on success error if extraction or validation fails
Example:
result, err := ExtractHeader(coefficients, permutation, random, cfg.MaxFileSize)
if err != nil {
return nil, fmt.Errorf("header extraction failed: %w", err)
}
// Use result.K, result.FileSize, result.NextIndex for message extraction
type Option ¶
type Option func(*Config)
Option is a functional option for configuring Recover and TryRecover calls. Options follow the functional options pattern for clean, extensible configuration.
Example usage:
data, err := RecoverCoefficients(
coefficients,
password,
extractedMessage,
WithMaxFileSize(5_000_000), // 5MB limit
WithLogger(myLogger), // Enable debug logging
WithStrictMode(true), // Fail on any inconsistency
WithShrinkageRecovery(true), // Enable shrinkage recovery
)
func WithLogger ¶
WithLogger sets a logger for debug output during recovery. When a logger is configured, Recover will log diagnostic information at various stages of the recovery process.
Logged information includes:
- Permutation generation (size, timing)
- Header extraction (k parameter, file size)
- Recovery progress (coefficients processed, modifications reversed)
- Shrinkage recovery events (zeros restored to +/-1)
- Error conditions with context
The logger is used for the duration of a single Recover call and does not affect the global package logger or other concurrent Recover calls.
Parameters:
l - The logger to use (nil disables logging for this call)
Example:
// Enable debug logging for a specific recovery data, err := RecoverCoefficients(coefficients, password, msg, WithLogger(myDebugLogger)) // Disable logging (explicit) data, err := RecoverCoefficients(coefficients, password, msg, WithLogger(nil))
func WithMaxFileSize ¶
WithMaxFileSize sets the maximum allowed file size for recovered data. If the extracted F5 header indicates a file size larger than this limit, Recover will return an error before attempting to extract the full message.
This option provides protection against:
- Memory exhaustion from malformed headers
- Long-running extractions on corrupted data
- Denial-of-service attacks via crafted images
Parameters:
bytes - Maximum file size in bytes (must be positive)
If bytes <= 0, the default limit (DefaultMaxFileSize) is used.
Example:
// Limit extraction to 1MB data, err := RecoverCoefficients(coefficients, password, msg, WithMaxFileSize(1_000_000)) // Use a larger limit for known large files data, err := RecoverCoefficients(coefficients, password, msg, WithMaxFileSize(50_000_000))
func WithShrinkageRecovery ¶
WithShrinkageRecovery sets whether shrinkage recovery should be attempted.
During F5 embedding, shrinkage occurs when modifying a +1 or -1 coefficient causes it to become 0. These zeros no longer carry steganographic data. Shrinkage recovery attempts to restore these coefficients to their original +/-1 values based on the message bits that were being encoded.
When shrinkage recovery is enabled (true, default):
- Zeros at change positions are analyzed to determine if they were shrunk
- Original +/-1 values are restored based on expected bit values
- Provides more accurate coefficient recovery
When shrinkage recovery is disabled (false):
- Zero coefficients are left unchanged
- Faster recovery but less accurate for images with shrinkage
- Useful for quick analysis where shrinkage details don't matter
Parameters:
enable - true to enable shrinkage recovery, false to skip it
Example:
// Enable shrinkage recovery (default) data, err := RecoverCoefficients(coefficients, password, msg, WithShrinkageRecovery(true)) // Disable shrinkage recovery for faster processing data, err := RecoverCoefficients(coefficients, password, msg, WithShrinkageRecovery(false))
func WithStrictMode ¶
WithStrictMode sets whether recovery should fail on any inconsistency.
When strict mode is enabled (true):
- Any mismatch between expected and actual coefficient state causes an error
- Helps detect wrong passwords or corrupted data early
- Provides more reliable error messages about what went wrong
When strict mode is disabled (false, default):
- Recovery continues on a best-effort basis
- Attempts to recover as many coefficients as possible
- Useful when some data corruption is acceptable
- May produce partially correct results with wrong password
Parameters:
strict - true to enable strict mode, false for best-effort recovery
Example:
// Fail immediately on any inconsistency data, err := RecoverCoefficients(coefficients, password, msg, WithStrictMode(true)) // Best-effort recovery (default) data, err := RecoverCoefficients(coefficients, password, msg, WithStrictMode(false))
type PRNGFactory ¶
type PRNGFactory = f5prng.PRNGFactory
PRNGFactory is a type alias to f5prng.PRNGFactory for convenience. This provides access to the unified PRNG factory interface.
Example usage:
factory := f5prng.NewDefaultFactory()
prng := factory.NewPRNG()
prng.Seed([]byte("password"))
type Permutator ¶
type Permutator interface {
// Generate creates and returns a permutation of integers from 0 to size-1.
// The permutation is determined by the provided RandomSource, which must
// be properly seeded before calling this method.
//
// The returned slice contains each integer from 0 to size-1 exactly once,
// arranged in a pseudo-random order determined by the RandomSource.
// The same RandomSource state must always produce the same permutation.
//
// Parameters:
// size - The number of elements in the permutation (must be >= 0)
// random - The RandomSource used to generate the permutation order
//
// Returns:
// A slice of size integers containing a permutation of [0, size-1]
// An error if size is negative or exceeds implementation limits
//
// Example:
// Generate(5, seededRandom) might return [2, 4, 0, 3, 1], nil
Generate(size int, random RandomSource) ([]int, error)
// GenerateInto generates a permutation into the provided buffer.
// This is a zero-allocation variant that reuses an existing buffer.
// The buffer will be resized if needed to fit 'size' elements.
//
// Parameters:
// buf - Reusable buffer (will be resized if needed)
// size - The number of elements in the permutation (must be >= 0)
// random - The RandomSource used to generate the permutation order
//
// Returns:
// The buffer containing the permutation (may be reallocated if too small)
// An error if size is negative or exceeds implementation limits
GenerateInto(buf []int, size int, random RandomSource) ([]int, error)
}
Permutator defines the interface for generating permutations of integers. This abstraction follows the Single Responsibility Principle by focusing solely on permutation generation, and the Dependency Inversion Principle by depending on the RandomSource abstraction rather than concrete implementations.
For F5 steganography, this generates the Fisher-Yates shuffle that determines the order in which DCT coefficients are processed during extraction.
Thread Safety: Implementations need not be thread-safe. Create separate instances or use external synchronization for concurrent use.
type RandomSource
deprecated
type RandomSource = f5prng.RandomSource
RandomSource is a type alias to f5prng.RandomSource for backward compatibility.
Deprecated: Use f5prng.RandomSource directly. This type alias is provided for backward compatibility and may be removed in a future major version. The f5prng package provides the unified PRNG interface used across all F5 steganography packages.
Migration: Replace f5imagerecover.RandomSource with f5prng.RandomSource in your code. The interface signature is identical.
type RandomSourceFactory ¶
type RandomSourceFactory interface {
// New creates and returns a new RandomSource instance.
// Each call should return a fresh, independent instance that can be
// safely used without affecting other instances.
//
// Parameters:
// hasher - The Hasher to use for the RandomSource (for SHA1PRNG compatibility)
//
// Returns:
// A new RandomSource instance ready to be seeded
New(hasher Hasher) RandomSource
}
RandomSourceFactory creates new RandomSource instances. This factory pattern enables the Recoverer to create fresh PRNG instances per Recover() call for thread safety, without being coupled to a specific RandomSource implementation.
Thread Safety: Implementations must be safe for concurrent use. The factory itself should be stateless or use proper synchronization.
type Recoverer ¶
type Recoverer struct {
// contains filtered or unexported fields
}
Recoverer handles the recovery of original DCT coefficients from F5-embedded images. It uses dependency injection for PRNG, hashing, and permutation generation to enable testing and ensure compatibility with the F5 algorithm.
func NewRecoverer ¶
func NewRecoverer(hasher Hasher, randomFactory RandomSourceFactory, permutator Permutator) *Recoverer
NewRecoverer creates a new Recoverer with the provided dependencies. All dependencies must be non-nil.
Parameters:
- hasher: Hash function for PRNG seeding (typically SHA-1)
- randomFactory: Factory for creating RandomSource instances
- permutator: Permutation generator (Fisher-Yates)
Returns:
- A configured Recoverer instance
type TranslatorProvider ¶
type TranslatorProvider interface {
// Translate returns the translated string for the given key.
Translate(key string) string
// TranslateWithArgs returns the translated string with format arguments.
TranslateWithArgs(key string, args ...interface{}) string
// HasKey returns true if the translation key exists.
HasKey(key string) bool
// SetLocale changes the current locale.
SetLocale(locale string)
// GetLocale returns the current locale.
GetLocale() string
}
TranslatorProvider defines the interface for translation providers. This allows loose coupling - the package works with or without i18n. The interface matches github.com/0verkilll/i18n.Translator.
func GetTranslator ¶
func GetTranslator() TranslatorProvider
GetTranslator returns the global translator, or nil if not set.
func NewTranslator ¶
func NewTranslator(locale string) (TranslatorProvider, error)
NewTranslator creates a new translator configured for the f5imagerecover package. If locale is empty, it will auto-detect from environment variables. The translator uses embedded locale files and the i18n package for translation.