feat(dubp): impl opaque hd wallet

This commit is contained in:
librelois 2021-02-28 03:09:01 +01:00
parent d551a3032c
commit 57e2a88116
11 changed files with 783 additions and 260 deletions

105
Cargo.lock generated
View File

@ -59,6 +59,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
[[package]]
name = "assert_matches"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b34d609dfbaf33d6889b2b7106d3ca345eacad44200913df5ba02bfd31d2ba9"
[[package]]
name = "async-oneshot"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -250,19 +256,21 @@ name = "dubp_rs"
version = "0.1.0"
dependencies = [
"allo-isolate",
"assert_matches",
"cbindgen",
"dart-bindgen",
"dup-crypto",
"fast-threadpool",
"once_cell",
"parking_lot",
"thiserror",
]
[[package]]
name = "dup-crypto"
version = "0.44.0"
version = "0.45.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e4281497a99e7bb67d54800671558d707c48ca35320f8c5747fb0eda4d8cbc71"
checksum = "58a3b88cbd886b092a7e6c58608d87744706e0fc66a48eaf452c297e5aac3802"
dependencies = [
"aes",
"arrayvec",
@ -272,7 +280,7 @@ dependencies = [
"byteorder",
"cryptoxide",
"ed25519-bip32",
"getrandom 0.2.2",
"getrandom",
"once_cell",
"ring",
"serde",
@ -328,24 +336,13 @@ dependencies = [
[[package]]
name = "getrandom"
version = "0.1.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8fc3cb4d91f53b50155bdcfd23f6a4c39ae1969c2ae85982b135750cccaf5fce"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi 0.9.0+wasi-snapshot-preview1",
]
[[package]]
name = "getrandom"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9495705279e7140bf035dde1f6e750c162df8b625267cd52cc44e0b156732c8"
dependencies = [
"cfg-if 1.0.0",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
"wasi",
]
[[package]]
@ -373,6 +370,15 @@ dependencies = [
]
[[package]]
name = "instant"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "61124eeebbd69b8190558df225adf7e4caafce0d743919e5d6b19652314ec5ec"
dependencies = [
"cfg-if 1.0.0",
]
[[package]]
name = "itoa"
version = "0.4.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -440,6 +446,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624a8340c38c1b80fd549087862da4ba43e08858af025b236e509b6649fc13d5"
[[package]]
name = "parking_lot"
version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6d7744ac029df22dca6284efe4e898991d28e3085c706c972bcd7da4a27a15eb"
dependencies = [
"instant",
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fa7a782938e745763fe6907fc6ba86946d72f49fe7e21de074e08128a99fb018"
dependencies = [
"cfg-if 1.0.0",
"instant",
"libc",
"redox_syscall",
"smallvec",
"winapi",
]
[[package]]
name = "ppv-lite86"
version = "0.2.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -465,11 +496,10 @@ dependencies = [
[[package]]
name = "rand"
version = "0.7.3"
version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6a6b1679d49b24bbfe0c803429aa1874472f50d9b363131f0e89fc356b544d03"
checksum = "0ef9e7e66b4468674bfcb0c81af8b7fa0bb154fa9f28eb840da5c447baeb8d7e"
dependencies = [
"getrandom 0.1.16",
"libc",
"rand_chacha",
"rand_core",
@ -478,9 +508,9 @@ dependencies = [
[[package]]
name = "rand_chacha"
version = "0.2.2"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4c8ed856279c9737206bf725bf36935d8666ead7aa69b52be55af369d193402"
checksum = "e12735cf05c9e10bf21534da50a147b924d555dc7a547c42e6bb2d5b6017ae0d"
dependencies = [
"ppv-lite86",
"rand_core",
@ -488,27 +518,30 @@ dependencies = [
[[package]]
name = "rand_core"
version = "0.5.1"
version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
checksum = "34cf66eb183df1c5876e2dcf6b13d57340741e8dc255b48e40a26de954d06ae7"
dependencies = [
"getrandom 0.1.16",
"getrandom",
]
[[package]]
name = "rand_hc"
version = "0.2.0"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ca3129af7b92a17112d59ad498c6f81eaf463253766b90396d39ea7a39d6613c"
checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
dependencies = [
"rand_core",
]
[[package]]
name = "redox_syscall"
version = "0.1.57"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41cc0f7e4d5d4544e8861606a285bb08d3e70712ccc7d2b84d7c0ccfaf4b05ce"
checksum = "94341e4e44e24f6b591b59e47a8a027df12e008d73fd5672dbea9cc22f4507d9"
dependencies = [
"bitflags",
]
[[package]]
name = "remove_dir_all"
@ -578,6 +611,12 @@ dependencies = [
]
[[package]]
name = "smallvec"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe0f37c9e8f3c5a4a66ad655a93c74daac4ad00c441533bf5c6e7990bb42604e"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
@ -629,11 +668,11 @@ dependencies = [
[[package]]
name = "tempfile"
version = "3.1.0"
version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a6e24d9338a0a5be79593e2fa15a648add6138caa803e2d5bc782c371732ca9"
checksum = "dac1c663cfc93810f88aed9b8941d48cabf856a1b111c29a40439018d870eb22"
dependencies = [
"cfg-if 0.1.10",
"cfg-if 1.0.0",
"libc",
"rand",
"redox_syscall",
@ -723,12 +762,6 @@ checksum = "b5a972e5669d67ba988ce3dc826706fb0a8b01471c088cb0b6110b805cc36aed"
[[package]]
name = "wasi"
version = "0.9.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cccddf32554fecc6acb585f82a32a72e28b48f8c4c1883ddfeeeaa96f7d8e519"
[[package]]
name = "wasi"
version = "0.10.2+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"

View File

@ -11,11 +11,15 @@ crate-type = ["rlib"]
[dependencies]
allo-isolate = "0.1.6"
dup-crypto = { version = "0.44.0", features = ["bip32-ed25519", "dewif", "mnemonic", "mnemonic_french", "scrypt"] }
dup-crypto = { version = "0.45.0", features = ["bip32-ed25519", "dewif", "mnemonic", "mnemonic_french", "scrypt"] }
fast-threadpool = { version = "0.3.0", default-features = false }
once_cell = { version = "1.3.1", default-features = false, features = ["std"] }
parking_lot = "0.11.1"
thiserror = "1.0.24"
[build-dependencies]
cbindgen = "0.14.3"
dart-bindgen = "0.1.7"
dart-bindgen = "0.1.7"
[dev-dependencies]
assert_matches = "1.5.0"

View File

@ -81,22 +81,38 @@ pub(super) fn get_dewif_meta(
}
pub(super) fn get_pubkey(
account_index_opt: Option<u32>,
address_index_opt: Option<U31>,
currency: Currency,
dewif: &str,
external_opt: Option<bool>,
secret_code: &str,
) -> Result<String, DubpError> {
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
if let Some(account_index) = account_index_opt {
dewif::bip32::get_bip32_pubkey(
account_index,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
)
} else if address_index_opt.is_none() && external_opt.is_none() {
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
match keypairs.next() {
Some(KeyPairEnum::Ed25519(keypair)) => Ok(keypair.public_key().to_base58()),
Some(KeyPairEnum::Bip32Ed25519(_)) => Err(DubpError::GetMasterPubkeyOfHdWallet),
Some(_) => Err(DubpError::UnsupportedDewifVersion),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
match keypairs.next() {
Some(KeyPairEnum::Ed25519(keypair)) => Ok(keypair.public_key().to_base58()),
Some(KeyPairEnum::Bip32Ed25519(_)) => Err(DubpError::GetMasterPubkeyOfHdWallet),
Some(_) => Err(DubpError::UnsupportedDewifVersion),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
} else {
Err(DubpError::GiveExternalBoolOrAddressIndexForLegacyWallet)
}
}
@ -126,3 +142,51 @@ pub(crate) fn log_n(system_memory: i64) -> u8 {
12
}
}
pub(super) fn sign(
account_index_opt: Option<u32>,
address_index_opt: Option<U31>,
currency: Currency,
dewif: &str,
external_opt: Option<bool>,
secret_code: &str,
msg: &str,
) -> Result<String, DubpError> {
if let Some(account_index) = account_index_opt {
dewif::bip32::sign_bip32(
account_index,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
msg,
)
} else {
dewif::classic::sign(currency, dewif, secret_code, msg)
}
}
pub(super) fn sign_several(
account_index_opt: Option<u32>,
address_index_opt: Option<U31>,
currency: Currency,
dewif: &str,
external_opt: Option<bool>,
secret_code: &str,
msgs: &[&str],
) -> Result<Vec<String>, DubpError> {
if let Some(account_index) = account_index_opt {
dewif::bip32::sign_several_bip32(
account_index,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
msgs,
)
} else {
dewif::classic::sign_several(currency, dewif, secret_code, msgs)
}
}

View File

@ -15,30 +15,24 @@
use crate::*;
const MEMBER_ACCOUNT_INDEX: u32 = 0;
#[derive(Debug)]
struct OpaqueAccount {
address_index: u32,
chain_code: ChainCode,
public_key: PublicKey,
}
static CHAINING_EXTERNAL_PUBKEYS: Lazy<Arc<Mutex<HashMap<u32, OpaqueAccount>>>> =
Lazy::new(|| Arc::new(Mutex::new(HashMap::new())));
pub(crate) fn get_accounts_pubkeys(
currency: Currency,
dewif: &str,
secret_code: &str,
accounts_indexs: Vec<DerivationIndex>,
accounts_indexs: Vec<U31>,
) -> Result<Vec<String>, DubpError> {
if accounts_indexs.contains(
&DerivationIndex::hard(MEMBER_ACCOUNT_INDEX).map_err(DubpError::InvalidDerivationIndex)?,
) {
if crate::secret_code::is_ascii_letters(secret_code) {
let log_n =
dup_crypto::dewif::read_dewif_log_n(ExpectedCurrency::Specific(currency), dewif)
.map_err(DubpError::DewifReadError)?;
let expected_secret_code_len =
crate::secret_code::compute_secret_code_len(true, SecretCodeType::Letters, log_n)?;
if secret_code.len() < expected_secret_code_len {
return Err(DubpError::SecretCodeTooShort);
}
} else {
return Err(DubpError::InvalidSecretCodeType);
}
if accounts_indexs.contains(&U31::new(0)?) {
verify_member_secret_code(currency, dewif, secret_code)?;
}
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
@ -50,25 +44,23 @@ pub(crate) fn get_accounts_pubkeys(
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => Ok(accounts_indexs
.into_iter()
.map(|account_index| {
master_keypair
.derive(account_index)
.public_key()
.to_base58()
PrivateDerivationPath::transparent(account_index)
.map(|path| master_keypair.derive(path).public_key().to_base58())
})
.collect()),
.collect::<Result<Vec<_>, InvalidAccountIndex>>()?),
Some(_) => Err(DubpError::NotHdWallet),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}
pub(crate) fn sign_transparent(
account_index: DerivationIndex,
currency: &str,
pub(crate) fn get_bip32_pubkey(
account_index: u32,
address_index_opt: Option<U31>,
currency: Currency,
dewif: &str,
external_opt: Option<bool>,
secret_code: &str,
msg: &str,
) -> Result<String, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
@ -76,41 +68,325 @@ pub(crate) fn sign_transparent(
)
.map_err(DubpError::DewifReadError)?;
if account_index == 0 {
verify_member_secret_code(currency, dewif, secret_code)?;
}
match keypairs.next() {
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => Ok(master_keypair
.derive(account_index)
.generate_signator()
.sign(msg.as_bytes())
.to_base64()),
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => get_bip32_pubkey_inner(
account_index,
address_index_opt,
external_opt,
master_keypair,
),
Some(_) => Err(DubpError::NotHdWallet),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}
pub(crate) fn sign_several_transparent(
account_index: DerivationIndex,
currency: &str,
pub(crate) fn get_opaque_account_next_external_address(
account_index: U31,
) -> Result<String, DubpError> {
let mut guard = CHAINING_EXTERNAL_PUBKEYS.lock();
if let Some(opaque_account) = guard.get_mut(&account_index.into_u32()) {
let next_address = PublicKeyWithChainCode {
chain_code: opaque_account.chain_code,
public_key: opaque_account.public_key,
}
.derive(U31::new(opaque_account.address_index).expect("unreachable"))
.expect("unreachable")
.public_key;
opaque_account.address_index += 1;
Ok(next_address.to_base58())
} else {
Err(DubpError::OpaqueAccountNotLoaded)
}
}
pub(crate) fn load_opaque_bip32_accounts(
accounts_indexs: Vec<U31>,
currency: Currency,
dewif: &str,
secret_code: &str,
msgs: &[&str],
) -> Result<Vec<String>, DubpError> {
let currency = parse_currency(currency)?;
) -> Result<(), DubpError> {
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
match keypairs.next() {
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => {
let signator = master_keypair.derive(account_index).generate_signator();
Ok(msgs
.iter()
.map(|msg| signator.sign(msg.as_bytes()).to_base64())
.collect())
for account_index in accounts_indexs {
let external_path = PrivateDerivationPath::opaque(account_index, true, None)?;
let external_kp = master_keypair.derive(external_path);
let opaque_account = OpaqueAccount {
address_index: 0,
public_key: external_kp.public_key(),
chain_code: external_kp.chain_code(),
};
let mut guard = CHAINING_EXTERNAL_PUBKEYS.lock();
guard.insert(account_index.into_u32(), opaque_account);
}
Ok(())
}
Some(_) => Err(DubpError::NotHdWallet),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}
pub(crate) fn sign_bip32(
account_index: u32,
address_index_opt: Option<U31>,
currency: Currency,
dewif: &str,
external_opt: Option<bool>,
secret_code: &str,
msg: &str,
) -> Result<String, DubpError> {
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
if account_index == 0 {
verify_member_secret_code(currency, dewif, secret_code)?;
}
match keypairs.next() {
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => sign_bip32_inner(
account_index,
address_index_opt,
external_opt,
master_keypair,
msg,
),
Some(_) => Err(DubpError::NotHdWallet),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}
pub(crate) fn sign_several_bip32(
account_index: u32,
address_index_opt: Option<U31>,
currency: Currency,
dewif: &str,
external_opt: Option<bool>,
secret_code: &str,
msgs: &[&str],
) -> Result<Vec<String>, DubpError> {
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
&secret_code.to_ascii_uppercase(),
)
.map_err(DubpError::DewifReadError)?;
if account_index == 0 {
verify_member_secret_code(currency, dewif, secret_code)?;
}
match keypairs.next() {
Some(KeyPairEnum::Bip32Ed25519(master_keypair)) => sign_several_bip32_inner(
account_index,
address_index_opt,
external_opt,
master_keypair,
msgs,
),
Some(_) => Err(DubpError::NotHdWallet),
None => Err(DubpError::DewifReadError(DewifReadError::CorruptedContent)),
}
}
fn get_bip32_pubkey_inner(
account_index: u32,
address_index_opt: Option<U31>,
external_opt: Option<bool>,
master_keypair: KeyPair,
) -> Result<String, DubpError> {
Ok(master_keypair
.derive(z_get_derivation_path(
account_index,
address_index_opt,
external_opt,
)?)
.public_key()
.to_base58())
}
fn sign_bip32_inner(
account_index: u32,
address_index_opt: Option<U31>,
external_opt: Option<bool>,
master_keypair: KeyPair,
msg: &str,
) -> Result<String, DubpError> {
Ok(master_keypair
.derive(z_get_derivation_path(
account_index,
address_index_opt,
external_opt,
)?)
.generate_signator()
.sign(msg.as_bytes())
.to_base64())
}
fn sign_several_bip32_inner(
account_index: u32,
address_index_opt: Option<U31>,
external_opt: Option<bool>,
master_keypair: KeyPair,
msgs: &[&str],
) -> Result<Vec<String>, DubpError> {
let signator = master_keypair
.derive(z_get_derivation_path(
account_index,
address_index_opt,
external_opt,
)?)
.generate_signator();
Ok(msgs
.iter()
.map(|msg| signator.sign(msg.as_bytes()).to_base64())
.collect())
}
fn verify_member_secret_code(
currency: Currency,
dewif: &str,
secret_code: &str,
) -> Result<(), DubpError> {
if crate::secret_code::is_ascii_letters(secret_code) {
let log_n =
dup_crypto::dewif::read_dewif_log_n(ExpectedCurrency::Specific(currency), dewif)
.map_err(DubpError::DewifReadError)?;
let expected_secret_code_len =
crate::secret_code::compute_secret_code_len(true, SecretCodeType::Letters, log_n)?;
if secret_code.len() < expected_secret_code_len {
Err(DubpError::SecretCodeTooShort)
} else {
Ok(())
}
} else {
Err(DubpError::InvalidSecretCodeType)
}
}
fn z_get_derivation_path(
account_index: u32,
address_index_opt: Option<U31>,
external_opt: Option<bool>,
) -> Result<PrivateDerivationPath, DubpError> {
let account_index_u31 = U31::new(account_index)?;
match account_index % 3 {
0 => Ok(PrivateDerivationPath::transparent(account_index_u31)?),
1 => {
if let Some(external) = external_opt {
if external {
if address_index_opt.is_none() {
Ok(PrivateDerivationPath::semi_opaque_external(
account_index_u31,
)?)
} else {
Err(DubpError::InvalidAccountIndex(InvalidAccountIndex))
}
} else if address_index_opt.is_some() {
Ok(PrivateDerivationPath::semi_opaque_internal(
account_index_u31,
address_index_opt,
)?)
} else {
Err(DubpError::TryToSignWithInternalChainingAddress)
}
} else {
Err(DubpError::MissingExternalBool)
}
}
2 => {
if address_index_opt.is_some() {
if let Some(external) = external_opt {
Ok(PrivateDerivationPath::opaque(
account_index_u31,
external,
address_index_opt,
)?)
} else {
Err(DubpError::MissingExternalBool)
}
} else {
Err(DubpError::TryToSignWithChainingAddress)
}
}
_ => unreachable!(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use assert_matches::assert_matches;
const MNEMONIC: &str =
"tongue cute mail fossil great frozen same social weasel impact brush kind";
#[test]
fn test_get_accounts_pubkeys() -> Result<(), DubpError> {
let vec = crate::dewif::gen_dewif(
"g1",
Language::English,
MNEMONIC,
false,
SecretCodeType::Letters,
1_000,
)?;
let dewif = &vec[0];
let secret_code = &vec[1];
//println!("TMP: secret_code={}", secret_code);
assert_matches!(
get_accounts_pubkeys(
Currency::from(G1_CURRENCY),
dewif,
secret_code,
vec![U31::new(0).expect("unreachable")],
),
Err(DubpError::SecretCodeTooShort)
);
Ok(())
}
#[test]
fn test_sign_bip32() {
assert_matches!(
z_get_derivation_path(1, None, None),
Err(DubpError::MissingExternalBool)
);
assert_matches!(
z_get_derivation_path(2, None, None),
Err(DubpError::TryToSignWithChainingAddress)
);
assert_matches!(z_get_derivation_path(3, None, None), Ok(_));
assert_matches!(
z_get_derivation_path(1, None, Some(false)),
Err(DubpError::TryToSignWithInternalChainingAddress)
);
assert_matches!(
z_get_derivation_path(2, None, Some(true)),
Err(DubpError::TryToSignWithChainingAddress)
);
assert_matches!(
z_get_derivation_path(1, Some(U31::new(5).expect("unreachable")), Some(true),),
Err(DubpError::InvalidAccountIndex(_))
);
}
}

View File

@ -16,12 +16,11 @@
use crate::*;
pub(crate) fn sign(
currency: &str,
currency: Currency,
dewif: &str,
secret_code: &str,
msg: &str,
) -> Result<String, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,
@ -36,12 +35,11 @@ pub(crate) fn sign(
}
pub(crate) fn sign_several(
currency: &str,
currency: Currency,
dewif: &str,
secret_code: &str,
msgs: &[&str],
) -> Result<Vec<String>, DubpError> {
let currency = parse_currency(currency)?;
let mut keypairs = dup_crypto::dewif::read_dewif_file_content(
ExpectedCurrency::Specific(currency),
dewif,

View File

@ -13,8 +13,6 @@
// 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/>.
use dup_crypto::keys::ed25519::bip32::InvalidDerivationIndex;
use crate::*;
/// Dubp error
@ -26,20 +24,30 @@ pub(crate) enum DubpError {
DigitsCodeForbidForMemberWallet,
#[error("It is forbidden to retrieve the master public key of an HD wallet.")]
GetMasterPubkeyOfHdWallet,
#[error("Give external bool or address index for legacy wallet.")]
GiveExternalBoolOrAddressIndexForLegacyWallet,
#[error("I/O error: {0}")]
IoErr(io::Error),
#[error("{0}")]
InvalidDerivationIndex(InvalidDerivationIndex),
InvalidAccountIndex(InvalidAccountIndex),
#[error("{0}")]
InvalidU31(U31Error),
#[error("Invalid secret code type")]
InvalidSecretCodeType,
#[error("Missing external bool")]
MissingExternalBool,
#[error("this wallet is not an HD wallet")]
NotHdWallet,
#[error("this account index is not a transparent account index")]
NotTransparentAccountIndex,
#[error("A given parameter is null")]
NullParamErr,
#[error("Opaque account not loaded")]
OpaqueAccountNotLoaded,
#[error("Secret code too short: please change your secret code")]
SecretCodeTooShort,
#[error("The chaining address cannot be used to sign with opaque account")]
TryToSignWithChainingAddress,
#[error("The internal chaining address cannot be used to sign with semi-opaque account")]
TryToSignWithInternalChainingAddress,
#[error("fail to generate random bytes")]
RandErr,
#[error("Unknown currency name")]
@ -60,9 +68,15 @@ impl From<io::Error> for DubpError {
}
}
impl From<InvalidDerivationIndex> for DubpError {
fn from(e: InvalidDerivationIndex) -> Self {
Self::InvalidDerivationIndex(e)
impl From<InvalidAccountIndex> for DubpError {
fn from(e: InvalidAccountIndex) -> Self {
Self::InvalidAccountIndex(e)
}
}
impl From<U31Error> for DubpError {
fn from(e: U31Error) -> Self {
Self::InvalidU31(e)
}
}
@ -77,6 +91,17 @@ impl IntoDart for DartRes {
self.0.into_dart()
}
}
impl<E> From<Result<(), E>> for DartRes
where
E: ToString,
{
fn from(res: Result<(), E>) -> Self {
match res {
Ok(()) => Self("".into_dart()),
Err(e) => Self(format!("DUBP_RS_ERROR: {}", e.to_string()).into_dart()),
}
}
}
impl<E> From<Result<String, E>> for DartRes
where
E: ToString,

View File

@ -52,13 +52,13 @@ pub(crate) fn char_ptr_to_str<'a>(c_char_ptr: *const raw::c_char) -> Result<&'a
}
}
pub(crate) fn char_ptr_prt_to_vec_hard_derivation_index(
pub(crate) fn char_ptr_prt_to_vec_u31(
u32_ptr: *const u32,
len: u32,
) -> Result<Vec<DerivationIndex>, DubpError> {
) -> Result<Vec<U31>, DubpError> {
u32_ptr_to_vec_u32(u32_ptr, len)
.into_iter()
.map(|ai| DerivationIndex::hard(ai).map_err(DubpError::InvalidDerivationIndex))
.map(|ai| U31::new(ai).map_err(DubpError::InvalidU31))
.collect()
}
@ -76,6 +76,30 @@ pub(crate) fn char_ptr_prt_to_vec_str<'a>(
Ok(str_vec)
}
pub(crate) fn i32_to_opt_bool(i: i32) -> Option<bool> {
match i {
1 => Some(true),
0 => Some(false),
_ => None,
}
}
pub(crate) fn i32_to_opt_u31(i: i32) -> Result<Option<U31>, DubpError> {
if i >= 0 {
Ok(Some(U31::new(i as u32)?))
} else {
Ok(None)
}
}
pub(crate) fn i32_to_opt_u32(i: i32) -> Option<u32> {
if i >= 0 {
Some(i as u32)
} else {
None
}
}
pub(crate) fn parse_currency(currency: &str) -> Result<Currency, DubpError> {
let currency_code = match currency {
"g1" => G1_CURRENCY,
@ -85,14 +109,6 @@ pub(crate) fn parse_currency(currency: &str) -> Result<Currency, DubpError> {
Ok(Currency::from(currency_code))
}
pub(crate) fn transparent_account_index(account_index: u32) -> Result<DerivationIndex, DubpError> {
if account_index % 3 == 0 {
DerivationIndex::hard(account_index).map_err(DubpError::InvalidDerivationIndex)
} else {
Err(DubpError::NotTransparentAccountIndex)
}
}
pub(crate) fn u32_ptr_to_vec_u32(u32_ptr: *const u32, len: u32) -> Vec<u32> {
let len = len as usize;
let u32_slice: &[u32] = unsafe { std::slice::from_raw_parts(u32_ptr, len) };

View File

@ -42,14 +42,6 @@ pub(super) fn get_pubkey(salt: &str, password: &str) -> String {
.to_base58()
}
pub(super) fn sign(salt: &str, password: &str, msg: &str) -> String {
KeyPairFromSaltedPasswordGenerator::with_default_parameters()
.generate(SaltedPassword::new(salt.to_owned(), password.to_owned()))
.generate_signator()
.sign(msg.as_bytes())
.to_base64()
}
#[cfg(test)]
mod tests {
use super::*;
@ -73,7 +65,14 @@ mod tests {
assert_eq!(get_pubkey("salt", "pass"), pubkey.to_owned());
assert_eq!(
crate::dewif::get_pubkey(Currency::from(G1_CURRENCY), &dewif, &secret_code)?,
crate::dewif::get_pubkey(
None,
None,
Currency::from(G1_CURRENCY),
&dewif,
None,
&secret_code
)?,
pubkey.to_owned()
);

View File

@ -32,14 +32,19 @@ use dup_crypto::{
bases::b58::ToBase58,
dewif::{Currency, DewifReadError, ExpectedCurrency, G1_CURRENCY, G1_TEST_CURRENCY},
keys::{
ed25519::bip32::DerivationIndex, ed25519::KeyPairFromSeed32Generator, KeyPair as _,
KeyPairEnum, Signator as _, Signature as _,
ed25519::bip32::{
ChainCode, InvalidAccountIndex, KeyPair, PrivateDerivationPath, PublicKeyWithChainCode,
},
ed25519::{KeyPairFromSeed32Generator, PublicKey},
KeyPair as _, KeyPairEnum, Signator as _, Signature as _,
},
mnemonic::{Language, Mnemonic, MnemonicType},
utils::{U31Error, U31},
};
use fast_threadpool::{ThreadPool, ThreadPoolConfig, ThreadPoolSyncHandler};
use once_cell::sync::Lazy;
use std::{ffi::CStr, io, os::raw};
use parking_lot::Mutex;
use std::{collections::HashMap, ffi::CStr, io, os::raw, sync::Arc};
use thiserror::Error;
#[no_mangle]
@ -164,6 +169,30 @@ pub extern "C" fn gen_mnemonic(port: i64, language: u32) {
}
#[no_mangle]
pub extern "C" fn get_bip32_dewif_accounts_pubkeys(
port: i64,
currency: *const raw::c_char,
dewif: *const raw::c_char,
secret_code: *const raw::c_char,
accounts_indexs_len: u32,
accounts_indexs: *const u32,
) {
exec_async(
port,
|| {
let currency = parse_currency(char_ptr_to_str(currency)?)?;
let dewif = char_ptr_to_str(dewif)?;
let secret_code = char_ptr_to_str(secret_code)?;
let accounts_indexs = char_ptr_prt_to_vec_u31(accounts_indexs, accounts_indexs_len)?;
Ok((currency, dewif, secret_code, accounts_indexs))
},
|(currency, dewif, secret_code, accounts_indexs)| {
dewif::bip32::get_accounts_pubkeys(currency, dewif, secret_code, accounts_indexs)
},
)
}
#[no_mangle]
pub extern "C" fn get_dewif_meta(
port: i64,
dewif: *const raw::c_char,
@ -201,43 +230,76 @@ pub extern "C" fn get_dewif_secret_code_len(
#[no_mangle]
pub extern "C" fn get_dewif_pubkey(
port: i64,
account_index: i32,
address_index: i32,
currency: *const raw::c_char,
dewif: *const raw::c_char,
external_opt: i32,
pin: *const raw::c_char,
) {
exec_async(
port,
|| {
let account_index_opt = i32_to_opt_u32(account_index);
let address_index_opt = i32_to_opt_u31(address_index)?;
let currency = parse_currency(char_ptr_to_str(currency)?)?;
let dewif = char_ptr_to_str(dewif)?;
let external_opt = i32_to_opt_bool(external_opt);
let pin = char_ptr_to_str(pin)?;
Ok((currency, dewif, pin))
Ok((
account_index_opt,
address_index_opt,
currency,
dewif,
external_opt,
pin,
))
},
|(account_index_opt, address_index_opt, currency, dewif, external_opt, pin)| {
dewif::get_pubkey(
account_index_opt,
address_index_opt,
currency,
dewif,
external_opt,
pin,
)
},
|(currency, dewif, pin)| dewif::get_pubkey(currency, dewif, pin),
)
}
#[no_mangle]
pub extern "C" fn get_bip32_dewif_accounts_pubkeys(
pub extern "C" fn get_opaque_account_next_external_address(port: i64, account_index: u32) {
exec_async(
port,
|| {
let account_index = U31::new(account_index)?;
Ok(account_index)
},
dewif::bip32::get_opaque_account_next_external_address,
)
}
#[no_mangle]
pub extern "C" fn load_opaque_bip32_accounts(
port: i64,
accounts_indexs_len: u32,
accounts_indexs: *const u32,
currency: *const raw::c_char,
dewif: *const raw::c_char,
secret_code: *const raw::c_char,
accounts_indexs_len: u32,
accounts_indexs: *const u32,
) {
exec_async(
port,
|| {
let accounts_indexs = char_ptr_prt_to_vec_u31(accounts_indexs, accounts_indexs_len)?;
let currency = parse_currency(char_ptr_to_str(currency)?)?;
let dewif = char_ptr_to_str(dewif)?;
let secret_code = char_ptr_to_str(secret_code)?;
let accounts_indexs =
char_ptr_prt_to_vec_hard_derivation_index(accounts_indexs, accounts_indexs_len)?;
Ok((currency, dewif, secret_code, accounts_indexs))
Ok((accounts_indexs, currency, dewif, secret_code))
},
|(currency, dewif, secret_code, accounts_indexs)| {
dewif::bip32::get_accounts_pubkeys(currency, dewif, secret_code, accounts_indexs)
|(accounts_indexs, currency, dewif, secret_code)| {
dewif::bip32::load_opaque_bip32_accounts(accounts_indexs, currency, dewif, secret_code)
},
)
}
@ -279,112 +341,106 @@ pub extern "C" fn mnemonic_to_pubkey(
#[no_mangle]
pub extern "C" fn sign(
port: i64,
account_index: i32,
address_index: i32,
currency: *const raw::c_char,
dewif: *const raw::c_char,
pin: *const raw::c_char,
msg: *const raw::c_char,
) {
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::classic::sign(currency, dewif, pin, msg),
)
}
#[no_mangle]
pub extern "C" fn sign_bip32_transparent(
port: i64,
account_index: u32,
currency: *const raw::c_char,
dewif: *const raw::c_char,
external_opt: i32,
secret_code: *const raw::c_char,
msg: *const raw::c_char,
) {
exec_async(
port,
|| {
let account_index = transparent_account_index(account_index)?;
let currency = char_ptr_to_str(currency)?;
let account_index_opt = i32_to_opt_u32(account_index);
let address_index_opt = i32_to_opt_u31(address_index)?;
let currency = parse_currency(char_ptr_to_str(currency)?)?;
let dewif = char_ptr_to_str(dewif)?;
let pin = char_ptr_to_str(secret_code)?;
let external_opt = i32_to_opt_bool(external_opt);
let secret_code = char_ptr_to_str(secret_code)?;
let msg = char_ptr_to_str(msg)?;
Ok((currency, dewif, pin, msg, account_index))
Ok((
account_index_opt,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
msg,
))
},
|(currency, dewif, secret_code, msg, account_index)| {
dewif::bip32::sign_transparent(account_index, currency, dewif, secret_code, msg)
|(
account_index_opt,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
msg,
)| {
dewif::sign(
account_index_opt,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
msg,
)
},
)
}
#[no_mangle]
pub extern "C" fn sign_legacy(
port: i64,
salt: *const raw::c_char,
password: *const raw::c_char,
msg: *const raw::c_char,
) {
exec_async(
port,
|| {
let salt = char_ptr_to_str(salt)?;
let password = char_ptr_to_str(password)?;
let msg = char_ptr_to_str(msg)?;
Ok((salt, password, msg))
},
|(salt, password, msg)| Ok::<_, DubpError>(legacy::sign(salt, password, msg)),
)
}
#[no_mangle]
pub extern "C" fn sign_several(
port: i64,
account_index: i32,
address_index: i32,
currency: *const raw::c_char,
dewif: *const raw::c_char,
pin: *const raw::c_char,
external_opt: i32,
secret_code: *const raw::c_char,
msgs_len: u32,
msgs: *const *const raw::c_char,
) {
exec_async(
port,
|| {
let currency = char_ptr_to_str(currency)?;
let account_index_opt = i32_to_opt_u32(account_index);
let address_index_opt = i32_to_opt_u31(address_index)?;
let currency = parse_currency(char_ptr_to_str(currency)?)?;
let dewif = char_ptr_to_str(dewif)?;
let pin = char_ptr_to_str(pin)?;
let external_opt = i32_to_opt_bool(external_opt);
let secret_code = char_ptr_to_str(secret_code)?;
let msgs = char_ptr_prt_to_vec_str(msgs, msgs_len)?;
Ok((currency, dewif, pin, msgs))
Ok((
account_index_opt,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
msgs,
))
},
|(currency, dewif, pin, msgs)| dewif::classic::sign_several(currency, dewif, pin, &msgs),
)
}
#[no_mangle]
pub extern "C" fn sign_several_bip32_transparent(
port: i64,
account_index: u32,
currency: *const raw::c_char,
dewif: *const raw::c_char,
pin: *const raw::c_char,
msgs_len: u32,
msgs: *const *const raw::c_char,
) {
exec_async(
port,
|| {
let account_index = transparent_account_index(account_index)?;
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, account_index))
},
|(currency, dewif, pin, msgs, account_index)| {
dewif::bip32::sign_several_transparent(account_index, currency, dewif, pin, &msgs)
|(
account_index_opt,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
msgs,
)| {
dewif::sign_several(
account_index_opt,
address_index_opt,
currency,
dewif,
external_opt,
secret_code,
&msgs,
)
},
)
}

View File

@ -144,10 +144,12 @@ NewWallet new_wallet = await DubpRust.genWalletFromMnemonic(
#### Function signature
```dart
static Future<String> signBip32Transparent({
static Future<String> sign({
int accountIndex,
int addressIndexOpt,
String currency = "g1",
String dewif,
bool externalOpt,
String secretCode,
String message
});
@ -158,7 +160,7 @@ If the wallet is not dedicated to the Ğ1 currency, you must indicate the curren
#### Usage example
```dart
String signature = await DubpRust.signBip32Transparent(
String signature = await DubpRust.sign(
accountIndex: 3,
dewif: "AAAAARAAAAGfFDAs+jVZYkfhBlHZZ2fEQIvBqnG16g5+02cY18wSOjW0cUg2JV3SUTJYN2CrbQeRDwGazWnzSFBphchMmiL0",
pin: "CDJ4UB",

View File

@ -177,7 +177,16 @@ class DubpRust {
return Future.value(NewWallet._(newWallet[0], newWallet[1]));
}
//get_bip32_dewif_accounts_pubkeys
/// Get next external public key of a specific opaque account
static Future<String> getOpaqueAccountNextExternalPublicKey(
{int accountIndex}) async {
final completer = Completer<String>();
final sendPort =
singleCompletePort<String, String>(completer, callback: _handleErr);
native.get_opaque_account_next_external_address(
sendPort.nativePort, accountIndex.toUnsigned(31));
return completer.future;
}
/// Get BIP32 accounts public keys (in base 58) of `dewif` master keypair.
static Future<List<String>> getBip32DewifAccountsPublicKeys(
@ -194,7 +203,7 @@ class DubpRust {
Utf8.toUtf8(dewif),
Utf8.toUtf8(secretCode),
accountsIndex.length,
_listIntToPtr(accountsIndex));
_listIntToPtrUint32(accountsIndex));
return completer.future;
}
@ -215,14 +224,26 @@ class DubpRust {
/// Get public key (in base 58) of `dewif` keypair.
static Future<String> getDewifPublicKey(
{String currency = "g1", String dewif, String pin}) async {
{int accountIndexOpt,
int addressIndexOpt,
String currency = "g1",
String dewif,
bool externalOpt,
String pin}) async {
var externalOptInt = -1;
if (externalOpt != null) {
externalOptInt = externalOpt ? 1 : 0;
}
final completer = Completer<String>();
final sendPort =
singleCompletePort<String, String>(completer, callback: _handleErr);
native.get_dewif_pubkey(
sendPort.nativePort,
accountIndexOpt ?? -1,
addressIndexOpt ?? -1,
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
externalOptInt,
Utf8.toUtf8(pin),
);
return completer.future;
@ -261,43 +282,55 @@ class DubpRust {
return completer.future;
}
/// Sign the message `message` with `dewif` Bip32-Ed25519 keypair encryted
/// in DEWIF format.
///
/// If you have several messages to sign, use `signSeveralBip32Transparent`
/// method instead.
static Future<String> signBip32Transparent(
{int accountIndex,
String currency = "g1",
String dewif,
String secretCode,
String message}) {
final completer = Completer<String>();
/// Load opaque accounts
static Future<void> loadOpaqueAccounts(
List<int> accountsIndex,
String currency,
String dewif,
String secretCode,
) {
final completer = Completer<void>();
final sendPort =
singleCompletePort<String, String>(completer, callback: _handleErr);
native.sign_bip32_transparent(
sendPort.nativePort,
accountIndex,
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
Utf8.toUtf8(secretCode),
Utf8.toUtf8(message),
);
singleCompletePort<void, String>(completer, callback: _handleErrVoid);
native.load_opaque_bip32_accounts(
sendPort.nativePort,
accountsIndex.length,
_listIntToPtrUint32(accountsIndex),
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
Utf8.toUtf8(secretCode));
return completer.future;
}
/// Sign the message `message` with legacy wallet (password + salt)
/// Sign the message `message` with `dewif` keypair encryted
/// in DEWIF format.
///
/// This deprecated method must be used only for compatibility purpose !
static Future<String> signLegacy(
{String password, String salt, String message}) {
/// If you have several messages to sign, use `signSeveral`
/// method instead.
static Future<String> sign(
{int accountIndexOpt,
int addressIndexOpt,
String currency = "g1",
String dewif,
bool externalOpt,
String secretCode,
String message}) {
var externalOptInt = -1;
if (externalOpt != null) {
externalOptInt = externalOpt ? 1 : 0;
}
final completer = Completer<String>();
final sendPort =
singleCompletePort<String, String>(completer, callback: _handleErr);
native.sign_legacy(
native.sign(
sendPort.nativePort,
Utf8.toUtf8(password),
Utf8.toUtf8(salt),
accountIndexOpt ?? -1,
addressIndexOpt ?? -1,
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
externalOptInt,
Utf8.toUtf8(secretCode),
Utf8.toUtf8(message),
);
return completer.future;
@ -309,22 +342,31 @@ class DubpRust {
/// This method is optimized to sign several messages at once. If you have
/// several messages to sign, avoid calling the `sign` method for each
/// message. Use this `signSeveral` method instead.
static Future<List<String>> signSeveralBip32Transparent(
{int accountIndex,
static Future<List<String>> signSeveral(
{int accountIndexOpt,
int addressIndexOpt,
String currency = "g1",
String dewif,
String pin,
bool externalOpt,
String secretCode,
List<String> messages}) {
var externalOptInt = -1;
if (externalOpt != null) {
externalOptInt = externalOpt ? 1 : 0;
}
final completer = Completer<List<String>>();
final sendPort = singleCompletePort<List<String>, List>(completer,
callback: _handleErrList);
native.sign_several_bip32_transparent(
native.sign_several(
sendPort.nativePort,
accountIndex,
accountIndexOpt ?? -1,
addressIndexOpt ?? -1,
Utf8.toUtf8(currency),
Utf8.toUtf8(dewif),
Utf8.toUtf8(pin),
externalOptInt,
Utf8.toUtf8(secretCode),
messages.length,
_listStringToPtr(messages),
);
@ -332,11 +374,11 @@ class DubpRust {
return completer.future;
}
static Pointer<Uint32> _listIntToPtr(List<int> list) {
//final listUint32 = list.map(int.toUnsigned).toList();
final Pointer<Uint32> ptr = allocate(count: list.length);
for (var i = 0; i < list.length; i++) {
ptr[i] = list[i];
static Pointer<Uint32> _listIntToPtrUint32(List<int> list) {
final listUint32 = list.map((i) => i.toUnsigned(31)).toList();
final Pointer<Uint32> ptr = allocate(count: listUint32.length);
for (var i = 0; i < listUint32.length; i++) {
ptr[i] = listUint32[i];
}
return ptr;
}
@ -371,6 +413,14 @@ class DubpRust {
}
}
static void _handleErrVoid(String res) {
if (res.startsWith('DUBP_RS_ERROR: ')) {
final error = res;
print(error);
throw error;
}
}
/*static int _handleErrInt(String res) {
if (res.startsWith('DUBP_RS_ERROR: ')) {
final error = res;