web_sys::Crypto how to use?

I have added web_sys and the Crypto feature. But I cannot call methods on crypto. I can't create an instance of crypto because it has private fields. I really can't figure out how I'm supposed to use this thing. What am I missing?

example errors:

let mut crypto = Crypto{}; //cannot construct `Crypto` with struct literal syntax due to private fields
let test = Vec::new();
let crypto_vals = Crypto::get_random_values_with_u8_array(&mut test);//expected value, found struct `Crypto`

What I'm trying to do is use webassembly and I need a secure method of getting random numbers. The best way to my knowledge is to use the native Crypto features in the browser. To my knowledge that is was web_sys is for - access to native features in webassembly. But I may be wrong, so I am very open to alternatives.

Apparently you have to have a window object first.
This works:

let window = web_sys::window().unwrap();
let crypto = window.crypto().unwrap();
crypto.get_random_values_with_u8_array(buffer)
1 Like

While that works, I'm not sure will work in my application. It provides an "Object" and I really can't figure out how to make that jive with Rust libraries since it essentially offers no guarantees and therefore no types to be compatible with the rust ecosystem. Maybe I'm wrong; that's just the way it seems on it's face.

The 'getrandom' crate apparently will work if the "js" feature is enabled; although, I have not tested it yet.

It modifies the array in-place. You can just use the passed byte array and ignore the return type (apart from checking whether it's Ok).

Awesome. Somehow I missed that. Unfortunately, apparently because of the library I'd like to use even this is insufficient. I was wanting to get ed25519_dalek to work in webassembly, but it requires traits I'm not sure I can implement myself. I was going to try to use getrandom as it indicated it could be used in the browser, but it doesn't implement the right traits either. Not sure if the rand trait would work in webassembly, but even it won't work as given in the example from the docs for ec25519_dalek.

I guess my whole idea was doomed from the start.

Surely it's not impossible to obtain random numbers in wasm. What traits are we talking about?

it starts with rand_core::CryptoRng

I don't think either that or its supertrait (RngCore) is sealed. You can absolutely implement it for your own types yourself.

Obviously this isn't functional code, I'm just trying to satisfy the compiler at this point. But it still gives me the error :

the trait bound `st: rand_core::CryptoRng` is not satisfied 
the following other types implement trait `rand_core::CryptoRng`:
&'a mut R
Box<R>
rand::rngs::adapter::reseeding::ReseedingRng<R, Rsdr>
rand::rngs::entropy::EntropyRng\n  rand::rngs::std::StdRng
rand::rngs::thread::ThreadRng
rand_chacha::chacha::ChaCha12Core
rand_chacha::chacha::ChaCha12Rng
and 6 others
use ed25519_dalek::{Keypair, Signature};
use rand::prelude::*;
use rand_core::{CryptoRngCore, OsRng};
use web_sys::{self, Crypto, Window};

fn main() {
    let test = st { value: [0u8; 32] };
    //let mut csprng = Crypto::get_random_values_with_u8_array(&mut test);
    let keypair: Keypair = Keypair::generate(&mut test);
}
struct st {
    value: [u8; 32],
}
impl rand_core::CryptoRng for st {}
impl RngCore for st {
    fn next_u32(&mut self) -> u32 {
        0u32
    }
    fn next_u64(&mut self) -> u64 {
        0u64
    }
    fn fill_bytes(&mut self, dest: &mut [u8]) {}
    fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
        Ok(())
    }
}

ed25519_dalek seems to depend on rand 0.7 and rand_core 0.5. These are not the latest versions (0.6 and 0.8), which are not semver compatible, therefore they count as different crates. Are you sure you are correctly depending on these older versions of rand and rand_core?

Probably not since I added the crates through cargo add. Yeah the whole version of imported crate thing trips me up A LOT. Rust REALLY needs to work on that flow for sure (other languages too I'm sure).

I'll try to adjust that and see what happens

If you explicitly, very obviously did implement a trait for a type, then there's no real reason other than version resolution that would result in a seemingly inexplicable lack of meeting trait bounds. You should generally think of this cause as the first thing to check if you come across this situation.

1 Like

Gotcha. Well, that did work and also allowed me to skip that whole process and just use the rand::OsRng like in the example.

Curious. Is there a way to 'containerize' imported crates to allow multiple versions to be used? (Not containerize as in the Operating System sense, but more in the namespacing sense)

I'm not sure what you are getting at. Multiple versions of the same crate can already be used as long as they are not semver-compatible; Cargo automatically and correctly performs version resolution accordingly. This was in fact the very cause your problem.

I cannot put rand_core = "0.5" and rand_core = "0.6" in my cargo.toml

But if I needed to say use a trait from 0.5, but use functions from 0.6 that are not available in 0.5 or some other reason to use both. How would I do that?

Does this help?

1 Like

That's it exactly. Thanks!

I'm not adding much to the overall conversation and questions, but I wanted to take a moment to address this particular observation. It seems like good feedback to the Cargo team (but I'm not sure it's actionable by itself). I agree with the general sentiment that the workflow could probably be improved, though.

I personally never use cargo add, and this is one of the reasons. (I am fairly strict on how I manage dependencies, including transitively.) I mostly use cargo tree and cargo-outdated for simple analysis. Then manually edit dependencies/versions/features in my Cargo.toml. This gives me a sense of what I actually need, and not just "cargo add give me whatever you can find".

I don't expect everyone to manage dependencies "the hard way", but it definitely has its advantages. Perhaps one day the automation tools will improve to the point where it will "do what I mean" and "Just Work".

2 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.