diff --git a/native/dubp_rs/src/dewif.rs b/native/dubp_rs/src/dewif.rs index 6fe856a..a7905e3 100644 --- a/native/dubp_rs/src/dewif.rs +++ b/native/dubp_rs/src/dewif.rs @@ -15,48 +15,26 @@ use crate::*; -pub(super) fn gen_pin6() -> Result { - let i = dup_crypto::rand::gen_u32().map_err(|_| DubpError::RandErr)?; - Ok(gen_pin6_inner(i)) -} -pub(super) fn gen_pin8() -> Result { - let i = dup_crypto::rand::gen_u32().map_err(|_| DubpError::RandErr)?; - let i2 = dup_crypto::rand::gen_u32().map_err(|_| DubpError::RandErr)?; - let mut pin = gen_pin6_inner(i); - gen_pin2_inner(i2, &mut pin); - Ok(pin) -} -pub(super) fn gen_pin10() -> Result { - let i = dup_crypto::rand::gen_u32().map_err(|_| DubpError::RandErr)?; - let i2 = dup_crypto::rand::gen_u32().map_err(|_| DubpError::RandErr)?; - let mut pin = gen_pin6_inner(i); - gen_pin4_inner(i2, &mut pin); - Ok(pin) -} - -pub(super) fn change_pin( +pub(super) fn change_secret_code( currency: &str, dewif: &str, - old_pin: &str, + old_secret_code: &str, member_wallet: bool, + secret_code_type: SecretCodeType, ) -> Result, DubpError> { let currency = parse_currency(currency)?; let mut keypairs = dup_crypto::dewif::read_dewif_file_content( ExpectedCurrency::Specific(currency), dewif, - old_pin, + old_secret_code, ) .map_err(DubpError::DewifReadError)?; if let Some(KeyPairEnum::Ed25519(keypair)) = keypairs.next() { - let new_pin = if member_wallet { - gen_pin10()? - } else { - gen_pin6()? - }; + let new_secret_code = gen_secret_code(member_wallet, secret_code_type)?; - let dewif = dup_crypto::dewif::write_dewif_v1_content(currency, &keypair, &new_pin); + let dewif = dup_crypto::dewif::write_dewif_v1_content(currency, &keypair, &new_secret_code); let pubkey = keypair.public_key().to_base58(); - Ok(vec![dewif, new_pin, pubkey]) + Ok(vec![dewif, new_secret_code, pubkey]) } else { Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)) } @@ -64,24 +42,21 @@ pub(super) fn change_pin( pub(super) fn gen_dewif( currency: &str, - language: u32, + language: Language, mnemonic: &str, member_wallet: bool, + secret_code_type: SecretCodeType, ) -> Result, DubpError> { let currency = parse_currency(currency)?; - let mnemonic = Mnemonic::from_phrase(mnemonic, u32_to_language(language)?) - .map_err(|_| DubpError::WrongLanguage)?; + let mnemonic = + Mnemonic::from_phrase(mnemonic, language).map_err(|_| DubpError::WrongLanguage)?; let seed = dup_crypto::mnemonic::mnemonic_to_seed(&mnemonic); let keypair = KeyPairFromSeed32Generator::generate(seed); - let pin = if member_wallet { - gen_pin10()? - } else { - gen_pin6()? - }; - let dewif = dup_crypto::dewif::write_dewif_v1_content(currency, &keypair, &pin); + let secret_code = gen_secret_code(member_wallet, secret_code_type)?; + let dewif = dup_crypto::dewif::write_dewif_v1_content(currency, &keypair, &secret_code); let pubkey = keypair.public_key().to_base58(); - Ok(vec![dewif, pin, pubkey]) + Ok(vec![dewif, secret_code, pubkey]) } pub(super) fn get_pubkey(currency: Currency, dewif: &str, pin: &str) -> Result { @@ -136,83 +111,3 @@ pub(super) fn sign_several( Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)) } } - -fn gen_pin2_inner(mut i: u32, pin: &mut String) { - for _ in 0..2 { - pin.push(to_char(i)); - i /= 35; - } -} -fn gen_pin4_inner(mut i: u32, pin: &mut String) { - for _ in 0..4 { - pin.push(to_char(i)); - i /= 35; - } -} -fn gen_pin6_inner(mut i: u32) -> String { - let mut pin = String::new(); - - for _ in 0..6 { - pin.push(to_char(i)); - i /= 35; - } - - pin -} - -fn to_char(i: u32) -> char { - match i % 35 { - 0 => 'Z', - 1 => '1', - 2 => '2', - 3 => '3', - 4 => '4', - 5 => '5', - 6 => '6', - 7 => '7', - 8 => '8', - 9 => '9', - 10 => 'A', - 11 => 'B', - 12 => 'C', - 13 => 'D', - 14 => 'E', - 15 => 'F', - 16 => 'G', - 17 => 'H', - 18 => 'I', - 19 => 'J', - 20 => 'K', - 21 => 'L', - 22 => 'M', - 23 => 'N', - 24 => 'O', - 25 => 'P', - 26 => 'Q', - 27 => 'R', - 28 => 'S', - 29 => 'T', - 30 => 'U', - 31 => 'V', - 32 => 'W', - 33 => 'X', - 34 => 'Y', - _ => unreachable!(), - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn test_gen_pin_6() { - assert_eq!("ZZZZZZ", &gen_pin6_inner(0)); - assert_eq!("YZZZZZ", &gen_pin6_inner(34)); - assert_eq!("Z1ZZZZ", &gen_pin6_inner(35)); - assert_eq!("ZZ1ZZZ", &gen_pin6_inner(1225)); - assert_eq!("2Z1ZZZ", &gen_pin6_inner(1227)); - assert_eq!("Z11ZZZ", &gen_pin6_inner(1260)); - assert_eq!("111ZZZ", &gen_pin6_inner(1261)); - } -} diff --git a/native/dubp_rs/src/error.rs b/native/dubp_rs/src/error.rs index 63bd5ec..b8f3d41 100644 --- a/native/dubp_rs/src/error.rs +++ b/native/dubp_rs/src/error.rs @@ -22,6 +22,8 @@ pub(crate) enum DubpError { DewifReadError(DewifReadError), #[error("I/O error: {0}")] IoErr(io::Error), + #[error("Digits secret code forbid for member wallet")] + DigitsCodeForbidForMemberWallet, #[error("A given parameter is null")] NullParamErr, #[error("fail to generate random bytes")] diff --git a/native/dubp_rs/src/inputs.rs b/native/dubp_rs/src/inputs.rs new file mode 100644 index 0000000..7959580 --- /dev/null +++ b/native/dubp_rs/src/inputs.rs @@ -0,0 +1,69 @@ +// 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 . + +use crate::*; + +pub(crate) enum SecretCodeType { + Digits, + Letters, +} +impl From for SecretCodeType { + fn from(i: u32) -> Self { + if i == 0 { + SecretCodeType::Digits + } else { + SecretCodeType::Letters + } + } +} + +pub(crate) fn char_ptr_to_str<'a>(c_char_ptr: *const raw::c_char) -> Result<&'a str, DubpError> { + if c_char_ptr.is_null() { + Err(DubpError::NullParamErr) + } else { + unsafe { CStr::from_ptr(c_char_ptr).to_str() }.map_err(DubpError::Utf8Error) + } +} + +pub(crate) fn char_ptr_prt_to_vec_str<'a>( + char_ptr_ptr: *const *const raw::c_char, + len: u32, +) -> Result, DubpError> { + let len = len as usize; + let char_ptr_slice: &[*const raw::c_char] = + unsafe { std::slice::from_raw_parts(char_ptr_ptr, len) }; + let mut str_vec = Vec::with_capacity(len); + for char_ptr in char_ptr_slice { + str_vec.push(char_ptr_to_str(*char_ptr)?); + } + Ok(str_vec) +} + +pub(crate) fn parse_currency(currency: &str) -> Result { + let currency_code = match currency { + "g1" => G1_CURRENCY, + "g1-test" | "gt" => G1_TEST_CURRENCY, + _ => return Err(DubpError::UnknownCurrencyName), + }; + Ok(Currency::from(currency_code)) +} + +pub(crate) fn u32_to_language(i: u32) -> Result { + match i { + 0 => Ok(Language::English), + 1 => Ok(Language::French), + _ => Err(DubpError::UnknownLanguage), + } +} diff --git a/native/dubp_rs/src/lib.rs b/native/dubp_rs/src/lib.rs index 65be056..e0280e0 100644 --- a/native/dubp_rs/src/lib.rs +++ b/native/dubp_rs/src/lib.rs @@ -18,10 +18,14 @@ mod r#async; mod dewif; mod error; +mod inputs; mod mnemonic; +mod secret_code; use crate::error::{DartRes, DubpError}; +use crate::inputs::*; use crate::r#async::exec_async; +use crate::secret_code::gen_secret_code; use allo_isolate::{IntoDart, Isolate}; use dup_crypto::{ bases::b58::ToBase58, @@ -37,52 +41,14 @@ use once_cell::sync::Lazy; use std::{ffi::CStr, io, os::raw}; use thiserror::Error; -pub(crate) fn char_ptr_to_str<'a>(c_char_ptr: *const raw::c_char) -> Result<&'a str, DubpError> { - if c_char_ptr.is_null() { - Err(DubpError::NullParamErr) - } else { - unsafe { CStr::from_ptr(c_char_ptr).to_str() }.map_err(DubpError::Utf8Error) - } -} - -fn char_ptr_prt_to_vec_str<'a>( - char_ptr_ptr: *const *const raw::c_char, - len: u32, -) -> Result, DubpError> { - let len = len as usize; - let char_ptr_slice: &[*const raw::c_char] = - unsafe { std::slice::from_raw_parts(char_ptr_ptr, len) }; - let mut str_vec = Vec::with_capacity(len); - for char_ptr in char_ptr_slice { - str_vec.push(char_ptr_to_str(*char_ptr)?); - } - Ok(str_vec) -} - -pub(crate) fn parse_currency(currency: &str) -> Result { - let currency_code = match currency { - "g1" => G1_CURRENCY, - "g1-test" | "gt" => G1_TEST_CURRENCY, - _ => return Err(DubpError::UnknownCurrencyName), - }; - Ok(Currency::from(currency_code)) -} - -fn u32_to_language(i: u32) -> Result { - match i { - 0 => Ok(Language::English), - 1 => Ok(Language::French), - _ => Err(DubpError::UnknownLanguage), - } -} - #[no_mangle] -pub extern "C" fn change_dewif_pin( +pub extern "C" fn change_dewif_secret_code( port: i64, currency: *const raw::c_char, dewif: *const raw::c_char, old_pin: *const raw::c_char, member_wallet: u32, + secret_code_type: u32, ) { exec_async( port, @@ -91,10 +57,11 @@ pub extern "C" fn change_dewif_pin( let dewif = char_ptr_to_str(dewif)?; let old_pin = char_ptr_to_str(old_pin)?; let member_wallet = member_wallet != 0; - Ok((currency, dewif, old_pin, member_wallet)) + let secret_code_type = SecretCodeType::from(secret_code_type); + Ok((currency, dewif, old_pin, member_wallet, secret_code_type)) }, - |(currency, dewif, old_pin, member_wallet)| { - dewif::change_pin(currency, dewif, old_pin, member_wallet) + |(currency, dewif, old_pin, member_wallet, secret_code_type)| { + dewif::change_secret_code(currency, dewif, old_pin, member_wallet, secret_code_type) }, ) } @@ -106,17 +73,32 @@ pub extern "C" fn gen_dewif( language: u32, mnemonic: *const raw::c_char, member_wallet: u32, + secret_code_type: u32, ) { exec_async( port, || { let currency = char_ptr_to_str(currency)?; + let language = u32_to_language(language)?; let mnemonic = char_ptr_to_str(mnemonic)?; let member_wallet = member_wallet != 0; - Ok((currency, language, mnemonic, member_wallet)) + let secret_code_type = SecretCodeType::from(secret_code_type); + Ok(( + currency, + language, + mnemonic, + member_wallet, + secret_code_type, + )) }, - |(currency, language, mnemonic, member_wallet)| { - dewif::gen_dewif(currency, language, mnemonic, member_wallet) + |(currency, language, mnemonic, member_wallet, secret_code_type)| { + dewif::gen_dewif( + currency, + language, + mnemonic, + member_wallet, + secret_code_type, + ) }, ) } @@ -126,21 +108,6 @@ pub extern "C" fn gen_mnemonic(port: i64, language: u32) { Isolate::new(port).post(DartRes::from(mnemonic::gen_mnemonic(language))); } -#[no_mangle] -pub extern "C" fn gen_pin6(port: i64) { - Isolate::new(port).post(DartRes::from(dewif::gen_pin6())); -} - -#[no_mangle] -pub extern "C" fn gen_pin8(port: i64) { - Isolate::new(port).post(DartRes::from(dewif::gen_pin8())); -} - -#[no_mangle] -pub extern "C" fn gen_pin10(port: i64) { - Isolate::new(port).post(DartRes::from(dewif::gen_pin10())); -} - #[no_mangle] pub extern "C" fn get_dewif_pubkey( port: i64, diff --git a/native/dubp_rs/src/secret_code.rs b/native/dubp_rs/src/secret_code.rs new file mode 100644 index 0000000..f9581e4 --- /dev/null +++ b/native/dubp_rs/src/secret_code.rs @@ -0,0 +1,130 @@ +// 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 . + +use crate::*; + +pub(crate) fn gen_secret_code( + member_wallet: bool, + secret_code_type: SecretCodeType, +) -> Result { + match secret_code_type { + SecretCodeType::Digits => { + if member_wallet { + Err(DubpError::DigitsCodeForbidForMemberWallet) + } else { + gen_random_digits(8) + } + } + SecretCodeType::Letters => { + if member_wallet { + gen_random_letters(10) + } else { + gen_random_letters(6) + } + } + } +} + +fn gen_random_digits(n: usize) -> Result { + let mut digits_string = dup_crypto::rand::gen_u32() + .map_err(|_| DubpError::RandErr)? + .to_string(); + digits_string.truncate(n); + + if digits_string.len() == n { + Ok(digits_string) + } else { + let missing_digits = n - digits_string.len(); + let mut digits_string_ = String::with_capacity(n); + for _ in 0..missing_digits { + digits_string_.push('0'); + } + digits_string_.push_str(&digits_string); + Ok(digits_string_) + } +} + +fn gen_random_letters(mut n: usize) -> Result { + let mut letters = String::with_capacity(n); + while n >= 6 { + letters.push_str(&gen_random_letters_inner(6)?); + n -= 6; + } + letters.push_str(&gen_random_letters_inner(n)?); + + Ok(letters) +} + +fn gen_random_letters_inner(n: usize) -> Result { + let mut i = dup_crypto::rand::gen_u32().map_err(|_| DubpError::RandErr)?; + let mut letters = String::new(); + + for _ in 0..n { + letters.push(to_char(i)); + i /= 26; + } + + Ok(letters) +} + +fn to_char(i: u32) -> char { + match i % 26 { + 0 => 'A', + 1 => 'B', + 2 => 'C', + 3 => 'D', + 4 => 'E', + 5 => 'F', + 6 => 'G', + 7 => 'H', + 8 => 'I', + 9 => 'J', + 10 => 'K', + 11 => 'L', + 12 => 'M', + 13 => 'N', + 14 => 'O', + 15 => 'P', + 16 => 'Q', + 17 => 'R', + 18 => 'S', + 19 => 'T', + 20 => 'U', + 21 => 'V', + 22 => 'W', + 23 => 'X', + 24 => 'Y', + 25 => 'Z', + _ => unreachable!(), + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_gen_random_digits() -> Result<(), DubpError> { + assert_eq!(gen_random_digits(8)?.len(), 8); + //println!("TMP: {}", gen_random_digits(8)?); + Ok(()) + } + #[test] + fn test_gen_random_letters() -> Result<(), DubpError> { + assert_eq!(gen_random_letters(6)?.len(), 6); + //println!("TMP: {}", gen_random_letters(6)?); + Ok(()) + } +} diff --git a/packages/dubp_rs/lib/dubp.dart b/packages/dubp_rs/lib/dubp.dart index 9856183..e2f1d2e 100644 --- a/packages/dubp_rs/lib/dubp.dart +++ b/packages/dubp_rs/lib/dubp.dart @@ -19,7 +19,7 @@ class NewWallet { /// DEWIF: Encrypted wallet String dewif; - /// Pin code + /// Secret code String pin; /// Public key @@ -28,16 +28,13 @@ class NewWallet { NewWallet._(this.dewif, this.pin, this.publicKey); } -/// Pin code length -enum PinLength { - /// 6 characters - six, +/// Secret code type +enum SecretCodeType { + /// Digits + digits, - /// 8 characters - eight, - - /// 10 characters - ten, + /// Letters + letters, } /// DUBP Rust utilities @@ -63,21 +60,23 @@ class DubpRust { return completer.future; } - /// Change the pin code that encrypts the `dewif` keypair. - static Future changeDewifPin( - {String currency = "g1", - String dewif, - String oldPin, - PinLength newPinLength = PinLength.six}) async { + /// Change the secret code that encrypts the `dewif` keypair. + static Future changeDewifPin({ + String currency = "g1", + String dewif, + String oldPin, + SecretCodeType secretCodeType = SecretCodeType.letters, + }) async { final completer = Completer>(); final sendPort = singleCompletePort, List>(completer, callback: _handleErrList); - native.change_dewif_pin( + native.change_dewif_secret_code( sendPort.nativePort, Utf8.toUtf8(currency), Utf8.toUtf8(dewif), Utf8.toUtf8(oldPin), 0, + secretCodeType.index, ); List newWallet = await completer.future; @@ -91,11 +90,12 @@ class DubpRust { /// /// If the wallet to be generated is not dedicated to the Ğ1 currency, you /// must indicate the currency for which this wallet will be used. - static Future genWalletFromMnemonic( - {String currency = "g1", - Language language = Language.english, - String mnemonic, - PinLength pinLength = PinLength.six}) async { + static Future genWalletFromMnemonic({ + String currency = "g1", + Language language = Language.english, + String mnemonic, + SecretCodeType secretCodeType = SecretCodeType.letters, + }) async { final completer = Completer>(); final sendPort = singleCompletePort, List>(completer, callback: _handleErrList); @@ -105,6 +105,7 @@ class DubpRust { language.index, Utf8.toUtf8(mnemonic), 0, + secretCodeType.index, ); List newWallet = await completer.future;