Skip to content

Latest commit

 

History

History
211 lines (165 loc) · 6.67 KB

File metadata and controls

211 lines (165 loc) · 6.67 KB

Library Guide

Installation

Add to your Cargo.toml:

[dependencies]
lupin = "1.0"

Quick Start

use lupin::operations::{embed, extract};
use lupin::EmbedMode;

// Read source and payload
let source_data = std::fs::read("document.pdf")?;
let payload_data = std::fs::read("secret.txt")?;

// Embed with rich metadata (EmbedMode::Capacity: unlimited size, easier to detect;
// EmbedMode::Stealth: harder to detect, not yet supported by every engine)
let (embedded_data, embed_result) = embed(&source_data, &payload_data, EmbedMode::Capacity)?;
println!("Embedded {} bytes into {} using {} engine",
         payload_data.len(), embed_result.source_size, embed_result.engine);

// Save result
std::fs::write("output.pdf", embedded_data)?;

// Extract later - detection is automatic, no mode needed
let output_data = std::fs::read("output.pdf")?;
let (extracted_data, extract_result) = extract(&output_data)?;
std::fs::write("recovered.txt", extracted_data)?;

Core Types

Operations Module

use lupin::operations::{embed, extract, EmbedResult, ExtractResult};
use lupin::EmbedMode;

// Vector-based operations
pub fn embed(source_data: &[u8], payload_data: &[u8], mode: EmbedMode) -> Result<(Vec<u8>, EmbedResult)>
pub fn extract(source_data: &[u8]) -> Result<(Vec<u8>, ExtractResult)>

EmbedMode

pub enum EmbedMode {
    Capacity, // default: unlimited payload size, easy to detect
    Stealth,  // reserved for a future low-detectability strategy; no engine implements
              // it yet, so passing it returns LupinError::StealthNotSupported
}

Result Types

#[derive(Debug, Clone)]
pub struct EmbedResult {
    pub source_size: usize,    // Original file size
    pub output_size: usize,    // Final file size (source + hidden data)
    pub engine: String,        // Engine used (e.g., "PDF")
}

#[derive(Debug, Clone)]
pub struct ExtractResult {
    pub source_size: usize,    // Source file size
    pub payload_size: usize,   // Extracted data size
    pub engine: String,        // Engine used
}

Why a byte-vector API

Operations take and return &[u8]/Vec<u8> rather than file paths, so the library itself never touches the filesystem. That means you can embed and extract data from files, network responses, or in-memory buffers, and tests can use byte literals directly instead of fixture files. Reading and writing files is left to the caller.

Advanced Examples

Working with Different Data Sources

use lupin::operations::{embed, extract};
use lupin::EmbedMode;

// From network or any byte source
let source_data = download_pdf_from_url("https://example.com/doc.pdf").await?;
let payload_data = b"secret message".to_vec();

// Embed without touching filesystem
let (result, metadata) = embed(&source_data, &payload_data, EmbedMode::Capacity)?;
println!("Output size: {} bytes (+{:.1}% increase)",
         metadata.output_size,
         (metadata.output_size as f64 / metadata.source_size as f64 - 1.0) * 100.0);

// Stream result anywhere
send_to_storage(&result).await?;

Error Handling

use lupin::error::LupinError;
use lupin::operations::embed;
use lupin::EmbedMode;

let source_data = std::fs::read("document.pdf")?;
let payload_data = std::fs::read("secret.txt")?;

match embed(&source_data, &payload_data, EmbedMode::Capacity) {
    Ok((embedded_data, metadata)) => {
        println!("Success! Used {} engine", metadata.engine);
        std::fs::write("output.pdf", embedded_data)?;
    }
    Err(LupinError::EngineDetection { .. }) => {
        eprintln!("File format not supported");
    }
    Err(LupinError::EmbedCollision { .. }) => {
        eprintln!("File already contains hidden data");
    }
    Err(LupinError::PdfNoEofMarker) => {
        eprintln!("Invalid PDF file");
    }
    // Returned if you pass EmbedMode::Stealth: no engine implements stealth yet
    Err(LupinError::StealthNotSupported { format }) => {
        eprintln!("Stealth mode isn't implemented for {format} yet");
    }
    Err(e) => {
        eprintln!("Other error: {}", e);
    }
}

Engine System Direct Access

For more control, you can use the engine system directly:

use lupin::{EmbedMode, EngineRouter, SteganographyEngine};

let router = EngineRouter::new();
let data = std::fs::read("document.pdf")?;

// Auto-detect and get the appropriate engine
let engine = router.detect_engine(&data)?;
println!("Detected format: {}", engine.format_name());

// Use the engine directly
let payload = b"secret data";
let result = engine.embed(&data, payload, EmbedMode::Capacity)?;

// Save the embedded data
std::fs::write("embedded.pdf", result)?;

Testing with In-Memory Data

#[cfg(test)]
mod tests {
    use lupin::operations::{embed, extract};
    use lupin::EmbedMode;

    #[test]
    fn test_round_trip() {
        // Create minimal PDF
        let pdf_data = b"%PDF-1.4\n%%EOF".to_vec();
        let payload = b"test payload";

        // Embed
        let (embedded, embed_result) = embed(&pdf_data, payload, EmbedMode::Capacity).unwrap();
        assert_eq!(embed_result.engine, "PDF");
        assert_eq!(embed_result.source_size, pdf_data.len());
        assert!(embed_result.output_size > embed_result.source_size);

        // Extract
        let (extracted, extract_result) = extract(&embedded).unwrap();
        assert_eq!(extracted, payload);
        assert_eq!(extract_result.payload_size, payload.len());
    }
}

Error Types

The full list of error types can always be found in the lupin::error module.

use lupin::error::LupinError;

// Common error types you'll encounter:
LupinError::EngineDetection { source }          // File format not supported
LupinError::EmbedCollision { source }           // Source already has hidden data
LupinError::EmptyPayload                        // Payload must not be empty
LupinError::StealthNotSupported { format }      // That engine doesn't implement stealth mode yet
LupinError::PdfNoEofMarker            // Invalid PDF (no %%EOF)
LupinError::PdfNoHiddenData           // No steganographic data found
LupinError::PdfCorruptedData          // Hidden data is corrupted
LupinError::PngNoIendChunk            // Invalid PNG (no IEND chunk)
LupinError::PngNoHiddenData           // No steganographic data found
LupinError::PngCorruptedData          // Hidden data is corrupted
LupinError::JpegInvalidFormat { reason }             // Invalid JPEG (e.g. no SOI marker)
LupinError::JpegNoHiddenData                         // No steganographic data found
LupinError::SourceFileRead { path, source }     // CLI: Can't read source file
LupinError::PayloadFileRead { path, source }    // CLI: Can't read payload file
LupinError::OutputFileWrite { path, source }    // CLI: Can't write output file