'Is there a way to seed a cryptographically secure RNG engine in rust using a [u8; 64] array?
ORIGINAL QUESTION:
I am currently trying to write a library in rust - to be compiled to WASM - for converting a bip39 mnemonic passphrase into an Arweave JWK. I am currently using tiny-bip39 and RSA.
When generating a private key using RSA as per the example given on RSA I want to seed the rng based on the mnemonic passphrase I have passed into the function. I tried achieving this by simply getting the seed from the mnemonic object generated by tiny-bip39, however this seems to generate a &[u8]
with a length of 64
. However, Seed is defined as [u8; 32]
, and without having to write my own rng, I cannot figure out how to use a len 64
seed.
#[wasm_bindgen]
pub fn get_key_from_mnemonic(phrase: &str) {
let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
assert_eq!(phrase, mnemonic.phrase());
let seed = Seed::new(&mnemonic, "");
let seed_bytes = seed.as_bytes();
let mut rng = ChaCha12Rng::from_seed(seed_bytes);
[...]
}
Is there a cryptographically secure rng that allows for len 64
seed?
I tried simply trying into, but that did not seem to work, which makes sense.
let seed_bytes: <ChaCha12Rng as SeedableRng>::Seed = seed.as_bytes().try_into().unwrap();
EDIT:
I came up with a solution that seem to work in every way except the random number generation.
let mnemonic = Mnemonic::from_phrase(phrase, Language::English).unwrap();
assert_eq!(phrase, mnemonic.phrase());
let seed = Seed::new(&mnemonic, "");
let seed_bytes = seed.as_bytes();
let mut seed_buf: [u8; 32] = Default::default();
let mut hmac_drgb = HmacDRBG::<Sha256>::new(&seed_bytes, &[], &[]);
hmac_drgb.generate_to_slice(&mut seed_buf, None);
let mut chacha = ChaCha20Rng::from_seed(seed_buf);
let modulus_length = 4098;
let rsa_private_key = RsaPrivateKey::new(&mut chacha, modulus_length).unwrap();
let der = rsa_private_key.to_pkcs1_der().unwrap();
let jwk = JWK {
modulus: der.private_key().modulus.as_bytes().to_vec(),
public_exponent: der.private_key().public_exponent.as_bytes().to_vec(),
private_exponent: der.private_key().private_exponent.as_bytes().to_vec(),
prime1: der.private_key().prime1.as_bytes().to_vec(),
prime2: der.private_key().prime2.as_bytes().to_vec(),
exponent1: der.private_key().exponent1.as_bytes().to_vec(),
exponent2: der.private_key().exponent2.as_bytes().to_vec(),
coefficient: der.private_key().coefficient.as_bytes().to_vec(),
};
As I am trying to rewrite some of the functionality provided by arweave-mnemonic-keys, I have tried to go through all of the dependencies, figuring out which rust modules I need, and think I have managed to figure out everything except how to generate the random numbers for the RSA algorithm.
I have tried looking through the node-forge/lib/rsa.js file, and found this snippet:
function generateRandom(bits, rng) {
var num = new BigInteger(bits, rng);
// force MSB set
var bits1 = bits - 1;
if(!num.testBit(bits1)) {
num.bitwiseTo(BigInteger.ONE.shiftLeft(bits1), op_or, num);
}
// align number on 30k+1 boundary
num.dAddOffset(31 - num.mod(THIRTY).byteValue(), 0);
return num;
}
However, I am not sure how to reproduce this in rust. So far I have tried to use ChaCha8Rng
, ChaCha12Rng
, ChaCha20Rng
, and Pcg64
, none of which produces the wanted result.
Solution 1:[1]
It depends on the CSPRNG. If you were seeding an HMAC DRBG using HMAC-SHA-512, then this would be a perfectly normal amount of input. However, in your case, the CSPRNG is ChaCha, which is configured to have a 256-bit key.
If this mnemonic has been generated from a CSPRNG and has sufficient entropy, then all you need is a simple, straightforward key derivation function like HKDF. You can use HKDF with SHA-256 or SHA-512, with the seed as the input keying material, no salt, and an output keying material which is 32 bytes in size. Then, you can use that to seed your CSPRNG.
You will also need an info string, which is usually some text string for the purpose. I like to use a version number to make things future proof, so you could use something like "v1 PRNG seed".
My recommendation here is that since you have a 64-byte input seed, that HKDF using SHA-512 is best, since it avoids losing entropy if you end up needing to seed other data. Also, while ChaCha12Rng is the default, ChaCha20Rng is more conservative and would probably be appropriate for generating a long term key.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | bk2204 |