diff --git a/Cargo.toml b/Cargo.toml index cd88c96..8ba6b52 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,10 +22,12 @@ ethsign-crypto = { version = "0.3", path = "./ethsign-crypto" } [dev-dependencies] serde_json = "1.0" +hex = "0.4" [features] default = ["secp256k1"] pure-rust = ["libsecp256k1"] +export-private-key = [] [workspace] members = [ diff --git a/README.md b/README.md index 7be103e..1d36e28 100644 --- a/README.md +++ b/README.md @@ -2,9 +2,12 @@ A library to read JSON keyfiles and sign Ethereum stuff. +Library by defaults hide private key from access, +but you can add --features export-private-key to export it. + ## Usage: ```rust -use ethsign::{Protected, KeyFile}; +use ethsign::{KeyFile, Protected}; fn main() { let file = std::fs::File::open("./res/wallet.json").unwrap(); @@ -21,6 +24,13 @@ fn main() { let public = signature.recover(&message).unwrap(); println!("{:?}", public); + #[cfg(feature = "export-private-key")] + { + //Do not print private key in that way in production code + let private = secret.private(); + println!("Extracted private key: {}", hex::encode(private)); + } + // Verify the signature let res = public.verify(&signature, &message).unwrap(); println!("{}", if res { "signature correct" } else { "invalid signature" }); diff --git a/examples/sign.rs b/examples/sign.rs new file mode 100644 index 0000000..ed81aa3 --- /dev/null +++ b/examples/sign.rs @@ -0,0 +1,28 @@ +use ethsign::{KeyFile, Protected}; + +fn main() { + let file = std::fs::File::open("./res/wallet.json").unwrap(); + let key: KeyFile = serde_json::from_reader(file).unwrap(); + let password: Protected = "".into(); + let secret = key.to_secret_key(&password).unwrap(); + let message = [1_u8; 32]; + + // Sign the message + let signature = secret.sign(&message).unwrap(); + println!("{:?}", signature); + + // Recover the signer + let public = signature.recover(&message).unwrap(); + println!("{:?}", public); + + #[cfg(feature = "export-private-key")] + { + //Do not print private key in that way in production code + let private = secret.private(); + println!("Extracted private key: {}", hex::encode(private)); + } + + // Verify the signature + let res = public.verify(&signature, &message).unwrap(); + println!("{}", if res { "signature correct" } else { "invalid signature" }); +} diff --git a/src/key.rs b/src/key.rs index f834b1f..d2df233 100644 --- a/src/key.rs +++ b/src/key.rs @@ -122,6 +122,17 @@ impl SecretKey { PublicKey::from_slice(&uncompressed[1..]).expect("The length of the key is correct; qed") } + /// Export stored, unencrypted, plain private key, use with caution + /// Do not expose this key in logs, etc. Use only if needed + #[cfg(feature = "export-private-key")] + pub fn private(&self) -> [u8; 32] { + use std::convert::TryInto; + self.secret + .as_ref() + .try_into() + .expect("The length of the key is correct; qed") + } + /// Sign given 32-byte message with the key. pub fn sign(&self, message: &[u8]) -> Result { let (v, data) = ec::sign(self.secret.as_ref(), message)?; @@ -152,6 +163,15 @@ mod tests { pub_key.address().to_hex::(), "005b3bcf82085eededd551f50de7892471ffb272" ); + #[cfg(feature = "export-private-key")] + { + let secret_key = key.private(); + assert_eq!( + secret_key.to_hex::(), + "43cd5154df157a4ec26e3a04f9db252280e0c840b6a209026accb70dc124328c".to_string() + ); + } + assert_eq!(&pub_key.bytes().to_hex::(), "782cc7dd72426893ae0d71477e41c41b03249a2b72e78eefcfe0baa9df604a8f979ab94cd23d872dac7bfa8d07d8b76b26efcbede7079f1c5cacd88fe9858f6e"); }