dalek-cryptography / bulletproofs

A pure-Rust implementation of Bulletproofs using Ristretto.
MIT License
1.02k stars 218 forks source link

`no_std` API #275

Open isislovecruft opened 5 years ago

isislovecruft commented 5 years ago

Hi! It was briefly discussed in the dalek-cryptography slack that this crate currently cannot be used in no_std environments due to its internal usage of rand::thread_rng(), which obviously requires thread-local storage and thus the std library. @oleganza proposed exposing an API such as verify_with_rng(), which then the current verify() would be a backwards-compatible wrapper for by passing in a rand::thread_rng(). verify_with_rng() should have a trait bound for Rng + CryptoRng (and possibly also SeedableRng? since the FromEntropy trait also requires std).

Questions:

  1. Would you like me to feature-gate the publicity of this? I.e.:

    #[cfg(not(feature = "std"))]
    pub fn verify_with_rng() { ... }
    
    #[cfg(feature = "std")]
    fn verify_with_rng() { ... }

    So that explicitly only no_std users are trusted with passing in their own correctly-seeded PRNG?

oleganza commented 5 years ago

Doesn't this equally apply to prove/prove_with_rng?

oleganza commented 5 years ago

I'd vote for not depriving std-users from having prove_with_rng. It's useful to compose with higher-level APIs that make the same distinction for the same reason and extra restrictions would force higher-level libs to make exactly the same dichotomy as lower-level lib.

As of footguns, i think it's enough that the safer-to-use API is (1) shorter-named (2) takes fewer arguments.

hdevalence commented 5 years ago

I would prefer not to have a verify_with_rng and instead find a way for verification be a stand-alone operation that depends only on the proof.

oleganza commented 5 years ago

Unfortunately, verification involves merging multiple statements in one with a random factor. Here are the available options to make thread_rng dependency optional or remove entirely:

  1. verify / verify_with_rng as mentioned above.
  2. verify uses deterministic challenges w/o any RNG at all (via Transcript instance). This is a bit weaker since attacker can learn those, but it seems to be strictly the same trick as deterministic merge of statements within the previous steps of the protocols, so if that's a weakness, the whole protocol is weak already.
  3. verify uses internally thread_rng if built with std and uses deterministic challenges if not. That's IMHO a poor dichotomy akin to "try /dev/random, and if it's blocking, try /dev/urandom instead". This makes other people think that one option is clearly worse, but somehow still acceptable.

I'd personally go with option 2 for verify and have dual prove/prove_with_rng APIs to work around "RNG against no_std" problem.