penis 0.1.1

A Rust implementation of the Penis Protocol
Documentation
use crate::{
    consts::INITIAL_STATE,
    hashfns::{execute_from_rounds, execute_until_rounds},
    structs::{PenisBlock, RawBlock, SecsParam, ToBeHashed},
    utils::pad,
    Penis, State,
};
use alloc::vec::Vec;

/// Penis Generator
///
/// Generates Partially Executed Nth-round Intermediate State (PENIS) from input data while
/// preserving confidentiality of specified data ranges. The generator executes the SHA-256
/// hash computation up to a specified round n, producing verifiable intermediate states.
///
/// # Security Considerations
///
/// - Use n=40 for maximum security in production environments
/// - Values of n<32 are NOT recommended for sensitive data
/// - Values of n<16 are UNSAFE as they reveal unhashed data
/// - Values of n≥48 are invalid and cause implementation errors
///
/// # Examples
///
/// ```rust
/// use penis::{PenisGenerator, SecsParam, DataRange, ToBeHashed};
///
/// // Initialize with maximum security (n=40)
/// let generator = PenisGenerator(SecsParam::new(40));
///
/// // Prepare data with confidential ranges
/// let tbh = ToBeHashed {
///     data: b"Hello, World!".to_vec(),
///     confidentials: vec![DataRange::new(0, 5)], // Keep "Hello" confidential
/// };
///
/// // Generate intermediate state
/// let penis = generator.execute(tbh);
///
/// // Encode for transmission
/// let encoded = penis.encode_sparse();
/// ```
#[derive(Debug)]
pub struct PenisGenerator(pub SecsParam);

impl PenisGenerator {
    /// Executes the PenisGenerator with a custom initial state.
    ///
    /// This method generates Partially Executed Nth-round Intermediate State from the given
    /// data and confidential ranges, using a specified initial state for the hash computation.
    ///
    /// # Arguments
    ///
    /// * `tbh` - The data to be hashed along with its confidential ranges
    /// * `initial_state` - The initial state to use for hash computation
    ///
    /// # Returns
    ///
    /// A Penis instance containing the intermediate states
    ///
    /// # Panics
    ///
    /// Panics if the data ranges in tbh are invalid (overlapping or out of bounds)
    pub fn execute_with_initial_state(&self, tbh: ToBeHashed, initial_state: &State) -> Penis {
        tbh.check_datarange().unwrap();

        let padded = pad(&tbh.data);
        let block_count = padded.len() / 64;

        let mut penis_blocks = Vec::<PenisBlock>::with_capacity(block_count);

        let mut previous_state = initial_state.clone();

        let n = self.0.n as usize;

        for i in 0..block_count {
            let block_range_start = i * 64;
            let block_range_end = (i + 1) * 64;
            let block = &padded[block_range_start..block_range_end];
            let is_private = tbh.is_in_confidentials(block_range_start, block_range_end);

            let private_block = execute_until_rounds(block, &previous_state, n);
            previous_state = execute_from_rounds(&private_block, previous_state, n);
            if is_private {
                penis_blocks.push(PenisBlock::Private(private_block));
            } else {
                let mut block_owned: RawBlock = [0u8; 64];
                block_owned.copy_from_slice(block);
                penis_blocks.push(PenisBlock::Public(block_owned));
            }
        }

        Penis(penis_blocks)
    }

    /// Executes the PenisGenerator with the default initial state.
    ///
    /// This is a convenience method that uses the standard SHA-256 initial state values.
    ///
    /// # Arguments
    ///
    /// * `tbh` - The data to be hashed along with its confidential ranges
    ///
    /// # Returns
    ///
    /// A Penis instance containing the intermediate states
    ///
    /// # Panics
    ///
    /// Panics if the data ranges in tbh are invalid (overlapping or out of bounds)
    #[inline]
    pub fn execute(&self, tbh: ToBeHashed) -> Penis {
        self.execute_with_initial_state(tbh, &INITIAL_STATE)
    }
}