arcium-primitives 0.8.1

Arcium primitives
Documentation
pub mod keys;
pub mod open_share;
pub mod share;

use std::any::{Any, TypeId};

pub use keys::*;
pub use open_share::*;
pub use share::*;

use crate::{
    algebra::field::{
        binary::{Gf2_128, Gf2_128Field},
        FieldElement,
        FieldExtension,
    },
    random::{BaseRng, Random, RandomWith, Seed, SeedableRng},
};

/// A trait for secret shares that hold keys for pairwise authentication.
pub trait PairwiseAuthenticated: RandomWith<Vec<Self::GlobalAuthKey>> {
    /// Global keys used to authenticate the shares of all other parties.
    type GlobalAuthKey: Random + Clone + Any;

    /// Get the global keys used to authenticate the shares of every other party.
    fn get_global_keys(&self) -> impl ExactSizeIterator<Item = Self::GlobalAuthKey>;

    /// Deal global keys used to authenticate the shares of all parties.
    fn deal_global_keys(
        n_parties: usize,
        seed: Seed,
        compatibility: bool,
    ) -> Vec<Vec<Self::GlobalAuthKey>> {
        let rng = if compatibility {
            BaseRng::from_seed(seed)
        } else {
            BaseRng::from_tagged_seed(seed, std::any::type_name::<Self::GlobalAuthKey>())
        };
        let mut alphas: Vec<Vec<Self::GlobalAuthKey>> =
            Vec::<Self::GlobalAuthKey>::random_n_with(rng, n_parties, n_parties - 1);
        if !compatibility {
            // Both binary backends get the DPF shape: the bit backend (Gf2_128 keys) and the
            // Gf2_128Field MPC-field backend, so dealer material stays interchangeable with real
            // preprocessing. Each call is a no-op unless the key type matches its field.
            force_binary_lsb_constraint::<Self::GlobalAuthKey, Gf2_128>(&mut alphas, n_parties);
            force_binary_lsb_constraint::<Self::GlobalAuthKey, Gf2_128Field>(
                &mut alphas,
                n_parties,
            );
        }
        alphas
    }
}

/// Forces the per-pair invariant DPF/SPFSS requires of binary global MAC keys, if the key type is
/// `GlobalFieldKey<F>` (no-op otherwise):
///
///   lsb(Δ_ij) = 0 if i < j, else 1
///
/// where Δ_ij is the key party i holds for party j. This implies the 2-party condition
/// lsb(Δ_01 ⊕ Δ_10) = 1 and generalizes it for n > 2.
///
/// Only valid for char-2 fields: the fix-up flips the low bit by adding one, which is an XOR.
fn force_binary_lsb_constraint<K: Any, F: FieldExtension>(alphas: &mut [Vec<K>], n_parties: usize) {
    if TypeId::of::<K>() != TypeId::of::<GlobalFieldKey<F>>() {
        return;
    }
    for (i, alphas_i) in alphas.iter_mut().enumerate() {
        for j in 0..n_parties {
            if i == j {
                continue;
            }
            let pos = if j < i { j } else { j - 1 };
            let target_lsb = u8::from(i > j);
            let key: &mut GlobalFieldKey<F> = (&mut alphas_i[pos] as &mut dyn Any)
                .downcast_mut()
                .expect("TypeId guarantees this cast");
            let current = key.inner();
            if current.to_le_bytes()[0] & 1 != target_lsb {
                *key = GlobalFieldKey::<F>::new(FieldElement::new(current + F::from(1u128)));
            }
        }
    }
}

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

    fn assert_lsb_constraint<F: FieldExtension>()
    where
        FieldShare<F>: PairwiseAuthenticated<GlobalAuthKey = GlobalFieldKey<F>>,
    {
        let n_parties = 4;
        let seed = Seed::default();
        let alphas = FieldShare::<F>::deal_global_keys(n_parties, seed, false);
        for (i, alphas_i) in alphas.iter().enumerate() {
            for j in 0..n_parties {
                if i == j {
                    continue;
                }
                let pos = if j < i { j } else { j - 1 };
                let lsb = alphas_i[pos].inner().to_le_bytes()[0] & 1;
                assert_eq!(lsb, u8::from(i > j), "lsb(Δ_{i}{j}) violates the DPF shape");
            }
        }
    }

    #[test]
    fn binary_global_keys_have_dpf_lsb_shape() {
        assert_lsb_constraint::<Gf2_128>();
        assert_lsb_constraint::<Gf2_128Field>();
    }
}