use std::marker::PhantomData; use std::ops::ControlFlow;
const BUFFER_SIZE_IN_ELEMENTS: usize = 32;
/// Errors propagated by methods in this module. #[derive(Debug, thiserror::Error)] #[non_exhaustive] pubenum PrngError { /// Failure when calling getrandom(). #[error("getrandom: {0}")]
GetRandom(#[from] getrandom::Error),
}
/// This type implements an iterator that generates a pseudorandom sequence of field elements. The /// sequence is derived from a XOF's key stream. #[derive(Debug)] pub(crate) struct Prng<F, S> {
phantom: PhantomData<F>,
seed_stream: S,
buffer: Vec<u8>,
buffer_index: usize,
}
#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] impl<F: FieldElement> Prng<F, SeedStreamAes128> { /// Create a [`Prng`] from a seed for Prio 2. The first 16 bytes of the seed and the last 16 /// bytes of the seed are used, respectively, for the key and initialization vector for AES128 /// in CTR mode. pub(crate) fn from_prio2_seed(seed: &[u8; 32]) -> Self { let seed_stream = SeedStreamAes128::new(&seed[..16], &seed[16..]); Self::from_seed_stream(seed_stream)
}
}
impl<F: FieldElement> Prng<F, SeedStreamTurboShake128> { /// Create a [`Prng`] from a randomly generated seed. pub(crate) fn new() -> Result<Self, PrngError> { let seed = Seed::generate()?;
Ok(Prng::from_seed_stream(XofTurboShake128::seed_stream(
&seed,
&[],
&[],
)))
}
}
pub(crate) fn get(&mutself) -> F { loop { // Seek to the next chunk of the buffer that encodes an element of F. for i in (self.buffer_index..self.buffer.len()).step_by(F::ENCODED_SIZE) { let j = i + F::ENCODED_SIZE;
if j > self.buffer.len() { break;
}
self.buffer_index = j;
match F::from_random_rejection(&self.buffer[i..j]) {
ControlFlow::Break(x) => return x,
ControlFlow::Continue(()) => continue, // reject this sample
}
}
// Refresh buffer with the next chunk of XOF output, filling the front of the buffer // with the leftovers. This ensures continuity of the seed stream after converting the // `Prng` to a new field type via `into_new_field()`. let left_over = self.buffer.len() - self.buffer_index; self.buffer.copy_within(self.buffer_index.., 0); self.seed_stream.fill_bytes(&mutself.buffer[left_over..]); self.buffer_index = 0;
}
}
/// Convert this object into a field element generator for a different field. #[cfg(all(feature = "crypto-dependencies", feature = "experimental"))] pub(crate) fn into_new_field<F1: FieldElement>(self) -> Prng<F1, S> {
Prng {
phantom: PhantomData,
seed_stream: self.seed_stream,
buffer: self.buffer,
buffer_index: self.buffer_index,
}
}
}
impl<F, S> Iterator for Prng<F, S> where
F: FieldElement,
S: RngCore,
{ type Item = F;
#[test] fn rejection_sampling_test_vector() { // These constants were found in a brute-force search, and they test that the XOF performs // rejection sampling correctly when the raw output exceeds the prime modulus. let seed = Seed::get_decoded(&[ 0xd5, 0x3f, 0xff, 0x5d, 0x88, 0x8c, 0x60, 0x4e, 0x9f, 0x24, 0x16, 0xe1, 0xa2, 0x0a, 0x62, 0x34,
])
.unwrap(); let expected = Field64::from(3401316594827516850);
let seed_stream = XofTurboShake128::seed_stream(&seed, b"", b""); letmut prng = Prng::<Field64, _>::from_seed_stream(seed_stream); let actual = prng.nth(662).unwrap();
assert_eq!(actual, expected);
#[cfg(all(feature = "crypto-dependencies", feature = "experimental"))]
{ letmut seed_stream = XofTurboShake128::seed_stream(&seed, b"", b""); letmut actual = <Field64 as FieldElement>::zero(); for _ in0..=662 {
actual = <Field64 ascrate::idpf::IdpfValue>::generate(&mut seed_stream, &());
}
assert_eq!(actual, expected);
}
}
// Test that the `Prng`'s internal buffer properly copies the end of the buffer to the front // once it reaches the end. #[test] fn left_over_buffer_back_fill() { let seed = Seed::generate().unwrap();
// Construct a `Prng` with a longer-than-usual buffer. letmut prng_weird_buffer_size: Prng<Field64, SeedStreamTurboShake128> =
Prng::from_seed_stream(XofTurboShake128::seed_stream(&seed, b"", b"")); letmut extra = [0; 7];
prng_weird_buffer_size.seed_stream.fill_bytes(&mut extra);
prng_weird_buffer_size.buffer.extend_from_slice(&extra);
// Check that the next several outputs match. We need to check enough outputs to ensure // that we have to refill the buffer. for _ in0..BUFFER_SIZE_IN_ELEMENTS * 2 {
assert_eq!(prng.next().unwrap(), prng_weird_buffer_size.next().unwrap());
}
}
#[cfg(feature = "experimental")] #[test] fn into_different_field() { let seed = Seed::generate().unwrap(); let want: Prng<Field64, SeedStreamTurboShake128> =
Prng::from_seed_stream(XofTurboShake128::seed_stream(&seed, b"", b"")); let want_buffer = want.buffer.clone();
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.