Skip to content

Conversation

@fjarri
Copy link
Contributor

@fjarri fjarri commented Nov 8, 2023

The current version of Uint::random_mod() produces different results on 32- and 64-bit targets because it exhausts the byte stream differently if the number of bytes in modulus is not a multiple of 8 (e.g. if it is 20 bytes, the 32-bit version will read 20 bytes - 5 limbs, but the 64-bit version will read 24 bytes - 3 limbs). This PR makes the behavior identical, so that an RNG with the same seed will produce the same stream of Uints (as long as the RNG itself produces the bytestream consistently, of course).

I am not sure if that is actually a desired behavior, but I did bump into it when I generated a ZK proof challenge on a 64-bit and 32-bit clients, and expected them to be the same. If it is not a guarantee we want to provide, perhaps an explicit note in the docs will be helpful.

The code in this PR would be much simpler if I could use Uint::from_le_bytes(), but then I would have to add an Encoding bound.

@fjarri
Copy link
Contributor Author

fjarri commented Nov 8, 2023

Not sure why the powerpc job failed, it has been queued for 20 minutes previously waiting for the job runner. I don't think I have the necessary permission to restart it.

@tarcieri
Copy link
Member

tarcieri commented Nov 8, 2023

Looks like a random GitHub Actions runner failure. I restarted it.

@tarcieri tarcieri merged commit ef32e28 into RustCrypto:master Nov 9, 2023
@tarcieri tarcieri mentioned this pull request Nov 13, 2023
@fjarri fjarri deleted the consistent-rng branch November 29, 2023 04:46
mrdomino added a commit to mrdomino/crypto-bigint that referenced this pull request Dec 2, 2025
## Breaking changes

- `Encoding::Repr` can no longer be assumed to implement `Copy`, so
  consumers of `Encoding::Repr` will need to explicitly call `clone`.

- The numbers produced by both `random_bits` and `random_mod` will
  generally be different, and calling these functions will leave the RNG
  in a different state, than before.

## Summary

This essentially applies RustCrypto#285 to `random_bits`
as well as `random_mod`. Both functions behave the same way now, with
the only difference being that `random_mod` adds rejection sampling;
otherwise both will produce the same numbers over the same entropy
stream.

Questions of platform dependence are now easy; we do not define these
algorithms in terms of machine words but in terms of bytes. Randomly
sampled `Uint`s are constructed little-endian over the entropy stream.

This leverages the existing work making `Uint` implement `Encoding`. It
additionally needs `Encoding` on `BoxedUint` to make `RandomMod` and
`RandomBits` work there; this is implemented, but requires dropping the
`Copy` constraint on `Encoding::Repr`.

This change also uses a variable- rather than constant-time comparison
for the rejection loop. The only real reason to do this is the linked
[compiler bug][0], but the only downside is that this makes `random_mod`
leak somewhat more timing data than it otherwise would. However,
`random_mod` inherently must leak some timing data (in the form of
rejections), and so should arguably just be named `random_mod_vartime`
in the first place.

[0]: rust-lang/rust#149522
mrdomino added a commit to mrdomino/crypto-bigint that referenced this pull request Dec 10, 2025
## Breaking changes

- `Encoding::Repr` can no longer be assumed to implement `Copy`, so
  consumers of `Encoding::Repr` will need to explicitly call `clone`.

- The numbers produced by both `random_bits` and `random_mod` will
  generally be different, and calling these functions will leave the RNG
  in a different state, than before.

## Summary

This essentially applies RustCrypto#285 to `random_bits`
as well as `random_mod`. Both functions behave the same way now, with
the only difference being that `random_mod` adds rejection sampling;
otherwise both will produce the same numbers over the same entropy
stream.

Questions of platform dependence are now easy; we do not define these
algorithms in terms of machine words but in terms of bytes. Randomly
sampled `Uint`s are constructed little-endian over the entropy stream.

This leverages the existing work making `Uint` implement `Encoding`. It
additionally needs `Encoding` on `BoxedUint` to make `RandomMod` and
`RandomBits` work there; this is implemented, but requires dropping the
`Copy` constraint on `Encoding::Repr`.

In order to use `random_bits_core` in `random_mod_core`, we need to make
it produce `R::Error`; then we call `map_err` in `RandomBits`, rather
than in `random_bits_core`, to get a `RandomBitsError`.

[0]: rust-lang/rust#149522
tarcieri pushed a commit that referenced this pull request Dec 10, 2025
## Breaking changes

- `Encoding::Repr` is no longer required to implement `Copy`, so
consumers of `Encoding::Repr` will need to explicitly call `clone`.

- The numbers produced by both `random_bits` and `random_mod` will
generally be different, and calling these functions will leave the RNG
in a different state, than before.

## Fixes

- Adds a mitigation for rust-lang/rust#149522, modifying
`Word::borrowing_sub` to use `overflowing_sub` instead of `WideWord`
casts.

## Summary

This essentially applies
#285 to `random_bits` as
well as `random_mod`. Both functions behave the same way now, with the
only difference being that `random_mod` adds rejection sampling;
otherwise both will produce the same numbers over the same entropy
stream.

Questions of platform dependence are now easy; we do not define these
algorithms in terms of sequences of machine words but of bytes. Randomly
sampled `Uint`s are now always constructed little-endian over the
entropy stream. This does not preclude future machine-specific
optimizations, but given how perilous the landscape has been (e.g.
#1018), I’ve elected to err in the direction of parsimony rather than
performance for this change.

This leverages the existing work making `Uint` implement `Encoding`. It
additionally needs `Encoding` on `BoxedUint` to make `RandomMod` and
`RandomBits` work there; this is implemented, but requires dropping the
`Copy` constraint on `Encoding::Repr`.

Fixes #1009

[0]: rust-lang/rust#149522
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants