poulpy-core 0.8.3

A backend-agnostic crate implementing Module-LWE-based encryption and arithmetic
Documentation
use poulpy_hal::{
    api::{
        ScratchArenaTakeBasic, VecZnxBigAddSmallAssign, VecZnxBigBytesOf, VecZnxBigColWeightedSum, VecZnxBigNormalize,
        VecZnxBigNormalizeTmpBytes, VecZnxCopyRangeBackend, VecZnxZeroBackend,
    },
    layouts::{Backend, Module, ScratchArena, VecZnxBigToBackendRef, VecZnxToBackendRef},
};

use crate::layouts::{
    GLWEInfos, GLWEToBackendMut, LWEInfos, LWEMatrixInfos, LWEMatrixToBackendRef, LWESecretToBackendRef, Rank, SetBase2k,
};

pub fn lwe_matrix_decrypt_tmp_bytes_default<BE: Backend, A>(module: &Module<BE>, infos: &A) -> usize
where
    Module<BE>: VecZnxBigBytesOf + VecZnxBigNormalizeTmpBytes,
    A: LWEMatrixInfos,
{
    module.bytes_of_vec_znx_big_n(infos.rows(), 1, infos.size())
        + module.bytes_of_vec_znx_n(infos.rows(), 1, infos.size())
        + module.vec_znx_big_normalize_tmp_bytes()
        + 3 * (BE::SCRATCH_ALIGN - 1)
}

pub fn lwe_matrix_decrypt_default<BE, R, P, S>(
    module: &Module<BE>,
    res: &R,
    pt: &mut P,
    sk: &S,
    scratch: &mut ScratchArena<'_, BE>,
) where
    Module<BE>: VecZnxZeroBackend<BE>
        + VecZnxBigColWeightedSum<BE>
        + VecZnxBigAddSmallAssign<BE>
        + VecZnxBigNormalize<BE>
        + VecZnxCopyRangeBackend<BE>
        + VecZnxBigBytesOf
        + VecZnxBigNormalizeTmpBytes,
    R: LWEMatrixToBackendRef<BE> + LWEMatrixInfos,
    P: GLWEToBackendMut<BE> + SetBase2k + GLWEInfos,
    S: LWESecretToBackendRef<BE> + LWEInfos,
    BE: Backend,
{
    let res = res.to_backend_ref();
    let sk = sk.to_backend_ref();

    assert_eq!(res.n(), sk.n(), "lwe_matrix_decrypt: secret dimension mismatch");
    assert!(
        scratch.available() >= lwe_matrix_decrypt_tmp_bytes_default::<BE, _>(module, &res),
        "scratch.available(): {} < LWEMatrixDecrypt::lwe_matrix_decrypt_tmp_bytes: {}",
        scratch.available(),
        lwe_matrix_decrypt_tmp_bytes_default::<BE, _>(module, &res)
    );

    let pt_base2k = pt.base2k().into();
    let pt_k = pt.k().as_usize();
    let res_base2k = res.base2k().into();
    let mut pt = pt.to_backend_mut();
    assert_eq!(pt.rank(), Rank(0), "lwe_matrix_decrypt: plaintext must have rank 0");
    assert!(
        res.rows() <= pt.n().as_usize(),
        "lwe_matrix_decrypt: plaintext ring degree is too small for row count"
    );
    assert_eq!(
        pt.size(),
        res.size(),
        "lwe_matrix_decrypt currently expects matching limb counts"
    );

    module.vec_znx_zero_backend(&mut pt.data, 0);

    let scratch = scratch.borrow();
    let (mut tmp, scratch_1) = scratch.take_vec_znx_big_scratch_n(res.rows(), 1, res.size());
    let (mut rows_pt, mut scratch_2) = scratch_1.take_vec_znx_scratch(res.rows(), 1, pt.size());

    module.vec_znx_big_col_weighted_sum(&mut tmp, 0, &res.mask, &sk.data, 0, res.n().as_usize(), res.rows());
    module.vec_znx_big_add_small_assign(&mut tmp, 0, &res.body, 0);
    module.vec_znx_big_normalize(
        &mut rows_pt,
        pt_base2k,
        pt_k,
        0,
        0,
        &tmp.to_backend_ref(),
        res_base2k,
        0,
        &mut scratch_2,
    );

    let rows_pt_ref = rows_pt.to_backend_ref();
    for limb in 0..pt.size() {
        module.vec_znx_copy_range_backend(&mut pt.data, 0, limb, 0, &rows_pt_ref, 0, limb, 0, res.rows());
    }
}