Skip to content
Snippets Groups Projects
Commit d260efcf authored by Éloïs's avatar Éloïs
Browse files

[feat] dewif: add field currency

parent aa3213a5
No related branches found
No related tags found
1 merge request!4[feat] dewif: add field currency
Pipeline #8227 passed
...@@ -18,8 +18,9 @@ ...@@ -18,8 +18,9 @@
//! # Write ed25519 key-pair in DEWIF file //! # Write ed25519 key-pair in DEWIF file
//! //!
//! ``` //! ```
//! use dup_crypto::dewif::write_dewif_v1_content; //! use dup_crypto::dewif::{Currency, G1_TEST_CURRENCY, write_dewif_v1_content};
//! use dup_crypto::keys::ed25519::{KeyPairFromSaltedPasswordGenerator, SaltedPassword}; //! use dup_crypto::keys::ed25519::{KeyPairFromSaltedPasswordGenerator, SaltedPassword};
//! use std::num::NonZeroU32;
//! //!
//! // Get user credentials (from cli prompt or gui) //! // Get user credentials (from cli prompt or gui)
//! let credentials = SaltedPassword::new("user salt".to_owned(), "user password".to_owned()); //! let credentials = SaltedPassword::new("user salt".to_owned(), "user password".to_owned());
...@@ -31,10 +32,10 @@ ...@@ -31,10 +32,10 @@
//! let encryption_passphrase = "toto titi tata"; //! let encryption_passphrase = "toto titi tata";
//! //!
//! // Serialize keypair in DEWIF format //! // Serialize keypair in DEWIF format
//! let dewif_content = write_dewif_v1_content(&keypair, encryption_passphrase); //! let dewif_content = write_dewif_v1_content(Currency::from(G1_TEST_CURRENCY), &keypair, encryption_passphrase);
//! //!
//! assert_eq!( //! assert_eq!(
//! "AAAAATHfJ3vTvEPcXm22NwhJtnNdGuSjikpSYIMgX96Z9xVT0y8GoIlBL1HaxaWpu0jVDfuwtCGSP9bu2pj6HGbuYVA=", //! "AAAAARAAAAEx3yd707xD3F5ttjcISbZzXRrko4pKUmCDIF/emfcVU9MvBqCJQS9R2sWlqbtI1Q37sLQhkj/W7tqY+hxm7mFQ",
//! dewif_content //! dewif_content
//! ) //! )
//! ``` //! ```
...@@ -42,18 +43,23 @@ ...@@ -42,18 +43,23 @@
//! # Read DEWIF file //! # Read DEWIF file
//! //!
//! ``` //! ```
//! use dup_crypto::dewif::read_dewif_file_content; //! use dup_crypto::dewif::{Currency, ExpectedCurrency, read_dewif_file_content};
//! use dup_crypto::keys::{KeyPair, Signator}; //! use dup_crypto::keys::{KeyPair, Signator};
//! use std::num::NonZeroU32;
//! use std::str::FromStr;
//! //!
//! // Get DEWIF file content (Usually from disk) //! // Get DEWIF file content (Usually from disk)
//! let dewif_file_content = "AAAAATHfJ3vTvEPcXm22NwhJtnNdGuSjikpSYIMgX96Z9xVT0y8GoIlBL1HaxaWpu0jVDfuwtCGSP9bu2pj6HGbuYVA="; //! let dewif_file_content = "AAAAARAAAAEx3yd707xD3F5ttjcISbZzXRrko4pKUmCDIF/emfcVU9MvBqCJQS9R2sWlqbtI1Q37sLQhkj/W7tqY+hxm7mFQ";
//! //!
//! // Get user passphrase for DEWIF decryption (from cli prompt or gui) //! // Get user passphrase for DEWIF decryption (from cli prompt or gui)
//! let encryption_passphrase = "toto titi tata"; //! let encryption_passphrase = "toto titi tata";
//! //!
//! // Expected currency
//! let expected_currency = ExpectedCurrency::Specific(Currency::from_str("g1-test").expect("unknown currency"));
//!
//! // Read DEWIF file content //! // Read DEWIF file content
//! // If the file content is correct, we get a key-pair iterator. //! // If the file content is correct, we get a key-pair iterator.
//! let mut key_pair_iter = read_dewif_file_content(dewif_file_content, encryption_passphrase)?; //! let mut key_pair_iter = read_dewif_file_content(expected_currency, dewif_file_content, encryption_passphrase)?;
//! //!
//! // Get first key-pair //! // Get first key-pair
//! let key_pair = key_pair_iter //! let key_pair = key_pair_iter
...@@ -81,9 +87,11 @@ ...@@ -81,9 +87,11 @@
//! ``` //! ```
//! //!
mod currency;
mod read; mod read;
mod write; mod write;
pub use currency::{Currency, ExpectedCurrency, G1_CURRENCY, G1_TEST_CURRENCY};
pub use read::{read_dewif_file_content, DewifReadError}; pub use read::{read_dewif_file_content, DewifReadError};
pub use write::{write_dewif_v1_content, write_dewif_v2_content}; pub use write::{write_dewif_v1_content, write_dewif_v2_content};
...@@ -92,17 +100,17 @@ use crate::seeds::Seed32; ...@@ -92,17 +100,17 @@ use crate::seeds::Seed32;
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use unwrap::unwrap; use unwrap::unwrap;
const VERSION_BYTES: usize = 4; const UNENCRYPTED_BYTES_LEN: usize = 8;
// v1 // v1
static VERSION_V1: &[u8] = &[0, 0, 0, 1]; static VERSION_V1: &[u8] = &[0, 0, 0, 1];
const V1_BYTES_LEN: usize = 68; const V1_BYTES_LEN: usize = 72;
const V1_ENCRYPTED_BYTES_LEN: usize = 64; const V1_ENCRYPTED_BYTES_LEN: usize = 64;
const V1_AES_BLOCKS_COUNT: usize = 4; const V1_AES_BLOCKS_COUNT: usize = 4;
// v2 // v2
static VERSION_V2: &[u8] = &[0, 0, 0, 2]; static VERSION_V2: &[u8] = &[0, 0, 0, 2];
const V2_BYTES_LEN: usize = 132; const V2_BYTES_LEN: usize = 136;
const V2_ENCRYPTED_BYTES_LEN: usize = 128; const V2_ENCRYPTED_BYTES_LEN: usize = 128;
fn gen_aes_seed(passphrase: &str) -> Seed32 { fn gen_aes_seed(passphrase: &str) -> Seed32 {
...@@ -133,11 +141,13 @@ mod tests { ...@@ -133,11 +141,13 @@ mod tests {
#[test] #[test]
fn dewif_v1() { fn dewif_v1() {
let written_keypair = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32])); let written_keypair = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32]));
let currency = Currency::from(G1_TEST_CURRENCY);
let dewif_content = write_dewif_v1_content(&written_keypair, "toto"); let dewif_content = write_dewif_v1_content(currency, &written_keypair, "toto");
let mut keypairs_iter = read_dewif_file_content(&dewif_content, "toto") let mut keypairs_iter =
.expect("dewif content must be readed successfully"); read_dewif_file_content(ExpectedCurrency::Specific(currency), &dewif_content, "toto")
.expect("dewif content must be readed successfully");
let keypair_read = keypairs_iter.next().expect("Must read one keypair"); let keypair_read = keypairs_iter.next().expect("Must read one keypair");
assert_eq!(KeyPairEnum::Ed25519(written_keypair), keypair_read,) assert_eq!(KeyPairEnum::Ed25519(written_keypair), keypair_read,)
...@@ -146,15 +156,16 @@ mod tests { ...@@ -146,15 +156,16 @@ mod tests {
#[test] #[test]
fn dewif_v1_corrupted() -> Result<(), ()> { fn dewif_v1_corrupted() -> Result<(), ()> {
let written_keypair = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32])); let written_keypair = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32]));
let currency = Currency::from(G1_TEST_CURRENCY);
let mut dewif_content = write_dewif_v1_content(&written_keypair, "toto"); let mut dewif_content = write_dewif_v1_content(currency, &written_keypair, "toto");
// Corrupt one byte in dewif_content // Corrupt one byte in dewif_content
let dewif_bytes_mut = unsafe { dewif_content.as_bytes_mut() }; let dewif_bytes_mut = unsafe { dewif_content.as_bytes_mut() };
dewif_bytes_mut[13] = 0x52; dewif_bytes_mut[13] = 0x52;
if let Err(DewifReadError::CorruptedContent) = if let Err(DewifReadError::CorruptedContent) =
read_dewif_file_content(&dewif_content, "toto") read_dewif_file_content(ExpectedCurrency::Specific(currency), &dewif_content, "toto")
{ {
Ok(()) Ok(())
} else { } else {
...@@ -166,11 +177,14 @@ mod tests { ...@@ -166,11 +177,14 @@ mod tests {
fn dewif_v2() { fn dewif_v2() {
let written_keypair1 = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32])); let written_keypair1 = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32]));
let written_keypair2 = KeyPairFromSeed32Generator::generate(Seed32::new([1u8; 32])); let written_keypair2 = KeyPairFromSeed32Generator::generate(Seed32::new([1u8; 32]));
let currency = Currency::from(G1_TEST_CURRENCY);
let dewif_content = write_dewif_v2_content(&written_keypair1, &written_keypair2, "toto"); let dewif_content =
write_dewif_v2_content(currency, &written_keypair1, &written_keypair2, "toto");
let mut keypairs_iter = read_dewif_file_content(&dewif_content, "toto") let mut keypairs_iter =
.expect("dewif content must be readed successfully"); read_dewif_file_content(ExpectedCurrency::Specific(currency), &dewif_content, "toto")
.expect("dewif content must be readed successfully");
let keypair1_read = keypairs_iter.next().expect("Must read one keypair"); let keypair1_read = keypairs_iter.next().expect("Must read one keypair");
let keypair2_read = keypairs_iter.next().expect("Must read one keypair"); let keypair2_read = keypairs_iter.next().expect("Must read one keypair");
...@@ -182,16 +196,17 @@ mod tests { ...@@ -182,16 +196,17 @@ mod tests {
fn dewif_v2_corrupted() -> Result<(), ()> { fn dewif_v2_corrupted() -> Result<(), ()> {
let written_keypair1 = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32])); let written_keypair1 = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32]));
let written_keypair2 = KeyPairFromSeed32Generator::generate(Seed32::new([1u8; 32])); let written_keypair2 = KeyPairFromSeed32Generator::generate(Seed32::new([1u8; 32]));
let currency = Currency::from(G1_TEST_CURRENCY);
let mut dewif_content = let mut dewif_content =
write_dewif_v2_content(&written_keypair1, &written_keypair2, "toto"); write_dewif_v2_content(currency, &written_keypair1, &written_keypair2, "toto");
// Corrupt one byte in dewif_content // Corrupt one byte in dewif_content
let dewif_bytes_mut = unsafe { dewif_content.as_bytes_mut() }; let dewif_bytes_mut = unsafe { dewif_content.as_bytes_mut() };
dewif_bytes_mut[13] = 0x52; dewif_bytes_mut[13] = 0x52;
if let Err(DewifReadError::CorruptedContent) = if let Err(DewifReadError::CorruptedContent) =
read_dewif_file_content(&dewif_content, "toto") read_dewif_file_content(ExpectedCurrency::Specific(currency), &dewif_content, "toto")
{ {
Ok(()) Ok(())
} else { } else {
......
// Copyright (C) 2020 Éloïs SANCHEZ.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
//! Define DEWIF currency field
use std::fmt::Display;
use std::num::NonZeroU32;
use std::str::FromStr;
/// Ğ1 currency
pub const G1_CURRENCY: u32 = 1;
const G1_CURRENCY_STR: &str = "g1";
/// Ğ1-Test currency
pub const G1_TEST_CURRENCY: u32 = 268_435_457;
const G1_TEST_CURRENCY_STR: &str = "g1-test";
#[derive(Copy, Clone, Debug, PartialEq)]
/// Expected DEWIF currency
pub enum ExpectedCurrency {
/// Expected any currency (no limitation)
Any,
/// Expected specific currency
Specific(Currency),
}
impl ExpectedCurrency {
pub(crate) fn is_valid(self, currency: Currency) -> bool {
match self {
Self::Any => true,
Self::Specific(expected_currency) => expected_currency == currency,
}
}
}
impl Display for ExpectedCurrency {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Any => write!(f, "Any"),
Self::Specific(expected_currency) => expected_currency.fmt(f),
}
}
}
/// DEWIF currency
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct Currency(Option<NonZeroU32>);
impl Currency {
/// None currency
pub fn none() -> Self {
Currency(None)
}
}
impl Display for Currency {
fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
if let Some(currency_code) = self.0 {
match currency_code.get() {
G1_CURRENCY => write!(f, "{}", G1_CURRENCY_STR),
G1_TEST_CURRENCY => write!(f, "{}", G1_TEST_CURRENCY_STR),
other => write!(f, "{}", other),
}
} else {
write!(f, "None")
}
}
}
impl From<u32> for Currency {
fn from(source: u32) -> Self {
Self(NonZeroU32::new(source))
}
}
impl Into<u32> for Currency {
fn into(self) -> u32 {
if let Some(currency_code) = self.0 {
currency_code.get()
} else {
0u32
}
}
}
/// Unknown currency name
#[derive(Copy, Clone, Debug, PartialEq)]
pub struct UnknownCurrencyName;
impl FromStr for Currency {
type Err = UnknownCurrencyName;
fn from_str(source: &str) -> Result<Self, Self::Err> {
match source {
"" => Ok(Currency(None)),
G1_CURRENCY_STR => Ok(Currency(NonZeroU32::new(G1_CURRENCY))),
G1_TEST_CURRENCY_STR => Ok(Currency(NonZeroU32::new(G1_TEST_CURRENCY))),
_ => Err(UnknownCurrencyName),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn display_expected_currency() {
assert_eq!(
"None",
&format!("{}", ExpectedCurrency::Specific(Currency::from(0))),
);
assert_eq!("Any", &format!("{}", ExpectedCurrency::Any));
}
#[test]
fn display_currency() {
assert_eq!(G1_CURRENCY_STR, &format!("{}", Currency::from(G1_CURRENCY)),);
assert_eq!(
G1_TEST_CURRENCY_STR,
&format!("{}", Currency::from(G1_TEST_CURRENCY)),
);
assert_eq!("42", &format!("{}", Currency::from(42)),);
assert_eq!("None", &format!("{}", Currency::from(0)),);
}
#[test]
fn currency_from_str() {
assert_eq!(
Currency::from(G1_CURRENCY),
Currency::from_str(G1_CURRENCY_STR).expect("unknown currency"),
);
assert_eq!(
Currency::from(G1_TEST_CURRENCY),
Currency::from_str(G1_TEST_CURRENCY_STR).expect("unknown currency"),
);
assert_eq!(
Err(UnknownCurrencyName),
Currency::from_str("unknown currency"),
);
}
}
...@@ -15,6 +15,7 @@ ...@@ -15,6 +15,7 @@
//! Read [DEWIF](https://git.duniter.org/nodes/common/doc/blob/dewif/rfc/0013_Duniter_Encrypted_Wallet_Import_Format.md) file content //! Read [DEWIF](https://git.duniter.org/nodes/common/doc/blob/dewif/rfc/0013_Duniter_Encrypted_Wallet_Import_Format.md) file content
use super::{Currency, ExpectedCurrency};
use crate::keys::ed25519::{KeyPairFromSeed32Generator, PublicKey, PUBKEY_SIZE_IN_BYTES}; use crate::keys::ed25519::{KeyPairFromSeed32Generator, PublicKey, PUBKEY_SIZE_IN_BYTES};
use crate::keys::KeyPairEnum; use crate::keys::KeyPairEnum;
use crate::seeds::Seed32; use crate::seeds::Seed32;
...@@ -32,22 +33,30 @@ type KeyPairsIter = arrayvec::IntoIter<[KeyPairEnum; MAX_KEYPAIRS_COUNT]>; ...@@ -32,22 +33,30 @@ type KeyPairsIter = arrayvec::IntoIter<[KeyPairEnum; MAX_KEYPAIRS_COUNT]>;
#[derive(Clone, Debug, Error)] #[derive(Clone, Debug, Error)]
pub enum DewifReadError { pub enum DewifReadError {
/// DEWIF file content is corrupted /// DEWIF file content is corrupted
#[error("DEWIF file content is corrupted")] #[error("DEWIF file content is corrupted.")]
CorruptedContent, CorruptedContent,
/// Invalid base 64 string /// Invalid base 64 string
#[error("Invalid base 64 string: {0}")] #[error("Invalid base 64 string: {0}.")]
InvalidBase64Str(base64::DecodeError), InvalidBase64Str(base64::DecodeError),
/// Invalid format /// Invalid format
#[error("Invalid format")] #[error("Invalid format.")]
InvalidFormat, InvalidFormat,
/// Too short content /// Too short content
#[error("Too short content")] #[error("Too short content.")]
TooShortContent, TooShortContent,
/// Too long content /// Too long content
#[error("Too long content")] #[error("Too long content.")]
TooLongContent, TooLongContent,
/// Unexpected currency
#[error("Unexpected currency '{actual}', expected: '{expected}'.")]
UnexpectedCurrency {
/// Expected currency
expected: ExpectedCurrency,
/// Actual currency
actual: Currency,
},
/// Unsupported version /// Unsupported version
#[error("Version {actual:?} is not supported. Supported versions: [1, 2].")] #[error("Version {actual} is not supported. Supported versions: [1, 2].")]
UnsupportedVersion { UnsupportedVersion {
/// Actual version /// Actual version
actual: u32, actual: u32,
...@@ -56,24 +65,33 @@ pub enum DewifReadError { ...@@ -56,24 +65,33 @@ pub enum DewifReadError {
/// read dewif file content with user passphrase /// read dewif file content with user passphrase
pub fn read_dewif_file_content( pub fn read_dewif_file_content(
expected_currency: ExpectedCurrency,
file_content: &str, file_content: &str,
passphrase: &str, passphrase: &str,
) -> Result<impl Iterator<Item = KeyPairEnum>, DewifReadError> { ) -> Result<impl Iterator<Item = KeyPairEnum>, DewifReadError> {
let mut bytes = base64::decode(file_content).map_err(DewifReadError::InvalidBase64Str)?; let mut bytes = base64::decode(file_content).map_err(DewifReadError::InvalidBase64Str)?;
if bytes.len() < 4 { if bytes.len() < 8 {
return Err(DewifReadError::TooShortContent); return Err(DewifReadError::TooShortContent);
} }
let version = byteorder::BigEndian::read_u32(&bytes[0..4]); let version = byteorder::BigEndian::read_u32(&bytes[0..4]);
let currency = Currency::from(byteorder::BigEndian::read_u32(&bytes[4..8]));
if !expected_currency.is_valid(currency) {
return Err(DewifReadError::UnexpectedCurrency {
expected: expected_currency,
actual: currency,
});
}
match version { match version {
1 => Ok({ 1 => Ok({
let mut array_keypairs = KeyPairsArray::new(); let mut array_keypairs = KeyPairsArray::new();
array_keypairs.push(read_dewif_v1(&mut bytes[4..], passphrase)?); array_keypairs.push(read_dewif_v1(&mut bytes[8..], passphrase)?);
array_keypairs.into_iter() array_keypairs.into_iter()
}), }),
2 => read_dewif_v2(&mut bytes[4..], passphrase), 2 => read_dewif_v2(&mut bytes[8..], passphrase),
other_version => Err(DewifReadError::UnsupportedVersion { other_version => Err(DewifReadError::UnsupportedVersion {
actual: other_version, actual: other_version,
}), }),
...@@ -142,7 +160,8 @@ mod tests { ...@@ -142,7 +160,8 @@ mod tests {
#[test] #[test]
fn read_unsupported_version() -> Result<(), ()> { fn read_unsupported_version() -> Result<(), ()> {
if let Err(DewifReadError::UnsupportedVersion { .. }) = read_dewif_file_content( if let Err(DewifReadError::UnsupportedVersion { .. }) = read_dewif_file_content(
"ABAAAfKjMzOFfhwgypF3mAx0QDXyozMzhX4cIMqRd5gMdEA1WZwQjCR49iZDK2QhYfdTbPz9AGB01edt4iRSzdTp3c4=", ExpectedCurrency::Any,
"ABAAAb30ng3kI9QGMbR7TYCqPhS99J4N5CPUBjG0e02Aqj4U1UmOemI6pweNm1Ab1AR4V6ZWFnwkkp9QPxppVeMv7aaLWdop",
"toto" "toto"
) { ) {
Ok(()) Ok(())
...@@ -153,7 +172,9 @@ mod tests { ...@@ -153,7 +172,9 @@ mod tests {
#[test] #[test]
fn read_too_short_content() -> Result<(), ()> { fn read_too_short_content() -> Result<(), ()> {
if let Err(DewifReadError::TooShortContent) = read_dewif_file_content("AAA", "toto") { if let Err(DewifReadError::TooShortContent) =
read_dewif_file_content(ExpectedCurrency::Any, "AAA", "toto")
{
Ok(()) Ok(())
} else { } else {
panic!("Read must be fail with error TooShortContent.") panic!("Read must be fail with error TooShortContent.")
...@@ -161,19 +182,39 @@ mod tests { ...@@ -161,19 +182,39 @@ mod tests {
} }
#[test] #[test]
fn tmp() { fn read_unexpected_currency() -> Result<(), ()> {
if let Err(DewifReadError::UnexpectedCurrency { .. }) = read_dewif_file_content(
ExpectedCurrency::Specific(Currency::from(42)),
"AAAAARAAAAEx3yd707xD3F5ttjcISbZzXRrko4pKUmCDIF/emfcVU9MvBqCJQS9R2sWlqbtI1Q37sLQhkj/W7tqY+hxm7mFQ",
"toto titi tata"
) {
Ok(())
} else {
panic!("Read must be fail with error UnexpectedCurrency.")
}
}
#[test]
fn read_ok() {
use crate::dewif::Currency;
use crate::keys::{KeyPair, Signator}; use crate::keys::{KeyPair, Signator};
use std::str::FromStr;
// Get DEWIF file content (Usually from disk) // Get DEWIF file content (Usually from disk)
let dewif_file_content = "AAAAATHfJ3vTvEPcXm22NwhJtnNdGuSjikpSYIMgX96Z9xVT0y8GoIlBL1HaxaWpu0jVDfuwtCGSP9bu2pj6HGbuYVA="; let dewif_file_content = "AAAAARAAAAEx3yd707xD3F5ttjcISbZzXRrko4pKUmCDIF/emfcVU9MvBqCJQS9R2sWlqbtI1Q37sLQhkj/W7tqY+hxm7mFQ";
// Get user passphrase for DEWIF decryption (from cli prompt or gui) // Get user passphrase for DEWIF decryption (from cli prompt or gui)
let encryption_passphrase = "toto titi tata"; let encryption_passphrase = "toto titi tata";
// Expected currency
let expected_currency =
ExpectedCurrency::Specific(Currency::from_str("g1-test").expect("unknown currency"));
// Read DEWIF file content // Read DEWIF file content
// If the file content is correct, we get a key-pair iterator. // If the file content is correct, we get a key-pair iterator.
let mut key_pair_iter = read_dewif_file_content(dewif_file_content, encryption_passphrase) let mut key_pair_iter =
.expect("invalid DEWIF file."); read_dewif_file_content(expected_currency, dewif_file_content, encryption_passphrase)
.expect("invalid DEWIF file");
// Get first key-pair // Get first key-pair
let key_pair = key_pair_iter let key_pair = key_pair_iter
......
...@@ -15,38 +15,52 @@ ...@@ -15,38 +15,52 @@
//! Write [DEWIF](https://git.duniter.org/nodes/common/doc/blob/dewif/rfc/0013_Duniter_Encrypted_Wallet_Import_Format.md) file content //! Write [DEWIF](https://git.duniter.org/nodes/common/doc/blob/dewif/rfc/0013_Duniter_Encrypted_Wallet_Import_Format.md) file content
use super::Currency;
use crate::keys::ed25519::Ed25519KeyPair; use crate::keys::ed25519::Ed25519KeyPair;
use arrayvec::ArrayVec; use arrayvec::ArrayVec;
use unwrap::unwrap; use unwrap::unwrap;
/// Write dewif v1 file content with user passphrase /// Write dewif v1 file content with user passphrase
pub fn write_dewif_v1_content(keypair: &Ed25519KeyPair, passphrase: &str) -> String { pub fn write_dewif_v1_content(
currency: Currency,
keypair: &Ed25519KeyPair,
passphrase: &str,
) -> String {
let mut bytes = ArrayVec::<[u8; super::V1_BYTES_LEN]>::new(); let mut bytes = ArrayVec::<[u8; super::V1_BYTES_LEN]>::new();
unwrap!(bytes.try_extend_from_slice(super::VERSION_V1)); unwrap!(bytes.try_extend_from_slice(super::VERSION_V1));
let currency_code: u32 = currency.into();
unwrap!(bytes.try_extend_from_slice(&currency_code.to_be_bytes()));
unwrap!(bytes.try_extend_from_slice(keypair.seed().as_ref())); unwrap!(bytes.try_extend_from_slice(keypair.seed().as_ref()));
unwrap!(bytes.try_extend_from_slice(keypair.pubkey().as_ref())); unwrap!(bytes.try_extend_from_slice(keypair.pubkey().as_ref()));
let cipher = crate::aes256::new_cipher(super::gen_aes_seed(passphrase)); let cipher = crate::aes256::new_cipher(super::gen_aes_seed(passphrase));
crate::aes256::encrypt::encrypt_n_blocks(&cipher, &mut bytes[4..], super::V1_AES_BLOCKS_COUNT); crate::aes256::encrypt::encrypt_n_blocks(
&cipher,
&mut bytes[super::UNENCRYPTED_BYTES_LEN..],
super::V1_AES_BLOCKS_COUNT,
);
base64::encode(bytes.as_ref()) base64::encode(bytes.as_ref())
} }
/// Write dewif v2 file content with user passphrase /// Write dewif v2 file content with user passphrase
pub fn write_dewif_v2_content( pub fn write_dewif_v2_content(
currency: Currency,
keypair1: &Ed25519KeyPair, keypair1: &Ed25519KeyPair,
keypair2: &Ed25519KeyPair, keypair2: &Ed25519KeyPair,
passphrase: &str, passphrase: &str,
) -> String { ) -> String {
let mut bytes = ArrayVec::<[u8; super::V2_BYTES_LEN]>::new(); let mut bytes = ArrayVec::<[u8; super::V2_BYTES_LEN]>::new();
unwrap!(bytes.try_extend_from_slice(super::VERSION_V2)); unwrap!(bytes.try_extend_from_slice(super::VERSION_V2));
let currency_code: u32 = currency.into();
unwrap!(bytes.try_extend_from_slice(&currency_code.to_be_bytes()));
unwrap!(bytes.try_extend_from_slice(keypair1.seed().as_ref())); unwrap!(bytes.try_extend_from_slice(keypair1.seed().as_ref()));
unwrap!(bytes.try_extend_from_slice(keypair1.pubkey().as_ref())); unwrap!(bytes.try_extend_from_slice(keypair1.pubkey().as_ref()));
unwrap!(bytes.try_extend_from_slice(keypair2.seed().as_ref())); unwrap!(bytes.try_extend_from_slice(keypair2.seed().as_ref()));
unwrap!(bytes.try_extend_from_slice(keypair2.pubkey().as_ref())); unwrap!(bytes.try_extend_from_slice(keypair2.pubkey().as_ref()));
let cipher = crate::aes256::new_cipher(super::gen_aes_seed(passphrase)); let cipher = crate::aes256::new_cipher(super::gen_aes_seed(passphrase));
crate::aes256::encrypt::encrypt_8_blocks(&cipher, &mut bytes[super::VERSION_BYTES..]); crate::aes256::encrypt::encrypt_8_blocks(&cipher, &mut bytes[super::UNENCRYPTED_BYTES_LEN..]);
base64::encode(bytes.as_ref()) base64::encode(bytes.as_ref())
} }
...@@ -57,15 +71,44 @@ mod tests { ...@@ -57,15 +71,44 @@ mod tests {
use super::*; use super::*;
use crate::keys::ed25519::KeyPairFromSeed32Generator; use crate::keys::ed25519::KeyPairFromSeed32Generator;
use crate::seeds::Seed32; use crate::seeds::Seed32;
use std::str::FromStr;
#[test] #[test]
fn write_dewif_v1() { fn write_dewif_v1() {
let keypair = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32])); let keypair = KeyPairFromSeed32Generator::generate(Seed32::new([0u8; 32]));
let dewif_content = write_dewif_v1_content(&keypair, "toto"); let dewif_content = write_dewif_v1_content(Currency::none(), &keypair, "toto");
println!("{}", dewif_content); println!("{}", dewif_content);
assert_eq!( assert_eq!(
"AAAAAb30ng3kI9QGMbR7TYCqPhS99J4N5CPUBjG0e02Aqj4UElionaHOt0kv+eaWgGSGkrP1LQfuwivuvg7+9n0gd18=", "AAAAAQAAAAC99J4N5CPUBjG0e02Aqj4UvfSeDeQj1AYxtHtNgKo+FBJYqJ2hzrdJL/nmloBkhpKz9S0H7sIr7r4O/vZ9IHdf",
dewif_content
)
}
#[test]
fn write_ok() {
use crate::dewif::write_dewif_v1_content;
use crate::keys::ed25519::{KeyPairFromSaltedPasswordGenerator, SaltedPassword};
// Get user credentials (from cli prompt or gui)
let credentials = SaltedPassword::new("user salt".to_owned(), "user password".to_owned());
// Generate ed25519 keypair
let keypair =
KeyPairFromSaltedPasswordGenerator::with_default_parameters().generate(credentials);
// Get user passphrase for DEWIF encryption
let encryption_passphrase = "toto titi tata";
// Expected currency
let expected_currency = Currency::from_str("g1-test").expect("unknown currency");
// Serialize keypair in DEWIF format
let dewif_content =
write_dewif_v1_content(expected_currency, &keypair, encryption_passphrase);
assert_eq!(
"AAAAARAAAAEx3yd707xD3F5ttjcISbZzXRrko4pKUmCDIF/emfcVU9MvBqCJQS9R2sWlqbtI1Q37sLQhkj/W7tqY+hxm7mFQ",
dewif_content dewif_content
) )
} }
......
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