libcrux-ml-dsa 0.0.8

Libcrux ML-DSA implementation
Documentation
// Functions for serializing and deserializing an error ring element.

use crate::{
    constants::Eta, helper::cloop, ntt::ntt, polynomial::PolynomialRingElement,
    simd::traits::Operations,
};

#[inline(always)]
pub(crate) fn serialize<SIMDUnit: Operations>(
    eta: Eta,
    re: &PolynomialRingElement<SIMDUnit>,
    serialized: &mut [u8], // OUTPUT_SIZE
) {
    let output_bytes_per_simd_unit = chunk_size(eta);

    cloop! {
        for (i, simd_unit) in re.simd_units.iter().enumerate() {
            SIMDUnit::error_serialize(eta,
                    simd_unit,
                    &mut serialized[i * output_bytes_per_simd_unit..(i + 1) * output_bytes_per_simd_unit]
                );
        }
    }
}

#[inline(always)]
fn chunk_size(eta: Eta) -> usize {
    match eta {
        Eta::Two => 3,
        Eta::Four => 4,
    }
}

#[inline(always)]
fn deserialize<SIMDUnit: Operations>(
    eta: Eta,
    serialized: &[u8],
    result: &mut PolynomialRingElement<SIMDUnit>,
) {
    let chunk_size = chunk_size(eta);

    for i in 0..result.simd_units.len() {
        SIMDUnit::error_deserialize(
            eta,
            &serialized[i * chunk_size..(i + 1) * chunk_size],
            &mut result.simd_units[i],
        );
    }
}

