Commit 41281ed1 authored by dvermd's avatar dvermd
Browse files

[feat] whole: migrate dup-crypto to v0.15.0 and use DEWIF format

parent 2f3cc193
Pipeline #8670 skipped with stage
......@@ -275,6 +275,38 @@ version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2"
[[package]]
name = "aes"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "54eb1d8fe354e5fc611daf4f2ea97dd45a765f4f1e4512306ec183ae2e8f20c9"
dependencies = [
"aes-soft",
"aesni",
"block-cipher-trait",
]
[[package]]
name = "aes-soft"
version = "0.3.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfd7e7ae3f9a1fb5c03b389fc6bb9a51400d0c13053f0dca698c832bfd893a0d"
dependencies = [
"block-cipher-trait",
"byteorder",
"opaque-debug",
]
[[package]]
name = "aesni"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f70a6b5f971e473091ab7cfb5ffac6cde81666c4556751d8d5620ead8abf100"
dependencies = [
"block-cipher-trait",
"opaque-debug",
]
[[package]]
name = "aho-corasick"
version = "0.7.10"
......@@ -472,6 +504,15 @@ dependencies = [
"generic-array",
]
[[package]]
name = "block-cipher-trait"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1c924d49bd09e7c06003acda26cd9742e796e34282ec6c1189404dee0c1f4774"
dependencies = [
"generic-array",
]
[[package]]
name = "block-padding"
version = "0.1.5"
......@@ -696,7 +737,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4434400df11d95d556bac068ddfedd482915eb18fe8bea89bc80b6e4b1c179e5"
dependencies = [
"generic-array",
"subtle",
"subtle 1.0.0",
]
[[package]]
......@@ -709,6 +750,19 @@ dependencies = [
"syn",
]
[[package]]
name = "curve25519-dalek"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26778518a7f6cffa1d25a44b602b62b979bd88adb9e99ffec546998cf3404839"
dependencies = [
"byteorder",
"digest",
"rand_core",
"subtle 2.2.2",
"zeroize",
]
[[package]]
name = "derive_more"
version = "0.99.3"
......@@ -902,13 +956,16 @@ dependencies = [
[[package]]
name = "dup-crypto"
version = "0.8.4"
version = "0.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2f3dd6cfda1cf0b324169c142cc678a8fe5c3ce4fed3bac2958aab6faf745e25"
checksum = "0d1f07e794fd7bbe2851c9c2538b470ab5f99ef46b470b1ec28a2dc565d52127"
dependencies = [
"aes",
"arrayvec",
"base64 0.11.0",
"bs58",
"byteorder",
"curve25519-dalek",
"ring",
"scrypt",
"serde",
......@@ -2847,6 +2904,12 @@ version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2d67a5a62ba6e01cb2192ff309324cb4875d0c451d55fe2319433abe7a05a8ee"
[[package]]
name = "subtle"
version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c65d530b10ccaeac294f349038a597e435b18fb456aadd0840a623f83b9e941"
[[package]]
name = "syn"
version = "1.0.17"
......
......@@ -11,7 +11,7 @@ path = "src/lib.rs"
[dependencies]
dirs = "2.0.2"
dup-crypto = "0.8.4"
dup-crypto = "0.15.0"
dubp-currency-params = { path = "../../dubp/currency-params" }
dubp-user-docs= { path = "../../dubp/user-docs" }
durs-message = { path = "../message" }
......
......@@ -15,6 +15,7 @@
//! Dunitrust configuration errors
use dup_crypto::dewif::DewifReadError;
use failure::Fail;
/// Error with configuration file
......@@ -26,6 +27,9 @@ pub enum DursConfError {
/// File error
#[fail(display = "{}", _0)]
FileErr(DursConfFileError),
/// Keypairs error
#[fail(display = "{}", _0)]
KeypairsError(DursKeypairsError),
}
/// Error with configuration file
......@@ -63,3 +67,20 @@ pub enum DursConfFileError {
#[fail(display = "fail to write configuration file: {}", _0)]
WriteError(std::io::Error),
}
/// Error with keypairs file
#[derive(Debug, Fail)]
pub enum DursKeypairsError {
/// Currency error
#[fail(display = "unknown currency {}", _0)]
CurrencyError(String),
/// File error
#[fail(display = "fail to read keypairs file: {}", _0)]
FileError(String),
/// Read error
#[fail(display = "fail to read keypairs file: {}", _0)]
ReadError(DewifReadError),
/// Write error
#[fail(display = "fail to write keypairs file: {}", _0)]
WriteError(std::io::Error),
}
......@@ -18,13 +18,20 @@
pub mod cli;
use crate::constants;
use crate::errors::DursConfError;
use crate::errors::DursKeypairsError;
use crate::ui::ui_trait::UserInteractionTrait;
use dubp_currency_params::CurrencyName;
use dup_crypto::dewif::{
read_dewif_file_content, write_dewif_v1_content, write_dewif_v2_content, Currency,
ExpectedCurrency,
};
use dup_crypto::keys::*;
use durs_module::{RequiredKeys, RequiredKeysContent};
use serde::ser::{Serialize, SerializeStruct, Serializer};
use std::fs::File;
use std::io::{Read, Write};
use std::path::PathBuf;
use std::str::FromStr;
#[derive(Debug, Clone, PartialEq, Eq)]
/// Keypairs filled in by the user (via a file or by direct entry in the terminal).
......@@ -106,24 +113,72 @@ fn generate_random_keypair(algo: KeysAlgo) -> KeyPairEnum {
/// Save keypairs in profile folder
// Warning: This function cannot use the macro fatal_error! because the logger is not yet initialized, so it must use panic !
pub fn write_keypairs_file(
currency_name: &Option<CurrencyName>,
file_path: &PathBuf,
keypairs: &DuniterKeyPairs,
keypairs_passphrase: &String,
) -> Result<(), std::io::Error> {
let currency = if let Some(currency) = currency_name {
Currency::from_str(currency.to_string().as_str()).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::Other,
"Unkown currency for DEWIF format",
)
})?
} else {
Currency::none()
};
let network_keypair = if let KeyPairEnum::Ed25519(ref keypair) = keypairs.network_keypair {
Ok(keypair)
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Wrong network keypair format: only Ed25519 format it allowed",
))
}?;
// Serialize keypair in DEWIF format
let dewif_content = if let Some(ref member_keypairs) = keypairs.member_keypair {
let member_keypair = if let KeyPairEnum::Ed25519(keypair) = member_keypairs {
Ok(keypair)
} else {
Err(std::io::Error::new(
std::io::ErrorKind::Other,
"Wrong member keypair format: only Ed25519 format it allowed",
))
}?;
write_dewif_v2_content(
currency,
network_keypair,
member_keypair,
keypairs_passphrase,
)
} else {
write_dewif_v1_content(currency, network_keypair, keypairs_passphrase)
};
let mut f = File::create(file_path.as_path())?;
f.write_all(
serde_json::to_string_pretty(keypairs)
.unwrap_or_else(|_| panic!(dbg!("Fatal error : fail to deserialize keypairs !")))
.as_bytes(),
)?;
f.write_all(dewif_content.as_bytes())?;
f.sync_all()?;
Ok(())
}
/// Create keypairs with random network keypair
pub fn generate_random_keypairs() -> DuniterKeyPairs {
DuniterKeyPairs {
network_keypair: generate_random_keypair(KeysAlgo::Ed25519),
member_keypair: None,
}
}
/// Load keypairs from file
pub fn load_keypairs_from_file(
pub fn load_keypairs_from_file<UI: UserInteractionTrait>(
profile_path: &PathBuf,
keypairs_file_path: &Option<PathBuf>,
) -> Result<DuniterKeyPairs, DursConfError> {
currency: &CurrencyName,
ui: &UI,
) -> Result<(String, DuniterKeyPairs), DursKeypairsError> {
let dewif_currency = Currency::from_str(&*currency.0)
.map_err(|_| DursKeypairsError::CurrencyError(currency.0.clone()))?;
// Get KeyPairs
let keypairs_path = if let Some(ref keypairs_file_path) = keypairs_file_path {
keypairs_file_path.clone()
......@@ -136,80 +191,41 @@ pub fn load_keypairs_from_file(
if let Ok(mut f) = File::open(keypairs_path.as_path()) {
let mut contents = String::new();
if f.read_to_string(&mut contents).is_ok() {
let json_conf: serde_json::Value =
serde_json::from_str(&contents).expect("Conf: Fail to parse keypairs file !");
if let Some(network_seed) = json_conf.get("network_seed") {
if let Some(network_pub) = json_conf.get("network_pub") {
let network_seed = network_seed
.as_str()
.expect("Conf: Fail to parse keypairs file !");
let network_pub = network_pub
.as_str()
.expect("Conf: Fail to parse keypairs file !");
let network_keypair = KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(network_seed)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(network_pub)
.expect("conf : keypairs file : fail to parse network_pub !"),
});
let member_keypair = if let Some(member_seed) = json_conf.get("member_seed")
{
if let Some(member_pub) = json_conf.get("member_pub") {
let member_seed = member_seed
.as_str()
.expect("Conf: Fail to parse keypairs file !");
let member_pub = member_pub
.as_str()
.expect("Conf: Fail to parse keypairs file !");
if member_seed.is_empty() || member_pub.is_empty() {
None
} else {
Some(KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(member_seed).expect(
"conf : keypairs file : fail to parse member_seed !",
),
pubkey: ed25519::PublicKey::from_base58(member_pub).expect(
"conf : keypairs file : fail to parse member_pub !",
),
}))
}
} else {
panic!("Fatal error : keypairs file wrong format : no field member_pub !")
}
} else {
panic!(
"Fatal error : keypairs file wrong format : no field member_seed !"
)
};
// Return keypairs
Ok(DuniterKeyPairs {
network_keypair,
member_keypair,
})
} else {
panic!("Fatal error : keypairs file wrong format : no field salt !")
}
} else {
panic!("Fatal error : keypairs file wrong format : no field password !")
}
let passphrase = ui
.get_password("PassPhrase: ")
.map_err(|_| DursKeypairsError::FileError("Bad Input for passphrase".into()))?;
let expected_currency = ExpectedCurrency::Specific(dewif_currency);
// Read DEWIF file content
// If the file content is correct, we get a key-pair iterator.
let mut key_pair_iter =
read_dewif_file_content(expected_currency, &contents, &passphrase)
.map_err(DursKeypairsError::ReadError)?;
// Return keypairs
Ok((
passphrase,
DuniterKeyPairs {
network_keypair: key_pair_iter
.next()
.expect("There should always be at least a keypair in the file"),
member_keypair: key_pair_iter.next(),
},
))
} else {
panic!("Fail to read keypairs file !");
Err(DursKeypairsError::FileError(format!(
"Fail to read content of keypairs file {}",
profile_path.display()
)))
}
} else {
panic!("Fail to open keypairs file !");
Err(DursKeypairsError::FileError(format!(
"Fail to open keypairs file {}",
profile_path.display()
)))
}
} else {
// Create keypairs file with random keypair
let keypairs = DuniterKeyPairs {
network_keypair: generate_random_keypair(KeysAlgo::Ed25519),
member_keypair: None,
};
write_keypairs_file(&keypairs_path, &keypairs).unwrap_or_else(|_| {
panic!(dbg!("Fatal error : fail to write default keypairs file !"))
});
Ok(keypairs)
Err(DursKeypairsError::FileError(format!(
"No such keypairs file {}",
profile_path.display()
)))
}
}
......@@ -31,6 +31,48 @@ use crate::ui::ui_trait::*;
use crate::ui::UiError;
use crate::*;
/// The wizard key function
pub fn create_keys<UI: UserInteractionTrait>(
user_interaction: &UI,
) -> Result<DuniterKeyPairs, UiError> {
let mut answer = user_interaction.question_prompt(
"Network keypair: Generate a random (r) or salted (s) keypair?",
&["r", "s"],
)?;
let network_keypair = if answer == 0 {
// Generate random keys
Ok(super::generate_random_keypair(KeysAlgo::Ed25519))
} else if answer == 1 {
// Get salt and password for keys
salt_password_prompt(user_interaction)
} else {
Err(UiError::BadInput)
}?;
answer = user_interaction.question_prompt(
"Member keypair: Generate a random (r), salted (s) or no (n) keypair?",
&["r", "s", "n"],
)?;
let member_keypair = if answer == 0 {
// Generate random keys
Ok(Some(super::generate_random_keypair(KeysAlgo::Ed25519)))
} else if answer == 1 {
// Get salt and password for keys
Ok(Some(salt_password_prompt(user_interaction)?))
} else if answer == 2 {
// Do not create a member key
Ok(None)
} else {
Err(UiError::BadInput)
}?;
Ok(DuniterKeyPairs {
network_keypair,
member_keypair,
})
}
#[inline]
/// Modify network keys command
pub fn modify_network_keys<UI: UserInteractionTrait>(
......@@ -131,9 +173,11 @@ pub fn show_member_keys<UI: UserInteractionTrait>(
/// Save keys after a command run
pub fn save_keypairs(
currency_name: &Option<CurrencyName>,
profile_path: PathBuf,
keypairs_file_path: &Option<PathBuf>,
key_pairs: &DuniterKeyPairs,
keypairs_passphrase: &String,
) -> Result<(), std::io::Error> {
let conf_keys_path: PathBuf = if let Some(keypairs_file_path) = keypairs_file_path {
keypairs_file_path.to_path_buf()
......@@ -142,7 +186,12 @@ pub fn save_keypairs(
conf_keys_path.push(crate::constants::KEYPAIRS_FILENAME);
conf_keys_path
};
super::write_keypairs_file(&conf_keys_path, &key_pairs)
super::write_keypairs_file(
currency_name,
&conf_keys_path,
&key_pairs,
keypairs_passphrase,
)
}
fn salt_password_prompt<UI: UserInteractionTrait>(
......@@ -190,12 +239,13 @@ pub fn key_wizard<UI: UserInteractionTrait>(
#[cfg(test)]
mod tests {
use super::*;
// use crate::ui::ui_trait::MockU;
// use crate::ui::ui_trait::MockU;
use dup_crypto::keys::ed25519::KeyPairFromSeed32Generator;
use unwrap::unwrap;
static BASE58_SEED_INIT: &str = "4iXXx5GgRkZ85BVPwn8vFXvztdXAAa5yB573ErcAnngA";
static BASE58_PUB_INIT: &str = "otDgSpKvKAPPmE1MUYxc3UQ3RtEnKYz4iGD3BmwKPzM";
static BASE58_PUB_INIT: &str = "J2rQNuhMGwXKDJ4v9WJDczMSH7oVE4yyg5hDcroFmwCi";
static BASE58_SEED_TEST: &str = "ELjDWGPyCGMuhr7R7H2aip6UJA9qLRepmK77pcD41UqQ";
static BASE58_PUB_TEST: &str = "6sewkaNWyEMqkEa2PVRWrDb3hxWtjPdUSB1zXVCqhdWV";
......@@ -221,22 +271,18 @@ mod tests {
fn setup_keys(both_keys: bool) -> DuniterKeyPairs {
let member_keypair = if both_keys {
Some(KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
}))
Some(KeyPairEnum::Ed25519(KeyPairFromSeed32Generator::generate(
Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse member_seed !"),
)))
} else {
None
};
DuniterKeyPairs {
network_keypair: KeyPairEnum::Ed25519(ed25519::Ed25519KeyPair {
seed: Seed32::from_base58(BASE58_SEED_INIT)
network_keypair: KeyPairEnum::Ed25519(KeyPairFromSeed32Generator::generate(
Seed32::from_base58(BASE58_SEED_INIT)
.expect("conf : keypairs file : fail to parse network_seed !"),
pubkey: ed25519::PublicKey::from_base58(BASE58_PUB_INIT)
.expect("conf : keypairs file : fail to parse network_pub !"),
}),
)),
member_keypair,
}
}
......
//! Durs-core ui module.
pub mod ui_cli;
pub mod ui_trait;
use failure::Fail;
#[derive(Debug, Copy, Clone)]
#[derive(Debug, Copy, Clone, Fail)]
/// Errors encountered by the user interaction
pub enum UiError {
/// Canceled
#[fail(display = "input canceled")]
Canceled,
/// Bad input
#[fail(display = "wrong input")]
BadInput,
}
......
......@@ -10,7 +10,7 @@ pub trait UserInteractionTrait {
/// Print a message to the user
fn message(&self, message: &str);
/// Ask a question to the user
/// Ask a question to the user, return the index of the answer in `answers` or a `UiError`
fn question_prompt<'a>(&self, question: &str, answers: &[&'a str]) -> Result<usize, UiError>;
/// Get a password from the user
......
......@@ -17,7 +17,7 @@ durs-common-tools = { path = "../../tools/common-tools" }
durs-bc-db-reader = { path = "../../modules-lib/bc-db-reader" }
durs-conf = { path = "../conf" }
durs-dbs-tools = { path = "../../tools/dbs-tools" }
dup-crypto = "0.8.4"
dup-crypto = "0.15.0"
dubp-currency-params = { path = "../../dubp/currency-params" }
durs-message = { path = "../message" }
durs-module = { path = "../module" }
......
......@@ -21,6 +21,7 @@ use crate::DursCore;
use clap::arg_enum;
use durs_conf::keypairs::cli::*;
use durs_conf::ui::ui_cli::CLI;
use durs_conf::ui::ui_trait::UserInteractionTrait;
use durs_conf::{DuRsConf, DuniterKeyPairs};
#[derive(StructOpt, Debug, Clone, Copy)]
......@@ -38,13 +39,21 @@ pub struct KeysOpt {
impl CommandNeedKeypairs for KeysOpt {
fn needs_keypairs(&self) -> bool {
true
if let KeysSubCommand::Create(_) = self.subcommand {
false
} else {
true
}
}
}
#[derive(StructOpt, Debug, Clone, Copy)]
/// keys subcommands
pub enum KeysSubCommand {
/// Create keys
#[structopt(name = "create", setting(structopt::clap::AppSettings::ColoredHelp))]
Create(CreateOpt),
/// Modify keys
#[structopt(
name = "modify",
......@@ -78,6 +87,10 @@ pub enum KeysSubCommand {
Wizard(WizardOpt),
}
#[derive(StructOpt, Debug, Copy, Clone)]
/// CreateOpt
pub struct CreateOpt {}
#[derive(StructOpt, Debug, Clone, Copy)]
/// ModifyOpt
pub struct ModifyOpt {
......@@ -146,33 +159,90 @@ impl DursExecutableCoreCommand for KeysOpt {
let keypairs_file = durs_core.options.keypairs_file;
match self.subcommand {
KeysSubCommand::Create(_) => {
let new_keypairs = create_keys(&cli)?;
let passphrase =
if let Some(keypairs_file_passphrase) = durs_core.keypairs_passphrase {
keypairs_file_passphrase
} else {
cli.get_password("PassPhrase: ")?
};
save_keypairs(
&durs_core.currency_name,
profile_path,
&keypairs_file,
&new_keypairs,
&passphrase,
)
.map_err(DursCoreError::FailWriteKeypairsFile)
.and_then(|_| {
show_keys(&cli, new_keypairs);
Ok(())
})
}
KeysSubCommand::Wizard(_) => {
let new_keypairs = key_wizard(&cli, keypairs)?;
save_keypairs(profile_path, &keypairs_file, &new_keypairs)
.map_err(DursCoreError::FailWriteKeypairsFile)
.and_then(|_| {
show_keys(&cli, new_keypairs);
Ok(())
})
let passphrase =
if let Some(keypairs_file_passphrase) = durs_core.keypairs_passphrase {
keypairs_file_passphrase
} else {
cli.get_password("PassPhrase: ")?
};
save_keypairs(
&durs_core.currency_name,
profile_path,
&keypairs_file,
&new_keypairs,
&passphrase,