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

feat: add "pub", "sec" and "keyring" commands

parent 856a5a0e
No related branches found
No related tags found
No related merge requests found
......@@ -2,6 +2,220 @@
# It is not intended for manual editing.
version = 3
[[package]]
name = "base64"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd"
[[package]]
name = "base64ct"
version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a32fd6af2b5827bce66c29053ba0e7c42b9dcab01835835058558c10851a46b"
[[package]]
name = "block-buffer"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4152116fd6e9dadb291ae18fc1ec3575ed6d84c29642d97890f4b4a3417297e4"
dependencies = [
"generic-array",
]
[[package]]
name = "bs58"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "771fe0050b883fcc3ea2359b1a96bcfbc090b7116eae7c3c512c7a083fdf23d3"
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "cipher"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7ee52072ec15386f770805afd189a01c8841be8696bed250fa2f13c4c0d6dfb7"
dependencies = [
"generic-array",
]
[[package]]
name = "cpufeatures"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "66c99696f6c9dd7f35d486b9d04d7e6e202aa3e8c40d553f2fdf5e7e0c6a71ef"
dependencies = [
"libc",
]
[[package]]
name = "crypto-mac"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b1d1a86f49236c215f271d40892d5fc950490551400b02ef360692c29815c714"
dependencies = [
"generic-array",
"subtle",
]
[[package]]
name = "cryptoxide"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b8c4fdc86023bc33b265f256ce8205329125b86c38a8a96e243a6a705b7230ec"
[[package]]
name = "digest"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
dependencies = [
"generic-array",
]
[[package]]
name = "duniter-mini-client"
version = "0.1.0"
dependencies = [
"base64",
"bs58",
"cryptoxide",
"scrypt",
]
[[package]]
name = "generic-array"
version = "0.14.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
dependencies = [
"typenum",
"version_check",
]
[[package]]
name = "getrandom"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"libc",
"wasi",
]
[[package]]
name = "hmac"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2a2a2320eb7ec0ebe8da8f744d7812d9fc4cb4d09344ac01898dbcb6a20ae69b"
dependencies = [
"crypto-mac",
"digest",
]
[[package]]
name = "libc"
version = "0.2.100"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1fa8cddc8fbbee11227ef194b5317ed014b8acbf15139bd716a18ad3fe99ec5"
[[package]]
name = "opaque-debug"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "password-hash"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "77e0b28ace46c5a396546bcf443bf422b57049617433d8854227352a4a9b24e7"
dependencies = [
"base64ct",
"rand_core",
"subtle",
]
[[package]]
name = "pbkdf2"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d95f5254224e617595d2cc3cc73ff0a5eaf2637519e25f03388154e9378b6ffa"
dependencies = [
"crypto-mac",
]
[[package]]
name = "rand_core"
version = "0.6.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
dependencies = [
"getrandom",
]
[[package]]
name = "salsa20"
version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ecbd2eb639fd7cab5804a0837fe373cc2172d15437e804c054a9fb885cb923b0"
dependencies = [
"cipher",
]
[[package]]
name = "scrypt"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879588d8f90906e73302547e20fffefdd240eb3e0e744e142321f5d49dea0518"
dependencies = [
"base64ct",
"hmac",
"password-hash",
"pbkdf2",
"salsa20",
"sha2",
]
[[package]]
name = "sha2"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b362ae5752fd2137731f9fa25fd4d9058af34666ca1966fb969119cc35719f12"
dependencies = [
"block-buffer",
"cfg-if",
"cpufeatures",
"digest",
"opaque-debug",
]
[[package]]
name = "subtle"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
[[package]]
name = "typenum"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "879f6906492a7cd215bfa4cf595b600146ccfac0c79bcbd1f3000162af5e8b06"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
......@@ -6,3 +6,7 @@ edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
scrypt = "0.7.0"
base64 = "0.13.0"
bs58 = "0.4.0"
cryptoxide = "0.3.3"
\ No newline at end of file
use std::env::Args;
use crate::cli::Command::*;
pub enum Command {
/// Compute the public key from salt/passwd and displays it
PUB(String, String),
/// Compute the secret key from salt/passwd and displays it
SEC(String, String),
/// Compute the keyring from salt/passwd and displays it
KEYRING(String, String),
/// Unknown command
UNKNOWN(String)
}
impl Command {
pub fn from(mut args: Args) -> Command {
args.next();
let command = args.next().expect("Command must be provided");
let command = command.as_str();
match command {
"pub" => {
let salt = args.next().expect("Salt must be provided");
let passwd = args.next().expect("Password must be provided");
PUB(salt, passwd)
},
"sec" => {
let salt = args.next().expect("Salt must be provided");
let passwd = args.next().expect("Password must be provided");
SEC(salt, passwd)
},
"keyring" => {
let salt = args.next().expect("Salt must be provided");
let passwd = args.next().expect("Password must be provided");
KEYRING(salt, passwd)
},
_ => UNKNOWN(String::from(command)),
}
}
}
\ No newline at end of file
use std::error::Error;
use std::fmt::Formatter;
use bs58;
use cryptoxide::ed25519;
use scrypt::{Params, Scrypt};
use scrypt::password_hash::{Output, PasswordHasher, SaltString};
use crate::crypto::duniter_signature::DuniterSignature;
/// The main functional class for handle Duniter crypto.
pub struct DuniterKey {
pub public: [u8; 32],
pub secret: [u8; 64],
}
/// 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 {
write!(f, "{}:{}", bs58::encode(&self.public).into_string(), bs58::encode(&self.secret).into_string())
}
}
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; 32];
for (i, v) in public_vec.iter().enumerate() {
if i < 32 {
public[i] = *v;
}
}
let mut secret = [0u8; 64];
for (i, v) in secret_vec.iter().enumerate() {
if i < 64 {
secret[i] = *v;
}
}
Ok(DuniterKey { public, secret })
}
pub fn sign(&self, message: &str) -> DuniterSignature {
DuniterSignature::new(ed25519::signature(message.as_bytes(), &self.secret[..]))
}
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(12, 16, 1)?;
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 std::fmt::Formatter;
const SIGNATURE_LENGTH: usize = 64;
pub struct DuniterSignature([u8; SIGNATURE_LENGTH]);
/// A DuniterSignature wraps an actuel signature (64 bytes data)
impl DuniterSignature {
pub fn new(signature: [u8; SIGNATURE_LENGTH]) -> DuniterSignature {
DuniterSignature(signature)
}
pub fn as_bytes(&self) -> [u8; SIGNATURE_LENGTH] {
self.0
}
}
impl std::fmt::Display for DuniterSignature {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", base64::encode(self.0))
}
}
\ No newline at end of file
mod duniter_signature;
/// The API of crypto module.
pub mod duniter_key;
mod crypto;
use crate::crypto::duniter_key::DuniterKey;
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_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();
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)
}
#[cfg(test)]
mod tests {
use crate::*;
const SALT: &str = "test_salt";
const PASSWD: &str = "test_passwd";
const PUBKEY: &str = "953SE7QJoDC2vFFwkSDojGKjUFU6BCu1kH6s4MnoyQCw";
const SECKEY: &str = "s3G7v2FPuTSWh8f9BSrU8EurwcM214x4fMLWFDLYmH7doqfyCqM96Ai5q4xB8y4GBKTCUvHoR3Et8AJKf7XTTZR";
#[test]
fn should_compute_pub() {
assert_eq!(compute_pub(SALT, PASSWD), PUBKEY);
}
#[test]
fn should_compute_sec() {
assert_eq!(compute_sec(SALT, PASSWD), SECKEY);
}
#[test]
fn should_compute_keyring() {
assert_eq!(compute_key(SALT, PASSWD), 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");
assert_eq!(bs58_key.public, scrypt_key.public);
assert_eq!(bs58_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 key = bs58_key;
let signature = key.sign("GOOD MESSAGE");
assert_eq!(true, key.verify("GOOD MESSAGE", &signature));
assert_eq!(false, key.verify("WRONG MESSAGE", &signature));
}
}
\ No newline at end of file
use std::env;
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};
mod cli;
fn main() {
println!("Hello, world!");
}
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())),
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