#[inline(always)]
pub(crate) fn deserialize_to_vector_then_ntt<SIMDUnit: Operations>(
    eta: Eta,
    ring_element_size: usize,
    serialized: &[u8],
    ring_elements: &mut [PolynomialRingElement<SIMDUnit>],
) {
    cloop! {
        for (i, bytes) in serialized.chunks_exact(ring_element_size).enumerate() {
            deserialize::<SIMDUnit>(eta, bytes, &mut ring_elements[i]);
            ntt(&mut ring_elements[i]);
        }
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use crate::simd::{self, traits::Operations};

    fn test_deserialize_generic<SIMDUnit: Operations>() {
        let serialized = [
            220, 24, 44, 136, 134, 36, 11, 195, 72, 82, 34, 144, 36, 33, 9, 196, 22, 70, 100, 148,
            65, 32, 163, 1, 210, 40, 14, 224, 38, 72, 33, 41, 136, 156, 146, 80, 25, 37, 50, 92,
            66, 140, 227, 144, 96, 9, 64, 141, 193, 50, 1, 140, 20, 141, 92, 32, 97, 2, 25, 42, 34,
            66, 50, 193, 72, 113, 202, 8, 16, 209, 54, 44, 129, 194, 48, 144, 20, 141, 228, 166,
            141, 228, 20, 144, 146, 54, 12, 99, 4, 140, 226, 18, 12, 194, 38, 97,
        ];

        let expected_coefficients = [
            -2, -1, -1, -2, 1, 2, -1, 1, 2, 1, 0, -1, 2, 1, 1, 1, -1, 1, -2, 1, -2, 1, 0, 0, 0, 0,
            1, 1, 0, 2, -2, -2, -2, -2, -2, 2, 0, 0, 0, 2, -2, 2, -1, -1, 1, -2, 1, 0, -2, -2, 1,
            0, 1, -1, 2, 0, 2, -2, -2, 1, 0, -1, 2, 2, 0, 0, -1, -2, 0, -2, -1, 2, 2, -2, -1, -1,
            0, 2, 0, 0, 1, -2, -2, -2, 0, 2, 0, -2, -2, -1, 0, 1, 1, 1, -2, 0, 1, -1, -2, 0, 0, -2,
            -2, 1, -2, -1, 1, 1, -2, 2, -1, -2, -1, -2, -1, 2, 1, 1, 2, -1, 1, 1, 2, 2, -2, 0, -1,
            -2, 1, 2, -1, 1, -1, 0, 2, 2, -2, 1, 0, 0, 1, 0, -1, -2, -2, -1, 1, 2, 0, 0, 2, -1, 0,
            2, -2, -2, 1, -2, 0, 1, 0, -2, 2, 1, -2, -2, -2, 1, 1, 2, -1, -2, -2, 0, -2, -1, 0, 1,
            -1, -2, 2, 2, -2, 2, 1, 0, -1, -1, -1, 2, -1, 1, 1, 2, 0, 1, -2, 1, -2, 1, 2, 0, 0, 0,
            1, 0, -1, -2, -2, -2, -1, -1, 0, -1, -1, -2, -2, -2, -1, 0, 1, 2, -2, -2, 0, 0, 0, -1,
            -1, 2, -1, 2, -1, -2, 1, 0, 2, 2, -1, -2, 0, -2, -1, 1, 1, 2, -1, 2, 0, 2, -1, -1, 0,
            0, 2, -1,
        ];

        let mut deserialized = PolynomialRingElement::<SIMDUnit>::zero();
        deserialize::<SIMDUnit>(Eta::Two, &serialized, &mut deserialized);
        assert_eq!(deserialized.to_i32_array(), expected_coefficients);

        let serialized = [
            22, 103, 55, 49, 34, 65, 50, 129, 52, 65, 21, 85, 82, 69, 3, 55, 52, 101, 80, 64, 114,
            136, 53, 8, 135, 67, 64, 71, 131, 21, 117, 81, 23, 99, 17, 84, 51, 23, 117, 56, 52, 85,
            131, 17, 22, 117, 85, 68, 34, 113, 87, 24, 65, 81, 2, 80, 118, 53, 34, 8, 32, 51, 51,
            82, 24, 2, 69, 0, 80, 68, 129, 133, 134, 17, 134, 82, 18, 21, 37, 114, 55, 87, 83, 8,
            80, 52, 103, 1, 84, 82, 99, 16, 86, 48, 16, 133, 17, 2, 67, 51, 120, 71, 19, 5, 72, 19,
            21, 103, 114, 115, 69, 36, 97, 68, 115, 56, 18, 83, 99, 32, 83, 88, 37, 71, 35, 82, 24,
            19,
        ];

        let expected_coefficients = [
            -2, 3, -3, -2, -3, 1, 3, 1, 2, 2, 3, 0, 2, 1, 3, -4, 0, 1, 3, 0, -1, 3, -1, -1, 2, -1,
            -1, 0, 1, 4, -3, 1, 0, 1, -1, -2, 4, -1, 4, 0, 2, -3, -4, -4, -1, 1, -4, 4, -3, -4, 1,
            0, 4, 0, -3, 0, 1, -4, -1, 3, -1, -3, 3, -1, -3, 3, 1, -2, 3, 3, 0, -1, 1, 1, -3, 3,
            -1, -3, -4, 1, 0, 1, -1, -1, 1, -4, 3, 3, -2, 3, -1, -3, -1, -1, 0, 0, 2, 2, 3, -3, -3,
            -1, -4, 3, 3, 0, 3, -1, 2, 4, 4, -1, -2, -3, -1, 1, 2, 2, -4, 4, 4, 2, 1, 1, 1, 1, 2,
            -1, -4, 3, 2, 4, -1, 0, 4, 4, 4, -1, 0, 0, 3, -4, -1, -4, -2, -4, 3, 3, -2, -4, 2, -1,
            2, 3, -1, 3, -1, 2, 2, -3, -3, 1, -3, -1, 1, -1, -4, 4, 4, -1, 0, 1, -3, -2, 3, 4, 0,
            -1, 2, -1, 1, -2, 4, 3, -2, -1, 4, 1, 4, 3, -1, -4, 3, 3, 2, 4, 1, 0, 1, 1, -4, -3, -3,
            0, 1, 3, -1, 4, -4, 0, 1, 3, -1, 3, -3, -2, 2, -3, 1, -3, -1, 0, 0, 2, 3, -2, 0, 0, 1,
            -3, -4, 1, 2, 3, 1, -1, 1, -2, 4, 2, 1, -1, -4, -1, -1, 2, -3, 0, 1, 2, 2, -1, -4, 3,
            1, 3,
        ];

        let mut deserialized = PolynomialRingElement::<SIMDUnit>::zero();
        deserialize::<SIMDUnit>(Eta::Four, &serialized, &mut deserialized);
        assert_eq!(deserialized.to_i32_array(), expected_coefficients);
    }

    #[test]
    fn test_deserialize_portable() {
        test_deserialize_generic::<simd::portable::PortableSIMDUnit>();
    }

    #[cfg(feature = "simd256")]
    #[test]
    fn test_deserialize_simd256() {
        test_deserialize_generic::<simd::avx2::AVX2SIMDUnit>();
    }
}