diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..393611e --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,3 @@ +[alias] +bd = "make android-dev32" +br = "make" diff --git a/Cargo.lock b/Cargo.lock index 93595d1..2c6ad0e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -59,6 +59,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b" +[[package]] +name = "async-oneshot" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50f4770cbbff928c30a991de67fb3976f44d8e3e202f8c79ef91b47006e04904" +dependencies = [ + "futures-micro", +] + [[package]] name = "atty" version = "0.2.14" @@ -245,6 +254,8 @@ dependencies = [ "cbindgen", "dart-bindgen", "dup-crypto", + "fast-threadpool", + "once_cell", "thiserror", ] @@ -270,6 +281,32 @@ dependencies = [ "zeroize", ] +[[package]] +name = "fast-threadpool" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cccb66774e02743b4be61c19e09bf59de9058e9a7b4096040689003e7a901a3d" +dependencies = [ + "async-oneshot", + "flume", + "num_cpus", +] + +[[package]] +name = "flume" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0362ef9c4c1fa854ff95b4cb78045a86e810d804dc04937961988b45427104a9" +dependencies = [ + "spinning_top", +] + +[[package]] +name = "futures-micro" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61e9325be55c5581082cd110294fa988c1f920bc573ec370ef201e33c469a95a" + [[package]] name = "generic-array" version = "0.14.4" @@ -344,6 +381,15 @@ version = "0.2.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1482821306169ec4d07f6aca392a4681f66c75c9918aa49641a2595db64053cb" +[[package]] +name = "lock_api" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd96ffd135b2fd7b973ac026d28085defbe8983df057ced3eb4f2130b0831312" +dependencies = [ + "scopeguard", +] + [[package]] name = "log" version = "0.4.11" @@ -353,6 +399,16 @@ dependencies = [ "cfg-if 0.1.10", ] +[[package]] +name = "num_cpus" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05499f3756671c15885fee9034446956fff3f243d6077b91e5767df161f766b3" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "once_cell" version = "1.5.2" @@ -466,6 +522,12 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71d301d4193d031abdd79ff7e3dd721168a9572ef3fe51a1517aba235bd8f86e" +[[package]] +name = "scopeguard" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" + [[package]] name = "serde" version = "1.0.118" @@ -503,6 +565,15 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d" +[[package]] +name = "spinning_top" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7e529d73e80d64b5f2631f9035113347c578a1c9c7774b83a2b880788459ab36" +dependencies = [ + "lock_api", +] + [[package]] name = "strsim" version = "0.8.0" diff --git a/native/dubp_rs/Cargo.toml b/native/dubp_rs/Cargo.toml index feeb05b..58d56c9 100644 --- a/native/dubp_rs/Cargo.toml +++ b/native/dubp_rs/Cargo.toml @@ -11,6 +11,8 @@ crate-type = ["rlib"] [dependencies] allo-isolate = "0.1.6" dup-crypto = { version = "0.36.0", features = ["dewif", "mnemonic", "mnemonic_french", "rand", "scrypt"] } +fast-threadpool = { version = "0.3.0", default-features = false } +once_cell = { version = "1.3.1", default-features = false, features = ["std"] } thiserror = "1.0.23" [build-dependencies] diff --git a/native/dubp_rs/src/async.rs b/native/dubp_rs/src/async.rs new file mode 100644 index 0000000..b7ce764 --- /dev/null +++ b/native/dubp_rs/src/async.rs @@ -0,0 +1,42 @@ +// 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::*; + +static THREAD_POOL: Lazy> = Lazy::new(|| { + ThreadPool::start(ThreadPoolConfig::low().queue_size(Some(16)), ()).into_sync_handler() +}); + +pub(crate) fn exec_async(port: i64, parse_params: F, async_job: F2) +where + P: 'static + Send + Sync, + F: FnOnce() -> Result, + F2: 'static + Send + Sync + FnOnce(P) -> R, + DartRes: From, +{ + match parse_params() { + Ok(parsed_params) => { + if THREAD_POOL + .launch(move |_| Isolate::new(port).post(DartRes::from(async_job(parsed_params)))) + .is_err() + { + Isolate::new(port).post(DartRes::err("thread pool panicked")); + } + } + Err(e) => { + Isolate::new(port).post(DartRes::err(e)); + } + } +} diff --git a/native/dubp_rs/src/dewif.rs b/native/dubp_rs/src/dewif.rs index 7d07565..a7905e3 100644 --- a/native/dubp_rs/src/dewif.rs +++ b/native/dubp_rs/src/dewif.rs @@ -15,82 +15,51 @@ 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( - currency: *const raw::c_char, - dewif: *const raw::c_char, - old_pin: *const raw::c_char, - new_pin: *const raw::c_char, -) -> Result { - let currency = char_ptr_to_str(currency)?; - let dewif = char_ptr_to_str(dewif)?; - let old_pin = char_ptr_to_str(old_pin)?; - let new_pin = char_ptr_to_str(new_pin)?; - +pub(super) fn change_secret_code( + currency: &str, + dewif: &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() { - Ok(dup_crypto::dewif::write_dewif_v1_content( - currency, &keypair, new_pin, - )) + let new_secret_code = gen_secret_code(member_wallet, secret_code_type)?; + + 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_secret_code, pubkey]) } else { Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)) } } pub(super) fn gen_dewif( - currency: *const raw::c_char, - language: u32, - mnemonic: *const raw::c_char, - pin: *const raw::c_char, -) -> Result { - let currency = char_ptr_to_str(currency)?; - let mnemonic = char_ptr_to_str(mnemonic)?; - let pin = char_ptr_to_str(pin)?; - + currency: &str, + 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); - Ok(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, secret_code, pubkey]) } -pub(super) fn get_pubkey( - currency: *const raw::c_char, - dewif: *const raw::c_char, - pin: *const raw::c_char, -) -> Result { - let currency = char_ptr_to_str(currency)?; - let dewif = char_ptr_to_str(dewif)?; - let pin = char_ptr_to_str(pin)?; - - let currency = parse_currency(currency)?; +pub(super) fn get_pubkey(currency: Currency, dewif: &str, pin: &str) -> Result { let mut keypairs = dup_crypto::dewif::read_dewif_file_content( ExpectedCurrency::Specific(currency), dewif, @@ -104,17 +73,7 @@ pub(super) fn get_pubkey( } } -pub(super) fn sign( - currency: *const raw::c_char, - dewif: *const raw::c_char, - pin: *const raw::c_char, - msg: *const raw::c_char, -) -> Result { - let currency = char_ptr_to_str(currency)?; - let dewif = char_ptr_to_str(dewif)?; - let pin = char_ptr_to_str(pin)?; - let msg = char_ptr_to_str(msg)?; - +pub(super) fn sign(currency: &str, dewif: &str, pin: &str, msg: &str) -> Result { let currency = parse_currency(currency)?; let mut keypairs = dup_crypto::dewif::read_dewif_file_content( ExpectedCurrency::Specific(currency), @@ -130,22 +89,11 @@ pub(super) fn sign( } pub(super) fn sign_several( - currency: *const raw::c_char, - dewif: *const raw::c_char, - pin: *const raw::c_char, - msgs_len: usize, - msgs: *const *const raw::c_char, + currency: &str, + dewif: &str, + pin: &str, + msgs: &[&str], ) -> Result, DubpError> { - let currency = char_ptr_to_str(currency)?; - let dewif = char_ptr_to_str(dewif)?; - let pin = char_ptr_to_str(pin)?; - - let msgs_slice: &[*const raw::c_char] = unsafe { std::slice::from_raw_parts(msgs, msgs_len) }; - let mut msgs = Vec::with_capacity(msgs_len); - for ptr_c_char in msgs_slice { - msgs.push(char_ptr_to_str(*ptr_c_char)?); - } - let currency = parse_currency(currency)?; let mut keypairs = dup_crypto::dewif::read_dewif_file_content( ExpectedCurrency::Specific(currency), @@ -163,92 +111,3 @@ pub(super) fn sign_several( Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)) } } - -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 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 new file mode 100644 index 0000000..b8f3d41 --- /dev/null +++ b/native/dubp_rs/src/error.rs @@ -0,0 +1,79 @@ +// 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::*; + +/// Dubp error +#[derive(Debug, Error)] +pub(crate) enum DubpError { + #[error("{0}")] + 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")] + RandErr, + #[error("Unknown currency name")] + UnknownCurrencyName, + #[error("Unknown language")] + UnknownLanguage, + #[error("{0}")] + Utf8Error(std::str::Utf8Error), + #[error("Wrong language")] + WrongLanguage, +} + +impl From for DubpError { + fn from(e: io::Error) -> Self { + Self::IoErr(e) + } +} + +pub(crate) struct DartRes(allo_isolate::ffi::DartCObject); +impl DartRes { + pub(crate) fn err(e: E) -> allo_isolate::ffi::DartCObject { + vec![format!("DUBP_RS_ERROR: {}", e.to_string())].into_dart() + } +} +impl IntoDart for DartRes { + fn into_dart(self) -> allo_isolate::ffi::DartCObject { + self.0.into_dart() + } +} +impl From> for DartRes +where + E: ToString, +{ + fn from(res: Result) -> Self { + match res { + Ok(string) => Self(string.into_dart()), + Err(e) => Self(format!("DUBP_RS_ERROR: {}", e.to_string()).into_dart()), + } + } +} +impl From, E>> for DartRes +where + E: ToString, +{ + fn from(res: Result, E>) -> Self { + match res { + Ok(vec_string) => Self(vec_string.into_dart()), + Err(e) => Self(vec![format!("DUBP_RS_ERROR: {}", e.to_string())].into_dart()), + } + } +} 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 8d2f03a..e0280e0 100644 --- a/native/dubp_rs/src/lib.rs +++ b/native/dubp_rs/src/lib.rs @@ -15,9 +15,17 @@ #![allow(clippy::missing_safety_doc, clippy::not_unsafe_ptr_arg_deref)] +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, @@ -28,84 +36,34 @@ use dup_crypto::{ }, mnemonic::{Language, Mnemonic, MnemonicType}, }; +use fast_threadpool::{ThreadPool, ThreadPoolConfig, ThreadPoolSyncHandler}; +use once_cell::sync::Lazy; use std::{ffi::CStr, io, os::raw}; use thiserror::Error; -/// Dubp error -#[derive(Debug, Error)] -pub enum DubpError { - #[error("{0}")] - DewifReadError(DewifReadError), - #[error("I/O error: {0}")] - IoErr(io::Error), - #[error("A given parameter is null")] - NullParamErr, - #[error("fail to generate random bytes")] - RandErr, - #[error("Unknown currency name")] - UnknownCurrencyName, - #[error("Unknown language")] - UnknownLanguage, - #[error("{0}")] - Utf8Error(std::str::Utf8Error), - #[error("Wrong language")] - WrongLanguage, -} - -impl From for DubpError { - fn from(e: io::Error) -> Self { - Self::IoErr(e) - } -} - -struct DartRes(allo_isolate::ffi::DartCObject); -impl IntoDart for DartRes { - fn into_dart(self) -> allo_isolate::ffi::DartCObject { - self.0.into_dart() - } -} -impl From> for DartRes -where - E: ToString, -{ - fn from(res: Result) -> Self { - match res { - Ok(string) => Self(vec![string].into_dart()), - Err(e) => Self(vec![String::from("_"), e.to_string()].into_dart()), - } - } -} -impl From, E>> for DartRes -where - E: ToString, -{ - fn from(res: Result, E>) -> Self { - match res { - Ok(vec_string) => Self(vec_string.into_dart()), - Err(e) => Self(vec![String::with_capacity(0), e.to_string()].into_dart()), - } - } -} - -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) - } -} - #[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, - new_pin: *const raw::c_char, + member_wallet: u32, + secret_code_type: u32, ) { - Isolate::new(port).post(DartRes::from(dewif::change_pin( - currency, dewif, old_pin, new_pin, - ))); + exec_async( + port, + || { + let currency = char_ptr_to_str(currency)?; + let dewif = char_ptr_to_str(dewif)?; + let old_pin = char_ptr_to_str(old_pin)?; + let member_wallet = member_wallet != 0; + 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, secret_code_type)| { + dewif::change_secret_code(currency, dewif, old_pin, member_wallet, secret_code_type) + }, + ) } #[no_mangle] @@ -114,11 +72,35 @@ pub extern "C" fn gen_dewif( currency: *const raw::c_char, language: u32, mnemonic: *const raw::c_char, - pin: *const raw::c_char, + member_wallet: u32, + secret_code_type: u32, ) { - Isolate::new(port).post(DartRes::from(dewif::gen_dewif( - currency, language, mnemonic, pin, - ))); + 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; + let secret_code_type = SecretCodeType::from(secret_code_type); + Ok(( + currency, + language, + mnemonic, + member_wallet, + secret_code_type, + )) + }, + |(currency, language, mnemonic, member_wallet, secret_code_type)| { + dewif::gen_dewif( + currency, + language, + mnemonic, + member_wallet, + secret_code_type, + ) + }, + ) } #[no_mangle] @@ -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, @@ -148,7 +115,16 @@ pub extern "C" fn get_dewif_pubkey( dewif: *const raw::c_char, pin: *const raw::c_char, ) { - Isolate::new(port).post(DartRes::from(dewif::get_pubkey(currency, dewif, pin))); + exec_async( + port, + || { + let currency = parse_currency(char_ptr_to_str(currency)?)?; + let dewif = char_ptr_to_str(dewif)?; + let pin = char_ptr_to_str(pin)?; + Ok((currency, dewif, pin)) + }, + |(currency, dewif, pin)| dewif::get_pubkey(currency, dewif, pin), + ) } #[no_mangle] @@ -157,10 +133,14 @@ pub extern "C" fn mnemonic_to_pubkey( language: u32, mnemonic_phrase: *const raw::c_char, ) { - Isolate::new(port).post(DartRes::from(mnemonic::mnemonic_to_pubkey( - language, - mnemonic_phrase, - ))); + exec_async( + port, + || { + let mnemonic_phrase = char_ptr_to_str(mnemonic_phrase)?; + Ok((language, mnemonic_phrase)) + }, + |(language, mnemonic_phrase)| mnemonic::mnemonic_to_pubkey(language, mnemonic_phrase), + ) } #[no_mangle] @@ -171,7 +151,17 @@ pub extern "C" fn sign( pin: *const raw::c_char, msg: *const raw::c_char, ) { - Isolate::new(port).post(DartRes::from(dewif::sign(currency, dewif, pin, msg))); + exec_async( + port, + || { + let currency = char_ptr_to_str(currency)?; + let dewif = char_ptr_to_str(dewif)?; + let pin = char_ptr_to_str(pin)?; + let msg = char_ptr_to_str(msg)?; + Ok((currency, dewif, pin, msg)) + }, + |(currency, dewif, pin, msg)| dewif::sign(currency, dewif, pin, msg), + ) } #[no_mangle] @@ -180,16 +170,18 @@ pub extern "C" fn sign_several( currency: *const raw::c_char, dewif: *const raw::c_char, pin: *const raw::c_char, - msgs_len: usize, + msgs_len: u32, msgs: *const *const raw::c_char, ) { - Isolate::new(port).post(dewif::sign_several(currency, dewif, pin, msgs_len, msgs)); -} - -fn u32_to_language(i: u32) -> Result { - match i { - 0 => Ok(Language::English), - 1 => Ok(Language::French), - _ => Err(DubpError::UnknownLanguage), - } + exec_async( + port, + || { + let currency = char_ptr_to_str(currency)?; + let dewif = char_ptr_to_str(dewif)?; + let pin = char_ptr_to_str(pin)?; + let msgs = char_ptr_prt_to_vec_str(msgs, msgs_len)?; + Ok((currency, dewif, pin, msgs)) + }, + |(currency, dewif, pin, msgs)| dewif::sign_several(currency, dewif, pin, &msgs), + ) } diff --git a/native/dubp_rs/src/mnemonic.rs b/native/dubp_rs/src/mnemonic.rs index ac17486..765427d 100644 --- a/native/dubp_rs/src/mnemonic.rs +++ b/native/dubp_rs/src/mnemonic.rs @@ -21,12 +21,7 @@ pub(super) fn gen_mnemonic(language: u32) -> Result { Ok(mnemonic.phrase().to_owned()) } -pub(super) fn mnemonic_to_pubkey( - language: u32, - mnemonic: *const raw::c_char, -) -> Result { - let mnemonic = char_ptr_to_str(mnemonic)?; - +pub(super) fn mnemonic_to_pubkey(language: u32, mnemonic: &str) -> Result { let mnemonic = Mnemonic::from_phrase(mnemonic, u32_to_language(language)?) .map_err(|_| DubpError::WrongLanguage)?; let seed = dup_crypto::mnemonic::mnemonic_to_seed(&mnemonic); 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/README.md b/packages/dubp_rs/README.md index 305dcb4..2c27fe8 100644 --- a/packages/dubp_rs/README.md +++ b/packages/dubp_rs/README.md @@ -57,7 +57,7 @@ The project use [`dart-bindgen`](https://github.com/sunshine-protocol/dart-bindg * 32bit emulator (`x86`/`i686` architecture) ```sh -cargo make android-dev32 +cargo bd ``` * 64bit emulator (`x86_64` architecture) @@ -71,7 +71,7 @@ cargo make android-dev In the Root of the project simply run: ```sh -cargo make +cargo br ``` WARNING: This will take a lot of time because the Rust code will have to be recompiled for each different architecture, 4 times for android and 2 times for iOS! @@ -113,7 +113,7 @@ static Future genWalletFromMnemonic({ String currency = "g1", Language language = Language.english, String mnemonic, - PinLength pinLength = PinLength.six + SecretCodeType secretCodeType = SecretCodeType.letters }); ``` @@ -129,13 +129,13 @@ NewWallet new_wallet = await DubpRust.genWalletFromMnemonic( ); ``` -You can choose a different length for the pin code (6 by default): +You can choose a different secret code type: ```dart NewWallet new_wallet = await DubpRust.genWalletFromMnemonic( language: Language.english, mnemonic: "tongue cute mail fossil great frozen same social weasel impact brush kind", - pinLength: PinLength.eight + secretCodeType: SecretCodeType.digits ); ``` @@ -164,9 +164,9 @@ String signature = await DubpRust.sign( ); ``` -### Change pin code +### Change secret code -You can change the pin code that encrypts the [DEWIF]. +You can change the secret code that encrypts the [DEWIF]. #### Function signature @@ -175,7 +175,7 @@ static Future changeDewifPin({ String currency = "g1", String dewif, String oldPin, - PinLength newPinLength = PinLength.six + SecretCodeType secretCodeType = SecretCodeType.letters }); ``` @@ -186,7 +186,7 @@ If the wallet is not dedicated to the Ğ1 currency, you must indicate the curren ```dart NewWallet new_wallet = await DubpRust.changeDewifPin( dewif: "AAAAARAAAAGfFDAs+jVZYkfhBlHZZ2fEQIvBqnG16g5+02cY18wSOjW0cUg2JV3SUTJYN2CrbQeRDwGazWnzSFBphchMmiL0", - oldPin: "CDJ4UB", + oldPin: "CDJAUB", ); ``` diff --git a/packages/dubp_rs/lib/dubp.dart b/packages/dubp_rs/lib/dubp.dart index a3384e8..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 @@ -55,7 +52,7 @@ class DubpRust { static Future genMnemonic({Language language = Language.english}) { final completer = Completer(); final sendPort = - singleCompletePort(completer, callback: _handleErr); + singleCompletePort(completer, callback: _handleErr); native.gen_mnemonic( sendPort.nativePort, language.index, @@ -63,70 +60,27 @@ class DubpRust { return completer.future; } - static Future _genPin(PinLength pinLength) { - final completer = Completer(); - final sendPort = - singleCompletePort(completer, callback: _handleErr); - switch (pinLength) { - case PinLength.ten: - native.gen_pin10( - sendPort.nativePort, - ); - break; - case PinLength.eight: - native.gen_pin8( - sendPort.nativePort, - ); - break; - case PinLength.six: - default: - native.gen_pin6( - sendPort.nativePort, - ); - break; - } - return completer.future; - } + /// 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_secret_code( + sendPort.nativePort, + Utf8.toUtf8(currency), + Utf8.toUtf8(dewif), + Utf8.toUtf8(oldPin), + 0, + secretCodeType.index, + ); + List newWallet = await 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 { - // pin - String newPin = await _genPin(newPinLength); - // dewif - String newDewif; - { - final completer = Completer(); - final sendPort = - singleCompletePort(completer, callback: _handleErr); - native.change_dewif_pin( - sendPort.nativePort, - Utf8.toUtf8(currency), - Utf8.toUtf8(dewif), - Utf8.toUtf8(oldPin), - Utf8.toUtf8(newPin), - ); - newDewif = await completer.future; - } - // publicKey - String publicKey; - { - final completer = Completer(); - final sendPort = - singleCompletePort(completer, callback: _handleErr); - native.get_dewif_pubkey( - sendPort.nativePort, - Utf8.toUtf8(currency), - Utf8.toUtf8(newDewif), - Utf8.toUtf8(newPin), - ); - publicKey = await completer.future; - } - - return Future.value(NewWallet._(newDewif, newPin, publicKey)); + return Future.value(NewWallet._(newWallet[0], newWallet[1], newWallet[2])); } /// Generate a wallet from a mnemonic phrase. @@ -136,42 +90,26 @@ 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 { - // pin - String pin = await _genPin(pinLength); - // publicKey - String publicKey; - { - final completer = Completer(); - final sendPort = - singleCompletePort(completer, callback: _handleErr); - native.mnemonic_to_pubkey( - sendPort.nativePort, - language.index, - Utf8.toUtf8(mnemonic), - ); - publicKey = await completer.future; - } - // dewif - String dewif; - { - final completer = Completer(); - final sendPort = - singleCompletePort(completer, callback: _handleErr); - native.gen_dewif( - sendPort.nativePort, - Utf8.toUtf8(currency), - language.index, - Utf8.toUtf8(mnemonic), - Utf8.toUtf8(pin), - ); - dewif = await completer.future; - } - return Future.value(NewWallet._(dewif, pin, publicKey)); + 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); + native.gen_dewif( + sendPort.nativePort, + Utf8.toUtf8(currency), + language.index, + Utf8.toUtf8(mnemonic), + 0, + secretCodeType.index, + ); + List newWallet = await completer.future; + + return Future.value(NewWallet._(newWallet[0], newWallet[1], newWallet[2])); } /// Get pulblic key (in base 58) of `dewif` keypair. @@ -179,7 +117,7 @@ class DubpRust { {String currency = "g1", String dewif, String pin}) async { final completer = Completer(); final sendPort = - singleCompletePort(completer, callback: _handleErr); + singleCompletePort(completer, callback: _handleErr); native.get_dewif_pubkey( sendPort.nativePort, Utf8.toUtf8(currency), @@ -196,7 +134,7 @@ class DubpRust { {String currency = "g1", String dewif, String pin, String message}) { final completer = Completer(); final sendPort = - singleCompletePort(completer, callback: _handleErr); + singleCompletePort(completer, callback: _handleErr); native.sign( sendPort.nativePort, Utf8.toUtf8(currency), @@ -243,21 +181,20 @@ class DubpRust { return ptr; } - static String _handleErr(List res) { - final List arr = res.cast(); - if (arr.length == 1) { - return arr[0]; - } else { - final error = arr[1]; + static String _handleErr(String res) { + if (res.startsWith('DUBP_RS_ERROR: ')) { + final error = res; print(error); throw error; + } else { + return res; } } static List _handleErrList(List res) { final List arr = res.cast(); - if (arr.isNotEmpty && arr[0].isEmpty) { - final error = arr[1]; + if (arr.isNotEmpty && arr[0].startsWith('DUBP_RS_ERROR: ')) { + final error = arr[0]; print(error); throw error; } else { diff --git a/pubspec.lock b/pubspec.lock index 376b9d0..8446555 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -21,42 +21,42 @@ packages: name: async url: "https://pub.dartlang.org" source: hosted - version: "2.5.0-nullsafety.3" + version: "2.5.0-nullsafety.1" boolean_selector: dependency: transitive description: name: boolean_selector url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.1" characters: dependency: transitive description: name: characters url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.5" + version: "1.1.0-nullsafety.3" charcode: dependency: transitive description: name: charcode url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" clock: dependency: transitive description: name: clock url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.1" collection: dependency: transitive description: name: collection url: "https://pub.dartlang.org" source: hosted - version: "1.15.0-nullsafety.5" + version: "1.15.0-nullsafety.3" connectivity: dependency: transitive description: @@ -112,7 +112,7 @@ packages: name: fake_async url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" ffi: dependency: transitive description: @@ -282,27 +282,20 @@ packages: url: "https://pub.dartlang.org" source: hosted version: "2.0.3" - js: - dependency: transitive - description: - name: js - url: "https://pub.dartlang.org" - source: hosted - version: "0.6.3-nullsafety.3" matcher: dependency: transitive description: name: matcher url: "https://pub.dartlang.org" source: hosted - version: "0.12.10-nullsafety.3" + version: "0.12.10-nullsafety.1" meta: dependency: transitive description: name: meta url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.6" + version: "1.3.0-nullsafety.3" nested: dependency: transitive description: @@ -330,7 +323,7 @@ packages: name: path url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.3" + version: "1.8.0-nullsafety.1" path_provider: dependency: "direct main" description: @@ -468,42 +461,42 @@ packages: name: source_span url: "https://pub.dartlang.org" source: hosted - version: "1.8.0-nullsafety.4" + version: "1.8.0-nullsafety.2" stack_trace: dependency: transitive description: name: stack_trace url: "https://pub.dartlang.org" source: hosted - version: "1.10.0-nullsafety.6" + version: "1.10.0-nullsafety.1" stream_channel: dependency: transitive description: name: stream_channel url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.3" + version: "2.1.0-nullsafety.1" string_scanner: dependency: transitive description: name: string_scanner url: "https://pub.dartlang.org" source: hosted - version: "1.1.0-nullsafety.3" + version: "1.1.0-nullsafety.1" term_glyph: dependency: transitive description: name: term_glyph url: "https://pub.dartlang.org" source: hosted - version: "1.2.0-nullsafety.3" + version: "1.2.0-nullsafety.1" test_api: dependency: transitive description: name: test_api url: "https://pub.dartlang.org" source: hosted - version: "0.2.19-nullsafety.6" + version: "0.2.19-nullsafety.2" truncate: dependency: "direct main" description: @@ -517,7 +510,7 @@ packages: name: typed_data url: "https://pub.dartlang.org" source: hosted - version: "1.3.0-nullsafety.5" + version: "1.3.0-nullsafety.3" uuid: dependency: transitive description: @@ -538,7 +531,7 @@ packages: name: vector_math url: "https://pub.dartlang.org" source: hosted - version: "2.1.0-nullsafety.5" + version: "2.1.0-nullsafety.3" websocket: dependency: transitive description: @@ -575,5 +568,5 @@ packages: source: hosted version: "2.2.1" sdks: - dart: ">=2.12.0-0.0 <3.0.0" + dart: ">=2.10.0-110 <2.11.0" flutter: ">=1.22.0 <2.0.0"