Skip to content

Seeding PRNGs (traits and bit size) #18

Closed
@dhardy

Description

@dhardy

Update again:

pub trait SeedRestriction: private::Sealed + Default + AsMut<[u8]> {}

pub trait SeedableRng: Sized {
    type Seed: SeedRestriction;

    fn from_seed(seed: Self::Seed) -> Self;

    fn from_rng<R: Rng>(mut rng: R) -> Result<Self, Error> {
        let mut seed = Self::Seed::default();
        let size = mem::size_of::<Self::Seed>() as usize;
        unsafe {
            let ptr = seed.as_mut().as_mut_ptr() as *mut u8;
            let slice = slice::from_raw_parts_mut(ptr, size);
            rng.try_fill_bytes(slice)?;
        }
        Ok(Self::from_seed(seed))
    }
}

So far we have proposed the following:

  • seeding via a u64 (convenient non-crypto usage)
  • seeding via the full state size (512-bits for ChaCha, 8192 for Isaac, 16384 for Isaac64)
  • seeding from another RNG

If we're seeding from another RNG, there's probably no reason not to fill the entire state space from this source; we are very likely seeding from a CSPRNG which can easily provide as many bytes as we need for little more cost than just copying a smaller set of bytes multiple times.

On the other hand if we are providing a seed, we might want to use the maximum key size possible. On the other hand, we may well only want to provide a sub-set, such as a 128-bit or 256-bit key (AES uses these key sizes). So should we add functions to seed from 128-bit and 256-bit keys instead of forcing users to copy their key into a larger zero-initialised array?

Actually, the current implementations allow seeding from u32 or u64 slices of undefined length. Keeping this functionality seems sensible.

But why not support seeding from byte slices instead? The only difference is that enforced alignment may allow more efficient copying from a slice of u32 or u64. Considering the user may already have a key in a byte-slice, this enforcement probably does more harm than good (forcing an extra copy or unsafe pointer-coercion).

This leaves me wondering if the current SeedableRng proposal is such a good idea; perhaps instead we should have the following:

pub trait SeedableRng: Rng {
    // when associated constants are allowed:
    const MAX_SEED_BYTES: usize;
    // alternative:
    fn max_seed_bytes() -> usize;
    
    // supports any number of bytes up to max_seed_bytes()
    fn seed_from_slice(&mut self, &[u8]);
    
    // not technically required, but convenient:
    fn seed_from_u64(&mut self, seed: u64);
}

(SeedFromRng may stay a separate trait providing the other type of seeding.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions