Skip to content
Snippets Groups Projects
Commit b944c412 authored by Cédric Moreau's avatar Cédric Moreau
Browse files

refact: split keyring implementations into their own file

parent ef63450c
No related branches found
No related tags found
No related merge requests found
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
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
......@@ -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;
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
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
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));
......
......@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment