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

Commit

Permalink
bring key and mnemonic gen more inline with the js and java libraries
Browse files Browse the repository at this point in the history
  • Loading branch information
QuestofIranon committed Sep 12, 2019
1 parent aa18b49 commit e018150
Show file tree
Hide file tree
Showing 6 changed files with 85 additions and 27 deletions.
21 changes: 20 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ rand_core = "0.4.0"
sha2 = "0.8.0"
sha3 = "0.8.2"
hex = "0.3.2"
hmac = "0.7.0"
failure = "0.1.5"
simple_asn1 = "0.4.0"
failure_derive = "0.1.5"
Expand All @@ -26,13 +27,14 @@ protobuf = "2.8.1"
itertools = "0.8.0"
chrono = "0.4.9"
parking_lot = "0.9.0"
pbkdf2 = { version = "0.3.0", default-features = false }
getrandom = "0.1.12"
grpc = "0.6.1"
query_interface = "0.3.5"
httpbis = "0.7.0"
log = "0.4.8"
try_from = "0.3.2"
tiny-bip39 = "0.6.2"
rand_chacha = "0.1.1"
tiny-bip39 = { version="0.6.2", default-features = false }
tokio = { version = "0.2.0-alpha.4" }
futures = { version = "0.3.0-alpha.18", package = "futures-preview", features = [ "compat" ] }

Expand Down
3 changes: 1 addition & 2 deletions examples/create_account.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
use failure::{format_err, Error};
use futures::FutureExt;
use hedera::{Client, SecretKey, Status};
use std::{env, thread::sleep, time::Duration};

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

let (secret, _) = SecretKey::generate("");
let secret = SecretKey::generate();
let public = secret.public();

println!("secret = {}", secret);
Expand Down
2 changes: 1 addition & 1 deletion examples/generate_key.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use hedera::SecretKey;

fn main() {
let (secret, mnemonic) = SecretKey::generate("");
let (secret, mnemonic) = SecretKey::generate_mnemonic();
let public = secret.public();

println!("secret = {}", secret);
Expand Down
1 change: 0 additions & 1 deletion examples/update_account.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use failure::{format_err, Error};
use futures::FutureExt;
use hedera::{Client, Status};
use std::{env, thread::sleep, time::Duration};

Expand Down
79 changes: 59 additions & 20 deletions src/crypto.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use crate::proto::{self, ToProto};
use bip39::{Language, Mnemonic, MnemonicType, Seed};
use bip39::{Language, Mnemonic};
use ed25519_dalek;
use failure::{bail, err_msg, Error};
use failure_derive::Fail;
use hex;
use num::BigUint;
use once_cell::{sync::Lazy};
use rand_core::SeedableRng;
use rand_chacha::ChaChaRng;
use simple_asn1::{
der_decode, der_encode, oid, to_der, ASN1Block, ASN1Class, ASN1DecodeErr, ASN1EncodeErr,
FromASN1, ToASN1, OID,
Expand All @@ -17,6 +15,7 @@ use std::{
str::FromStr,
};
use try_from::{TryFrom, TryInto};
use hmac::Hmac;

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

impl SecretKey {
/// Generate a `SecretKey` with a BIP-39 mnemonic using a cryptographically
/// Generate a `SecretKey` with 32 cryptographically random bytes
pub fn generate() -> Self {
let mut buf = [0u8; 32];

getrandom::getrandom(&mut buf).expect("Could not retrieve entropy from the os");

let bytes = Self::derive_seed(&buf);

// this should never fail unless getrandom fails which will cause a panic
SecretKey(ed25519_dalek::SecretKey::from_bytes(&bytes).unwrap())
}

/// Generate a `SecretKey` with 32 bytes of provided entropy
pub fn generate_from_entropy(entropy: &[u8; 32]) -> Self {
// this should never fail since entropy is required to be a 32-length u8 array
SecretKey(ed25519_dalek::SecretKey::from_bytes(entropy).unwrap())
}

/// Generate a `SecretKey` alongside a BIP-39 mnemonic using a cryptographically
/// secure random number generator.
///
/// The `password` is required with the mnemonic to reproduce the secret key.
pub fn generate(password: &str) -> (Self, String) {
let mnemonic = Mnemonic::new(MnemonicType::Words24, Language::English);
pub fn generate_mnemonic() -> (Self, String) {
let mut entropy = [0u8; 32];

let secret = Self::generate_with_mnemonic(&mnemonic, password);
getrandom::getrandom(&mut entropy).expect("Could not retrieve entropy from the os");

// this should never fail as 32 is a valid entropy length
let mnemonic = Mnemonic::from_entropy(&entropy, Language::English).unwrap();
let secret = Self::generate_from_entropy(&entropy);

(secret, mnemonic.into_phrase())
}

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

let salt = [u8::max_value()];

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

let mut rng = ChaChaRng::from_seed(seed);
SecretKey(ed25519_dalek::SecretKey::generate(&mut rng))
pbkdf2::pbkdf2::<Hmac<sha2::Sha512>>(&password, &salt, 2048, &mut seed);

seed
}

/// Construct a `SecretKey` from a slice of bytes.
Expand Down Expand Up @@ -421,15 +449,25 @@ impl SecretKey {
}

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

Ok(Self::generate_with_mnemonic(&mnemonic, password))
let mut entropy = [0u8; 32];

let seed_entropy = mnemonic.entropy();

for i in 0..32 {
entropy[i] = seed_entropy[i];
}

//let entropy: &[u8; 32] = vec!(mnemonic.entropy()).try_into()?;

Ok(Self::generate_from_entropy(&entropy))
}

/// Return the `SecretKey` as raw bytes.
#[inline]
pub fn as_bytes(&self) -> &[u8; ed25519_dalek::PUBLIC_KEY_LENGTH] {
pub fn as_bytes(&self) -> &[u8; ed25519_dalek::SECRET_KEY_LENGTH] {
self.0.as_bytes()
}

Expand Down Expand Up @@ -646,7 +684,8 @@ mod tests {

#[test]
fn test_generate() -> Result<(), Error> {
let (key, _mnemonic) = SecretKey::generate("");
let key = SecretKey::generate();

let signature = key.sign(MESSAGE.as_bytes());
let verified = key.public().verify(MESSAGE.as_bytes(), &signature)?;

Expand All @@ -671,8 +710,8 @@ mod tests {

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

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

Expand Down

0 comments on commit e018150

Please sign in to comment.