Skip to content
This repository was archived by the owner on May 8, 2021. It is now read-only.

Commit e018150

Browse files
committed
bring key and mnemonic gen more inline with the js and java libraries
1 parent aa18b49 commit e018150

File tree

6 files changed

+85
-27
lines changed

6 files changed

+85
-27
lines changed

Cargo.lock

+20-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+4-2
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ rand_core = "0.4.0"
1717
sha2 = "0.8.0"
1818
sha3 = "0.8.2"
1919
hex = "0.3.2"
20+
hmac = "0.7.0"
2021
failure = "0.1.5"
2122
simple_asn1 = "0.4.0"
2223
failure_derive = "0.1.5"
@@ -26,13 +27,14 @@ protobuf = "2.8.1"
2627
itertools = "0.8.0"
2728
chrono = "0.4.9"
2829
parking_lot = "0.9.0"
30+
pbkdf2 = { version = "0.3.0", default-features = false }
31+
getrandom = "0.1.12"
2932
grpc = "0.6.1"
3033
query_interface = "0.3.5"
3134
httpbis = "0.7.0"
3235
log = "0.4.8"
3336
try_from = "0.3.2"
34-
tiny-bip39 = "0.6.2"
35-
rand_chacha = "0.1.1"
37+
tiny-bip39 = { version="0.6.2", default-features = false }
3638
tokio = { version = "0.2.0-alpha.4" }
3739
futures = { version = "0.3.0-alpha.18", package = "futures-preview", features = [ "compat" ] }
3840

examples/create_account.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
use failure::{format_err, Error};
2-
use futures::FutureExt;
32
use hedera::{Client, SecretKey, Status};
43
use std::{env, thread::sleep, time::Duration};
54

