From b944c4129b53b3b7d497d87fd3e9e5908bc3e5cd Mon Sep 17 00:00:00 2001 From: cgeek <cem.moreau@gmail.com> Date: Sun, 29 Aug 2021 17:20:11 +0200 Subject: [PATCH] refact: split keyring implementations into their own file --- src/crypto/base58_duniter_key.rs | 30 +++++++++++++++++++++++ src/crypto/duniter_key.rs | 38 ++++++----------------------- src/crypto/mod.rs | 3 +++ src/crypto/scrypt_duniter_key.rs | 42 ++++++++++++++++++++++++++++++++ src/crypto/seed_duniter_key.rs | 31 +++++++++++++++++++++++ src/lib.rs | 40 ++++++++++++++++-------------- src/main.rs | 8 +++--- 7 files changed, 141 insertions(+), 51 deletions(-) create mode 100644 src/crypto/base58_duniter_key.rs create mode 100644 src/crypto/scrypt_duniter_key.rs create mode 100644 src/crypto/seed_duniter_key.rs diff --git a/src/crypto/base58_duniter_key.rs b/src/crypto/base58_duniter_key.rs new file mode 100644 index 0000000..63db0e3 --- /dev/null +++ b/src/crypto/base58_duniter_key.rs @@ -0,0 +1,30 @@ +use crate::crypto::duniter_key::{ToDuniterKey, DuniterKey, PUBLIC_KEY_LEN, SECRET_KEY_LEN}; +use base64; +use cryptoxide::ed25519; + +pub struct Base58DuniterKey(String, String); + +impl Base58DuniterKey { + pub fn new(public: String, secret: String) -> Self { + Self(public, secret) + } +} + +impl From<Base58DuniterKey> for DuniterKey { + fn from(sdk: Base58DuniterKey) -> Self { + sdk.derive() + } +} + +impl ToDuniterKey for Base58DuniterKey { + + fn derive(&self) -> DuniterKey { + let public_vec = bs58::decode(&self.0).into_vec().unwrap(); + let secret_vec = bs58::decode(&self.1).into_vec().unwrap(); + let mut public = [0u8; PUBLIC_KEY_LEN]; + let mut secret = [0u8; SECRET_KEY_LEN]; + for i in 0..PUBLIC_KEY_LEN { public[i] = public_vec[i] } + for i in 0..SECRET_KEY_LEN { secret[i] = secret_vec[i] } + DuniterKey { public, secret } + } +} \ No newline at end of file diff --git a/src/crypto/duniter_key.rs b/src/crypto/duniter_key.rs index eba3093..4a30681 100644 --- a/src/crypto/duniter_key.rs +++ b/src/crypto/duniter_key.rs @@ -1,4 +1,3 @@ -use std::error::Error; use std::fmt::Formatter; use bs58; @@ -7,12 +6,11 @@ use scrypt::{Params, Scrypt}; use scrypt::password_hash::{Output, PasswordHasher, SaltString}; use crate::crypto::duniter_signature::DuniterSignature; +pub use crate::crypto::scrypt_duniter_key::ScryptDuniterKey; +pub use crate::crypto::seed_duniter_key::SeedDuniterKey; -const PUBLIC_KEY_LEN: usize = 32; -const SECRET_KEY_LEN: usize = 64; -const SCRYPT_N: u8 = 12; -const SCRYPT_R: u32 = 16; -const SCRYPT_P: u32 = 1; +pub const PUBLIC_KEY_LEN: usize = 32; +pub const SECRET_KEY_LEN: usize = 64; /// The main functional class for handle Duniter crypto. pub struct DuniterKey { @@ -20,6 +18,10 @@ pub struct DuniterKey { pub secret: [u8; SECRET_KEY_LEN], } +pub trait ToDuniterKey { + fn derive(&self) -> DuniterKey; +} + /// How to display a duniter keyring: "pub_base58:sec_base58" format. impl std::fmt::Display for DuniterKey { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { @@ -29,22 +31,6 @@ impl std::fmt::Display for DuniterKey { impl DuniterKey { - pub fn from_scrypt(salt: &str, passwd: &str) -> Result<DuniterKey, Box<dyn Error>> { - let seed = derive_seed(salt, passwd)?; - let (secret, public) = ed25519::keypair(seed.as_bytes()); - Ok(DuniterKey { public, secret }) - } - - pub fn from_base58(public: &str, secret: &str) -> Result<DuniterKey, Box<dyn Error>> { - let public_vec = bs58::decode(public).into_vec().unwrap(); - let secret_vec = bs58::decode(secret).into_vec().unwrap(); - let mut public = [0u8; PUBLIC_KEY_LEN]; - let mut secret = [0u8; SECRET_KEY_LEN]; - for i in 0..PUBLIC_KEY_LEN { public[i] = public_vec[i] } - for i in 0..SECRET_KEY_LEN { secret[i] = secret_vec[i] } - Ok(DuniterKey { public, secret }) - } - pub fn sign(&self, message: &str) -> DuniterSignature { DuniterSignature::new(ed25519::signature(message.as_bytes(), &self.secret[..])) } @@ -52,12 +38,4 @@ impl DuniterKey { pub fn verify(&self, message: &str, signature: &DuniterSignature) -> bool { ed25519::verify(message.as_bytes(), &self.public, &signature.as_bytes()) } -} - -fn derive_seed(salt: &str, passwd: &str) -> Result<Output, Box<dyn Error>> { - let saltb64 = base64::encode(salt); // convert utf8 to base64 - let salt = SaltString::new(saltb64.as_str())?; // expectes base64-encoded str - let params = Params::new(SCRYPT_N, SCRYPT_R, SCRYPT_P)?; - let password_hash = Scrypt.hash_password(passwd.as_bytes(), None, params, &salt)?; - Ok(password_hash.hash.expect("Seed should have been generated")) } \ No newline at end of file diff --git a/src/crypto/mod.rs b/src/crypto/mod.rs index e838147..e069d15 100644 --- a/src/crypto/mod.rs +++ b/src/crypto/mod.rs @@ -2,3 +2,6 @@ mod duniter_signature; /// The API of crypto module. pub mod duniter_key; +pub mod scrypt_duniter_key; +pub mod seed_duniter_key; +pub mod base58_duniter_key; diff --git a/src/crypto/scrypt_duniter_key.rs b/src/crypto/scrypt_duniter_key.rs new file mode 100644 index 0000000..bff1e78 --- /dev/null +++ b/src/crypto/scrypt_duniter_key.rs @@ -0,0 +1,42 @@ +use crate::crypto::duniter_key::{ToDuniterKey, DuniterKey}; +use base64; +use cryptoxide::ed25519; +use std::error::Error; +use scrypt::password_hash::{Output, SaltString, PasswordHasher}; +use scrypt::{Params, Scrypt}; + +const SCRYPT_N: u8 = 12; +const SCRYPT_R: u32 = 16; +const SCRYPT_P: u32 = 1; + +pub struct ScryptDuniterKey((String, String)); + +impl ScryptDuniterKey { + pub fn new(salt: String, passwd: String) -> Self { + Self((salt, passwd)) + } +} + +impl From<ScryptDuniterKey> for DuniterKey { + fn from(sdk: ScryptDuniterKey) -> Self { + sdk.derive() + } +} + +impl ToDuniterKey for ScryptDuniterKey { + + fn derive(&self) -> DuniterKey { + let (salt, passwd) = &self.0; + let seed = derive_seed(salt.as_str(), passwd.as_str()).expect("Could not derive seed from salt/password."); + let (secret, public) = ed25519::keypair(seed.as_bytes()); + DuniterKey { public, secret } + } +} + +fn derive_seed(salt: &str, passwd: &str) -> Result<Output, Box<dyn Error>> { + let saltb64 = base64::encode(salt); // convert utf8 to base64 + let salt = SaltString::new(saltb64.as_str())?; // expectes base64-encoded str + let params = Params::new(SCRYPT_N, SCRYPT_R, SCRYPT_P)?; + let password_hash = Scrypt.hash_password(passwd.as_bytes(), None, params, &salt)?; + Ok(password_hash.hash.expect("Seed should have been generated")) +} \ No newline at end of file diff --git a/src/crypto/seed_duniter_key.rs b/src/crypto/seed_duniter_key.rs new file mode 100644 index 0000000..81dfb3c --- /dev/null +++ b/src/crypto/seed_duniter_key.rs @@ -0,0 +1,31 @@ +use crate::crypto::duniter_key::{ToDuniterKey, DuniterKey}; +use base64; +use cryptoxide::ed25519; + +const SEED_LEN: usize = 32; + +pub struct SeedDuniterKey(String); + +impl SeedDuniterKey { + pub fn new(seed: String) -> Self { + Self(seed) + } +} + +impl From<SeedDuniterKey> for DuniterKey { + fn from(sdk: SeedDuniterKey) -> Self { + sdk.derive() + } +} + +impl ToDuniterKey for SeedDuniterKey { + + fn derive(&self) -> DuniterKey { + let seed = &self.0; + let seed_vec = base64::decode(seed).unwrap(); + let mut seed = [0u8; SEED_LEN]; + for i in 0..SEED_LEN { seed[i] = seed_vec[i] } + let (secret, public) = ed25519::keypair(&seed); + DuniterKey { public, secret } + } +} \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index 85f9fdb..c1068db 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,61 +1,65 @@ -mod crypto; +pub mod crypto; -use crate::crypto::duniter_key::DuniterKey; +use crate::crypto::duniter_key::{DuniterKey, ToDuniterKey}; +use crate::crypto::scrypt_duniter_key::ScryptDuniterKey; -pub fn compute_pub(salt: &str, passwd: &str) -> String { - let key = DuniterKey::from_scrypt(salt, passwd).expect("Error generating key from scrypt"); - String::from(format!("{}", key).split(":").next().unwrap()) +pub fn compute_pub(sdk: impl ToDuniterKey) -> String { + String::from(sdk.derive().to_string().split(":").next().unwrap()) } -pub fn compute_sec(salt: &str, passwd: &str) -> String { - let key = DuniterKey::from_scrypt(salt, passwd).expect("Error generating key from scrypt"); - let key_str = key.to_string(); +pub fn compute_sec(sdk: impl ToDuniterKey) -> String { + let key_str = sdk.derive().to_string(); let mut split = key_str.split(":"); split.next(); String::from(split.next().unwrap()) } -pub fn compute_key(salt: &str, passwd: &str) -> String { - let key = DuniterKey::from_scrypt(salt, passwd).expect("Error generating key from scrypt"); - format!("{}", key) +pub fn compute_key(sdk: impl ToDuniterKey) -> String { + format!("{}", sdk.derive()) } #[cfg(test)] mod tests { use crate::*; + use crate::crypto::duniter_key::SeedDuniterKey; + use crate::crypto::base58_duniter_key::Base58DuniterKey; const SALT: &str = "test_salt"; const PASSWD: &str = "test_passwd"; const PUBKEY: &str = "953SE7QJoDC2vFFwkSDojGKjUFU6BCu1kH6s4MnoyQCw"; const SECKEY: &str = "s3G7v2FPuTSWh8f9BSrU8EurwcM214x4fMLWFDLYmH7doqfyCqM96Ai5q4xB8y4GBKTCUvHoR3Et8AJKf7XTTZR"; + const SEED: &str = "KybWwsHnqJrs+1F10GfuK08pOyvqpb2Jn1yNO58S0l0="; #[test] fn should_compute_pub() { - assert_eq!(compute_pub(SALT, PASSWD), PUBKEY); + assert_eq!(compute_pub(SeedDuniterKey::new(String::from(SEED))), PUBKEY); } #[test] fn should_compute_sec() { - assert_eq!(compute_sec(SALT, PASSWD), SECKEY); + assert_eq!(compute_sec(SeedDuniterKey::new(String::from(SEED))), SECKEY); } #[test] fn should_compute_keyring() { - assert_eq!(compute_key(SALT, PASSWD), format!("{}:{}", PUBKEY, SECKEY)); + assert_eq!(compute_key(SeedDuniterKey::new(String::from(SEED))), format!("{}:{}", PUBKEY, SECKEY)); } #[test] - fn base58_or_scrypt_is_the_same() { - let scrypt_key = DuniterKey::from_scrypt(SALT, PASSWD).expect("Error generating key from scrypt"); - let bs58_key = DuniterKey::from_base58(PUBKEY, SECKEY).expect("Wrong public/secret key"); + fn all_derivations_leads_to_the_same() { + let scrypt_key = ScryptDuniterKey::new(String::from(SALT), String::from(PASSWD)).derive(); + let bs58_key = Base58DuniterKey::new(String::from(PUBKEY), String::from(SECKEY)).derive(); + let seed_key = SeedDuniterKey::new(String::from(SEED)).derive(); assert_eq!(bs58_key.public, scrypt_key.public); assert_eq!(bs58_key.secret, scrypt_key.secret); + assert_eq!(seed_key.public, scrypt_key.public); + assert_eq!(seed_key.secret, scrypt_key.secret); } #[test] fn should_verify_or_reject_sig() { - let bs58_key = DuniterKey::from_base58(PUBKEY, SECKEY).expect("Wrong public/secret key"); + let bs58_key = Base58DuniterKey::new(String::from(PUBKEY), String::from(SECKEY)).derive(); let key = bs58_key; let signature = key.sign("GOOD MESSAGE"); assert_eq!(true, key.verify("GOOD MESSAGE", &signature)); diff --git a/src/main.rs b/src/main.rs index 368af76..a38f122 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,16 +5,18 @@ use duniter_mini_client::{compute_pub, compute_sec, compute_key}; use crate::cli::Command; pub use crate::cli::Command::{PUB, UNKNOWN}; use crate::cli::Command::{SEC, KEYRING}; +use duniter_mini_client::crypto::duniter_key::ScryptDuniterKey; mod cli; +mod crypto; fn main() { let command = Command::from(env::args()); match command { - PUB(salt, passwd) => println!("{}", compute_pub(salt.as_str(), passwd.as_str())), - SEC(salt, passwd) => println!("{}", compute_sec(salt.as_str(), passwd.as_str())), - KEYRING(salt, passwd) => println!("{}", compute_key(salt.as_str(), passwd.as_str())), + PUB(salt, passwd) => println!("{}", compute_pub(ScryptDuniterKey::new(salt, passwd))), + SEC(salt, passwd) => println!("{}", compute_sec(ScryptDuniterKey::new(salt, passwd))), + KEYRING(salt, passwd) => println!("{}", compute_key(ScryptDuniterKey::new(salt, passwd))), UNKNOWN(cmd) => eprintln!("Unknown command {}", cmd), }; } \ No newline at end of file -- GitLab