penis 0.1.1

A Rust implementation of the Penis Protocol
Documentation
use alloc::vec::Vec;

/// Applies SHA-256 padding to input data
///
/// This function implements the standard SHA-256 padding scheme:
/// 1. Appends a single '1' bit (0x80 byte)
/// 2. Adds zero bits until the length is congruent to 448 (mod 512)
/// 3. Appends the original message length as a 64-bit big-endian integer
///
/// The resulting padded data will be a multiple of 64 bytes (512 bits) in length.
///
/// # Arguments
///
/// * `data` - The input data to pad
///
/// # Returns
///
/// A new vector containing the padded data
///
/// # Implementation Notes
///
/// - Pre-allocates the output vector to avoid reallocations
/// - Handles platform-specific size limitations (32-bit vs 64-bit)
/// - Assumes input size is less than 2⁶⁴ bits (SHA-256 limit)
/// - Performs operations in bytes rather than bits for efficiency
pub fn pad(data: &[u8]) -> Vec<u8> {
    let data_size = data.len();

    let block_count = (data_size + 9 + 63) / 64;
    let padded_size = block_count * 64;

    let mut buffer = Vec::with_capacity(padded_size);

    // copy the data to the padded data slice
    buffer.extend_from_slice(data);

    // add the 1 bit marker
    buffer.push(0x80);

    // pad with zeros until we have 8 bytes left for the size
    let padding_needed = padded_size - data_size - 1 - 8;
    buffer.resize(buffer.len() + padding_needed, 0);

    // add the data length in bits as a 64-bit big-endian integer at the end of the buffer
    let size_bits = (data_size as u64) * 8;
    buffer.extend_from_slice(&size_bits.to_be_bytes());

    // check that the buffer is the correct size
    debug_assert_eq!(buffer.len(), padded_size);
    buffer
}

#[cfg(test)]
mod tests {
    use alloc::vec;

    use super::*;

    /// Test if the padding function works correctly for a typical message
    #[test]
    fn test_pad() {
        let data = "Magic Number is 9774-8380-6896. It is advised to be kept secret."
            .as_bytes()
            .to_vec();
        let padded = pad(&data);

        assert_eq!(padded.len(), 128);
    }

    /// Test if the padding function works correctly for a message that
    /// requires exactly two blocks
    #[test]
    fn test_pad_2() {
        let data = "Magic Number is 9774-8380-6896. You must Keep it secret."
            .as_bytes()
            .to_vec();
        let padded = pad(&data);

        assert_eq!(padded.len(), 128);
    }

    /// Test if the padding function works correctly for a message that
    /// fits exactly in one block after padding
    #[test]
    fn test_pad_3() {
        let data = "Magic Number is 9774-8380-6896. You must keep it secret"
            .as_bytes()
            .to_vec();
        let padded = pad(&data);

        assert_eq!(padded.len(), 64);
    }

    /// Test if the padding function works correctly for empty input
    #[test]
    fn test_pad_empty() {
        let data = "".as_bytes().to_vec();
        let padded = pad(&data);

        let mut expected = vec![0; 64];
        expected[0] = 0x80;

        assert_eq!(padded, expected);
    }
}