/// VIDPF-related errors. #[derive(Debug, thiserror::Error)] #[non_exhaustive] pubenum VidpfError { /// Error when key's identifier are equal. #[error("key's identifier should be different")]
SameKeyId,
/// Error when level does not fit in a 32-bit number. #[error("level is not representable as a 32-bit integer")]
LevelTooBig,
/// Error during VIDPF evaluation: tried to access a level index out of bounds. #[error("level index out of bounds")]
IndexLevel,
/// Error when weight's length mismatches the length in weight's parameter. #[error("invalid weight length")]
InvalidWeightLength,
/// Failure when calling getrandom(). #[error("getrandom: {0}")]
GetRandom(#[from] getrandom::Error),
}
/// Represents the domain of an incremental point function. pubtype VidpfInput = IdpfInput;
/// Represents the codomain of an incremental point function. pubtrait VidpfValue: IdpfValue + Clone {}
/// A VIDPF instance. pubstruct Vidpf<W: VidpfValue, const NONCE_SIZE: usize> { /// Any parameters required to instantiate a weight value.
weight_parameter: W::ValueParameter,
}
impl<W: VidpfValue, const NONCE_SIZE: usize> Vidpf<W, NONCE_SIZE> { /// Creates a VIDPF instance. /// /// # Arguments /// /// * `weight_parameter`, any parameters required to instantiate a weight value. pubconstfn new(weight_parameter: W::ValueParameter) -> Self { Self { weight_parameter }
}
/// The [`Vidpf::gen`] method splits an incremental point function `F` into two private keys /// used by the aggregation servers, and a common public share. /// /// The incremental point function is defined as `F`: [`VidpfInput`] --> [`VidpfValue`] /// such that: /// /// ```txt /// F(x) = weight, if x is a prefix of the input. /// F(x) = 0, if x is not a prefix of the input. /// ``` /// /// # Arguments /// /// * `input`, determines the input of the function. /// * `weight`, determines the input's weight of the function. /// * `nonce`, used to cryptographically bind some information. pubfngen(
&self,
input: &VidpfInput,
weight: &W,
nonce: &[u8; NONCE_SIZE],
) -> Result<(VidpfPublicShare<W>, [VidpfKey; 2]), VidpfError> { let keys = [
VidpfKey::gen(VidpfServerId::S0)?,
VidpfKey::gen(VidpfServerId::S1)?,
]; let public = self.gen_with_keys(&keys, input, weight, nonce)?;
Ok((public, keys))
}
/// [`Vidpf::gen_with_keys`] works as the [`Vidpf::gen`] method, except that two different /// keys must be provided. fn gen_with_keys(
&self,
keys: &[VidpfKey; 2],
input: &VidpfInput,
weight: &W,
nonce: &[u8; NONCE_SIZE],
) -> Result<VidpfPublicShare<W>, VidpfError> { if keys[0].id == keys[1].id { return Err(VidpfError::SameKeyId);
}
/// [`Vidpf::eval_next`] evaluates the `input` at the given level using the provided initial /// state, and returns a new state and a share of the input's weight at that level. fn eval_next(
&self,
id: VidpfServerId,
public: &VidpfPublicShare<W>,
input: &VidpfInput,
level: usize,
state: &VidpfEvalState,
nonce: &[u8; NONCE_SIZE],
) -> Result<(VidpfEvalState, W), VidpfError> { let cw = public.cw.get(level).ok_or(VidpfError::IndexLevel)?;
let seq_tilde = Self::prg(&state.seed, nonce);
let t_i = state.control_bit; let sl = conditional_xor_seeds(&seq_tilde.left_seed, &cw.seed, t_i); let sr = conditional_xor_seeds(&seq_tilde.right_seed, &cw.seed, t_i); let tl = seq_tilde.left_control_bit ^ (t_i & cw.left_control_bit); let tr = seq_tilde.right_control_bit ^ (t_i & cw.right_control_bit);
let x_i = Choice::from(u8::from(input.get(level).ok_or(VidpfError::IndexLevel)?)); let s_tilde_i = conditional_select_seed(x_i, &[sl, sr]);
let next_control_bit = Choice::conditional_select(&tl, &tr, x_i); let (next_seed, w_i) = self.convert(s_tilde_i, nonce);
let zero = <W as IdpfValue>::zero(&self.weight_parameter); letmut y = <W as IdpfValue>::conditional_select(&zero, &cw.weight, next_control_bit);
y += w_i;
y.conditional_negate(Choice::from(id));
let pi_i = &state.proof; let cs_i = public.cs.get(level).ok_or(VidpfError::IndexLevel)?; let pi_tilde = Self::node_proof(input, level, &next_seed)?; let h2_input = xor_proof(
conditional_xor_proof(pi_tilde, cs_i, next_control_bit),
pi_i,
); let next_proof = xor_proof(Self::node_proof_adjustment(h2_input), pi_i);
letmut left_seed = VidpfSeed::default(); letmut right_seed = VidpfSeed::default();
rng.fill_bytes(&mut left_seed);
rng.fill_bytes(&mut right_seed); // Use the LSB of seeds as control bits, and clears the bit, // i.e., seeds produced by `prg` always have their LSB = 0. // This ensures `prg` costs two AES calls only. let left_control_bit = Choice::from(left_seed[0] & 0x01); let right_control_bit = Choice::from(right_seed[0] & 0x01);
left_seed[0] &= 0xFE;
right_seed[0] &= 0xFE;
/// Private key of an aggregation server. pubstruct VidpfKey {
id: VidpfServerId,
value: [u8; 16],
}
impl VidpfKey { /// Generates a key at random. /// /// # Errors /// Triggers an error if the random generator fails. pub(crate) fngen(id: VidpfServerId) -> Result<Self, VidpfError> { letmut value = [0; 16];
getrandom::getrandom(&mut value)?;
Ok(Self { id, value })
}
}
/// Identifies the two aggregation servers. #[derive(Clone, Copy, PartialEq, Eq)] pub(crate) enum VidpfServerId { /// S0 is the first server.
S0, /// S1 is the second server.
S1,
}
impl From<VidpfServerId> for Choice { fn from(value: VidpfServerId) -> Self { match value {
VidpfServerId::S0 => Self::from(0),
VidpfServerId::S1 => Self::from(1),
}
}
}
/// Adjusts values of shares during the VIDPF evaluation. #[derive(Debug)] struct VidpfCorrectionWord<W: VidpfValue> {
seed: VidpfSeed,
left_control_bit: Choice,
right_control_bit: Choice,
weight: W,
}
/// Common public information used by aggregation servers. #[derive(Debug)] pubstruct VidpfPublicShare<W: VidpfValue> {
cw: Vec<VidpfCorrectionWord<W>>,
cs: Vec<VidpfProof>,
}
/// Contains the values produced during input evaluation at a given level. pubstruct VidpfEvalState {
seed: VidpfSeed,
control_bit: Choice,
proof: VidpfProof,
}
/// Contains a share of the input's weight together with a proof for verification. pubstruct VidpfValueShare<W: VidpfValue> { /// Secret share of the input's weight. pub share: W, /// Proof used to verify the share. pub proof: VidpfProof,
}
/// Proof size in bytes. const VIDPF_PROOF_SIZE: usize = 32;
/// Allows to validate user input and shares after evaluation. type VidpfProof = [u8; VIDPF_PROOF_SIZE];
/// Feeds a pseudorandom generator during evaluation. type VidpfSeed = [u8; 16];
/// Contains the seeds and control bits produced by [`Vidpf::prg`]. struct VidpfPrgOutput {
left_seed: VidpfSeed,
left_control_bit: Choice,
right_seed: VidpfSeed,
right_control_bit: Choice,
}
/// Represents an array of field elements that implements the [`VidpfValue`] trait. #[derive(Debug, PartialEq, Eq, Clone)] pubstruct VidpfWeight<F: FieldElement>(Vec<F>);
impl<F: FieldElement> VidpfValue for VidpfWeight<F> {}
impl<F: FieldElement> IdpfValue for VidpfWeight<F> { /// The parameter determines the number of field elements in the vector. type ValueParameter = usize;
#[test] fn correctness_at_last_level() { let input = VidpfInput::from_bytes(&[0xFF]); let weight = TestWeight::from(vec![21.into(), 22.into(), 23.into()]); let (vidpf, public, [key_0, key_1], nonce) = vidpf_gen_setup(&input, &weight);
let value_share_0 = vidpf.eval(&key_0, &public, &input, &nonce).unwrap(); let value_share_1 = vidpf.eval(&key_1, &public, &input, &nonce).unwrap();
assert_eq!(
value_share_0.share + value_share_1.share,
weight, "shares must add up to the expected weight",
);
assert_eq!(
value_share_0.proof, value_share_1.proof, "proofs must be equal"
);
let bad_input = VidpfInput::from_bytes(&[0x00]); let zero = TestWeight::zero(&TEST_WEIGHT_LEN); let value_share_0 = vidpf.eval(&key_0, &public, &bad_input, &nonce).unwrap(); let value_share_1 = vidpf.eval(&key_1, &public, &bad_input, &nonce).unwrap();
assert_eq!(
value_share_0.share + value_share_1.share,
zero, "shares must add up to zero",
);
assert_eq!(
value_share_0.proof, value_share_1.proof, "proofs must be equal"
);
}
#[test] fn correctness_at_each_level() { let input = VidpfInput::from_bytes(&[0xFF]); let weight = TestWeight::from(vec![21.into(), 22.into(), 23.into()]); let (vidpf, public, keys, nonce) = vidpf_gen_setup(&input, &weight);
assert_eq!(weight.encoded_len().unwrap(), expected_bytes.len()); // Check endianness of encoding
assert_eq!(bytes, expected_bytes);
let decoded =
TestWeight::decode_with_param(&TEST_WEIGHT_LEN, &mut Cursor::new(&bytes)).unwrap();
assert_eq!(weight, decoded);
}
#[test] fn add_sub() { let [a, b] = compatible_weights(); letmut c = a.clone();
c += a.clone();
assert_eq!(
(a.clone() + b.clone()) + (a.clone() - b.clone()),
c, "a: {:?} b:{:?}",
a,
b
);
}
#[test] fn conditional_negate() { let [a, _] = compatible_weights(); letmut c = a.clone();
c.conditional_negate(Choice::from(0)); letmut d = a.clone();
d.conditional_negate(Choice::from(1)); let zero = TestWeight::zero(&TEST_WEIGHT_LEN);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.