/// Error encoding or decoding a prepare share #[error("encode/decode prep share {0}")]
CodecPrepShare(CodecError),
/// Error encoding or decoding a prepare message #[error("encode/decode prep message {0}")]
CodecPrepMessage(CodecError),
/// Host is in an unexpected state #[error("host state mismatch: in {found} expected {expected}")]
HostStateMismatch { /// The state the host is in.
found: &'static str, /// The state the host expected to be in.
expected: &'static str,
},
/// Message from peer indicates it is in an unexpected state #[error("peer message mismatch: message is {found} expected {expected}")]
PeerMessageMismatch { /// The state in the message from the peer.
found: &'static str, /// The message expected from the peer.
expected: &'static str,
},
/// Corresponds to `struct Message` in [VDAF's Ping-Pong Topology][VDAF]. All of the fields of the /// variants are opaque byte buffers. This is because the ping-pong routines take responsibility for /// decoding preparation shares and messages, which usually requires having the preparation state. /// /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.8 #[derive(Clone, PartialEq, Eq)] pubenum PingPongMessage { /// Corresponds to MessageType.initialize.
Initialize { /// The leader's initial preparation share.
prep_share: Vec<u8>,
}, /// Corresponds to MessageType.continue. Continue { /// The current round's preparation message.
prep_msg: Vec<u8>, /// The next round's preparation share.
prep_share: Vec<u8>,
}, /// Corresponds to MessageType.finish.
Finish { /// The current round's preparation message.
prep_msg: Vec<u8>,
},
}
impl Debug for PingPongMessage { // We want `PingPongMessage` to implement `Debug`, but we don't want that impl to print out // prepare shares or messages, because (1) their contents are sensitive and (2) their contents // are long and not intelligible to humans. For both reasons they generally shouldn't get // logged. Normally, we'd use the `derivative` crate to customize a derived `Debug`, but that // crate has not been audited (in the `cargo vet` sense) so we can't use it here unless we audit // 8,000+ lines of proc macros. fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple(self.variant()).finish()
}
}
/// A transition in the pong-pong topology. This represents the `ping_pong_transition` function /// defined in [VDAF]. /// /// # Discussion /// /// The obvious implementation of `ping_pong_transition` would be a method on trait /// [`PingPongTopology`] that returns `(State, Message)`, and then `ContinuedValue::WithMessage` /// would contain those values. But then DAP implementations would have to store relatively large /// VDAF prepare shares between rounds of input preparation. /// /// Instead, this structure stores just the previous round's prepare state and the current round's /// preprocessed prepare message. Their encoding is much smaller than the `(State, Message)` tuple, /// which can always be recomputed with [`Self::evaluate`]. /// /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.8 #[derive(Clone, Debug, Eq)] pubstruct PingPongTransition< const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize,
A: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>,
> {
previous_prepare_state: A::PrepareState,
current_prepare_message: A::PrepareMessage,
}
impl< const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize,
A: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>,
> PingPongTransition<VERIFY_KEY_SIZE, NONCE_SIZE, A>
{ /// Evaluate this transition to obtain a new [`PingPongState`] and a [`PingPongMessage`] which /// should be transmitted to the peer. #[allow(clippy::type_complexity)] pubfn evaluate(
&self,
vdaf: &A,
) -> Result<
(
PingPongState<VERIFY_KEY_SIZE, NONCE_SIZE, A>,
PingPongMessage,
),
PingPongError,
> { let prep_msg = self
.current_prepare_message
.get_encoded()
.map_err(PingPongError::CodecPrepMessage)?;
/// Corresponds to the `State` enumeration implicitly defined in [VDAF's Ping-Pong Topology][VDAF]. /// VDAF describes `Start` and `Rejected` states, but the `Start` state is never instantiated in /// code, and the `Rejected` state is represented as `std::result::Result::Err`, so this enum does /// not include those variants. /// /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.8 #[derive(Clone, Debug, PartialEq, Eq)] pubenum PingPongState< const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize,
A: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>,
> { /// Preparation of the report will continue with the enclosed state.
Continued(A::PrepareState), /// Preparation of the report is finished and has yielded the enclosed output share.
Finished(A::OutputShare),
}
/// Values returned by [`PingPongTopology::leader_continued`] or /// [`PingPongTopology::helper_continued`]. #[derive(Clone, Debug)] pubenum PingPongContinuedValue< const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize,
A: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>,
> { /// The operation resulted in a new state and a message to transmit to the peer.
WithMessage { /// The transition that will be executed. Call `PingPongTransition::evaluate` to obtain the /// next /// [`PingPongState`] and a [`PingPongMessage`] to transmit to the peer.
transition: PingPongTransition<VERIFY_KEY_SIZE, NONCE_SIZE, A>,
}, /// The operation caused the host to finish preparation of the input share, yielding an output /// share and no message for the peer.
FinishedNoMessage { /// The output share which may now be accumulated.
output_share: A::OutputShare,
},
}
/// Extension trait on [`crate::vdaf::Aggregator`] which adds the [VDAF Ping-Pong Topology][VDAF]. /// /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.8 pubtrait PingPongTopology<const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize>:
Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>
{ /// Specialization of [`PingPongState`] for this VDAF. type State; /// Specialization of [`PingPongContinuedValue`] for this VDAF. type ContinuedValue; /// Specializaton of [`PingPongTransition`] for this VDAF. type Transition;
/// Initialize leader state using the leader's input share. Corresponds to /// `ping_pong_leader_init` in [VDAF]. /// /// If successful, the returned [`PingPongMessage`] (which will always be /// `PingPongMessage::Initialize`) should be transmitted to the helper. The returned /// [`PingPongState`] (which will always be `PingPongState::Continued`) should be used by the /// leader along with the next [`PingPongMessage`] received from the helper as input to /// [`Self::leader_continued`] to advance to the next round. /// /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.8 fn leader_initialized(
&self,
verify_key: &[u8; VERIFY_KEY_SIZE],
agg_param: &Self::AggregationParam,
nonce: &[u8; NONCE_SIZE],
public_share: &Self::PublicShare,
input_share: &Self::InputShare,
) -> Result<(Self::State, PingPongMessage), PingPongError>;
/// Initialize helper state using the helper's input share and the leader's first prepare share. /// Corresponds to `ping_pong_helper_init` in [VDAF]. /// /// If successful, the returned [`PingPongTransition`] should be evaluated, yielding a /// [`PingPongMessage`], which should be transmitted to the leader, and a [`PingPongState`]. /// /// If the state is `PingPongState::Continued`, then it should be used by the helper along with /// the next `PingPongMessage` received from the leader as input to [`Self::helper_continued`] /// to advance to the next round. The helper may store the `PingPongTransition` between rounds /// of preparation instead of the `PingPongState` and `PingPongMessage`. /// /// If the state is `PingPongState::Finished`, then preparation is finished and the output share /// may be accumulated. /// /// # Errors /// /// `inbound` must be `PingPongMessage::Initialize` or the function will fail. /// /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.8 fn helper_initialized(
&self,
verify_key: &[u8; VERIFY_KEY_SIZE],
agg_param: &Self::AggregationParam,
nonce: &[u8; NONCE_SIZE],
public_share: &Self::PublicShare,
input_share: &Self::InputShare,
inbound: &PingPongMessage,
) -> Result<PingPongTransition<VERIFY_KEY_SIZE, NONCE_SIZE, Self>, PingPongError>;
/// Continue preparation based on the leader's current state and an incoming [`PingPongMessage`] /// from the helper. Corresponds to `ping_pong_leader_continued` in [VDAF]. /// /// If successful, the returned [`PingPongContinuedValue`] will either be: /// /// - `PingPongContinuedValue::WithMessage { transition }`: `transition` should be evaluated, /// yielding a [`PingPongMessage`], which should be transmitted to the helper, and a /// [`PingPongState`]. /// /// If the state is `PingPongState::Continued`, then it should be used by the leader along /// with the next `PingPongMessage` received from the helper as input to /// [`Self::leader_continued`] to advance to the next round. The leader may store the /// `PingPongTransition` between rounds of preparation instead of of the `PingPongState` and /// `PingPongMessage`. /// /// If the state is `PingPongState::Finished`, then preparation is finished and the output /// share may be accumulated. /// /// - `PingPongContinuedValue::FinishedNoMessage`: preparation is finished and the output share /// may be accumulated. No message needs to be sent to the helper. /// /// # Errors /// /// `leader_state` must be `PingPongState::Continued` or the function will fail. /// /// `inbound` must not be `PingPongMessage::Initialize` or the function will fail. /// /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.8 fn leader_continued(
&self,
leader_state: Self::State,
agg_param: &Self::AggregationParam,
inbound: &PingPongMessage,
) -> Result<Self::ContinuedValue, PingPongError>;
/// PingPongContinue preparation based on the helper's current state and an incoming /// [`PingPongMessage`] from the leader. Corresponds to `ping_pong_helper_contnued` in [VDAF]. /// /// If successful, the returned [`PingPongContinuedValue`] will either be: /// /// - `PingPongContinuedValue::WithMessage { transition }`: `transition` should be evaluated, /// yielding a [`PingPongMessage`], which should be transmitted to the leader, and a /// [`PingPongState`]. /// /// If the state is `PingPongState::Continued`, then it should be used by the helper along /// with the next `PingPongMessage` received from the leader as input to /// [`Self::helper_continued`] to advance to the next round. The helper may store the /// `PingPongTransition` between rounds of preparation instead of the `PingPongState` and /// `PingPongMessage`. /// /// If the state is `PingPongState::Finished`, then preparation is finished and the output /// share may be accumulated. /// /// - `PingPongContinuedValue::FinishedNoMessage`: preparation is finished and the output share /// may be accumulated. No message needs to be sent to the leader. /// /// # Errors /// /// `helper_state` must be `PingPongState::Continued` or the function will fail. /// /// `inbound` must not be `PingPongMessage::Initialize` or the function will fail. /// /// [VDAF]: https://datatracker.ietf.org/doc/html/draft-irtf-cfrg-vdaf-08#section-5.8 fn helper_continued(
&self,
helper_state: Self::State,
agg_param: &Self::AggregationParam,
inbound: &PingPongMessage,
) -> Result<Self::ContinuedValue, PingPongError>;
}
impl<const VERIFY_KEY_SIZE: usize, const NONCE_SIZE: usize, A>
PingPongTopology<VERIFY_KEY_SIZE, NONCE_SIZE> for A where
A: Aggregator<VERIFY_KEY_SIZE, NONCE_SIZE>,
{ type State = PingPongState<VERIFY_KEY_SIZE, NONCE_SIZE, Self>; type ContinuedValue = PingPongContinuedValue<VERIFY_KEY_SIZE, NONCE_SIZE, Self>; type Transition = PingPongTransition<VERIFY_KEY_SIZE, NONCE_SIZE, Self>;
for (message, expected_hex) in messages { letmut encoded_val = Vec::new();
message.encode(&mut encoded_val).unwrap(); let got_hex = hex::encode(&encoded_val);
assert_eq!(
&got_hex, expected_hex, "Couldn't roundtrip (encoded value differs): {message:?}",
); let decoded_val = PingPongMessage::decode(&mut Cursor::new(&encoded_val)).unwrap();
assert_eq!(
decoded_val, message, "Couldn't roundtrip (decoded value differs): {message:?}"
);
assert_eq!(
encoded_val.len(),
message.encoded_len().expect("No encoded length hint"), "Encoded length hint is incorrect: {message:?}"
)
}
}
#[test] fn roundtrip_transition() { // VDAF implementations have tests for encoding/decoding their respective PrepareShare and // PrepareMessage types, so we test here using the dummy VDAF. let transition = PingPongTransition::<0, 16, dummy::Vdaf> {
previous_prepare_state: dummy::PrepareState::default(),
current_prepare_message: (),
};
let encoded = transition.get_encoded().unwrap(); let hex_encoded = hex::encode(&encoded);
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.