From cb723c43ccba3cd3987591e9c6596032d5e7169d Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Mon, 8 Jun 2020 13:36:36 +0800 Subject: [PATCH 01/16] add framework build sh --- .gitignore | 2 +- tcx/src/lib.rs | 8 +++++ tools/ios-framework-build.sh | 69 ++++++++++++++++++++++++++++++++++++ tools/ios-pod-release.sh | 0 4 files changed, 78 insertions(+), 1 deletion(-) create mode 100644 tools/ios-framework-build.sh create mode 100644 tools/ios-pod-release.sh diff --git a/.gitignore b/.gitignore index 98f8198b..4e4b2293 100644 --- a/.gitignore +++ b/.gitignore @@ -14,7 +14,7 @@ tools/android-token-v2-simulator-build.sh tools/ios-token-v2-build.sh tools/ios-token-v2-simulator-build.sh tools/ios-token-v2-release-build.sh -tools/ios-release-build.sh +tools/ios-internal-release.sh ios-release/** diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 29dd6299..6296cf09 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -1848,6 +1848,14 @@ mod tests { // ); // } + #[test] + fn decode_error() { + let err = "1211756e737570706f727465645f636861696e"; + let error_bytes = hex::decode(err).unwrap(); + let rsp: Response = Response::decode(error_bytes.as_slice()).unwrap(); + assert_eq!("1", format!("{:?}", rsp)) + } + #[test] fn test_panic_keystore_locked() { run_test(|| { diff --git a/tools/ios-framework-build.sh b/tools/ios-framework-build.sh new file mode 100644 index 00000000..4e15d3ac --- /dev/null +++ b/tools/ios-framework-build.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Step1. compile rust + +LIBS=examples/TokenCoreX/TokenCoreX + +# if [ ! -d $LIBS ]; then +# mkdir -p $LIBS/Include +# mkdir -p $LIBS/Libs +# fi + +pushd libs/secp256k1 +if ! type "cargo-lipo" > /dev/null; then + cargo install cargo-lipo + rustup target add aarch64-apple-ios x86_64-apple-ios armv7-apple-ios armv7s-apple-ios i386-apple-ios +fi +LIBS=../../examples/TokenCoreX/TokenCoreX +cargo lipo --release --targets aarch64-apple-ios x86_64-apple-ios armv7-apple-ios armv7s-apple-ios i386-apple-ios + +cp target/universal/release/libsecp256k1.a $LIBS +popd + +pushd tcx +LIBS=../examples/TokenCoreX/TokenCoreX +if [ ! -d cheader ]; then + mkdir -p cheader +fi +RUST_BACKTRACE=1 cbindgen src/lib.rs -l c > cheader/tcx.h +cargo lipo --release --targets aarch64-apple-ios x86_64-apple-ios armv7-apple-ios armv7s-apple-ios i386-apple-ios + +cp cheader/tcx.h $LIBS +cp ../target/universal/release/libtcx.a $LIBS +popd + + +pushd examples/TokenCoreX +# xcodebuild build -project TokenCoreX.xcodeproj -scheme TokenCoreX-Universal -sdk iphoneos13.2 +# xcodebuild build -project TokenCoreX.xcodeproj -scheme TokenCoreX-Universal -sdk iphonesimulator13.2 + +BUILD_DIR=./Products +BUILD_ROOT=./Products +SYMROOT=./Products +BUILD_PRODUCTS=./Products +CONFIGURATION=Release +PROJECT_NAME=TokenCoreX + +mkdir -p $BUILD_DIR +UNIVERSAL_OUTPUTFOLDER=$BUILD_DIR/$CONFIGURATION-Universal +rm -rf ../../ios-release/* + + +# Next, work out if we're in SIMULATOR or REAL DEVICE +xcodebuild clean +# Make sure the output directory exists +mkdir -p $UNIVERSAL_OUTPUTFOLDER +xcodebuild -target $PROJECT_NAME -configuration Debug -sdk iphonesimulator ONLY_ACTIVE_ARCH=NO BUILD_DIR=$BUILD_DIR BUILD_ROOT=$BUILD_ROOT build +xcodebuild -target $PROJECT_NAME ONLY_ACTIVE_ARCH=NO -configuration $CONFIGURATION -sdk iphoneos BUILD_DIR=$BUILD_DIR BUILD_ROOT=$BUILD_ROOT build +# Step 2. Copy the framework structure (from iphoneos build) to the universal folder +cp -R $BUILD_DIR/$CONFIGURATION-iphoneos/$PROJECT_NAME.framework $UNIVERSAL_OUTPUTFOLDER/ +# Step 3. Copy Swift modules from iphonesimulator build (if it exists) to the copied framework directory +# BUILD_PRODUCTS=$SYMROOT/../../../../Products +# cp -R $BUILD_PRODUCTS/Debug-iphonesimulator/$PROJECT_NAME.framework/Modules/$PROJECT_NAME.swiftmodule/. $UNIVERSAL_OUTPUTFOLDER/$PROJECT_NAME.framework/Modules/$PROJECT_NAME.swiftmodule +# Step 4. Create universal binary file using lipo and place the combined executable in the copied framework directory +lipo -create -output $UNIVERSAL_OUTPUTFOLDER/$PROJECT_NAME.framework/$PROJECT_NAME $BUILD_PRODUCTS/Debug-iphonesimulator/$PROJECT_NAME.framework/$PROJECT_NAME $BUILD_DIR/$CONFIGURATION-iphoneos/$PROJECT_NAME.framework/$PROJECT_NAME +# Step 5. Convenience step to copy the framework to the project's directory +cp -R $UNIVERSAL_OUTPUTFOLDER/$PROJECT_NAME.framework ../../ios-release +rm -rf $UNIVERSAL_OUTPUTFOLDER + +popd \ No newline at end of file diff --git a/tools/ios-pod-release.sh b/tools/ios-pod-release.sh new file mode 100644 index 00000000..e69de29b From 60d6995728336076a4cf2babc02c04b3179e8d38 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Mon, 29 Jun 2020 15:08:52 +0800 Subject: [PATCH 02/16] Add xsalsa20poly1305 --- tcx-substrate/Cargo.toml | 2 + tcx-substrate/src/keystore.rs | 97 +++++++++++++++++++++++++++++++++++ tcx-substrate/src/lib.rs | 4 ++ 3 files changed, 103 insertions(+) create mode 100644 tcx-substrate/src/keystore.rs diff --git a/tcx-substrate/Cargo.toml b/tcx-substrate/Cargo.toml index 21680efe..76ff74c4 100644 --- a/tcx-substrate/Cargo.toml +++ b/tcx-substrate/Cargo.toml @@ -25,6 +25,8 @@ bs58 = "0.3.0" codec = { package = "parity-scale-codec", branch = "v1.0", default-features = false, features = ["derive"] } failure = "0.1.5" +#tweetnacl = "0.2.1" +xsalsa20poly1305 = "0.4.2" [dev-dependencies] sp-runtime = "2.0.0-rc3" diff --git a/tcx-substrate/src/keystore.rs b/tcx-substrate/src/keystore.rs new file mode 100644 index 00000000..528d0401 --- /dev/null +++ b/tcx-substrate/src/keystore.rs @@ -0,0 +1,97 @@ +use std::convert::TryInto; +use tcx_constants::Result; +use xsalsa20poly1305::aead::{generic_array::GenericArray, Aead, NewAead}; +use xsalsa20poly1305::XSalsa20Poly1305; + +const NONCE_LENGTH: usize = 24; +const PKCS8_DIVIDER: [u8; 5] = [161, 35, 3, 33, 0]; +const PKCS8_HEADER: [u8; 16] = [48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]; +const PUB_LENGTH: usize = 32; +const SEC_LENGTH: usize = 64; +const SEED_OFFSET: usize = PKCS8_HEADER.len(); +const SEED_LENGTH: usize = 32; + +// const PKCS8_HEADER: [u8;16] =[48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]; +// const + +fn decrypt_content(password: &str, encrypted: &str) -> Result { + // let mut sec = [0u8; 64]; + let encrypted_bytes = hex::decode(encrypted)?; + let nonce: &[u8; 24] = &encrypted_bytes[0..NONCE_LENGTH].try_into().unwrap(); + println!("nonce: {}", hex::encode(nonce.clone())); + let ciphertext = &encrypted_bytes[NONCE_LENGTH..]; + println!("cipher_text: {}", hex::encode(ciphertext.clone())); + // let mut encoded = Vec::with_capacity(cipher_text.len()); + // for idx in 0..cipher_text.len() { + // encoded.push(0u8) + // } + // encoded.resize(cipher_text.len(), 0u8); + let padding_password = password_to_key(password); + let key = GenericArray::from_slice(&padding_password); + let cipher = XSalsa20Poly1305::new(key); + let nonce = GenericArray::from_slice(nonce); + let encoded = cipher + .decrypt(nonce, ciphertext.as_ref()) + .map_err(|e| format_err!("{}", "decrypt error"))?; + // let mut encoded = [0u8; 133]; + + // let key = password_to_key(password); + // let key: [u8; 32] = hex::decode("696d746f6b656e31000000000000000000000000000000000000000000") + println!("key: {}", hex::encode(key.clone())); + // let ret = secretbox::xsalsa20poly1305::secretbox_open(&mut encoded, cipher_text, nonce, &key); + // assert!(ret, "open secretbox failed"); + // let sec_hex = hex::encode(sec.to_vec()); + // let pkcs8_header = "3053020101300506032b657004220420"; + println!("encoded: {}", hex::encode(encoded.to_vec().clone())); + let header = &encoded[0..PKCS8_HEADER.len()]; + println!("header: {}", hex::encode(header.clone())); + + assert!(header == PKCS8_HEADER, "Invalid Pkcs8 header found in body"); + + let mut secret_key = &encoded[PKCS8_HEADER.len()..PKCS8_HEADER.len() + SEC_LENGTH]; + let mut div_offset: usize = SEED_OFFSET + SEC_LENGTH; + let mut divider = &encoded[div_offset..div_offset + PKCS8_DIVIDER.len()]; + if divider != PKCS8_DIVIDER { + div_offset = SEED_OFFSET + SEED_LENGTH; + secret_key = &encoded[SEED_OFFSET..div_offset]; + divider = &encoded[div_offset..div_offset + PKCS8_DIVIDER.len()]; + } + + assert!( + divider == PKCS8_DIVIDER, + "Invalid Pkcs8 divider found in body" + ); + + Ok(hex::encode(secret_key)) +} + +fn password_to_key(password: &str) -> [u8; 32] { + let mut key = [0u8; 32]; + let password_bytes = password.as_bytes(); + let pwd_len = password_bytes.len(); + let iter_len = if pwd_len > 32 { 0 } else { pwd_len }; + for idx in 0..iter_len { + key[idx] = password_bytes[idx] + } + key + // let remain = pwd_len - iter_len; + // if remain > 0 { + // for idx in 0..remain { + // key[iter_len + idx] = 0 + // } + // } +} + +#[cfg(test)] +mod test_super { + use super::*; + use tcx_constants::{CoinInfo, CurveType}; + use tcx_primitive::FromHex; + + #[test] + fn test_decrypt_from_keystore() { + let encoded = "d80bcaf72c744d5a9a6c4229280e360d98d408afbe67232c3418a2a591b3f2bf468a319b7e5c1717bb8285619a76584a7961eac2183f94cfa56ad975cb78ae87b4dc18e7c20036bd448aa52c5ee7a45c4cdf41923c8133d6bfc29c737b65dcfb357884b55fb36d4762446fb26bfd8fce49142cf0e7d3642e2095ea6e425a8e923629306875c36b72a82d517478a19c8786b1be611e77286ba6448bf93c"; + let decrypted = decrypt_content("testing", encoded).unwrap(); + assert_eq!(decrypted, "416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"); + } +} diff --git a/tcx-substrate/src/lib.rs b/tcx-substrate/src/lib.rs index 98cae84c..231171cc 100644 --- a/tcx-substrate/src/lib.rs +++ b/tcx-substrate/src/lib.rs @@ -1,4 +1,5 @@ mod address; +mod keystore; mod signer; mod transaction; @@ -7,3 +8,6 @@ pub use transaction::{SubstrateRawTxIn, SubstrateTxOut}; pub(crate) const SIGNATURE_TYPE_SR25519: u8 = 0x01; pub(crate) const PAYLOAD_HASH_THRESHOLD: usize = 256; + +#[macro_use] +extern crate failure; From 58c1e2ffae8d955bb0d82d9d893def379989de8f Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Tue, 30 Jun 2020 10:43:41 +0800 Subject: [PATCH 03/16] test ed25519 pk import --- tcx-substrate/src/keystore.rs | 24 ---------- tcx/src/handler.rs | 2 +- tcx/src/lib.rs | 87 +++++++++++++++++++++++++++++++++++ 3 files changed, 88 insertions(+), 25 deletions(-) diff --git a/tcx-substrate/src/keystore.rs b/tcx-substrate/src/keystore.rs index 528d0401..04150d47 100644 --- a/tcx-substrate/src/keystore.rs +++ b/tcx-substrate/src/keystore.rs @@ -15,17 +15,9 @@ const SEED_LENGTH: usize = 32; // const fn decrypt_content(password: &str, encrypted: &str) -> Result { - // let mut sec = [0u8; 64]; let encrypted_bytes = hex::decode(encrypted)?; let nonce: &[u8; 24] = &encrypted_bytes[0..NONCE_LENGTH].try_into().unwrap(); - println!("nonce: {}", hex::encode(nonce.clone())); let ciphertext = &encrypted_bytes[NONCE_LENGTH..]; - println!("cipher_text: {}", hex::encode(ciphertext.clone())); - // let mut encoded = Vec::with_capacity(cipher_text.len()); - // for idx in 0..cipher_text.len() { - // encoded.push(0u8) - // } - // encoded.resize(cipher_text.len(), 0u8); let padding_password = password_to_key(password); let key = GenericArray::from_slice(&padding_password); let cipher = XSalsa20Poly1305::new(key); @@ -33,18 +25,8 @@ fn decrypt_content(password: &str, encrypted: &str) -> Result { let encoded = cipher .decrypt(nonce, ciphertext.as_ref()) .map_err(|e| format_err!("{}", "decrypt error"))?; - // let mut encoded = [0u8; 133]; - // let key = password_to_key(password); - // let key: [u8; 32] = hex::decode("696d746f6b656e31000000000000000000000000000000000000000000") - println!("key: {}", hex::encode(key.clone())); - // let ret = secretbox::xsalsa20poly1305::secretbox_open(&mut encoded, cipher_text, nonce, &key); - // assert!(ret, "open secretbox failed"); - // let sec_hex = hex::encode(sec.to_vec()); - // let pkcs8_header = "3053020101300506032b657004220420"; - println!("encoded: {}", hex::encode(encoded.to_vec().clone())); let header = &encoded[0..PKCS8_HEADER.len()]; - println!("header: {}", hex::encode(header.clone())); assert!(header == PKCS8_HEADER, "Invalid Pkcs8 header found in body"); @@ -74,12 +56,6 @@ fn password_to_key(password: &str) -> [u8; 32] { key[idx] = password_bytes[idx] } key - // let remain = pwd_len - iter_len; - // if remain > 0 { - // for idx in 0..remain { - // key[iter_len + idx] = 0 - // } - // } } #[cfg(test)] diff --git a/tcx/src/handler.rs b/tcx/src/handler.rs index c9a2506a..5d5eb6cf 100644 --- a/tcx/src/handler.rs +++ b/tcx/src/handler.rs @@ -435,7 +435,7 @@ pub(crate) fn export_private_key(data: &[u8]) -> Result> { // private_key prefix is only about chain type and network let coin_info = coin_info_from_param(¶m.chain_type, ¶m.network, "")?; - let value = if param.chain_type.as_str() == "TRON" { + let value = if ["TRON", "POLKADOT", "KUSAMA"].contains(¶m.chain_type.as_str()) { Ok(pk_hex.to_string()) } else { let bytes = hex::decode(pk_hex.to_string())?; diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 29dd6299..d653d6b9 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -10,6 +10,7 @@ use crate::api::{Response, TcxAction}; pub mod error_handling; pub mod handler; + use crate::error_handling::{landingpad, LAST_BACKTRACE, LAST_ERROR}; #[allow(deprecated)] use crate::handler::{ @@ -780,6 +781,92 @@ mod tests { }) } + #[test] + pub fn test_64bytes_private_key_store_import() { + run_test(|| { + let param: PrivateKeyStoreImportParam = PrivateKeyStoreImportParam { + private_key: "416c696365202020202020202020202020202020202020202020202020202020" + .to_string(), + password: TEST_PASSWORD.to_string(), + overwrite: true, + }; + + let ret = private_key_store_import(&encode_message(param).unwrap()).unwrap(); + let import_result: WalletResult = WalletResult::decode(ret.as_slice()).unwrap(); + + assert_eq!(0, import_result.accounts.len()); + + let derivations = vec![Derivation { + chain_type: "POLKADOT".to_string(), + path: "".to_string(), + network: "".to_string(), + seg_wit: "".to_string(), + chain_id: "".to_string(), + }]; + let param = KeystoreCommonDeriveParam { + id: import_result.id.to_string(), + password: TEST_PASSWORD.to_string(), + derivations, + }; + let derived_accounts_bytes = call_api("keystore_common_derive", param).unwrap(); + let derived_accounts: AccountsResponse = + AccountsResponse::decode(derived_accounts_bytes.as_slice()).unwrap(); + assert_eq!(1, derived_accounts.accounts.len()); + + assert_eq!( + "13iz1UvC8XMnHTW2wgoG7SxUhNgbp7trCgjxcuqTne9bGMQX", + derived_accounts.accounts[0].address + ); + + let export_param = ExportPrivateKeyParam { + id: import_result.id.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "POLKADOT".to_string(), + network: "".to_string(), + main_address: "13iz1UvC8XMnHTW2wgoG7SxUhNgbp7trCgjxcuqTne9bGMQX".to_string(), + path: "".to_string(), + }; + + let export_pk_bytes = call_api("export_private_key", export_param).unwrap(); + let export_pk: KeystoreCommonExportResult = + KeystoreCommonExportResult::decode(export_pk_bytes.as_slice()).unwrap(); + assert_eq!( + export_pk.value, + "416c696365202020202020202020202020202020202020202020202020202020" + ); + // pk rederive + // let derivations = vec![Derivation { + // chain_type: "LITECOIN".to_string(), + // path: "m/44'/2'/0'/0/0".to_string(), + // network: "MAINNET".to_string(), + // seg_wit: "NONE".to_string(), + // chain_id: "".to_string(), + // }]; + // let param = KeystoreCommonDeriveParam { + // id: import_result.id.to_string(), + // password: TEST_PASSWORD.to_string(), + // derivations, + // }; + // let derived_accounts_bytes = call_api("keystore_common_derive", param).unwrap(); + // let derived_accounts: AccountsResponse = + // AccountsResponse::decode(derived_accounts_bytes.as_slice()).unwrap(); + // assert_eq!( + // "LgGNTHMkgETS7oQcoekvACJQcH355xECog", + // derived_accounts.accounts[0].address + // ); + // assert_eq!("", derived_accounts.accounts[0].extended_xpub_key); + // + // let param = KeystoreCommonAccountsParam { + // id: import_result.id.to_string(), + // }; + // let accounts_ret = call_api("keystore_common_accounts", param).unwrap(); + // let ret = AccountsResponse::decode(accounts_ret.as_slice()).unwrap(); + // assert_eq!(5, ret.accounts.len()); + + remove_created_wallet(&import_result.id); + }) + } + #[test] pub fn test_private_key_store_export() { run_test(|| { From 9a7f443f4dc4db913594dfb25cd2395f57c57f43 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Wed, 1 Jul 2020 13:38:51 +0800 Subject: [PATCH 04/16] Add the decrypt/encryptPkcs8 --- tcx-primitive/src/ecc.rs | 2 + tcx-primitive/src/sr25519.rs | 15 +- tcx-substrate/Cargo.toml | 3 + tcx-substrate/src/keystore.rs | 250 ++++++++++++++++++++++++++++++---- tcx-substrate/src/lib.rs | 1 + tcx/src/lib.rs | 37 +---- 6 files changed, 243 insertions(+), 65 deletions(-) diff --git a/tcx-primitive/src/ecc.rs b/tcx-primitive/src/ecc.rs index d9d47c45..963cd0b3 100644 --- a/tcx-primitive/src/ecc.rs +++ b/tcx-primitive/src/ecc.rs @@ -42,6 +42,8 @@ pub enum KeyError { NotEnoughMemory, #[fail(display = "invalid_curve_type")] InvalidCurveType, + #[fail(display = "invalid_sr25519_key")] + InvalidSr25519Key, } pub trait PublicKey: Sized { diff --git a/tcx-primitive/src/sr25519.rs b/tcx-primitive/src/sr25519.rs index ea71878a..af76499b 100644 --- a/tcx-primitive/src/sr25519.rs +++ b/tcx-primitive/src/sr25519.rs @@ -1,6 +1,6 @@ -use crate::ecc::{PrivateKey as TraitPrivateKey, PublicKey as TraitPublicKey}; +use crate::ecc::{KeyError, PrivateKey as TraitPrivateKey, PublicKey as TraitPublicKey}; use crate::{FromHex, Result, ToHex}; -use schnorrkel::{ExpansionMode, MiniSecretKey}; +use schnorrkel::{ExpansionMode, MiniSecretKey, SecretKey}; use sp_core::sr25519::{Pair, Public}; use sp_core::{Pair as TraitPair, Public as TraitPublic}; @@ -29,11 +29,12 @@ impl TraitPrivateKey for Sr25519PrivateKey { type PublicKey = Sr25519PublicKey; fn from_slice(data: &[u8]) -> Result { - let mini_key: MiniSecretKey = - MiniSecretKey::from_bytes(data).expect("32 bytes can always build a key; qed"); - - let kp = mini_key.expand_to_keypair(ExpansionMode::Ed25519); - Ok(Sr25519PrivateKey(Pair::from(kp))) + // let mini_key: MiniSecretKey = + // MiniSecretKey::from_bytes(data).expect("32 bytes can always build a key; qed"); + // + // let kp = mini_key.expand_to_keypair(ExpansionMode::Ed25519); + let pk = SecretKey::from_ed25519_bytes(data).map_err(|_| KeyError::InvalidSr25519Key)?; + Ok(Sr25519PrivateKey(Pair::from(pk))) } fn public_key(&self) -> Self::PublicKey { diff --git a/tcx-substrate/Cargo.toml b/tcx-substrate/Cargo.toml index 76ff74c4..1db7d846 100644 --- a/tcx-substrate/Cargo.toml +++ b/tcx-substrate/Cargo.toml @@ -23,10 +23,13 @@ prost-types = "0.6.1" base58 = "0.1.0" bs58 = "0.3.0" codec = { package = "parity-scale-codec", branch = "v1.0", default-features = false, features = ["derive"] } +rand = "0.6" failure = "0.1.5" #tweetnacl = "0.2.1" xsalsa20poly1305 = "0.4.2" +serde_json = "1.0" +serde = { version = "1.0", features = ["derive"] } [dev-dependencies] sp-runtime = "2.0.0-rc3" diff --git a/tcx-substrate/src/keystore.rs b/tcx-substrate/src/keystore.rs index 04150d47..8eaa6ce7 100644 --- a/tcx-substrate/src/keystore.rs +++ b/tcx-substrate/src/keystore.rs @@ -1,8 +1,46 @@ +use crate::SubstrateAddress; +use rand::Rng; +use serde::{Deserialize, Serialize}; use std::convert::TryInto; -use tcx_constants::Result; +use tcx_chain::Address; +use tcx_constants::coin_info::coin_info_from_param; +use tcx_constants::{CoinInfo, Result}; +use tcx_primitive::{ + DeterministicPrivateKey, PrivateKey, PublicKey, Sr25519PrivateKey, TypedPublicKey, +}; use xsalsa20poly1305::aead::{generic_array::GenericArray, Aead, NewAead}; use xsalsa20poly1305::XSalsa20Poly1305; +#[derive(Fail, Debug, PartialOrd, PartialEq)] +pub enum Error { + #[fail(display = "invalid_keystore# {}", _0)] + InvalidKeystore(String), +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct SubstrateKeystore { + pub address: String, + pub encoded: String, + pub encoding: SubstrateKeystoreEncoding, + pub meta: SubstrateKeystoreMeta, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct SubstrateKeystoreEncoding { + pub content: Vec, + #[serde(rename = "type")] + pub encoding_type: String, + pub version: String, +} + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +#[serde(rename_all = "camelCase")] +pub struct SubstrateKeystoreMeta { + pub name: String, +} + const NONCE_LENGTH: usize = 24; const PKCS8_DIVIDER: [u8; 5] = [161, 35, 3, 33, 0]; const PKCS8_HEADER: [u8; 16] = [48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]; @@ -11,10 +49,107 @@ const SEC_LENGTH: usize = 64; const SEED_OFFSET: usize = PKCS8_HEADER.len(); const SEED_LENGTH: usize = 32; -// const PKCS8_HEADER: [u8;16] =[48, 83, 2, 1, 1, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32]; -// const +// fn decrypt_keystore(keystore: &str) -> Result { +// let keystore: SubstrateKeystore = serde_json::from_str(keystore)?; +// } + +impl SubstrateKeystore { + pub fn new( + password: &str, + prv_key: &[u8], + pub_key: &[u8], + addr: &str, + ) -> Result { + // let pk = Sr25519PrivateKey::from_slice(prv_key)?; + // let pub_key = pk.public_key(); + // let coin_info = coin_info_from_param("KUSAMA", "", "").unwrap(); + // let new_addr = SubstrateAddress::from_public_key(&TypedPublicKey::Sr25519(pub_key.clone()), &coin_info)?; + let encoding = format!( + "0x{}", + SubstrateKeystore::encrypt_seed(password, prv_key, pub_key)? + ); + + Ok(SubstrateKeystore { + address: addr.to_string(), + encoded: encoding.to_string(), + encoding: SubstrateKeystoreEncoding { + content: vec!["pkcs8".to_string(), "sr25519".to_string()], + encoding_type: "xsalsa20-poly1305".to_string(), + version: "2".to_string(), + }, + meta: SubstrateKeystoreMeta { + name: "export from imToken".to_string(), + }, + }) + } + + pub fn validate(&self) -> Result<()> { + if self.address.is_empty() { + return Err(Error::InvalidKeystore("address is empty".to_string()).into()); + } + if self.encoded.is_empty() { + return Err(Error::InvalidKeystore("encoded is empty".to_string()).into()); + } + if self.encoding.content[0] != "pkcs8" { + return Err(Error::InvalidKeystore("need pkcs8 padding".to_string()).into()); + } + if self.encoding.content[1] != "sr25519" { + return Err(Error::InvalidKeystore("only support sr25519".to_string()).into()); + } + if self.encoding.encoding_type != "xsalsa20-poly1305" { + return Err( + Error::InvalidKeystore("only support xsalsa20-poly1305".to_string()).into(), + ); + } + if self.encoding.version != "2" { + return Err(Error::InvalidKeystore("only support version 2".to_string()).into()); + } + + Ok(()) + } + + pub fn decrypt(&self, password: &str) -> Result<(Vec, Vec)> { + let _ = self.validate()?; + let encoded = if self.encoded.starts_with("0x") { + &self.encoded[2..] + } else { + &self.encoded + }; + + let decrypted_hex = decrypt_content(password, encoded)?; + let decrypted = hex::decode(decrypted_hex)?; + let header = &decrypted[0..PKCS8_HEADER.len()]; + + assert!(header == PKCS8_HEADER, "Invalid Pkcs8 header found in body"); + + let mut secret_key = + decrypted[PKCS8_HEADER.len()..PKCS8_HEADER.len() + SEC_LENGTH].to_vec(); + + let mut div_offset: usize = SEED_OFFSET + SEC_LENGTH; + let mut divider = &decrypted[div_offset..div_offset + PKCS8_DIVIDER.len()]; + if divider != PKCS8_DIVIDER { + div_offset = SEED_OFFSET + SEED_LENGTH; + secret_key = decrypted[SEED_OFFSET..div_offset].to_vec(); + divider = &decrypted[div_offset..div_offset + PKCS8_DIVIDER.len()]; + } + + assert!( + divider == PKCS8_DIVIDER, + "Invalid Pkcs8 divider found in body" + ); -fn decrypt_content(password: &str, encrypted: &str) -> Result { + let pub_offset = div_offset + PKCS8_DIVIDER.len(); + let pub_key = &decrypted[pub_offset..pub_offset + PUB_LENGTH]; + Ok((secret_key, pub_key.to_vec())) + } + + pub fn encrypt_seed(password: &str, seed: &[u8], pub_key: &[u8]) -> Result { + let plaintext = [&PKCS8_HEADER, seed, &PKCS8_DIVIDER, pub_key].concat(); + encrypt_content(password, &plaintext) + } +} + +fn decrypt_content(password: &str, encrypted: &str) -> Result> { let encrypted_bytes = hex::decode(encrypted)?; let nonce: &[u8; 24] = &encrypted_bytes[0..NONCE_LENGTH].try_into().unwrap(); let ciphertext = &encrypted_bytes[NONCE_LENGTH..]; @@ -22,29 +157,32 @@ fn decrypt_content(password: &str, encrypted: &str) -> Result { let key = GenericArray::from_slice(&padding_password); let cipher = XSalsa20Poly1305::new(key); let nonce = GenericArray::from_slice(nonce); - let encoded = cipher + cipher .decrypt(nonce, ciphertext.as_ref()) - .map_err(|e| format_err!("{}", "decrypt error"))?; - - let header = &encoded[0..PKCS8_HEADER.len()]; + .map_err(|e| format_err!("{}", "decrypt error")) +} - assert!(header == PKCS8_HEADER, "Invalid Pkcs8 header found in body"); +fn encrypt_content(password: &str, plaintext: &[u8]) -> Result { + let nonce_bytes = gen_nonce(); + let padding_password = password_to_key(password); + let key = GenericArray::from_slice(&padding_password); + let cipher = XSalsa20Poly1305::new(key); + let nonce = GenericArray::from_slice(&nonce_bytes); + let encoded = cipher + .encrypt(&nonce, plaintext) + .map_err(|e| format_err!("{}", "encrypt error"))?; - let mut secret_key = &encoded[PKCS8_HEADER.len()..PKCS8_HEADER.len() + SEC_LENGTH]; - let mut div_offset: usize = SEED_OFFSET + SEC_LENGTH; - let mut divider = &encoded[div_offset..div_offset + PKCS8_DIVIDER.len()]; - if divider != PKCS8_DIVIDER { - div_offset = SEED_OFFSET + SEED_LENGTH; - secret_key = &encoded[SEED_OFFSET..div_offset]; - divider = &encoded[div_offset..div_offset + PKCS8_DIVIDER.len()]; - } + Ok(hex::encode([nonce_bytes.to_vec(), encoded].concat())) +} - assert!( - divider == PKCS8_DIVIDER, - "Invalid Pkcs8 divider found in body" - ); +fn gen_nonce() -> [u8; 24] { + let mut rng = rand::thread_rng(); + let mut nonce = [0u8; 24]; - Ok(hex::encode(secret_key)) + for idx in 0..24 { + nonce[idx] = rng.gen::() + } + nonce } fn password_to_key(password: &str) -> [u8; 32] { @@ -58,16 +196,78 @@ fn password_to_key(password: &str) -> [u8; 32] { key } +pub fn decode_pkcs8(keystore: &str, password: &str) -> Result> { + let ks: SubstrateKeystore = serde_json::from_str(keystore)?; + let (secret_key, pub_key) = ks.decrypt(password)?; + let priv_key = if secret_key.len() == 32 { + Sr25519PrivateKey::from_seed(&secret_key) + } else { + Sr25519PrivateKey::from_slice(&secret_key) + }?; + if priv_key.public_key().to_bytes() != pub_key { + return Err(Error::InvalidKeystore("invalid public key".to_string()).into()); + } + Ok(secret_key) +} + +pub fn encode_pkcs8(password: &str, prv_key: &[u8], coin: &CoinInfo) -> Result { + let pk = Sr25519PrivateKey::from_slice(prv_key)?; + let pub_key = pk.public_key(); + let addr = SubstrateAddress::from_public_key(&TypedPublicKey::Sr25519(pub_key.clone()), &coin)?; + let keystore = SubstrateKeystore::new(password, prv_key, &pub_key.to_bytes(), &addr)?; + let keystore_str = serde_json::to_string(&keystore)?; + Ok(keystore_str) +} + #[cfg(test)] mod test_super { use super::*; - use tcx_constants::{CoinInfo, CurveType}; + use tcx_constants::{CoinInfo, CurveType, TEST_PASSWORD}; use tcx_primitive::FromHex; #[test] - fn test_decrypt_from_keystore() { + fn test_decrypt_encoded() { let encoded = "d80bcaf72c744d5a9a6c4229280e360d98d408afbe67232c3418a2a591b3f2bf468a319b7e5c1717bb8285619a76584a7961eac2183f94cfa56ad975cb78ae87b4dc18e7c20036bd448aa52c5ee7a45c4cdf41923c8133d6bfc29c737b65dcfb357884b55fb36d4762446fb26bfd8fce49142cf0e7d3642e2095ea6e425a8e923629306875c36b72a82d517478a19c8786b1be611e77286ba6448bf93c"; let decrypted = decrypt_content("testing", encoded).unwrap(); - assert_eq!(decrypted, "416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"); + assert_eq!(hex::encode(decrypted), "416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"); + } + + const KEYSTORE_STR: &str = r#"{ + "address": "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS", + "encoded": "0x3f6fb3c7a49b647d31999394a54f76ee451a23f46978bbca9e7d8e6a4d5d5c7457edaecf4108e2b77b157353f446d7c27bfc6e75032b60f28012f54495b9148ebe06ea5522f4d95e2a87d46eaae156372ae1111627f6d17a0b02830f7eb0f207061df299c730ea6e50bef02e3a218dbe29a0649769a7ad0a3ee1e10bc001e7b19a0d44b9a73e4889933635e4d1faa4da203955f6e29ec7b2df4fcf3f42", + "encoding": { + "content": [ + "pkcs8", + "sr25519" + ], + "type": "xsalsa20-poly1305", + "version": "2" + }, + "meta": { + "genesisHash": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", + "name": "keystore_import_test", + "tags": [], + "whenCreated": 1593567896695 + } + }"#; + + #[test] + fn test_decrypt_from_keystore() { + let decrypted = decode_pkcs8(KEYSTORE_STR, TEST_PASSWORD).unwrap(); + assert_eq!(hex::encode(decrypted), "00ea01b0116da6ca425c477521fd49cc763988ac403ab560f4022936a18a4341016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd"); + } + + #[test] + fn test_export_from_seed() { + let prv_key = hex::decode("00ea01b0116da6ca425c477521fd49cc763988ac403ab560f4022936a18a4341016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd").unwrap(); + let coin_info = coin_info_from_param("KUSAMA", "", "").unwrap(); + let export_json = encode_pkcs8(&TEST_PASSWORD, &prv_key, &coin_info).unwrap(); + assert_eq!(export_json, ""); + } + + #[test] + fn is_valid_keystore() { + let keystore: SubstrateKeystore = serde_json::from_str(KEYSTORE_STR).unwrap(); + assert!(keystore.validate().is_ok()) } } diff --git a/tcx-substrate/src/lib.rs b/tcx-substrate/src/lib.rs index 231171cc..8bab592a 100644 --- a/tcx-substrate/src/lib.rs +++ b/tcx-substrate/src/lib.rs @@ -11,3 +11,4 @@ pub(crate) const PAYLOAD_HASH_THRESHOLD: usize = 256; #[macro_use] extern crate failure; +extern crate serde_json; diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index d653d6b9..707c1c52 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -785,7 +785,7 @@ mod tests { pub fn test_64bytes_private_key_store_import() { run_test(|| { let param: PrivateKeyStoreImportParam = PrivateKeyStoreImportParam { - private_key: "416c696365202020202020202020202020202020202020202020202020202020" + private_key: "416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f" .to_string(), password: TEST_PASSWORD.to_string(), overwrite: true, @@ -814,7 +814,7 @@ mod tests { assert_eq!(1, derived_accounts.accounts.len()); assert_eq!( - "13iz1UvC8XMnHTW2wgoG7SxUhNgbp7trCgjxcuqTne9bGMQX", + "133smEABgtt8FRkZGrZfAzCV522bxo2y5FwVoTcSaY8z1nEq", derived_accounts.accounts[0].address ); @@ -823,7 +823,7 @@ mod tests { password: TEST_PASSWORD.to_string(), chain_type: "POLKADOT".to_string(), network: "".to_string(), - main_address: "13iz1UvC8XMnHTW2wgoG7SxUhNgbp7trCgjxcuqTne9bGMQX".to_string(), + main_address: "133smEABgtt8FRkZGrZfAzCV522bxo2y5FwVoTcSaY8z1nEq".to_string(), path: "".to_string(), }; @@ -832,37 +832,8 @@ mod tests { KeystoreCommonExportResult::decode(export_pk_bytes.as_slice()).unwrap(); assert_eq!( export_pk.value, - "416c696365202020202020202020202020202020202020202020202020202020" + "416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f" ); - // pk rederive - // let derivations = vec![Derivation { - // chain_type: "LITECOIN".to_string(), - // path: "m/44'/2'/0'/0/0".to_string(), - // network: "MAINNET".to_string(), - // seg_wit: "NONE".to_string(), - // chain_id: "".to_string(), - // }]; - // let param = KeystoreCommonDeriveParam { - // id: import_result.id.to_string(), - // password: TEST_PASSWORD.to_string(), - // derivations, - // }; - // let derived_accounts_bytes = call_api("keystore_common_derive", param).unwrap(); - // let derived_accounts: AccountsResponse = - // AccountsResponse::decode(derived_accounts_bytes.as_slice()).unwrap(); - // assert_eq!( - // "LgGNTHMkgETS7oQcoekvACJQcH355xECog", - // derived_accounts.accounts[0].address - // ); - // assert_eq!("", derived_accounts.accounts[0].extended_xpub_key); - // - // let param = KeystoreCommonAccountsParam { - // id: import_result.id.to_string(), - // }; - // let accounts_ret = call_api("keystore_common_accounts", param).unwrap(); - // let ret = AccountsResponse::decode(accounts_ret.as_slice()).unwrap(); - // assert_eq!(5, ret.accounts.len()); - remove_created_wallet(&import_result.id); }) } From 2fd1953572314be32c20fd8d67a16785eedd6d12 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Thu, 2 Jul 2020 11:39:44 +0800 Subject: [PATCH 05/16] Add the substrate_import/export_api --- tcx-chain/src/keystore/mod.rs | 2 + tcx-chain/src/keystore/private.rs | 6 +++ tcx-primitive/src/sr25519.rs | 11 ++-- tcx-proto/src/api.proto | 5 +- tcx-proto/src/params.proto | 11 ++++ tcx-proto/src/substrate.proto | 1 + tcx-substrate/src/keystore.rs | 20 ++++--- tcx-substrate/src/lib.rs | 1 + tcx/src/api.rs | 16 ++++++ tcx/src/handler.rs | 36 +++++++++++-- tcx/src/lib.rs | 89 +++++++++++++++++++++++++++++-- 11 files changed, 177 insertions(+), 21 deletions(-) diff --git a/tcx-chain/src/keystore/mod.rs b/tcx-chain/src/keystore/mod.rs index c59150c8..c074077f 100644 --- a/tcx-chain/src/keystore/mod.rs +++ b/tcx-chain/src/keystore/mod.rs @@ -49,6 +49,8 @@ pub enum Error { KeystoreLocked, #[fail(display = "invalid_version")] InvalidVersion, + #[fail(display = "pkstore_can_not_add_other_curve_account")] + PkstoreCannotAddOtherCurveAccount, } fn transform_mnemonic_error(err: failure::Error) -> Error { diff --git a/tcx-chain/src/keystore/private.rs b/tcx-chain/src/keystore/private.rs index fb18224b..be9a0ebb 100644 --- a/tcx-chain/src/keystore/private.rs +++ b/tcx-chain/src/keystore/private.rs @@ -77,6 +77,12 @@ impl PrivateKeystore { pub(crate) fn derive_coin(&mut self, coin_info: &CoinInfo) -> Result { tcx_ensure!(self.private_key.is_some(), Error::KeystoreLocked); + if self.store.active_accounts.len() > 0 + && self.store.active_accounts[0].curve != coin_info.curve + { + return Err(Error::PkstoreCannotAddOtherCurveAccount.into()); + } + let sk = self.private_key.as_ref().unwrap(); let account = Self::private_key_to_account::(coin_info, sk)?; diff --git a/tcx-primitive/src/sr25519.rs b/tcx-primitive/src/sr25519.rs index af76499b..7a237290 100644 --- a/tcx-primitive/src/sr25519.rs +++ b/tcx-primitive/src/sr25519.rs @@ -92,17 +92,20 @@ mod tests { #[test] fn test_private_key_from_slice() { let pk_bytes: Vec = - hex::decode("1111111111111111111111111111111111111111111111111111111111111111") + hex::decode("00ea01b0116da6ca425c477521fd49cc763988ac403ab560f4022936a18a4341016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd") .unwrap(); let pk: Sr25519PrivateKey = Sr25519PrivateKey::from_slice(&pk_bytes).unwrap(); - assert_eq!("704b72419b89bab3a4632685428901bf5715b44e40ec0df50f01fdc1553bf60b092539815e61663c7f9c4c9c377346f5d2936875bd760f0ac5c5f4f6e78a6d76", hex::encode(pk.to_bytes())); + assert_eq!( + &hex::encode(pk.to_bytes())[64..], + "016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd" + ); let public_key: Sr25519PublicKey = pk.public_key(); assert_eq!( - "50780547322a1ceba67ea8c552c9bc6c686f8698ac9a8cafab7cd15a1db19859", + "fc581c897af481b10cf846d88754f1d115e486e5b7bcc39c0588c01b0a9b7a11", public_key.to_hex() ); assert_eq!( - "5DtDLm5rQHShDqojQpsvcN8tRXHVFaecfDoRet1SU6BFD9Fi", + "5Hma6gDS9yY7gPTuAFvmMDNcxPf9JqMZdPsaihfXiyw5NRnQ", format!("{}", public_key) ); } diff --git a/tcx-proto/src/api.proto b/tcx-proto/src/api.proto index 85fa040f..8a30b42b 100644 --- a/tcx-proto/src/api.proto +++ b/tcx-proto/src/api.proto @@ -52,4 +52,7 @@ message ExportPrivateKeyParam { message WalletKeyParam { string id = 1; string password = 2; -} \ No newline at end of file +} + + + diff --git a/tcx-proto/src/params.proto b/tcx-proto/src/params.proto index a9787fbc..2c6fb289 100644 --- a/tcx-proto/src/params.proto +++ b/tcx-proto/src/params.proto @@ -181,3 +181,14 @@ message HdStoreExtendedPublicKeyParam { message HdStoreExtendedPublicKeyResponse { string extendedPublicKey = 1; } + +message ImportSubstrateKeystoreParam { + string keystore = 1; + string password = 2; + string chainType = 3; + bool override = 4; +} + +message ExportSubstrateKeystoreResult { + string keystore = 1; +} diff --git a/tcx-proto/src/substrate.proto b/tcx-proto/src/substrate.proto index 06f0324f..48ed102b 100644 --- a/tcx-proto/src/substrate.proto +++ b/tcx-proto/src/substrate.proto @@ -1,6 +1,7 @@ syntax = "proto3"; package transaction; + message SubstrateRawTxIn { string rawData = 1; } diff --git a/tcx-substrate/src/keystore.rs b/tcx-substrate/src/keystore.rs index 8eaa6ce7..1a603eaf 100644 --- a/tcx-substrate/src/keystore.rs +++ b/tcx-substrate/src/keystore.rs @@ -116,8 +116,8 @@ impl SubstrateKeystore { &self.encoded }; - let decrypted_hex = decrypt_content(password, encoded)?; - let decrypted = hex::decode(decrypted_hex)?; + let decrypted = decrypt_content(password, encoded)?; + // let decrypted = hex::decode(decrypted_hex)?; let header = &decrypted[0..PKCS8_HEADER.len()]; assert!(header == PKCS8_HEADER, "Invalid Pkcs8 header found in body"); @@ -196,7 +196,7 @@ fn password_to_key(password: &str) -> [u8; 32] { key } -pub fn decode_pkcs8(keystore: &str, password: &str) -> Result> { +pub fn decode_substrate_keystore(keystore: &str, password: &str) -> Result> { let ks: SubstrateKeystore = serde_json::from_str(keystore)?; let (secret_key, pub_key) = ks.decrypt(password)?; let priv_key = if secret_key.len() == 32 { @@ -210,7 +210,11 @@ pub fn decode_pkcs8(keystore: &str, password: &str) -> Result> { Ok(secret_key) } -pub fn encode_pkcs8(password: &str, prv_key: &[u8], coin: &CoinInfo) -> Result { +pub fn encode_substrate_keystore( + password: &str, + prv_key: &[u8], + coin: &CoinInfo, +) -> Result { let pk = Sr25519PrivateKey::from_slice(prv_key)?; let pub_key = pk.public_key(); let addr = SubstrateAddress::from_public_key(&TypedPublicKey::Sr25519(pub_key.clone()), &coin)?; @@ -229,7 +233,7 @@ mod test_super { fn test_decrypt_encoded() { let encoded = "d80bcaf72c744d5a9a6c4229280e360d98d408afbe67232c3418a2a591b3f2bf468a319b7e5c1717bb8285619a76584a7961eac2183f94cfa56ad975cb78ae87b4dc18e7c20036bd448aa52c5ee7a45c4cdf41923c8133d6bfc29c737b65dcfb357884b55fb36d4762446fb26bfd8fce49142cf0e7d3642e2095ea6e425a8e923629306875c36b72a82d517478a19c8786b1be611e77286ba6448bf93c"; let decrypted = decrypt_content("testing", encoded).unwrap(); - assert_eq!(hex::encode(decrypted), "416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"); + assert_eq!(hex::encode(decrypted), "3053020101300506032b657004220420416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4fa123032100d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f"); } const KEYSTORE_STR: &str = r#"{ @@ -253,15 +257,15 @@ mod test_super { #[test] fn test_decrypt_from_keystore() { - let decrypted = decode_pkcs8(KEYSTORE_STR, TEST_PASSWORD).unwrap(); + let decrypted = decode_substrate_keystore(KEYSTORE_STR, TEST_PASSWORD).unwrap(); assert_eq!(hex::encode(decrypted), "00ea01b0116da6ca425c477521fd49cc763988ac403ab560f4022936a18a4341016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd"); } #[test] - fn test_export_from_seed() { + fn test_export_from_sertket_key() { let prv_key = hex::decode("00ea01b0116da6ca425c477521fd49cc763988ac403ab560f4022936a18a4341016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd").unwrap(); let coin_info = coin_info_from_param("KUSAMA", "", "").unwrap(); - let export_json = encode_pkcs8(&TEST_PASSWORD, &prv_key, &coin_info).unwrap(); + let export_json = encode_substrate_keystore(&TEST_PASSWORD, &prv_key, &coin_info).unwrap(); assert_eq!(export_json, ""); } diff --git a/tcx-substrate/src/lib.rs b/tcx-substrate/src/lib.rs index 8bab592a..d864cbf3 100644 --- a/tcx-substrate/src/lib.rs +++ b/tcx-substrate/src/lib.rs @@ -4,6 +4,7 @@ mod signer; mod transaction; pub use address::SubstrateAddress; +pub use keystore::{decode_substrate_keystore, encode_substrate_keystore}; pub use transaction::{SubstrateRawTxIn, SubstrateTxOut}; pub(crate) const SIGNATURE_TYPE_SR25519: u8 = 0x01; diff --git a/tcx/src/api.rs b/tcx/src/api.rs index 9b8f879c..7434bcc6 100644 --- a/tcx/src/api.rs +++ b/tcx/src/api.rs @@ -316,6 +316,22 @@ pub struct HdStoreExtendedPublicKeyResponse { #[prost(string, tag = "1")] pub extended_public_key: std::string::String, } +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ImportSubstrateKeystoreParam { + #[prost(string, tag = "1")] + pub keystore: std::string::String, + #[prost(string, tag = "2")] + pub password: std::string::String, + #[prost(string, tag = "3")] + pub chain_type: std::string::String, + #[prost(bool, tag = "4")] + pub r#override: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExportSubstrateKeystoreResult { + #[prost(string, tag = "1")] + pub keystore: std::string::String, +} /// only support two types #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] diff --git a/tcx/src/handler.rs b/tcx/src/handler.rs index 5d5eb6cf..feaaa9a7 100644 --- a/tcx/src/handler.rs +++ b/tcx/src/handler.rs @@ -21,8 +21,9 @@ use tcx_tron::TrxAddress; use crate::api::keystore_common_derive_param::Derivation; use crate::api::sign_param::Key; use crate::api::{ - AccountResponse, AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, HdStoreCreateParam, - HdStoreImportParam, KeyType, KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, + AccountResponse, AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, + ExportSubstrateKeystoreResult, HdStoreCreateParam, HdStoreImportParam, + ImportSubstrateKeystoreParam, KeyType, KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, KeystoreCommonExistsParam, KeystoreCommonExistsResult, KeystoreCommonExportResult, PrivateKeyStoreExportParam, PrivateKeyStoreImportParam, Response, WalletKeyParam, WalletResult, }; @@ -40,7 +41,9 @@ use tcx_constants::CurveType; use tcx_crypto::aes::cbc::encrypt_pkcs7; use tcx_crypto::KDF_ROUNDS; use tcx_primitive::{Bip32DeterministicPublicKey, Ss58Codec}; -use tcx_substrate::{SubstrateAddress, SubstrateRawTxIn}; +use tcx_substrate::{ + decode_substrate_keystore, encode_substrate_keystore, SubstrateAddress, SubstrateRawTxIn, +}; use tcx_tron::transaction::{TronMessageInput, TronTxInput}; pub(crate) fn encode_message(msg: impl Message) -> Result> { @@ -709,6 +712,33 @@ pub(crate) fn sign_substrate_tx_raw(param: &SignParam, keystore: &mut Keystore) encode_message(signed_tx) } +pub(crate) fn import_substrate_keystore(data: &[u8]) -> Result> { + let param: ImportSubstrateKeystoreParam = ImportSubstrateKeystoreParam::decode(data)?; + let pk = decode_substrate_keystore(¶m.keystore, ¶m.password)?; + let pk_import_param = PrivateKeyStoreImportParam { + private_key: hex::encode(pk), + password: param.password.to_string(), + overwrite: true, + }; + let param_bytes = encode_message(pk_import_param)?; + private_key_store_import(¶m_bytes) +} + +pub(crate) fn export_substrate_keystore(data: &[u8]) -> Result> { + let param: ExportPrivateKeyParam = ExportPrivateKeyParam::decode(data.clone())?; + let ret = export_private_key(data)?; + let export_result: KeystoreCommonExportResult = + KeystoreCommonExportResult::decode(ret.as_slice())?; + let pk = export_result.value; + let pk_bytes = hex::decode(pk)?; + let coin = coin_info_from_param(¶m.chain_type, ¶m.network, "")?; + let keystore_str = encode_substrate_keystore(¶m.password, &pk_bytes, &coin)?; + let ret = ExportSubstrateKeystoreResult { + keystore: keystore_str, + }; + encode_message(ret) +} + pub(crate) fn unlock_then_crash(data: &[u8]) -> Result> { let param: WalletKeyParam = WalletKeyParam::decode(data).unwrap(); let mut map = KEYSTORE_MAP.write(); diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 707c1c52..97b22c25 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -23,6 +23,7 @@ use crate::handler::{ mod filemanager; +use crate::handler::{export_substrate_keystore, import_substrate_keystore}; use parking_lot::RwLock; extern crate serde_json; @@ -94,6 +95,15 @@ pub unsafe extern "C" fn call_tcx_api(hex_str: *const c_char) -> *const c_char { "sign_tx" => landingpad(|| sign_tx(&action.param.unwrap().value)), "tron_sign_msg" => landingpad(|| tron_sign_message(&action.param.unwrap().value)), + + "substrate_keystore_import" => { + landingpad(|| import_substrate_keystore(&action.param.unwrap().value)) + } + + "substrate_keystore_export" => { + landingpad(|| export_substrate_keystore(&action.param.unwrap().value)) + } + // !!! WARNING !!! used for `cache_dk` feature "get_derived_key" => landingpad(|| get_derived_key(&action.param.unwrap().value)), // !!! WARNING !!! used for test only @@ -146,11 +156,11 @@ mod tests { use crate::api::keystore_common_derive_param::Derivation; use crate::api::{ - AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, HdStoreCreateParam, - InitTokenCoreXParam, KeyType, KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, - KeystoreCommonExistsParam, KeystoreCommonExistsResult, KeystoreCommonExportResult, - PrivateKeyStoreExportParam, PrivateKeyStoreImportParam, Response, SignParam, - WalletKeyParam, + AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, ExportSubstrateKeystoreResult, + HdStoreCreateParam, ImportSubstrateKeystoreParam, InitTokenCoreXParam, KeyType, + KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, KeystoreCommonExistsParam, + KeystoreCommonExistsResult, KeystoreCommonExportResult, PrivateKeyStoreExportParam, + PrivateKeyStoreImportParam, Response, SignParam, WalletKeyParam, }; use crate::api::{HdStoreImportParam, WalletResult}; use crate::handler::hd_store_import; @@ -1451,6 +1461,75 @@ mod tests { // }) // } + #[test] + pub fn test_import_substrate_keystore() { + run_test(|| { + let keystore_str: &str = r#"{ + "address": "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS", + "encoded": "0xf7e7e89d3016c9b4d93bb1129adf69e5949ca1fb58c29da4591ddc72c52238a35835e3f2ae023f9867ff301bc4132463527ac03525eaac54664a7cb658eae68a0bbc99354222c194d6100b2bf3a492639229077a2e2818d8196e002f0b5556104be23b11633858259dbbd3f91ea1d34d6ce182b62d8381af1ef3c35e9ab1583267cfa41aa58bfd64435c2b5047baf9052f0953d9f7854d2d396dfcad13", + "encoding": { + "content": [ + "pkcs8", + "sr25519" + ], + "type": "xsalsa20-poly1305", + "version": "2" + }, + "meta": { + "genesisHash": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", + "name": "keystore_import", + "tags": [], + "whenCreated": 1593591324334 + } +}"#; + + let param = ImportSubstrateKeystoreParam { + keystore: keystore_str.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "KUSAMA".to_string(), + r#override: true, + }; + // let param_bytes = encode_message(param).unwrap(); + let ret_bytes = call_api("substrate_keystore_import", param).unwrap(); + let wallet_ret: WalletResult = WalletResult::decode(ret_bytes.as_slice()).unwrap(); + let derivation = Derivation { + chain_type: "KUSAMA".to_string(), + path: "".to_string(), + network: "".to_string(), + seg_wit: "".to_string(), + chain_id: "".to_string(), + }; + + let param = KeystoreCommonDeriveParam { + id: wallet_ret.id.to_string(), + password: TEST_PASSWORD.to_string(), + derivations: vec![derivation], + }; + + let ret = call_api("keystore_common_derive", param).unwrap(); + let accounts: AccountsResponse = AccountsResponse::decode(ret.as_slice()).unwrap(); + + assert_eq!( + accounts.accounts[0].address, + "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" + ); + + let export_param = ExportPrivateKeyParam { + id: wallet_ret.id.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "KUSAMA".to_string(), + network: "".to_string(), + main_address: "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS".to_string(), + path: "".to_string(), + }; + let ret = call_api("substrate_keystore_export", export_param).unwrap(); + let keystore_ret: ExportSubstrateKeystoreResult = + ExportSubstrateKeystoreResult::decode(ret.as_slice()).unwrap(); + // assert_eq!(keystore_ret.keystore, ""); + remove_created_wallet(&wallet_ret.id); + }) + } + #[test] pub fn test_sign_substrate_raw_tx() { run_test(|| { From c9c559bc4df7564fc2aee5996106eeaccbd74df3 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Thu, 2 Jul 2020 14:11:49 +0800 Subject: [PATCH 06/16] Remove the armv7 --- .../UserInterfaceState.xcuserstate | Bin 22381 -> 22532 bytes tools/ios-example-build.sh | 6 +++--- tools/ios-framework-build.sh | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) mode change 100644 => 100755 tools/ios-framework-build.sh diff --git a/examples/TokenCoreX/TokenCoreX.xcodeproj/project.xcworkspace/xcuserdata/xyz.xcuserdatad/UserInterfaceState.xcuserstate b/examples/TokenCoreX/TokenCoreX.xcodeproj/project.xcworkspace/xcuserdata/xyz.xcuserdatad/UserInterfaceState.xcuserstate index 09939f2f79f08cd2770a0a38d18127887e756b51..ddfd78adb4da1d29de3df8ddfd60eff55dd368a7 100644 GIT binary patch delta 5614 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 Q+FkNh4?kod7CA%y7aXS4;{X5v diff --git a/tools/ios-example-build.sh b/tools/ios-example-build.sh index 4420b642..ca33256f 100755 --- a/tools/ios-example-build.sh +++ b/tools/ios-example-build.sh @@ -12,10 +12,10 @@ git submodule update --init --recursive if ! type "cargo-lipo" > /dev/null; then cargo install cargo-lipo # rustup target add aarch64-apple-ios x86_64-apple-ios armv7-apple-ios armv7s-apple-ios i386-apple-ios - rustup target add x86_64-apple-ios i386-apple-ios + rustup target add aarch64-apple-ios x86_64-apple-ios fi # cargo lipo --release --targets aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,x86_64-apple-ios,i386-apple-ios -cargo lipo --release --targets x86_64-apple-ios,i386-apple-ios +cargo lipo --release --targets x86_64-apple-ios,aarch64-apple-ios cp target/universal/release/libsecp256k1.a $LIBS/Libs popd @@ -24,7 +24,7 @@ if [ ! -d cheader ]; then mkdir -p cheader fi RUST_BACKTRACE=1 cbindgen src/lib.rs -l c > cheader/tcx.h -cargo lipo --release --targets x86_64-apple-ios +cargo lipo --release --targets x86_64-apple-ios,aarch64-apple-ios # cargo lipo --release --targets aarch64-apple-ios,armv7-apple-ios,armv7s-apple-ios,x86_64-apple-ios,i386-apple-ios cp cheader/tcx.h $LIBS/Include cp ../target/universal/release/libtcx.a $LIBS/Libs diff --git a/tools/ios-framework-build.sh b/tools/ios-framework-build.sh old mode 100644 new mode 100755 index 4e15d3ac..a02a3090 --- a/tools/ios-framework-build.sh +++ b/tools/ios-framework-build.sh @@ -12,10 +12,10 @@ LIBS=examples/TokenCoreX/TokenCoreX pushd libs/secp256k1 if ! type "cargo-lipo" > /dev/null; then cargo install cargo-lipo - rustup target add aarch64-apple-ios x86_64-apple-ios armv7-apple-ios armv7s-apple-ios i386-apple-ios + rustup target add aarch64-apple-ios x86_64-apple-ios fi LIBS=../../examples/TokenCoreX/TokenCoreX -cargo lipo --release --targets aarch64-apple-ios x86_64-apple-ios armv7-apple-ios armv7s-apple-ios i386-apple-ios +cargo lipo --release --targets aarch64-apple-ios x86_64-apple-ios cp target/universal/release/libsecp256k1.a $LIBS popd @@ -26,7 +26,7 @@ if [ ! -d cheader ]; then mkdir -p cheader fi RUST_BACKTRACE=1 cbindgen src/lib.rs -l c > cheader/tcx.h -cargo lipo --release --targets aarch64-apple-ios x86_64-apple-ios armv7-apple-ios armv7s-apple-ios i386-apple-ios +cargo lipo --release --targets aarch64-apple-ios x86_64-apple-ios cp cheader/tcx.h $LIBS cp ../target/universal/release/libtcx.a $LIBS From bd46037689dcad2c6ec2f417cb3a824fa52276c7 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Thu, 2 Jul 2020 14:22:49 +0800 Subject: [PATCH 07/16] Move the substrate keystore proto define to substrate --- tcx-proto/src/params.proto | 10 ---------- tcx-proto/src/substrate.proto | 11 +++++++++++ tcx-substrate/src/lib.rs | 4 +++- tcx-substrate/src/transaction.rs | 16 ++++++++++++++++ tcx/src/api.rs | 16 ---------------- tcx/src/handler.rs | 8 ++++---- tcx/src/lib.rs | 29 ++++++++++++++++------------- 7 files changed, 50 insertions(+), 44 deletions(-) diff --git a/tcx-proto/src/params.proto b/tcx-proto/src/params.proto index 2c6fb289..99d74629 100644 --- a/tcx-proto/src/params.proto +++ b/tcx-proto/src/params.proto @@ -182,13 +182,3 @@ message HdStoreExtendedPublicKeyResponse { string extendedPublicKey = 1; } -message ImportSubstrateKeystoreParam { - string keystore = 1; - string password = 2; - string chainType = 3; - bool override = 4; -} - -message ExportSubstrateKeystoreResult { - string keystore = 1; -} diff --git a/tcx-proto/src/substrate.proto b/tcx-proto/src/substrate.proto index 48ed102b..f5cc0309 100644 --- a/tcx-proto/src/substrate.proto +++ b/tcx-proto/src/substrate.proto @@ -2,6 +2,17 @@ syntax = "proto3"; package transaction; +message ImportSubstrateKeystoreParam { + string keystore = 1; + string password = 2; + string chainType = 3; + bool override = 4; +} + +message ExportSubstrateKeystoreResult { + string keystore = 1; +} + message SubstrateRawTxIn { string rawData = 1; } diff --git a/tcx-substrate/src/lib.rs b/tcx-substrate/src/lib.rs index d864cbf3..d5e7fc98 100644 --- a/tcx-substrate/src/lib.rs +++ b/tcx-substrate/src/lib.rs @@ -5,7 +5,9 @@ mod transaction; pub use address::SubstrateAddress; pub use keystore::{decode_substrate_keystore, encode_substrate_keystore}; -pub use transaction::{SubstrateRawTxIn, SubstrateTxOut}; +pub use transaction::{ + ExportSubstrateKeystoreResult, ImportSubstrateKeystoreParam, SubstrateRawTxIn, SubstrateTxOut, +}; pub(crate) const SIGNATURE_TYPE_SR25519: u8 = 0x01; pub(crate) const PAYLOAD_HASH_THRESHOLD: usize = 256; diff --git a/tcx-substrate/src/transaction.rs b/tcx-substrate/src/transaction.rs index 0fd3fbd5..affa6501 100644 --- a/tcx-substrate/src/transaction.rs +++ b/tcx-substrate/src/transaction.rs @@ -1,4 +1,20 @@ #[derive(Clone, PartialEq, ::prost::Message)] +pub struct ImportSubstrateKeystoreParam { + #[prost(string, tag = "1")] + pub keystore: std::string::String, + #[prost(string, tag = "2")] + pub password: std::string::String, + #[prost(string, tag = "3")] + pub chain_type: std::string::String, + #[prost(bool, tag = "4")] + pub r#override: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct ExportSubstrateKeystoreResult { + #[prost(string, tag = "1")] + pub keystore: std::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] pub struct SubstrateRawTxIn { #[prost(string, tag = "1")] pub raw_data: std::string::String, diff --git a/tcx/src/api.rs b/tcx/src/api.rs index 7434bcc6..9b8f879c 100644 --- a/tcx/src/api.rs +++ b/tcx/src/api.rs @@ -316,22 +316,6 @@ pub struct HdStoreExtendedPublicKeyResponse { #[prost(string, tag = "1")] pub extended_public_key: std::string::String, } -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ImportSubstrateKeystoreParam { - #[prost(string, tag = "1")] - pub keystore: std::string::String, - #[prost(string, tag = "2")] - pub password: std::string::String, - #[prost(string, tag = "3")] - pub chain_type: std::string::String, - #[prost(bool, tag = "4")] - pub r#override: bool, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct ExportSubstrateKeystoreResult { - #[prost(string, tag = "1")] - pub keystore: std::string::String, -} /// only support two types #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, ::prost::Enumeration)] #[repr(i32)] diff --git a/tcx/src/handler.rs b/tcx/src/handler.rs index feaaa9a7..ac0e67d1 100644 --- a/tcx/src/handler.rs +++ b/tcx/src/handler.rs @@ -21,9 +21,8 @@ use tcx_tron::TrxAddress; use crate::api::keystore_common_derive_param::Derivation; use crate::api::sign_param::Key; use crate::api::{ - AccountResponse, AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, - ExportSubstrateKeystoreResult, HdStoreCreateParam, HdStoreImportParam, - ImportSubstrateKeystoreParam, KeyType, KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, + AccountResponse, AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, HdStoreCreateParam, + HdStoreImportParam, KeyType, KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, KeystoreCommonExistsParam, KeystoreCommonExistsResult, KeystoreCommonExportResult, PrivateKeyStoreExportParam, PrivateKeyStoreImportParam, Response, WalletKeyParam, WalletResult, }; @@ -42,7 +41,8 @@ use tcx_crypto::aes::cbc::encrypt_pkcs7; use tcx_crypto::KDF_ROUNDS; use tcx_primitive::{Bip32DeterministicPublicKey, Ss58Codec}; use tcx_substrate::{ - decode_substrate_keystore, encode_substrate_keystore, SubstrateAddress, SubstrateRawTxIn, + decode_substrate_keystore, encode_substrate_keystore, ExportSubstrateKeystoreResult, + ImportSubstrateKeystoreParam, SubstrateAddress, SubstrateRawTxIn, }; use tcx_tron::transaction::{TronMessageInput, TronTxInput}; diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 1da0ef95..0d9198ba 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -156,11 +156,11 @@ mod tests { use crate::api::keystore_common_derive_param::Derivation; use crate::api::{ - AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, ExportSubstrateKeystoreResult, - HdStoreCreateParam, ImportSubstrateKeystoreParam, InitTokenCoreXParam, KeyType, - KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, KeystoreCommonExistsParam, - KeystoreCommonExistsResult, KeystoreCommonExportResult, PrivateKeyStoreExportParam, - PrivateKeyStoreImportParam, Response, SignParam, WalletKeyParam, + AccountsResponse, DerivedKeyResult, ExportPrivateKeyParam, HdStoreCreateParam, + InitTokenCoreXParam, KeyType, KeystoreCommonAccountsParam, KeystoreCommonDeriveParam, + KeystoreCommonExistsParam, KeystoreCommonExistsResult, KeystoreCommonExportResult, + PrivateKeyStoreExportParam, PrivateKeyStoreImportParam, Response, SignParam, + WalletKeyParam, }; use crate::api::{HdStoreImportParam, WalletResult}; use crate::handler::hd_store_import; @@ -176,7 +176,10 @@ mod tests { use sp_core::Public as TraitPublic; use sp_runtime::traits::Verify; use tcx_ckb::{CachedCell, CellInput, CkbTxInput, CkbTxOutput, OutPoint, Script, Witness}; - use tcx_substrate::{SubstrateRawTxIn, SubstrateTxOut}; + use tcx_substrate::{ + ExportSubstrateKeystoreResult, ImportSubstrateKeystoreParam, SubstrateRawTxIn, + SubstrateTxOut, + }; use tcx_tron::transaction::{TronMessageInput, TronMessageOutput, TronTxInput, TronTxOutput}; static OTHER_MNEMONIC: &'static str = @@ -1985,13 +1988,13 @@ mod tests { // ); // } - #[test] - fn decode_error() { - let err = "1211756e737570706f727465645f636861696e"; - let error_bytes = hex::decode(err).unwrap(); - let rsp: Response = Response::decode(error_bytes.as_slice()).unwrap(); - assert_eq!("1", format!("{:?}", rsp)) - } + // #[test] + // fn decode_error() { + // let err = "1211756e737570706f727465645f636861696e"; + // let error_bytes = hex::decode(err).unwrap(); + // let rsp: Response = Response::decode(error_bytes.as_slice()).unwrap(); + // assert_eq!("1", format!("{:?}", rsp)) + // } #[test] fn test_panic_keystore_locked() { From defcc9dbb2d5d19a1e8d84774c770efb996d6e7e Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Thu, 2 Jul 2020 15:43:57 +0800 Subject: [PATCH 08/16] Add the multi cruve constraint in pkstore --- tcx/src/lib.rs | 78 ++++++++++++++++++++++++++++++ test-data/default_keystore_hd.json | 38 ++++++++++++++- test-data/default_keystore_pk.json | 38 ++++++++++++++- 3 files changed, 152 insertions(+), 2 deletions(-) diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 0d9198ba..84b49b84 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -1533,6 +1533,84 @@ mod tests { }) } + #[test] + pub fn test_import_multi_curve() { + run_test(|| { + let keystore_str: &str = r#"{ + "address": "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS", + "encoded": "0xf7e7e89d3016c9b4d93bb1129adf69e5949ca1fb58c29da4591ddc72c52238a35835e3f2ae023f9867ff301bc4132463527ac03525eaac54664a7cb658eae68a0bbc99354222c194d6100b2bf3a492639229077a2e2818d8196e002f0b5556104be23b11633858259dbbd3f91ea1d34d6ce182b62d8381af1ef3c35e9ab1583267cfa41aa58bfd64435c2b5047baf9052f0953d9f7854d2d396dfcad13", + "encoding": { + "content": [ + "pkcs8", + "sr25519" + ], + "type": "xsalsa20-poly1305", + "version": "2" + }, + "meta": { + "genesisHash": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", + "name": "keystore_import", + "tags": [], + "whenCreated": 1593591324334 + } +}"#; + + let param = ImportSubstrateKeystoreParam { + keystore: keystore_str.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "KUSAMA".to_string(), + r#override: true, + }; + // let param_bytes = encode_message(param).unwrap(); + let ret_bytes = call_api("substrate_keystore_import", param).unwrap(); + let wallet_ret: WalletResult = WalletResult::decode(ret_bytes.as_slice()).unwrap(); + let derivation = Derivation { + chain_type: "KUSAMA".to_string(), + path: "".to_string(), + network: "".to_string(), + seg_wit: "".to_string(), + chain_id: "".to_string(), + }; + + let param = KeystoreCommonDeriveParam { + id: wallet_ret.id.to_string(), + password: TEST_PASSWORD.to_string(), + derivations: vec![derivation], + }; + + let ret = call_api("keystore_common_derive", param).unwrap(); + let accounts: AccountsResponse = AccountsResponse::decode(ret.as_slice()).unwrap(); + + assert_eq!( + accounts.accounts[0].address, + "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" + ); + + let derivation = Derivation { + chain_type: "LITECOIN".to_string(), + path: "".to_string(), + network: "MAINNET".to_string(), + seg_wit: "NONE".to_string(), + chain_id: "".to_string(), + }; + + let param = KeystoreCommonDeriveParam { + id: wallet_ret.id.to_string(), + password: TEST_PASSWORD.to_string(), + derivations: vec![derivation], + }; + + let ret = call_api("keystore_common_derive", param); + assert!(ret.is_err()); + assert_eq!( + format!("{}", ret.err().unwrap()), + "pkstore_can_not_add_other_curve_account" + ); + + remove_created_wallet(&wallet_ret.id); + }) + } + #[test] pub fn test_sign_substrate_raw_tx() { run_test(|| { diff --git a/test-data/default_keystore_hd.json b/test-data/default_keystore_hd.json index aba92f80..eca4fcaa 100644 --- a/test-data/default_keystore_hd.json +++ b/test-data/default_keystore_hd.json @@ -1 +1,37 @@ -{"id":"7719d1e3-3f67-439f-a18e-d9ae413e00e1","version":11000,"keyHash":"efbe00a55ddd4c5350e295a9533d28f93cac001bfdad8cf4275140461ea03e9e","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"6006bd4e828f2f93dca31e36590ca4c9"},"ciphertext":"b06b82b8cda0bc72761177b312dfd46318248ad8473b6c97d46c44aedf6a283f44f0267dd03f210dcddf4ea1a34f85b0b02533dd9c37ce2276cb087af3e43f2a76b968e17c816ca8ea5c","kdf":"pbkdf2","kdfparams":{"c":10240,"prf":"hmac-sha256","dklen":32,"salt":"5d85aaf812a613f810cc1cda18d35f46c013f5e537629e25372969f5f87402cd"},"mac":"56af7c5faf0a791cbb4911c4c20070156e4ad0a03f8253b2a2fb005a68d7a026"},"activeAccounts":[{"address":"qzld7dav7d2sfjdl6x9snkvf6raj8lfxjcj5fa8y2r","derivationPath":"m/44'/145'/0'/0/0","curve":"SECP256k1","coin":"BITCOINCASH","network":"MAINNET","segWit":"NONE","extPubKey":"031064f6a580000000251d72997d4cf931a7e6819f7da37725166100fc7dae9ca6afc3f8fd8a3d3a7f0303f2f84851514bf2f40a46b5bb9dbf4e5913fbacde1a96968cda08f9fd882caa"}],"imTokenMeta":{"name":"test-wallet","passwordHint":"imtoken","timestamp":1575605134,"source":"MNEMONIC"}} \ No newline at end of file +{ + "id": "7719d1e3-3f67-439f-a18e-d9ae413e00e1", + "version": 11000, + "keyHash": "efbe00a55ddd4c5350e295a9533d28f93cac001bfdad8cf4275140461ea03e9e", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "6006bd4e828f2f93dca31e36590ca4c9" + }, + "ciphertext": "b06b82b8cda0bc72761177b312dfd46318248ad8473b6c97d46c44aedf6a283f44f0267dd03f210dcddf4ea1a34f85b0b02533dd9c37ce2276cb087af3e43f2a76b968e17c816ca8ea5c", + "kdf": "pbkdf2", + "kdfparams": { + "c": 10240, + "prf": "hmac-sha256", + "dklen": 32, + "salt": "5d85aaf812a613f810cc1cda18d35f46c013f5e537629e25372969f5f87402cd" + }, + "mac": "56af7c5faf0a791cbb4911c4c20070156e4ad0a03f8253b2a2fb005a68d7a026" + }, + "activeAccounts": [ + { + "address": "qzld7dav7d2sfjdl6x9snkvf6raj8lfxjcj5fa8y2r", + "derivationPath": "m/44'/145'/0'/0/0", + "curve": "SECP256k1", + "coin": "BITCOINCASH", + "network": "MAINNET", + "segWit": "NONE", + "extPubKey": "031064f6a580000000251d72997d4cf931a7e6819f7da37725166100fc7dae9ca6afc3f8fd8a3d3a7f0303f2f84851514bf2f40a46b5bb9dbf4e5913fbacde1a96968cda08f9fd882caa" + } + ], + "imTokenMeta": { + "name": "test-wallet", + "passwordHint": "imtoken", + "timestamp": 1575605134, + "source": "MNEMONIC" + } +} \ No newline at end of file diff --git a/test-data/default_keystore_pk.json b/test-data/default_keystore_pk.json index e2823237..034fe74e 100644 --- a/test-data/default_keystore_pk.json +++ b/test-data/default_keystore_pk.json @@ -1 +1,37 @@ -{"id":"cb1ba2d7-7b89-4595-9753-d16b6e317c6b","version":11001,"keyHash":"4fc213ddcb6fa44a2e2f4c83d67502f88464e6ee","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"21cb134b52e3d76f6b0d287c884c27fb"},"ciphertext":"ce7df149b0a010165cc7bf2fdc8104f7dc0d131022aa221e5f4b909aa11ba7aa","kdf":"pbkdf2","kdfparams":{"c":1024,"prf":"hmac-sha256","dklen":32,"salt":"737cbf8e446e32fe4174cbc97efebe101d253013fcfe981b9220c99f22f4bb4e"},"mac":"a03fa095a9f36e0e12936e4d39ab3b942537ec1080fe77a30724f966f092a662"},"activeAccounts":[{"address":"TXo4VDm8Qc5YBSjPhu8pMaxzTApSvLshWG","derivationPath":"","curve":"SECP256k1","coin":"TRON","network":"","segWit":"","extPubKey":""}],"imTokenMeta":{"name":"Unknown","passwordHint":"","timestamp":1576654549,"source":"PRIVATE"}} \ No newline at end of file +{ + "id": "cb1ba2d7-7b89-4595-9753-d16b6e317c6b", + "version": 11001, + "keyHash": "4fc213ddcb6fa44a2e2f4c83d67502f88464e6ee", + "crypto": { + "cipher": "aes-128-ctr", + "cipherparams": { + "iv": "21cb134b52e3d76f6b0d287c884c27fb" + }, + "ciphertext": "ce7df149b0a010165cc7bf2fdc8104f7dc0d131022aa221e5f4b909aa11ba7aa", + "kdf": "pbkdf2", + "kdfparams": { + "c": 1024, + "prf": "hmac-sha256", + "dklen": 32, + "salt": "737cbf8e446e32fe4174cbc97efebe101d253013fcfe981b9220c99f22f4bb4e" + }, + "mac": "a03fa095a9f36e0e12936e4d39ab3b942537ec1080fe77a30724f966f092a662" + }, + "activeAccounts": [ + { + "address": "TXo4VDm8Qc5YBSjPhu8pMaxzTApSvLshWG", + "derivationPath": "", + "curve": "SECP256k1", + "coin": "TRON", + "network": "", + "segWit": "", + "extPubKey": "" + } + ], + "imTokenMeta": { + "name": "Unknown", + "passwordHint": "", + "timestamp": 1576654549, + "source": "PRIVATE" + } +} \ No newline at end of file From a096127c52aad56c59184549ccef25b00c9bfc25 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Fri, 3 Jul 2020 08:43:39 +0800 Subject: [PATCH 09/16] Add the substrate_keystore_exists --- .../UserInterfaceState.xcuserstate | Bin 22532 -> 22532 bytes tcx-proto/src/substrate.proto | 3 +- tcx-substrate/src/lib.rs | 2 +- tcx-substrate/src/transaction.rs | 2 +- tcx/Cargo.toml | 3 +- tcx/src/handler.rs | 28 +++++++++++++++-- tcx/src/lib.rs | 29 ++++++++++++++---- 7 files changed, 55 insertions(+), 12 deletions(-) diff --git a/examples/TokenCoreX/TokenCoreX.xcodeproj/project.xcworkspace/xcuserdata/xyz.xcuserdatad/UserInterfaceState.xcuserstate b/examples/TokenCoreX/TokenCoreX.xcodeproj/project.xcworkspace/xcuserdata/xyz.xcuserdatad/UserInterfaceState.xcuserstate index ddfd78adb4da1d29de3df8ddfd60eff55dd368a7..9511c8f172b9ac83e5cdda635b2c247d10b6c008 100644 GIT binary patch delta 31 lcmZqKz}T{Zal-~q*0<3oQ-wEg Result> { - let param: ImportSubstrateKeystoreParam = ImportSubstrateKeystoreParam::decode(data)?; + let param: SubstrateKeystoreParam = SubstrateKeystoreParam::decode(data)?; let pk = decode_substrate_keystore(¶m.keystore, ¶m.password)?; let pk_import_param = PrivateKeyStoreImportParam { private_key: hex::encode(pk), @@ -739,6 +739,30 @@ pub(crate) fn export_substrate_keystore(data: &[u8]) -> Result> { encode_message(ret) } +pub(crate) fn substrate_keystore_exists(data: &[u8]) -> Result> { + let param: SubstrateKeystoreParam = SubstrateKeystoreParam::decode(data)?; + let pk = decode_substrate_keystore(¶m.keystore, ¶m.password)?; + // let ret = export_private_key(data)?; + // let export_result: KeystoreCommonExportResult = + // KeystoreCommonExportResult::decode(ret.as_slice())?; + // let pk = export_result.value; + let pk_hex = hex::encode(&pk); + let exists_param = KeystoreCommonExistsParam { + r#type: KeyType::PrivateKey as i32, + value: pk_hex, + }; + let exists_param_bytes = encode_message(exists_param)?; + keystore_common_exists(&exists_param_bytes) + // + // let pk_bytes = hex::decode(pk)?; + // let coin = coin_info_from_param(¶m.chain_type, ¶m.network, "")?; + // let keystore_str = encode_substrate_keystore(¶m.password, &pk_bytes, &coin)?; + // let ret = ExportSubstrateKeystoreResult { + // keystore: keystore_str, + // }; + // encode_message(ret) +} + pub(crate) fn unlock_then_crash(data: &[u8]) -> Result> { let param: WalletKeyParam = WalletKeyParam::decode(data).unwrap(); let mut map = KEYSTORE_MAP.write(); diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 84b49b84..5dc0d778 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -23,7 +23,9 @@ use crate::handler::{ mod filemanager; -use crate::handler::{export_substrate_keystore, import_substrate_keystore}; +use crate::handler::{ + export_substrate_keystore, import_substrate_keystore, substrate_keystore_exists, +}; use parking_lot::RwLock; extern crate serde_json; @@ -96,6 +98,10 @@ pub unsafe extern "C" fn call_tcx_api(hex_str: *const c_char) -> *const c_char { "tron_sign_msg" => landingpad(|| tron_sign_message(&action.param.unwrap().value)), + "substrate_keystore_exists" => { + landingpad(|| substrate_keystore_exists(&action.param.unwrap().value)) + } + "substrate_keystore_import" => { landingpad(|| import_substrate_keystore(&action.param.unwrap().value)) } @@ -177,8 +183,7 @@ mod tests { use sp_runtime::traits::Verify; use tcx_ckb::{CachedCell, CellInput, CkbTxInput, CkbTxOutput, OutPoint, Script, Witness}; use tcx_substrate::{ - ExportSubstrateKeystoreResult, ImportSubstrateKeystoreParam, SubstrateRawTxIn, - SubstrateTxOut, + ExportSubstrateKeystoreResult, SubstrateKeystoreParam, SubstrateRawTxIn, SubstrateTxOut, }; use tcx_tron::transaction::{TronMessageInput, TronMessageOutput, TronTxInput, TronTxOutput}; @@ -1486,15 +1491,27 @@ mod tests { } }"#; - let param = ImportSubstrateKeystoreParam { + let param = SubstrateKeystoreParam { keystore: keystore_str.to_string(), password: TEST_PASSWORD.to_string(), chain_type: "KUSAMA".to_string(), r#override: true, }; // let param_bytes = encode_message(param).unwrap(); - let ret_bytes = call_api("substrate_keystore_import", param).unwrap(); + + let ret_bytes = call_api("substrate_keystore_exists", param.clone()).unwrap(); + let exists_result: KeystoreCommonExistsResult = + KeystoreCommonExistsResult::decode(ret_bytes.as_slice()).unwrap(); + assert!(!exists_result.is_exists); + + let ret_bytes = call_api("substrate_keystore_import", param.clone()).unwrap(); let wallet_ret: WalletResult = WalletResult::decode(ret_bytes.as_slice()).unwrap(); + + let ret_bytes = call_api("substrate_keystore_exists", param.clone()).unwrap(); + let exists_result: KeystoreCommonExistsResult = + KeystoreCommonExistsResult::decode(ret_bytes.as_slice()).unwrap(); + assert!(exists_result.is_exists); + let derivation = Derivation { chain_type: "KUSAMA".to_string(), path: "".to_string(), @@ -1555,7 +1572,7 @@ mod tests { } }"#; - let param = ImportSubstrateKeystoreParam { + let param = SubstrateKeystoreParam { keystore: keystore_str.to_string(), password: TEST_PASSWORD.to_string(), chain_type: "KUSAMA".to_string(), From 688fe3d1ff13e70c3aaf8923fa940ab92f7b8331 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Fri, 3 Jul 2020 11:12:46 +0800 Subject: [PATCH 10/16] pretty code --- tcx-constants/src/lib.rs | 2 +- tcx-primitive/src/sr25519.rs | 2 +- tcx-substrate/src/keystore.rs | 40 +++++++++++++++-------------------- tcx-substrate/src/lib.rs | 2 +- tcx/Cargo.toml | 1 - tcx/src/lib.rs | 10 ++++++++- 6 files changed, 29 insertions(+), 28 deletions(-) diff --git a/tcx-constants/src/lib.rs b/tcx-constants/src/lib.rs index 804eec6b..9ead7b79 100644 --- a/tcx-constants/src/lib.rs +++ b/tcx-constants/src/lib.rs @@ -6,7 +6,7 @@ pub use btc_fork_network::{ coin_from_xpub_prefix, network_form_hrp, network_from_coin, pub_version_from_prv_version, BtcForkNetwork, }; -pub use coin_info::CoinInfo; +pub use coin_info::{coin_info_from_param, CoinInfo}; pub use curve::CurveType; pub type Result = std::result::Result; diff --git a/tcx-primitive/src/sr25519.rs b/tcx-primitive/src/sr25519.rs index 7a237290..69b4e1b9 100644 --- a/tcx-primitive/src/sr25519.rs +++ b/tcx-primitive/src/sr25519.rs @@ -1,6 +1,6 @@ use crate::ecc::{KeyError, PrivateKey as TraitPrivateKey, PublicKey as TraitPublicKey}; use crate::{FromHex, Result, ToHex}; -use schnorrkel::{ExpansionMode, MiniSecretKey, SecretKey}; +use schnorrkel::SecretKey; use sp_core::sr25519::{Pair, Public}; use sp_core::{Pair as TraitPair, Public as TraitPublic}; diff --git a/tcx-substrate/src/keystore.rs b/tcx-substrate/src/keystore.rs index 1a603eaf..124d7cf7 100644 --- a/tcx-substrate/src/keystore.rs +++ b/tcx-substrate/src/keystore.rs @@ -3,7 +3,7 @@ use rand::Rng; use serde::{Deserialize, Serialize}; use std::convert::TryInto; use tcx_chain::Address; -use tcx_constants::coin_info::coin_info_from_param; + use tcx_constants::{CoinInfo, Result}; use tcx_primitive::{ DeterministicPrivateKey, PrivateKey, PublicKey, Sr25519PrivateKey, TypedPublicKey, @@ -49,10 +49,6 @@ const SEC_LENGTH: usize = 64; const SEED_OFFSET: usize = PKCS8_HEADER.len(); const SEED_LENGTH: usize = 32; -// fn decrypt_keystore(keystore: &str) -> Result { -// let keystore: SubstrateKeystore = serde_json::from_str(keystore)?; -// } - impl SubstrateKeystore { pub fn new( password: &str, @@ -60,13 +56,9 @@ impl SubstrateKeystore { pub_key: &[u8], addr: &str, ) -> Result { - // let pk = Sr25519PrivateKey::from_slice(prv_key)?; - // let pub_key = pk.public_key(); - // let coin_info = coin_info_from_param("KUSAMA", "", "").unwrap(); - // let new_addr = SubstrateAddress::from_public_key(&TypedPublicKey::Sr25519(pub_key.clone()), &coin_info)?; let encoding = format!( "0x{}", - SubstrateKeystore::encrypt_seed(password, prv_key, pub_key)? + SubstrateKeystore::encrypt(password, prv_key, pub_key)? ); Ok(SubstrateKeystore { @@ -117,7 +109,6 @@ impl SubstrateKeystore { }; let decrypted = decrypt_content(password, encoded)?; - // let decrypted = hex::decode(decrypted_hex)?; let header = &decrypted[0..PKCS8_HEADER.len()]; assert!(header == PKCS8_HEADER, "Invalid Pkcs8 header found in body"); @@ -143,23 +134,23 @@ impl SubstrateKeystore { Ok((secret_key, pub_key.to_vec())) } - pub fn encrypt_seed(password: &str, seed: &[u8], pub_key: &[u8]) -> Result { + pub fn encrypt(password: &str, seed: &[u8], pub_key: &[u8]) -> Result { let plaintext = [&PKCS8_HEADER, seed, &PKCS8_DIVIDER, pub_key].concat(); encrypt_content(password, &plaintext) } } -fn decrypt_content(password: &str, encrypted: &str) -> Result> { - let encrypted_bytes = hex::decode(encrypted)?; - let nonce: &[u8; 24] = &encrypted_bytes[0..NONCE_LENGTH].try_into().unwrap(); - let ciphertext = &encrypted_bytes[NONCE_LENGTH..]; +fn decrypt_content(password: &str, encoded: &str) -> Result> { + let encoded_bytes = hex::decode(encoded)?; + let nonce: &[u8; 24] = &encoded_bytes[0..NONCE_LENGTH].try_into().unwrap(); + let encrypted = &encoded_bytes[NONCE_LENGTH..]; let padding_password = password_to_key(password); let key = GenericArray::from_slice(&padding_password); let cipher = XSalsa20Poly1305::new(key); let nonce = GenericArray::from_slice(nonce); cipher - .decrypt(nonce, ciphertext.as_ref()) - .map_err(|e| format_err!("{}", "decrypt error")) + .decrypt(nonce, encrypted.as_ref()) + .map_err(|_e| format_err!("{}", "decrypt error")) } fn encrypt_content(password: &str, plaintext: &[u8]) -> Result { @@ -170,7 +161,7 @@ fn encrypt_content(password: &str, plaintext: &[u8]) -> Result { let nonce = GenericArray::from_slice(&nonce_bytes); let encoded = cipher .encrypt(&nonce, plaintext) - .map_err(|e| format_err!("{}", "encrypt error"))?; + .map_err(|_e| format_err!("{}", "encrypt error"))?; Ok(hex::encode([nonce_bytes.to_vec(), encoded].concat())) } @@ -226,8 +217,7 @@ pub fn encode_substrate_keystore( #[cfg(test)] mod test_super { use super::*; - use tcx_constants::{CoinInfo, CurveType, TEST_PASSWORD}; - use tcx_primitive::FromHex; + use tcx_constants::{coin_info_from_param, TEST_PASSWORD}; #[test] fn test_decrypt_encoded() { @@ -262,11 +252,15 @@ mod test_super { } #[test] - fn test_export_from_sertket_key() { + fn test_export_from_sertcet_key() { let prv_key = hex::decode("00ea01b0116da6ca425c477521fd49cc763988ac403ab560f4022936a18a4341016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd").unwrap(); let coin_info = coin_info_from_param("KUSAMA", "", "").unwrap(); let export_json = encode_substrate_keystore(&TEST_PASSWORD, &prv_key, &coin_info).unwrap(); - assert_eq!(export_json, ""); + let keystore: SubstrateKeystore = serde_json::from_str(&export_json).unwrap(); + assert_eq!( + keystore.address, + "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" + ); } #[test] diff --git a/tcx-substrate/src/lib.rs b/tcx-substrate/src/lib.rs index 7856dc5c..41481aa4 100644 --- a/tcx-substrate/src/lib.rs +++ b/tcx-substrate/src/lib.rs @@ -4,7 +4,7 @@ mod signer; mod transaction; pub use address::SubstrateAddress; -pub use keystore::{decode_substrate_keystore, encode_substrate_keystore}; +pub use keystore::{decode_substrate_keystore, encode_substrate_keystore, SubstrateKeystore}; pub use transaction::{ ExportSubstrateKeystoreResult, SubstrateKeystoreParam, SubstrateRawTxIn, SubstrateTxOut, }; diff --git a/tcx/Cargo.toml b/tcx/Cargo.toml index 9c78f918..a2e69e2c 100644 --- a/tcx/Cargo.toml +++ b/tcx/Cargo.toml @@ -41,4 +41,3 @@ prost-build = "0.5.0" [dev-dependencies] sp-core = "2.0.0-rc3" sp-runtime = "2.0.0-rc3" -pikkr = "0.16.0" \ No newline at end of file diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 5dc0d778..89dcc227 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -183,7 +183,8 @@ mod tests { use sp_runtime::traits::Verify; use tcx_ckb::{CachedCell, CellInput, CkbTxInput, CkbTxOutput, OutPoint, Script, Witness}; use tcx_substrate::{ - ExportSubstrateKeystoreResult, SubstrateKeystoreParam, SubstrateRawTxIn, SubstrateTxOut, + ExportSubstrateKeystoreResult, SubstrateKeystore, SubstrateKeystoreParam, SubstrateRawTxIn, + SubstrateTxOut, }; use tcx_tron::transaction::{TronMessageInput, TronMessageOutput, TronTxInput, TronTxOutput}; @@ -1545,6 +1546,13 @@ mod tests { let ret = call_api("substrate_keystore_export", export_param).unwrap(); let keystore_ret: ExportSubstrateKeystoreResult = ExportSubstrateKeystoreResult::decode(ret.as_slice()).unwrap(); + + let keystore: SubstrateKeystore = serde_json::from_str(&keystore_ret.keystore).unwrap(); + assert!(keystore.validate().is_ok()); + assert_eq!( + keystore.address, + "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" + ); // assert_eq!(keystore_ret.keystore, ""); remove_created_wallet(&wallet_ret.id); }) From 4d43457f04d20829060afbdf57aa964d82c86d96 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Mon, 6 Jul 2020 16:22:59 +0800 Subject: [PATCH 11/16] Add the password error when import pjs keystore --- tcx-substrate/src/keystore.rs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/tcx-substrate/src/keystore.rs b/tcx-substrate/src/keystore.rs index 124d7cf7..963cb127 100644 --- a/tcx-substrate/src/keystore.rs +++ b/tcx-substrate/src/keystore.rs @@ -15,6 +15,10 @@ use xsalsa20poly1305::XSalsa20Poly1305; pub enum Error { #[fail(display = "invalid_keystore# {}", _0)] InvalidKeystore(String), + #[fail(display = "keystore_public_key_unmatch")] + KeystorePublicKeyUnmatch, + #[fail(display = "password_incorrect")] + PasswordIncorrect, } #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -150,7 +154,7 @@ fn decrypt_content(password: &str, encoded: &str) -> Result> { let nonce = GenericArray::from_slice(nonce); cipher .decrypt(nonce, encrypted.as_ref()) - .map_err(|_e| format_err!("{}", "decrypt error")) + .map_err(|_e| Error::PasswordIncorrect.into()) } fn encrypt_content(password: &str, plaintext: &[u8]) -> Result { @@ -196,7 +200,7 @@ pub fn decode_substrate_keystore(keystore: &str, password: &str) -> Result Date: Tue, 7 Jul 2020 14:30:36 +0800 Subject: [PATCH 12/16] Save the name and whenCreated when import substrate keystore --- tcx-chain/src/keystore/mod.rs | 12 ++++++-- tcx-chain/src/keystore/private.rs | 18 +++++------ tcx-ckb/src/signer.rs | 1 + tcx-proto/src/params.proto | 4 ++- tcx-proto/src/substrate.proto | 2 +- tcx-substrate/src/keystore.rs | 40 +++++++++++++++--------- tcx-substrate/src/transaction.rs | 2 +- tcx/src/api.rs | 6 +++- tcx/src/handler.rs | 48 +++++++++++++++++------------ tcx/src/lib.rs | 51 ++++++++++++++++++++++++++++++- 10 files changed, 134 insertions(+), 50 deletions(-) diff --git a/tcx-chain/src/keystore/mod.rs b/tcx-chain/src/keystore/mod.rs index c074077f..406f80f1 100644 --- a/tcx-chain/src/keystore/mod.rs +++ b/tcx-chain/src/keystore/mod.rs @@ -137,11 +137,11 @@ pub enum Keystore { } impl Keystore { - pub fn from_private_key(private_key: &str, password: &str) -> Keystore { + pub fn from_private_key(private_key: &str, password: &str, meta: Metadata) -> Keystore { Keystore::PrivateKey(PrivateKeystore::from_private_key( private_key, password, - Source::Wif, + meta, )) } @@ -684,10 +684,16 @@ mod tests { "512115eca3ae86646aeb06861d551e403b543509" ); + let meta = Metadata { + name: "test_create".to_string(), + password_hint: TEST_PASSWORD.to_string(), + source: Source::Private, + ..Metadata::default() + }; let pk_store = PrivateKeystore::from_private_key( "a392604efc2fad9c0b3da43b5f698a2e3f270f170d859912be0d54742275c5f6", TEST_PASSWORD, - Source::Private, + meta, ); let keystore = PrivateKey(pk_store); assert_eq!(0, keystore.accounts().len()); diff --git a/tcx-chain/src/keystore/private.rs b/tcx-chain/src/keystore/private.rs index be9a0ebb..acb08cd2 100644 --- a/tcx-chain/src/keystore/private.rs +++ b/tcx-chain/src/keystore/private.rs @@ -1,5 +1,5 @@ use super::Account; -use super::{Address, Metadata, Source}; +use super::{Address, Metadata}; use tcx_constants::CoinInfo; use tcx_crypto::{Crypto, Key, Pbkdf2Params}; @@ -111,17 +111,12 @@ impl PrivateKeystore { self.store.crypto.verify_password(password) } - pub fn from_private_key(private_key: &str, password: &str, source: Source) -> PrivateKeystore { + pub fn from_private_key(private_key: &str, password: &str, meta: Metadata) -> PrivateKeystore { let key_data: Vec = hex::decode(private_key).expect("hex can't decode"); let key_hash = key_hash_from_private_key(&key_data); // let pk_bytes = hex::decode(private_key).expect("valid private_key"); let crypto: Crypto = Crypto::new(password, &key_data); - let meta = Metadata { - source, - ..Metadata::default() - }; - let store = Store { key_hash, crypto, @@ -170,15 +165,20 @@ impl PrivateKeystore { #[cfg(test)] mod tests { - use crate::{PrivateKeystore, Source}; + use crate::{Metadata, PrivateKeystore, Source}; use tcx_constants::TEST_PASSWORD; #[test] pub fn from_private_key_test() { + let meta = Metadata { + name: "from_private_key_test".to_string(), + source: Source::Private, + ..Metadata::default() + }; let keystore = PrivateKeystore::from_private_key( "a392604efc2fad9c0b3da43b5f698a2e3f270f170d859912be0d54742275c5f6", TEST_PASSWORD, - Source::Private, + meta, ); assert_eq!(keystore.store.version, 11001); assert_ne!(keystore.store.id, ""); diff --git a/tcx-ckb/src/signer.rs b/tcx-ckb/src/signer.rs index 249726c4..aea47bc4 100644 --- a/tcx-ckb/src/signer.rs +++ b/tcx-ckb/src/signer.rs @@ -417,6 +417,7 @@ mod tests { let mut ks = Keystore::from_private_key( "dcec27d0d975b0378471183a03f7071dea8532aaf968be796719ecd20af6988f", "Password", + Metadata::default(), ); ks.unlock_by_password("Password").unwrap(); diff --git a/tcx-proto/src/params.proto b/tcx-proto/src/params.proto index 99d74629..6324db0e 100644 --- a/tcx-proto/src/params.proto +++ b/tcx-proto/src/params.proto @@ -84,7 +84,9 @@ enum KeyType { message PrivateKeyStoreImportParam { string privateKey = 1; string password = 2; - bool overwrite = 3; + string name = 3; + string passwordHint = 4; + bool overwrite = 5; } // FUNCTION: private_key_store_export(PrivateKeyStoreExportParam): KeystoreCommonExportResult diff --git a/tcx-proto/src/substrate.proto b/tcx-proto/src/substrate.proto index b8bf9895..d5e7de29 100644 --- a/tcx-proto/src/substrate.proto +++ b/tcx-proto/src/substrate.proto @@ -6,7 +6,7 @@ message SubstrateKeystoreParam { string keystore = 1; string password = 2; string chainType = 3; - bool override = 4; + bool override = 6; } diff --git a/tcx-substrate/src/keystore.rs b/tcx-substrate/src/keystore.rs index 963cb127..e2d8991a 100644 --- a/tcx-substrate/src/keystore.rs +++ b/tcx-substrate/src/keystore.rs @@ -4,6 +4,7 @@ use serde::{Deserialize, Serialize}; use std::convert::TryInto; use tcx_chain::Address; +use std::time::{SystemTime, UNIX_EPOCH}; use tcx_constants::{CoinInfo, Result}; use tcx_primitive::{ DeterministicPrivateKey, PrivateKey, PublicKey, Sr25519PrivateKey, TypedPublicKey, @@ -43,6 +44,22 @@ pub struct SubstrateKeystoreEncoding { #[serde(rename_all = "camelCase")] pub struct SubstrateKeystoreMeta { pub name: String, + pub when_created: i64, +} + +fn metadata_default_time() -> i64 { + let start = SystemTime::now(); + let since_the_epoch = start.duration_since(UNIX_EPOCH).expect("get timestamp"); + since_the_epoch.as_secs() as i64 +} + +impl Default for SubstrateKeystoreMeta { + fn default() -> Self { + SubstrateKeystoreMeta { + name: "Unknown".to_string(), + when_created: metadata_default_time(), + } + } } const NONCE_LENGTH: usize = 24; @@ -73,9 +90,7 @@ impl SubstrateKeystore { encoding_type: "xsalsa20-poly1305".to_string(), version: "2".to_string(), }, - meta: SubstrateKeystoreMeta { - name: "export from imToken".to_string(), - }, + meta: SubstrateKeystoreMeta::default(), }) } @@ -191,9 +206,8 @@ fn password_to_key(password: &str) -> [u8; 32] { key } -pub fn decode_substrate_keystore(keystore: &str, password: &str) -> Result> { - let ks: SubstrateKeystore = serde_json::from_str(keystore)?; - let (secret_key, pub_key) = ks.decrypt(password)?; +pub fn decode_substrate_keystore(keystore: &SubstrateKeystore, password: &str) -> Result> { + let (secret_key, pub_key) = keystore.decrypt(password)?; let priv_key = if secret_key.len() == 32 { Sr25519PrivateKey::from_seed(&secret_key) } else { @@ -209,13 +223,11 @@ pub fn encode_substrate_keystore( password: &str, prv_key: &[u8], coin: &CoinInfo, -) -> Result { +) -> Result { let pk = Sr25519PrivateKey::from_slice(prv_key)?; let pub_key = pk.public_key(); let addr = SubstrateAddress::from_public_key(&TypedPublicKey::Sr25519(pub_key.clone()), &coin)?; - let keystore = SubstrateKeystore::new(password, prv_key, &pub_key.to_bytes(), &addr)?; - let keystore_str = serde_json::to_string(&keystore)?; - Ok(keystore_str) + SubstrateKeystore::new(password, prv_key, &pub_key.to_bytes(), &addr) } #[cfg(test)] @@ -251,9 +263,10 @@ mod test_super { #[test] fn test_decrypt_from_keystore() { - let decrypted = decode_substrate_keystore(KEYSTORE_STR, TEST_PASSWORD).unwrap(); + let ks: SubstrateKeystore = serde_json::from_str(KEYSTORE_STR).unwrap(); + let decrypted = decode_substrate_keystore(&ks, TEST_PASSWORD).unwrap(); assert_eq!(hex::encode(decrypted), "00ea01b0116da6ca425c477521fd49cc763988ac403ab560f4022936a18a4341016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd"); - let decrypted = decode_substrate_keystore(KEYSTORE_STR, "wrong_password"); + let decrypted = decode_substrate_keystore(&ks, "wrong_password"); assert_eq!( format!("{}", decrypted.err().unwrap()), "password_incorrect" @@ -264,8 +277,7 @@ mod test_super { fn test_export_from_sertcet_key() { let prv_key = hex::decode("00ea01b0116da6ca425c477521fd49cc763988ac403ab560f4022936a18a4341016e7df1f5020068c9b150e0722fea65a264d5fbb342d4af4ddf2f1cdbddf1fd").unwrap(); let coin_info = coin_info_from_param("KUSAMA", "", "").unwrap(); - let export_json = encode_substrate_keystore(&TEST_PASSWORD, &prv_key, &coin_info).unwrap(); - let keystore: SubstrateKeystore = serde_json::from_str(&export_json).unwrap(); + let keystore = encode_substrate_keystore(&TEST_PASSWORD, &prv_key, &coin_info).unwrap(); assert_eq!( keystore.address, "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" diff --git a/tcx-substrate/src/transaction.rs b/tcx-substrate/src/transaction.rs index 8fbb4119..f5a204fb 100644 --- a/tcx-substrate/src/transaction.rs +++ b/tcx-substrate/src/transaction.rs @@ -6,7 +6,7 @@ pub struct SubstrateKeystoreParam { pub password: std::string::String, #[prost(string, tag = "3")] pub chain_type: std::string::String, - #[prost(bool, tag = "4")] + #[prost(bool, tag = "6")] pub r#override: bool, } #[derive(Clone, PartialEq, ::prost::Message)] diff --git a/tcx/src/api.rs b/tcx/src/api.rs index 9b8f879c..18d33fdd 100644 --- a/tcx/src/api.rs +++ b/tcx/src/api.rs @@ -177,7 +177,11 @@ pub struct PrivateKeyStoreImportParam { pub private_key: std::string::String, #[prost(string, tag = "2")] pub password: std::string::String, - #[prost(bool, tag = "3")] + #[prost(string, tag = "3")] + pub name: std::string::String, + #[prost(string, tag = "4")] + pub password_hint: std::string::String, + #[prost(bool, tag = "5")] pub overwrite: bool, } /// FUNCTION: private_key_store_export(PrivateKeyStoreExportParam): KeystoreCommonExportResult diff --git a/tcx/src/handler.rs b/tcx/src/handler.rs index 1f07d7e3..0d2afc5c 100644 --- a/tcx/src/handler.rs +++ b/tcx/src/handler.rs @@ -42,7 +42,7 @@ use tcx_crypto::KDF_ROUNDS; use tcx_primitive::{Bip32DeterministicPublicKey, Ss58Codec}; use tcx_substrate::{ decode_substrate_keystore, encode_substrate_keystore, ExportSubstrateKeystoreResult, - SubstrateAddress, SubstrateKeystoreParam, SubstrateRawTxIn, + SubstrateAddress, SubstrateKeystore, SubstrateKeystoreParam, SubstrateRawTxIn, }; use tcx_tron::transaction::{TronMessageInput, TronTxInput}; @@ -342,8 +342,13 @@ pub(crate) fn private_key_store_import(data: &[u8]) -> Result> { let pk_bytes = key_data_from_any_format_pk(¶m.private_key)?; let private_key = hex::encode(pk_bytes); - let pk_store = - PrivateKeystore::from_private_key(&private_key, ¶m.password, Source::Private); + let meta = Metadata { + name: param.name, + password_hint: param.password_hint, + source: Source::Private, + ..Metadata::default() + }; + let pk_store = PrivateKeystore::from_private_key(&private_key, ¶m.password, meta); let mut keystore = Keystore::PrivateKey(pk_store); @@ -714,11 +719,15 @@ pub(crate) fn sign_substrate_tx_raw(param: &SignParam, keystore: &mut Keystore) pub(crate) fn import_substrate_keystore(data: &[u8]) -> Result> { let param: SubstrateKeystoreParam = SubstrateKeystoreParam::decode(data)?; - let pk = decode_substrate_keystore(¶m.keystore, ¶m.password)?; + let ks: SubstrateKeystore = serde_json::from_str(¶m.keystore)?; + let _ = ks.validate()?; + let pk = decode_substrate_keystore(&ks, ¶m.password)?; let pk_import_param = PrivateKeyStoreImportParam { private_key: hex::encode(pk), password: param.password.to_string(), - overwrite: true, + name: ks.meta.name, + password_hint: "".to_string(), + overwrite: param.r#override, }; let param_bytes = encode_message(pk_import_param)?; private_key_store_import(¶m_bytes) @@ -732,7 +741,17 @@ pub(crate) fn export_substrate_keystore(data: &[u8]) -> Result> { let pk = export_result.value; let pk_bytes = hex::decode(pk)?; let coin = coin_info_from_param(¶m.chain_type, ¶m.network, "")?; - let keystore_str = encode_substrate_keystore(¶m.password, &pk_bytes, &coin)?; + + let mut map = KEYSTORE_MAP.write(); + let keystore: &mut Keystore = match map.get_mut(¶m.id) { + Some(keystore) => Ok(keystore), + _ => Err(format_err!("{}", "wallet_not_found")), + }?; + + let mut substrate_ks = encode_substrate_keystore(¶m.password, &pk_bytes, &coin)?; + substrate_ks.meta.name = keystore.meta().name; + substrate_ks.meta.when_created = keystore.meta().timestamp; + let keystore_str = serde_json::to_string(&substrate_ks)?; let ret = ExportSubstrateKeystoreResult { keystore: keystore_str, }; @@ -741,11 +760,10 @@ pub(crate) fn export_substrate_keystore(data: &[u8]) -> Result> { pub(crate) fn substrate_keystore_exists(data: &[u8]) -> Result> { let param: SubstrateKeystoreParam = SubstrateKeystoreParam::decode(data)?; - let pk = decode_substrate_keystore(¶m.keystore, ¶m.password)?; - // let ret = export_private_key(data)?; - // let export_result: KeystoreCommonExportResult = - // KeystoreCommonExportResult::decode(ret.as_slice())?; - // let pk = export_result.value; + let ks: SubstrateKeystore = serde_json::from_str(¶m.keystore)?; + let _ = ks.validate()?; + let pk = decode_substrate_keystore(&ks, ¶m.password)?; + let pk_hex = hex::encode(&pk); let exists_param = KeystoreCommonExistsParam { r#type: KeyType::PrivateKey as i32, @@ -753,14 +771,6 @@ pub(crate) fn substrate_keystore_exists(data: &[u8]) -> Result> { }; let exists_param_bytes = encode_message(exists_param)?; keystore_common_exists(&exists_param_bytes) - // - // let pk_bytes = hex::decode(pk)?; - // let coin = coin_info_from_param(¶m.chain_type, ¶m.network, "")?; - // let keystore_str = encode_substrate_keystore(¶m.password, &pk_bytes, &coin)?; - // let ret = ExportSubstrateKeystoreResult { - // keystore: keystore_str, - // }; - // encode_message(ret) } pub(crate) fn unlock_then_crash(data: &[u8]) -> Result> { diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 89dcc227..3d8fac85 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -264,6 +264,8 @@ mod tests { let param: PrivateKeyStoreImportParam = PrivateKeyStoreImportParam { private_key: "L2hfzPyVC1jWH7n2QLTe7tVTb6btg9smp5UVzhEBxLYaSFF7sCZB".to_string(), password: TEST_PASSWORD.to_string(), + name: "import_default_pk_store".to_string(), + password_hint: "".to_string(), overwrite: true, }; @@ -807,6 +809,8 @@ mod tests { private_key: "416c696365202020202020202020202020202020202020202020202020202020d172a74cda4c865912c32ba0a80a57ae69abae410e5ccb59dee84e2f4432db4f" .to_string(), password: TEST_PASSWORD.to_string(), + name: "test_64bytes_private_key_store_import".to_string(), + password_hint: "".to_string(), overwrite: true, }; @@ -1066,6 +1070,8 @@ mod tests { let param: PrivateKeyStoreImportParam = PrivateKeyStoreImportParam { private_key: "L39VXyorp19JfsEJfbD7Tfr4pBEX93RJuVXW7E13C51ZYAhUWbYa".to_string(), password: TEST_PASSWORD.to_string(), + name: "test_import_to_pk_which_from_hd".to_string(), + password_hint: "".to_string(), overwrite: true, }; @@ -1127,6 +1133,8 @@ mod tests { let param: PrivateKeyStoreImportParam = PrivateKeyStoreImportParam { private_key: "5JZc7wGRUr4J1RHDcM9ySWKLfQ2xjRUEo612qC4RLJ3G7jzJ4qx".to_string(), password: TEST_PASSWORD.to_string(), + name: "test_keystore_common_delete".to_string(), + password_hint: "".to_string(), overwrite: true, }; @@ -1473,6 +1481,43 @@ mod tests { #[test] pub fn test_import_substrate_keystore() { run_test(|| { + let wrong_keystore_str: &str = r#"{ + "address": "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS", + "encoded": "0xf7e7e89d3016c9b4d93bb1129adf69e5949ca1fb58c29da4591ddc72c52238a35835e3f2ae023f9867ff301bc4132463527ac03525eaac54664a7cb658eae68a0bbc99354222c194d6100b2bf3a492639229077a2e2818d8196e002f0b5556104be23b11633858259dbbd3f91ea1d34d6ce182b62d8381af1ef3c35e9ab1583267cfa41aa58bfd64435c2b5047baf9052f0953d9f7854d2d396dfcad13", + "encoding": { + "content": [ + "pkcs8", + "sr25519" + ], + "type": "", + "version": "2" + }, + "meta": { + "genesisHash": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", + "name": "i_can_save_name", + "tags": [], + "whenCreated": 1593591324334 + } +}"#; + + let param = SubstrateKeystoreParam { + keystore: wrong_keystore_str.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "KUSAMA".to_string(), + r#override: true, + }; + // let param_bytes = encode_message(param).unwrap(); + + let ret = call_api("substrate_keystore_exists", param.clone()); + + // let ret: Response = Response::decode(ret_bytes.as_slice()).unwrap(); + + assert!(ret.is_err()); + assert_eq!( + format!("{}", ret.err().unwrap()), + "invalid_keystore# only support xsalsa20-poly1305" + ); + let keystore_str: &str = r#"{ "address": "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS", "encoded": "0xf7e7e89d3016c9b4d93bb1129adf69e5949ca1fb58c29da4591ddc72c52238a35835e3f2ae023f9867ff301bc4132463527ac03525eaac54664a7cb658eae68a0bbc99354222c194d6100b2bf3a492639229077a2e2818d8196e002f0b5556104be23b11633858259dbbd3f91ea1d34d6ce182b62d8381af1ef3c35e9ab1583267cfa41aa58bfd64435c2b5047baf9052f0953d9f7854d2d396dfcad13", @@ -1486,7 +1531,7 @@ mod tests { }, "meta": { "genesisHash": "0xb0a8d493285c2df73290dfb7e61f870f17b41801197a149ca93654499ea3dafe", - "name": "keystore_import", + "name": "i_can_save_name", "tags": [], "whenCreated": 1593591324334 } @@ -1501,6 +1546,7 @@ mod tests { // let param_bytes = encode_message(param).unwrap(); let ret_bytes = call_api("substrate_keystore_exists", param.clone()).unwrap(); + let exists_result: KeystoreCommonExistsResult = KeystoreCommonExistsResult::decode(ret_bytes.as_slice()).unwrap(); assert!(!exists_result.is_exists); @@ -1553,6 +1599,9 @@ mod tests { keystore.address, "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" ); + assert_eq!(keystore.meta.name, "i_can_save_name"); + assert!(keystore.meta.when_created > 1594102917); + // assert_eq!(keystore_ret.keystore, ""); remove_created_wallet(&wallet_ret.id); }) From baaba9085085da74b6d3e8086619c613683a96ea Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Wed, 8 Jul 2020 16:48:41 +0800 Subject: [PATCH 13/16] Forbid exporting polkadot keystore from hdkeystore --- tcx/src/handler.rs | 28 ++++++++++++++++++++-------- tcx/src/lib.rs | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+), 8 deletions(-) diff --git a/tcx/src/handler.rs b/tcx/src/handler.rs index 0d2afc5c..213076a9 100644 --- a/tcx/src/handler.rs +++ b/tcx/src/handler.rs @@ -735,6 +735,23 @@ pub(crate) fn import_substrate_keystore(data: &[u8]) -> Result> { pub(crate) fn export_substrate_keystore(data: &[u8]) -> Result> { let param: ExportPrivateKeyParam = ExportPrivateKeyParam::decode(data.clone())?; + let meta: Metadata; + { + let map = KEYSTORE_MAP.read(); + + let keystore: &Keystore = match map.get(¶m.id) { + Some(keystore) => Ok(keystore), + _ => Err(format_err!("{}", "wallet_not_found")), + }?; + + // !!! Warning !!! HDKeystore only can export raw sr25519 key, + // but polkadotjs keystore needs a Ed25519 expanded secret key. + if keystore.determinable() { + return Err(format_err!("{}", "hd_wallet_cannot_export_keystore")); + } + meta = keystore.meta().clone(); + } + let ret = export_private_key(data)?; let export_result: KeystoreCommonExportResult = KeystoreCommonExportResult::decode(ret.as_slice())?; @@ -742,15 +759,10 @@ pub(crate) fn export_substrate_keystore(data: &[u8]) -> Result> { let pk_bytes = hex::decode(pk)?; let coin = coin_info_from_param(¶m.chain_type, ¶m.network, "")?; - let mut map = KEYSTORE_MAP.write(); - let keystore: &mut Keystore = match map.get_mut(¶m.id) { - Some(keystore) => Ok(keystore), - _ => Err(format_err!("{}", "wallet_not_found")), - }?; - let mut substrate_ks = encode_substrate_keystore(¶m.password, &pk_bytes, &coin)?; - substrate_ks.meta.name = keystore.meta().name; - substrate_ks.meta.when_created = keystore.meta().timestamp; + + substrate_ks.meta.name = meta.name; + substrate_ks.meta.when_created = meta.timestamp; let keystore_str = serde_json::to_string(&substrate_ks)?; let ret = ExportSubstrateKeystoreResult { keystore: keystore_str, diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 3d8fac85..2e8e8209 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -1210,6 +1210,7 @@ mod tests { KeystoreCommonExistsResult::decode(ret_bytes.as_slice()).unwrap(); assert!(result.is_exists); assert_eq!(result.id, wallet.id); + remove_created_wallet(&wallet.id); }) } @@ -1246,6 +1247,8 @@ mod tests { "Ldfdegx3hJygDuFDUA7Rkzjjx8gfFhP9DP", derived_accounts.accounts[0].address ); + + remove_created_wallet(&wallet.id); }) } @@ -1607,6 +1610,41 @@ mod tests { }) } + #[test] + pub fn test_export_hd_polkadot_keystore() { + run_test(|| { + let derivation = Derivation { + chain_type: "KUSAMA".to_string(), + path: "".to_string(), + network: "".to_string(), + seg_wit: "".to_string(), + chain_id: "".to_string(), + }; + let wallet = import_and_derive(derivation); + assert_eq!( + wallet.accounts[0].address, + "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS" + ); + + let export_param = ExportPrivateKeyParam { + id: wallet.id.to_string(), + password: TEST_PASSWORD.to_string(), + chain_type: "KUSAMA".to_string(), + network: "".to_string(), + main_address: "JHBkzZJnLZ3S3HLvxjpFAjd6ywP7WAk5miL7MwVCn9a7jHS".to_string(), + path: "".to_string(), + }; + let ret = call_api("substrate_keystore_export", export_param); + assert!(ret.is_err()); + assert_eq!( + format!("{}", ret.err().unwrap()), + "hd_wallet_cannot_export_keystore" + ); + + remove_created_wallet(&wallet.id); + }) + } + #[test] pub fn test_import_multi_curve() { run_test(|| { From dc15b9fbefe5bceb1b3bfe665e17a2dce9968916 Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Thu, 9 Jul 2020 09:27:03 +0800 Subject: [PATCH 14/16] fix override typo --- tcx-proto/src/substrate.proto | 2 +- tcx-substrate/src/transaction.rs | 2 +- tcx/src/handler.rs | 2 +- tcx/src/lib.rs | 6 +++--- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/tcx-proto/src/substrate.proto b/tcx-proto/src/substrate.proto index d5e7de29..6d938c52 100644 --- a/tcx-proto/src/substrate.proto +++ b/tcx-proto/src/substrate.proto @@ -6,7 +6,7 @@ message SubstrateKeystoreParam { string keystore = 1; string password = 2; string chainType = 3; - bool override = 6; + bool overwrite = 6; } diff --git a/tcx-substrate/src/transaction.rs b/tcx-substrate/src/transaction.rs index f5a204fb..acbd270b 100644 --- a/tcx-substrate/src/transaction.rs +++ b/tcx-substrate/src/transaction.rs @@ -7,7 +7,7 @@ pub struct SubstrateKeystoreParam { #[prost(string, tag = "3")] pub chain_type: std::string::String, #[prost(bool, tag = "6")] - pub r#override: bool, + pub overwrite: bool, } #[derive(Clone, PartialEq, ::prost::Message)] pub struct ExportSubstrateKeystoreResult { diff --git a/tcx/src/handler.rs b/tcx/src/handler.rs index 213076a9..327d2ead 100644 --- a/tcx/src/handler.rs +++ b/tcx/src/handler.rs @@ -727,7 +727,7 @@ pub(crate) fn import_substrate_keystore(data: &[u8]) -> Result> { password: param.password.to_string(), name: ks.meta.name, password_hint: "".to_string(), - overwrite: param.r#override, + overwrite: param.overwrite, }; let param_bytes = encode_message(pk_import_param)?; private_key_store_import(¶m_bytes) diff --git a/tcx/src/lib.rs b/tcx/src/lib.rs index 2e8e8209..a11f5c0a 100644 --- a/tcx/src/lib.rs +++ b/tcx/src/lib.rs @@ -1507,7 +1507,7 @@ mod tests { keystore: wrong_keystore_str.to_string(), password: TEST_PASSWORD.to_string(), chain_type: "KUSAMA".to_string(), - r#override: true, + overwrite: true, }; // let param_bytes = encode_message(param).unwrap(); @@ -1544,7 +1544,7 @@ mod tests { keystore: keystore_str.to_string(), password: TEST_PASSWORD.to_string(), chain_type: "KUSAMA".to_string(), - r#override: true, + overwrite: true, }; // let param_bytes = encode_message(param).unwrap(); @@ -1671,7 +1671,7 @@ mod tests { keystore: keystore_str.to_string(), password: TEST_PASSWORD.to_string(), chain_type: "KUSAMA".to_string(), - r#override: true, + overwrite: true, }; // let param_bytes = encode_message(param).unwrap(); let ret_bytes = call_api("substrate_keystore_import", param).unwrap(); From bffc1b8c57886ba3d4cd90200416919f0c31a33e Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Thu, 9 Jul 2020 09:37:13 +0800 Subject: [PATCH 15/16] Fix proto index --- tcx-proto/src/substrate.proto | 2 +- tcx-substrate/src/transaction.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tcx-proto/src/substrate.proto b/tcx-proto/src/substrate.proto index 6d938c52..ace429f3 100644 --- a/tcx-proto/src/substrate.proto +++ b/tcx-proto/src/substrate.proto @@ -6,7 +6,7 @@ message SubstrateKeystoreParam { string keystore = 1; string password = 2; string chainType = 3; - bool overwrite = 6; + bool overwrite = 4; } diff --git a/tcx-substrate/src/transaction.rs b/tcx-substrate/src/transaction.rs index acbd270b..8d97648c 100644 --- a/tcx-substrate/src/transaction.rs +++ b/tcx-substrate/src/transaction.rs @@ -6,7 +6,7 @@ pub struct SubstrateKeystoreParam { pub password: std::string::String, #[prost(string, tag = "3")] pub chain_type: std::string::String, - #[prost(bool, tag = "6")] + #[prost(bool, tag = "4")] pub overwrite: bool, } #[derive(Clone, PartialEq, ::prost::Message)] From 4eeed0556d49edb8e2ea066fce8daa4ed66a0c4e Mon Sep 17 00:00:00 2001 From: Neal Xu Date: Thu, 9 Jul 2020 09:51:40 +0800 Subject: [PATCH 16/16] Update the makefile --- Makefile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/Makefile b/Makefile index 333e4d01..39e496fc 100644 --- a/Makefile +++ b/Makefile @@ -34,6 +34,13 @@ build-ios-rn-example: ##@build generate the iOS RN compiled file build-android-rn-example: ##@build generate the Android RN compiled file sh ./tools/android-rn-example-build.sh +internal-release-android: + ./tools/android-token-v2-build.sh + +internal-release-ios: ##@build generate the Android RN compiled file + sh ./tools/ios-framework-build.sh + sh ./tools/ios-internal-release.sh $(VER) + e2e: ##@test run e2e test (cd examples/RN && yarn) (cd examples/RN/ios && pod install)