65
#[tokio::main]
76
async fn main() -> Result<(), Error> {
87
pretty_env_logger::try_init()?;
98

10-
let (secret, _) = SecretKey::generate("");
9+
let secret = SecretKey::generate();
1110
let public = secret.public();
1211

1312
println!("secret = {}", secret);

examples/generate_key.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use hedera::SecretKey;
22

33
fn main() {
4-
let (secret, mnemonic) = SecretKey::generate("");
4+
let (secret, mnemonic) = SecretKey::generate_mnemonic();
55
let public = secret.public();
66

77
println!("secret = {}", secret);

examples/update_account.rs

-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
use failure::{format_err, Error};
2-
use futures::FutureExt;
32
use hedera::{Client, Status};
43
use std::{env, thread::sleep, time::Duration};
54

src/crypto.rs

+59-20
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,11 @@
11
use crate::proto::{self, ToProto};
2-
use bip39::{Language, Mnemonic, MnemonicType, Seed};
2+
use bip39::{Language, Mnemonic};
33
use ed25519_dalek;
44
use failure::{bail, err_msg, Error};
55
use failure_derive::Fail;
66
use hex;
77
use num::BigUint;
88
use once_cell::{sync::Lazy};
9-
use rand_core::SeedableRng;
10-
use rand_chacha::ChaChaRng;
119
use simple_asn1::{
1210
der_decode, der_encode, oid, to_der, ASN1Block, ASN1Class, ASN1DecodeErr, ASN1EncodeErr,
1311
FromASN1, ToASN1, OID,
@@ -17,6 +15,7 @@ use std::{
1715
str::FromStr,
1816
};
1917
use try_from::{TryFrom, TryInto};
18+
use hmac::Hmac;
2019

2120
// Types used for (de-)serializing public and secret keys from ASN.1 byte
2221
// streams.
@@ -370,25 +369,54 @@ impl TryFrom<proto::BasicTypes::Key> for PublicKey {
370369
pub struct SecretKey(ed25519_dalek::SecretKey);
371370

372371
impl SecretKey {
373-
/// Generate a `SecretKey` with a BIP-39 mnemonic using a cryptographically
372+
/// Generate a `SecretKey` with 32 cryptographically random bytes
373+
pub fn generate() -> Self {
374+
let mut buf = [0u8; 32];
375+
376+
getrandom::getrandom(&mut buf).expect("Could not retrieve entropy from the os");
377+
378+
let bytes = Self::derive_seed(&buf);
379+
380+
// this should never fail unless getrandom fails which will cause a panic
381+
SecretKey(ed25519_dalek::SecretKey::from_bytes(&bytes).unwrap())
382+
}
383+
384+
/// Generate a `SecretKey` with 32 bytes of provided entropy
385+
pub fn generate_from_entropy(entropy: &[u8; 32]) -> Self {
386+
// this should never fail since entropy is required to be a 32-length u8 array
387+
SecretKey(ed25519_dalek::SecretKey::from_bytes(entropy).unwrap())
388+
}
389+
390+
/// Generate a `SecretKey` alongside a BIP-39 mnemonic using a cryptographically
374391
/// secure random number generator.
375-
///
376-
/// The `password` is required with the mnemonic to reproduce the secret key.
377-
pub fn generate(password: &str) -> (Self, String) {
378-
let mnemonic = Mnemonic::new(MnemonicType::Words24, Language::English);
392+
pub fn generate_mnemonic() -> (Self, String) {
393+
let mut entropy = [0u8; 32];
379394

380-
let secret = Self::generate_with_mnemonic(&mnemonic, password);
395+
getrandom::getrandom(&mut entropy).expect("Could not retrieve entropy from the os");
396+
397+
// this should never fail as 32 is a valid entropy length
398+
let mnemonic = Mnemonic::from_entropy(&entropy, Language::English).unwrap();
399+
let secret = Self::generate_from_entropy(&entropy);
381400

382401
(secret, mnemonic.into_phrase())
383402
}
384403

385-
fn generate_with_mnemonic(mnemonic: &Mnemonic, password: &str) -> Self {
386-
let mut seed: [u8; 32] = Default::default();
404+
/// Duplicating what is done here:
405+
/// https://github.com/hashgraph/hedera-keygen-java/blob/master/src/main/java/com/hedera/sdk/keygen/CryptoUtils.java#L43
406+
fn derive_seed(entropy: &[u8]) -> [u8; 32] {
407+
let password = entropy
408+
.into_iter()
409+
.chain([u8::max_value(); 8].iter())
410+
.map(|u| { *u })
411+
.collect::<Vec<u8>>();
412+
413+
let salt = [u8::max_value()];
387414

388-
seed.copy_from_slice(&Seed::new(&mnemonic, password).as_bytes()[0..32]);
415+
let mut seed = [0u8; 32];
389416

390-
let mut rng = ChaChaRng::from_seed(seed);
391-
SecretKey(ed25519_dalek::SecretKey::generate(&mut rng))
417+
pbkdf2::pbkdf2::<Hmac<sha2::Sha512>>(&password, &salt, 2048, &mut seed);
418+
419+
seed
392420
}
393421

394422
/// Construct a `SecretKey` from a slice of bytes.
@@ -421,15 +449,25 @@ impl SecretKey {
421449
}
422450

423451
/// Re-construct a `SecretKey` from the supplied mnemonic and password.
424-
pub fn from_mnemonic(mnemonic: &str, password: &str) -> Result<Self, Error> {
452+
pub fn from_mnemonic(mnemonic: &str) -> Result<Self, Error> {
425453
let mnemonic = Mnemonic::from_phrase(mnemonic, Language::English)?;
426454

427-
Ok(Self::generate_with_mnemonic(&mnemonic, password))
455+
let mut entropy = [0u8; 32];
456+
457+
let seed_entropy = mnemonic.entropy();
458+
459+
for i in 0..32 {
460+
entropy[i] = seed_entropy[i];
461+
}
462+
463+
//let entropy: &[u8; 32] = vec!(mnemonic.entropy()).try_into()?;
464+
465+
Ok(Self::generate_from_entropy(&entropy))
428466
}
429467

430468
/// Return the `SecretKey` as raw bytes.
431469
#[inline]
432-
pub fn as_bytes(&self) -> &[u8; ed25519_dalek::PUBLIC_KEY_LENGTH] {
470+
pub fn as_bytes(&self) -> &[u8; ed25519_dalek::SECRET_KEY_LENGTH] {
433471
self.0.as_bytes()
434472
}
435473

@@ -646,7 +684,8 @@ mod tests {
646684

647685
#[test]
648686
fn test_generate() -> Result<(), Error> {
649-
let (key, _mnemonic) = SecretKey::generate("");
687+
let key = SecretKey::generate();
688+
650689
let signature = key.sign(MESSAGE.as_bytes());
651690
let verified = key.public().verify(MESSAGE.as_bytes(), &signature)?;
652691

@@ -671,8 +710,8 @@ mod tests {
671710

672711
#[test]
673712
fn test_reconstruct() -> Result<(), Error> {
674-
let (secret1, mnemonic) = SecretKey::generate("this-is-not-a-password");
675-
let secret2 = SecretKey::from_mnemonic(&mnemonic, "this-is-not-a-password")?;
713+
let (secret1, mnemonic) = SecretKey::generate_mnemonic();
714+
let secret2 = SecretKey::from_mnemonic(&mnemonic)?;
676715

677716
assert_eq!(secret1.as_bytes(), secret2.as_bytes());
678717

0 commit comments

Comments
 (0)