saorsa-seal

Crates.iosaorsa-seal
lib.rssaorsa-seal
version0.1.2
created_at2025-08-29 13:43:53.645062+00
updated_at2025-09-03 20:57:47.315025+00
descriptionThreshold sealing for group data in the Saorsa network
homepage
repositoryhttps://github.com/dirvine/saorsa-seal
max_upload_size
id1815853
size194,971
David Irvine (dirvine)

documentation

README

Saorsa Seal

Crates.io Documentation License CI

Threshold sealing for group data in the Saorsa network - a comprehensive solution for secure, distributed data storage with threshold cryptography and forward error correction.

Features

  • Threshold Sealing: Secure data using Shamir's Secret Sharing with configurable thresholds
  • Forward Error Correction: Reed-Solomon coding for data recovery and fault tolerance
  • AEAD Encryption: XChaCha20-Poly1305 authenticated encryption via saorsa-fec
  • Post-Quantum Cryptography: ML-KEM-768 encryption for quantum-resistant security
  • Distributed Storage: DHT abstraction for decentralized data storage
  • Verifiable Shares: Feldman commitments for share verification
  • Envelope Encryption: Post-quantum recipient encryption using ML-KEM-768
  • Async/Await: Full async support with tokio integration

Architecture

This crate leverages the Saorsa ecosystem:

  • [saorsa-core]: Threshold cryptography and DHT abstraction
  • [saorsa-fec]: Forward error correction and AEAD encryption
  • [saorsa-pqc]: Post-quantum cryptography

Quick Start

Add this to your Cargo.toml:

[dependencies]
saorsa-seal = "0.1.0"
tokio = { version = "1.0", features = ["full"] }

Basic Usage

use saorsa_seal::{seal_bytes, open_bytes, SealPolicy, FecParams, EnvelopeKind, Recipient, RecipientId, Dht};
use std::collections::HashMap;
use std::sync::Mutex;

// Simple DHT implementation for testing
#[derive(Debug)]
struct TestDht {
    storage: Mutex<HashMap<[u8; 32], Vec<u8>>>,
}

impl TestDht {
    fn new() -> Self {
        Self {
            storage: Mutex::new(HashMap::new()),
        }
    }
}

impl Dht for TestDht {
    fn put(&self, key: &[u8; 32], value: &[u8], _ttl: Option<u64>) -> anyhow::Result<()> {
        self.storage.lock().unwrap().insert(*key, value.to_vec());
        Ok(())
    }

    fn get(&self, key: &[u8; 32]) -> anyhow::Result<Vec<u8>> {
        self.storage
            .lock()
            .unwrap()
            .get(key)
            .cloned()
            .ok_or_else(|| anyhow::anyhow!("Key not found"))
    }
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let dht = TestDht::new();
    let plaintext = b"Hello, Saorsa Network!";

    // Create recipients (5 total, threshold of 3)
    let recipients: Vec<Recipient> = (0..5)
        .map(|i| Recipient {
            id: RecipientId::from_bytes(vec![i; 32]),
            public_key: None, // Would include ML-KEM public key for real usage
        })
        .collect();

    // Configure sealing policy
    let policy = SealPolicy {
        n: 5,                    // Total shares
        t: 3,                    // Threshold needed to recover
        recipients,
        fec: FecParams {
            data_shares: 3,      // Data chunks
            parity_shares: 2,    // Recovery chunks
            symbol_size: 1024,   // Chunk size
        },
        envelope: EnvelopeKind::PostQuantum, // Post-quantum recipient encryption
        aad: vec![],            // Additional authenticated data
    };

    // Seal the data
    let summary = seal_bytes(plaintext, &policy, &dht).await?;
    println!("Data sealed with handle: {:?}", summary.handle);

    // Simulate gathering shares from 3 recipients
    let sealed_meta_bytes = dht.get(&summary.handle.sealed_meta_key)?;
    let sealed_meta: saorsa_seal::types::SealedMetaV1 = 
        serde_cbor::from_slice(&sealed_meta_bytes)?;

    let shares: Vec<saorsa_seal::ProvidedShare> = sealed_meta
        .shares
        .iter()
        .take(3) // Use threshold number of shares
        .map(|sr| saorsa_seal::ProvidedShare {
            index: sr.index,
            share_bytes: sr.share_plain.clone().unwrap(),
            recipient_id: sr.recipient.clone(),
        })
        .collect();

    // Open (unseal) the data
    let recovered = open_bytes(&summary.handle, &shares, &dht)?;
    
    println!("Original:  {:?}", std::str::from_utf8(plaintext)?);
    println!("Recovered: {:?}", std::str::from_utf8(&recovered)?);
    assert_eq!(plaintext, &recovered[..]);

    Ok(())
}

Configuration

Seal Policy

use saorsa_seal::{SealPolicy, FecParams, EnvelopeKind};

let policy = SealPolicy {
    n: 5,                    // Total number of shares to generate
    t: 3,                    // Minimum shares needed for recovery
    recipients,              // List of share recipients
    fec: FecParams {
        data_shares: 10,     // Number of data chunks
        parity_shares: 4,    // Number of recovery chunks  
        symbol_size: 1024,   // Size of each chunk in bytes
    },
    envelope: EnvelopeKind::PostQuantum, // Post-quantum recipient encryption
    aad: b"context".to_vec(), // Additional authenticated data
};

Envelope Types

  • EnvelopeKind::None: No recipient-level encryption (shares stored in plaintext)
  • EnvelopeKind::PostQuantum: ML-KEM-768 post-quantum encryption

Performance

The library is optimized for throughput with configurable chunking:

  • Small files (< 1MB): Direct processing
  • Large files: Chunked processing with parallel FEC encoding
  • Symbol size: Configurable for optimal memory usage
  • Streaming: Async iterators for large datasets

Benchmarks show:

  • 1KB files: ~100μs sealing time
  • 100MB files: ~500ms sealing time
  • Memory usage: ~2x symbol_size × (data_shares + parity_shares)

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

Commit count: 3

cargo fmt