From d08403784cf784e2b5d56b26c408c891c2550436 Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Mon, 30 Nov 2020 17:27:26 +0800 Subject: [PATCH 1/7] add mini cos upgrade feature --- device/src/cos_upgrade.rs | 9 +++++++-- device/src/device_manager.rs | 13 +++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/device/src/cos_upgrade.rs b/device/src/cos_upgrade.rs index f1a64282..3508a2b2 100644 --- a/device/src/cos_upgrade.rs +++ b/device/src/cos_upgrade.rs @@ -1,5 +1,5 @@ use crate::app_download::AppDownloadRequest; -use crate::device_manager::{get_cert, get_firmware_version, get_se_id, get_sn}; +use crate::device_manager::{get_bl_version, get_cert, get_firmware_version, get_se_id, get_sn}; use crate::error::ImkeyError; use crate::ServiceResponse; use crate::{Result, TsmService}; @@ -25,6 +25,7 @@ pub struct CosUpgradeRequest { #[serde(rename = "commandID")] pub command_id: String, pub card_ret_data_list: Option>, + pub se_bl_versioon: Option, } #[derive(Serialize, Deserialize, Debug, Clone)] @@ -42,13 +43,13 @@ impl CosUpgradeRequest { pub fn cos_upgrade(sdk_version: Option) -> Result<()> { //read se device cert let mut device_cert = get_cert()?; - // ApduCheck::checke_response(&device_cert)?; //TODO 在所有manager里的接口中增加check方法 let mut is_jump = false; let seid; let sn; let mut se_cos_version = String::new(); let mut is_bl_status = true; + let mut se_bl_version = None; //read seid and sn number if device_cert.starts_with("bf21") || device_cert.starts_with("BF21") { seid = get_se_id()?; @@ -68,6 +69,7 @@ impl CosUpgradeRequest { .iter(), ); device_cert = hex::encode_upper(temp_device_cert); + se_bl_version = Some(get_bl_version()?); } else { return Err(ImkeyError::ImkeyTsmCosUpgradeFail.into()); } @@ -86,6 +88,7 @@ impl CosUpgradeRequest { status_word: None, command_id: String::from(constants::TSM_ACTION_COS_UPGRADE), card_ret_data_list: None, + se_bl_versioon: se_bl_version, }; loop { @@ -118,6 +121,8 @@ impl CosUpgradeRequest { { if "03".eq(next_step_key.as_str()) { reconnect()?; + se_bl_version = Some(get_bl_version()?); + request_data.se_bl_versioon = se_bl_version; } else if "05".eq(next_step_key.as_str()) { reconnect()?; se_cos_version = get_firmware_version()?; diff --git a/device/src/device_manager.rs b/device/src/device_manager.rs index 9481ca03..08f5df4e 100644 --- a/device/src/device_manager.rs +++ b/device/src/device_manager.rs @@ -63,6 +63,19 @@ pub fn get_firmware_version() -> Result { Ok(firmware_version) } +pub fn get_bl_version() -> Result { + select_isd(); + let res = send_apdu("80CA800900".to_string())?; + ApduCheck::check_response(res.as_str())?; + let bl_version = format!( + "{}.{}.{}", + res[0..1].to_string(), + res[1..2].to_string(), + res[2..res.len() - 4].to_string() + ); + Ok(bl_version) +} + pub fn get_battery_power() -> Result { select_isd(); let res = send_apdu("00D6FEED01".to_string())?; From 0c255e369ba465fbc47fd3d99a9e92914e85d72b Mon Sep 17 00:00:00 2001 From: xiemener Date: Thu, 10 Dec 2020 15:05:33 +0800 Subject: [PATCH 2/7] add tron lib --- Cargo.toml | 1 + common/src/constants.rs | 2 + wallet/coin-tron/Cargo.toml | 19 +++ wallet/coin-tron/src/address.rs | 105 +++++++++++++++ wallet/coin-tron/src/lib.rs | 16 +++ wallet/coin-tron/src/signer.rs | 221 ++++++++++++++++++++++++++++++++ wallet/coin-tron/src/tronapi.rs | 46 +++++++ 7 files changed, 410 insertions(+) create mode 100644 wallet/coin-tron/Cargo.toml create mode 100644 wallet/coin-tron/src/address.rs create mode 100644 wallet/coin-tron/src/lib.rs create mode 100644 wallet/coin-tron/src/signer.rs create mode 100644 wallet/coin-tron/src/tronapi.rs diff --git a/Cargo.toml b/Cargo.toml index 452a0678..2bf3f754 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,4 +10,5 @@ members = [ "wallet/coin-eos", "wallet/coin-cosmos", "wallet/coin-substrate", + "wallet/coin-tron", ] \ No newline at end of file diff --git a/common/src/constants.rs b/common/src/constants.rs index a0daf79b..68806fc9 100644 --- a/common/src/constants.rs +++ b/common/src/constants.rs @@ -23,6 +23,7 @@ pub const FILECOIN_AID: &str = "695F6B315F66696C"; pub const IMK_AID: &str = "695F696D6B"; pub const POLKADOT_AID: &str = "695F656473725F646F74"; pub const KUSAMA_AID: &str = "695F656473725F6B736D"; +pub const TRON_AID: &str = "695F6B315F74726F6E"; pub const BL_AID: &str = "D0426F6F746C6F61646572"; @@ -33,6 +34,7 @@ pub const ETH_PATH: &str = "m/44'/60'/0'/0/0"; pub const FILECOIN_PATH: &str = "m/44'/461'/0/0/0"; pub const POLKADOT_PATH: &str = "m/44'/354'/0'/0'/0'"; pub const KUSAMA_PATH: &str = "m/44'/434'/0'/0'/0'"; +pub const TRON_PATH: &str = "m/44'/195'/0'/0/0"; pub const MAX_UTXO_NUMBER: usize = 252; pub const EACH_ROUND_NUMBER: usize = 5; diff --git a/wallet/coin-tron/Cargo.toml b/wallet/coin-tron/Cargo.toml new file mode 100644 index 00000000..93d995b8 --- /dev/null +++ b/wallet/coin-tron/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "coin-tron" +version = "0.1.0" +authors = ["xiemener "] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +keccak-hash = "0.2.0" +failure = "0.1.6" +bitcoin = "0.21.0" +hex = "0.3" +common = {path = "../../common"} +device = {path = "../../device"} +transport = {path = "../../transport"} +prost = "0.6.1" +secp256k1 = {version ="0.15", features = ["recovery"] } +tiny-keccak = "1.4" \ No newline at end of file diff --git a/wallet/coin-tron/src/address.rs b/wallet/coin-tron/src/address.rs new file mode 100644 index 00000000..73da470b --- /dev/null +++ b/wallet/coin-tron/src/address.rs @@ -0,0 +1,105 @@ +use crate::Result; +use bitcoin::util::base58; +use common::apdu::{Apdu, ApduCheck, CoinCommonApdu, Secp256k1Apdu}; +use common::constants::TRON_AID; +use common::error::CoinError; +use common::path::check_path_validity; +use common::utility; +use common::utility::secp256k1_sign; +use device::device_binding::KEY_MANAGER; +use keccak_hash::keccak; +use transport::message::send_apdu; + +pub struct TronAddress {} + +impl TronAddress { + pub fn address_from_pubkey(pubkey: &[u8]) -> Result { + let pubkey_hash = keccak(pubkey[1..].as_ref()); + let address = [vec![0x41], pubkey_hash[12..].to_vec()].concat(); + let base58_address = base58::check_encode_slice(&address); + Ok(base58_address) + } + + pub fn get_address(path: &str) -> Result { + check_path_validity(path).unwrap(); + + let select_apdu = Apdu::select_applet(TRON_AID); + let select_response = send_apdu(select_apdu)?; + ApduCheck::check_response(&select_response)?; + + let key_manager_obj = KEY_MANAGER.lock().unwrap(); + let bind_signature = secp256k1_sign(&key_manager_obj.pri_key, &path.as_bytes())?; + + let mut apdu_pack: Vec = vec![]; + apdu_pack.push(0x00); + apdu_pack.push(bind_signature.len() as u8); + apdu_pack.extend(bind_signature.as_slice()); + apdu_pack.push(0x01); + apdu_pack.push(path.as_bytes().len() as u8); + apdu_pack.extend(path.as_bytes()); + + //get public + let msg_pubkey = Secp256k1Apdu::get_xpub(&apdu_pack); + let res_msg_pubkey = send_apdu(msg_pubkey)?; + ApduCheck::check_response(&res_msg_pubkey)?; + + let sign_source_val = &res_msg_pubkey[..194]; + let sign_result = &res_msg_pubkey[194..res_msg_pubkey.len() - 4]; + + //verify + let sign_verify_result = utility::secp256k1_sign_verify( + &key_manager_obj.se_pub_key, + hex::decode(sign_result).unwrap().as_slice(), + hex::decode(sign_source_val).unwrap().as_slice(), + )?; + if !sign_verify_result { + return Err(CoinError::ImkeySignatureVerifyFail.into()); + } + + let pubkey_raw = hex::decode(&res_msg_pubkey[..130]).unwrap(); + + let address = TronAddress::address_from_pubkey(pubkey_raw.as_slice())?; + Ok(address) + } + + pub fn display_address(path: &str) -> Result { + let address = TronAddress::get_address(path).unwrap(); + let tron_menu_name = "TRON".as_bytes(); + let reg_apdu = Secp256k1Apdu::register_address(tron_menu_name, address.as_bytes()); + let res_reg = send_apdu(reg_apdu)?; + ApduCheck::check_response(&res_reg)?; + Ok(address) + } +} + +#[cfg(test)] +mod tests { + use crate::address::TronAddress; + use common::constants; + use device::device_binding::bind_test; + + #[test] + fn tron_address() { + let bytes = hex::decode("04DAAC763B1B3492720E404C53D323BAF29391996F7DD5FA27EF0D12F7D50D694700684A32AD97FF4C09BF9CF0B9D0AC7F0091D9C6CB8BE9BB6A1106DA557285D8").unwrap(); + + assert_eq!( + TronAddress::address_from_pubkey(&bytes).unwrap(), + "THfuSDVRvSsjNDPFdGjMU19Ha4Kf7acotq" + ); + } + + #[test] + fn test_get_address() { + bind_test(); + let address = TronAddress::get_address(constants::TRON_PATH).unwrap(); + assert_eq!(&address, "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2"); + } + + #[test] + fn test_display_address() { + bind_test(); + let address = TronAddress::display_address(constants::TRON_PATH).unwrap(); + println!("address:{}", &address); + assert_eq!(&address, "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2"); + } +} diff --git a/wallet/coin-tron/src/lib.rs b/wallet/coin-tron/src/lib.rs new file mode 100644 index 00000000..4dc27b39 --- /dev/null +++ b/wallet/coin-tron/src/lib.rs @@ -0,0 +1,16 @@ +pub mod address; +pub mod signer; +pub mod tronapi; + +#[macro_use] +extern crate failure; +use core::result; +pub type Result = result::Result; + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + assert_eq!(2 + 2, 4); + } +} diff --git a/wallet/coin-tron/src/signer.rs b/wallet/coin-tron/src/signer.rs new file mode 100644 index 00000000..be8c9fb8 --- /dev/null +++ b/wallet/coin-tron/src/signer.rs @@ -0,0 +1,221 @@ +use crate::address::TronAddress; +use crate::tronapi::{TronMessageSignReq, TronMessageSignRes, TronTxReq, TronTxRes}; +use crate::Result; +use common::apdu::{Apdu, ApduCheck, CoinCommonApdu, Secp256k1Apdu}; +use common::constants::TRON_AID; +use common::error::CoinError; +use common::path::check_path_validity; +use common::utility::{is_valid_hex, secp256k1_sign, sha256_hash}; +use common::{constants, utility}; +use device::device_binding::KEY_MANAGER; +use device::key_manager::KeyManager; +use secp256k1::{self, Message as SecpMessage, Signature as SecpSignature}; +use transport::message::{send_apdu, send_apdu_timeout}; + +#[derive(Debug)] +pub struct TronSigner {} + +impl TronSigner { + pub fn sign_message(input: TronMessageSignReq) -> Result { + check_path_validity(&input.path).unwrap(); + + let message = match input.is_hex { + true => { + let mut raw_hex: String = input.message.to_owned(); + if raw_hex.to_uppercase().starts_with("0X") { + raw_hex.replace_range(..2, "") + } + hex::decode(&raw_hex)? + } + false => input.message.into_bytes(), + }; + let header = match input.is_tron_header { + true => "\x19TRON Signed Message:\n32".as_bytes(), + false => "\x19Ethereum Signed Message:\n32".as_bytes(), + }; + let mut msg_with_header = Vec::new(); + msg_with_header.extend(header); + msg_with_header.extend(&message); + + let mut data_pack = Vec::new(); + + let hash = tiny_keccak::keccak256(&msg_with_header); + data_pack.push(0x01); + data_pack.push(hash.len() as u8); + data_pack.extend(&hash); + + let path = input.path.as_bytes(); + data_pack.push(0x02); + data_pack.push(path.len() as u8); + data_pack.extend(path); + + let key_manager_obj = KEY_MANAGER.lock().unwrap(); + let msg_sig = secp256k1_sign(&key_manager_obj.pri_key, &data_pack)?; + let mut data_pack_with_sig = Vec::new(); + data_pack_with_sig.push(0x00); + data_pack_with_sig.push(msg_sig.len() as u8); + data_pack_with_sig.extend(msg_sig); + data_pack_with_sig.extend(&data_pack); + + drop(key_manager_obj); + let signature = TronSigner::sign(&input.path, &data_pack_with_sig, &hash, &input.address)?; + Ok(TronMessageSignRes { signature }) + } + + pub fn sign_transaction(input: TronTxReq) -> Result { + check_path_validity(&input.path).unwrap(); + + let mut data_pack = Vec::new(); + + let raw_data = hex::decode(input.raw_data)?; + let hash = sha256_hash(&raw_data); + data_pack.push(0x01); + data_pack.push(hash.len() as u8); + data_pack.extend(&hash); + + let path = input.path.as_bytes(); + data_pack.push(0x02); + data_pack.push(path.len() as u8); + data_pack.extend(path); + + let payment = input.payment.as_bytes(); + data_pack.push(0x07); + data_pack.push(payment.len() as u8); + data_pack.extend(payment); + + let to = input.to.as_bytes(); + data_pack.push(0x08); + data_pack.push(to.len() as u8); + data_pack.extend(to); + + let key_manager_obj = KEY_MANAGER.lock().unwrap(); + let data_pack_sig = secp256k1_sign(&key_manager_obj.pri_key, &data_pack)?; + drop(key_manager_obj); + + let mut data_pack_with_sig = Vec::new(); + data_pack_with_sig.push(0x00); + data_pack_with_sig.push(data_pack_sig.len() as u8); + data_pack_with_sig.extend(&data_pack_sig); + data_pack_with_sig.extend(&data_pack); + + let signature = TronSigner::sign(&input.path, &data_pack_with_sig, &hash, &input.address)?; + Ok(TronTxRes { signature }) + } + + pub fn sign(path: &str, data_pack: &[u8], hash: &[u8], sender: &str) -> Result { + let select_apdu = Apdu::select_applet(TRON_AID); + let select_result = send_apdu(select_apdu)?; + ApduCheck::check_response(&select_result)?; + + let key_manager_obj = KEY_MANAGER.lock().unwrap(); + let path_signature = secp256k1_sign(&key_manager_obj.pri_key, &path.as_bytes())?; + let mut path_pack: Vec = vec![]; + path_pack.push(0x00); + path_pack.push(path_signature.len() as u8); + path_pack.extend(path_signature.as_slice()); + path_pack.push(0x01); + path_pack.push(path.as_bytes().len() as u8); + path_pack.extend(path.as_bytes()); + + let msg_pubkey = Secp256k1Apdu::get_xpub(&path_pack); + let res_msg_pubkey = send_apdu(msg_pubkey)?; + let pubkey_raw = hex::decode(&res_msg_pubkey[..130]).unwrap(); + let address = TronAddress::address_from_pubkey(pubkey_raw.as_slice()).unwrap(); + if &address != sender { + return Err(CoinError::ImkeyAddressMismatchWithPath.into()); + } + + let mut sign_response = "".to_string(); + let sign_apdus = Secp256k1Apdu::sign(data_pack); + for apdu in sign_apdus { + sign_response = send_apdu_timeout(apdu, constants::TIMEOUT_LONG)?; + ApduCheck::check_response(&sign_response)?; + } + + // verify + let sign_source_val = &sign_response[..132]; + let sign_result = &sign_response[132..sign_response.len() - 4]; + let sign_verify_result = utility::secp256k1_sign_verify( + &key_manager_obj.se_pub_key, + hex::decode(sign_result).unwrap().as_slice(), + hex::decode(sign_source_val).unwrap().as_slice(), + )?; + + if !sign_verify_result { + return Err(CoinError::ImkeySignatureVerifyFail.into()); + } + + let sign_compact = hex::decode(&sign_response[2..130]).unwrap(); + let mut signnture_obj = SecpSignature::from_compact(sign_compact.as_slice()).unwrap(); + signnture_obj.normalize_s(); + let normalizes_sig_vec = signnture_obj.serialize_compact(); + + let rec_id = utility::retrieve_recid(&hash, &normalizes_sig_vec, &pubkey_raw).unwrap(); + let rec_id = rec_id.to_i32(); + let v = rec_id + 27; + + let mut signature = hex::encode(&normalizes_sig_vec.as_ref()); + signature.push_str(&format!("{:02x}", &v)); + + Ok(signature) + } +} + +#[cfg(test)] +mod tests { + use crate::signer::TronSigner; + use crate::tronapi::{TronMessageSignReq, TronTxReq}; + use bitcoin::util::misc::hex_bytes; + use common::constants; + use device::device_binding::bind_test; + + #[test] + fn sign_message() { + bind_test(); + + let input = TronMessageSignReq { + path: constants::TRON_PATH.to_string(), + message: "645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76".to_string(), + address: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), + is_hex: true, + is_tron_header: true, + }; + let res = TronSigner::sign_message(input).unwrap(); + assert_eq!("16417c6489da3a88ef980bf0a42551b9e76181d03e7334548ab3cb36e7622a484482722882a29e2fe4587b95c739a68624ebf9ada5f013a9340d883f03fcf9af1b", &res.signature); + + let input2 = TronMessageSignReq { + path: constants::TRON_PATH.to_string(), + message: "645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76".to_string(), + address: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), + is_hex: true, + is_tron_header: false, + }; + let res = TronSigner::sign_message(input2).unwrap(); + assert_eq!("06ff3c5f98b8e8e257f47a66ce8e953c7a7d0f96eb6687da6a98b66a36c2a725759cab3df94d014bd17760328adf860649303c68c4fa6644d9f307e2f32cc3311c", &res.signature); + + let input = TronMessageSignReq { + path: constants::TRON_PATH.to_string(), + message: "abcdef".to_string(), + address: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), + is_hex: false, + is_tron_header: true, + }; + let res = TronSigner::sign_message(input).unwrap(); + assert_eq!("a87eb6ae7e97621b6ba2e2f70db31fe0c744c6adcfdc005044026506b70ac11a33f415f4478b6cf84af32b3b5d70a13a77e53287613449b345bb16fe012c04081b", &res.signature); + } + + #[test] + fn sign_transaction() { + bind_test(); + + let input = TronTxReq{ + path: constants::TRON_PATH.to_string(), + raw_data: "0a0208312208b02efdc02638b61e40f083c3a7c92d5a65080112610a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412300a1541a1e81654258bf14f63feb2e8d1380075d45b0dac1215410b3e84ec677b3e63c99affcadb91a6b4e086798f186470a0bfbfa7c92d".to_string(), + address: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), + payment: "100 TRX".to_string(), + to: "TDQqJsFsStSy5fjG52KuiWW7HhJGAKGJLb".to_string() + }; + let res = TronSigner::sign_transaction(input).unwrap(); + assert_eq!("c65b4bde808f7fcfab7b0ef9c1e3946c83311f8ac0a5e95be2d8b6d2400cfe8b5e24dc8f0883132513e422f2aaad8a4ecc14438eae84b2683eefa626e3adffc61c", &res.signature); + } +} diff --git a/wallet/coin-tron/src/tronapi.rs b/wallet/coin-tron/src/tronapi.rs new file mode 100644 index 00000000..a6e3b3a8 --- /dev/null +++ b/wallet/coin-tron/src/tronapi.rs @@ -0,0 +1,46 @@ +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TronTxReq { + #[prost(string, tag = "1")] + pub path: std::string::String, + #[prost(string, tag = "2")] + pub raw_data: std::string::String, + #[prost(string, tag = "3")] + pub address: std::string::String, + #[prost(string, tag = "4")] + pub payment: std::string::String, + #[prost(string, tag = "5")] + pub to: std::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TronTxRes { + #[prost(string, tag = "1")] + pub signature: std::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TronAddressReq { + #[prost(string, tag = "1")] + pub path: std::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TronAddressRes { + #[prost(string, tag = "1")] + pub address: std::string::String, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TronMessageSignReq { + #[prost(string, tag = "1")] + pub path: std::string::String, + #[prost(string, tag = "2")] + pub message: std::string::String, + #[prost(string, tag = "3")] + pub address: std::string::String, + #[prost(bool, tag = "4")] + pub is_hex: bool, + #[prost(bool, tag = "5")] + pub is_tron_header: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct TronMessageSignRes { + #[prost(string, tag = "1")] + pub signature: std::string::String, +} From e612dab4a34101b1be0bb1c3adaf7e113e4b8d0e Mon Sep 17 00:00:00 2001 From: xiemener Date: Thu, 10 Dec 2020 16:27:21 +0800 Subject: [PATCH 3/7] add tron api --- api/Cargo.toml | 1 + api/src/lib.rs | 7 + api/src/substrate_signer.rs | 2 +- api/src/tron_address.rs | 27 +++ api/src/tron_signer.rs | 18 ++ examples/ios/iosExample/Proto/tron.pb.swift | 204 ++++++++++++++++++++ proto/build.rs | 4 + proto/src/tron.proto | 20 ++ wallet/coin-tron/src/signer.rs | 90 +++++---- wallet/coin-tron/src/tronapi.rs | 30 +-- 10 files changed, 343 insertions(+), 60 deletions(-) create mode 100644 api/src/tron_address.rs create mode 100644 api/src/tron_signer.rs create mode 100644 examples/ios/iosExample/Proto/tron.pb.swift create mode 100644 proto/src/tron.proto diff --git a/api/Cargo.toml b/api/Cargo.toml index 70ab7c79..c5b9d9dd 100644 --- a/api/Cargo.toml +++ b/api/Cargo.toml @@ -18,6 +18,7 @@ coin-eos = {path = "../wallet/coin-eos"} coin-cosmos = {path = "../wallet/coin-cosmos"} coin-filecoin = {path = "../wallet/coin-filecoin"} coin-substrate = {path = "../wallet/coin-substrate"} +coin-tron = {path = "../wallet/coin-tron"} common = {path = "../common"} bitcoin = "0.25.0" ethereum-types = "0.6.0" diff --git a/api/src/lib.rs b/api/src/lib.rs index 64642736..7ab0dd96 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -19,6 +19,8 @@ pub mod filecoin_signer; pub mod message_handler; pub mod substrate_address; pub mod substrate_signer; +pub mod tron_address; +pub mod tron_signer; use std::sync::Mutex; #[macro_use] @@ -123,6 +125,7 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char "FILECOIN" => filecoin_address::get_address(¶m), "POLKADOT" => substrate_address::get_address(¶m), "KUSAMA" => substrate_address::get_address(¶m), + "TRON" => tron_address::get_address(¶m), _ => Err(format_err!("get_address unsupported_chain")), } }), @@ -155,6 +158,7 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char "FILECOIN" => filecoin_address::display_filecoin_address(¶m), "POLKADOT" => substrate_address::display_address(¶m), "KUSAMA" => substrate_address::display_address(¶m), + "TRON" => tron_address::display_address(¶m), _ => Err(format_err!("register_address unsupported_chain")), } }), @@ -187,6 +191,9 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char "KUSAMA" => { substrate_signer::sign_transaction(¶m.clone().input.unwrap().value, ¶m) } + "TRON" => { + tron_signer::sign_transaction(¶m.clone().input.unwrap().value, ¶m) + } _ => Err(format_err!("sign_tx unsupported_chain")), } }), diff --git a/api/src/substrate_signer.rs b/api/src/substrate_signer.rs index 9e8696ef..b61c9452 100644 --- a/api/src/substrate_signer.rs +++ b/api/src/substrate_signer.rs @@ -6,7 +6,7 @@ use common::SignParam; use prost::Message; pub fn sign_transaction(data: &[u8], sign_param: &SignParam) -> Result> { - let input: SubstrateRawTxIn = SubstrateRawTxIn::decode(data).unwrap(); + let input: SubstrateRawTxIn = SubstrateRawTxIn::decode(data).expect("decode proto error"); let signed = Transaction::sign_transaction(&input, sign_param)?; encode_message(signed) } diff --git a/api/src/tron_address.rs b/api/src/tron_address.rs new file mode 100644 index 00000000..c798c5ea --- /dev/null +++ b/api/src/tron_address.rs @@ -0,0 +1,27 @@ +use crate::api::{AddressParam, AddressResult}; +use crate::error_handling::Result; +use crate::message_handler::encode_message; +use coin_tron::address::TronAddress; +use prost::Message; + +pub fn get_address(param: &AddressParam) -> Result> { + let address = TronAddress::get_address(param.path.as_ref())?; + + let address_message = AddressResult { + path: param.path.to_owned(), + chain_type: param.chain_type.to_string(), + address, + }; + encode_message(address_message) +} + +pub fn display_address(param: &AddressParam) -> Result> { + let address = TronAddress::display_address(param.path.as_ref())?; + + let address_message = AddressResult { + path: param.path.to_owned(), + chain_type: param.chain_type.to_string(), + address, + }; + encode_message(address_message) +} diff --git a/api/src/tron_signer.rs b/api/src/tron_signer.rs new file mode 100644 index 00000000..025925ec --- /dev/null +++ b/api/src/tron_signer.rs @@ -0,0 +1,18 @@ +use crate::error_handling::Result; +use crate::message_handler::encode_message; +use coin_tron::signer::TronSigner; +use coin_tron::tronapi::{TronMessageInput, TronTxInput}; +use common::SignParam; +use prost::Message; + +pub fn sign_transaction(data: &[u8], sign_param: &SignParam) -> Result> { + let input: TronTxInput = TronTxInput::decode(data).expect("decode proto error"); + let signed = TronSigner::sign_transaction(input, sign_param)?; + encode_message(signed) +} + +pub fn sign_message(data: &[u8], sign_param: &SignParam) -> Result> { + let input: TronMessageInput = TronMessageInput::decode(data).expect("decode proto error"); + let signed = TronSigner::sign_message(input, sign_param)?; + encode_message(signed) +} diff --git a/examples/ios/iosExample/Proto/tron.pb.swift b/examples/ios/iosExample/Proto/tron.pb.swift new file mode 100644 index 00000000..72040902 --- /dev/null +++ b/examples/ios/iosExample/Proto/tron.pb.swift @@ -0,0 +1,204 @@ +// DO NOT EDIT. +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: tron.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that your are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +public struct Tronapi_TronTxInput { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var rawData: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Tronapi_TronTxOutput { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var signature: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Tronapi_TronMessageInput { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var message: String = String() + + public var isHex: Bool = false + + public var isTronHeader: Bool = false + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Tronapi_TronMessageOutput { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var signature: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "tronapi" + +extension Tronapi_TronTxInput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".TronTxInput" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 2: .standard(proto: "raw_data"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 2: try decoder.decodeSingularStringField(value: &self.rawData) + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.rawData.isEmpty { + try visitor.visitSingularStringField(value: self.rawData, fieldNumber: 2) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Tronapi_TronTxInput, rhs: Tronapi_TronTxInput) -> Bool { + if lhs.rawData != rhs.rawData {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Tronapi_TronTxOutput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".TronTxOutput" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "signature"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularStringField(value: &self.signature) + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.signature.isEmpty { + try visitor.visitSingularStringField(value: self.signature, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Tronapi_TronTxOutput, rhs: Tronapi_TronTxOutput) -> Bool { + if lhs.signature != rhs.signature {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Tronapi_TronMessageInput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".TronMessageInput" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 2: .same(proto: "message"), + 4: .standard(proto: "is_hex"), + 5: .standard(proto: "is_tron_header"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 2: try decoder.decodeSingularStringField(value: &self.message) + case 4: try decoder.decodeSingularBoolField(value: &self.isHex) + case 5: try decoder.decodeSingularBoolField(value: &self.isTronHeader) + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.message.isEmpty { + try visitor.visitSingularStringField(value: self.message, fieldNumber: 2) + } + if self.isHex != false { + try visitor.visitSingularBoolField(value: self.isHex, fieldNumber: 4) + } + if self.isTronHeader != false { + try visitor.visitSingularBoolField(value: self.isTronHeader, fieldNumber: 5) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Tronapi_TronMessageInput, rhs: Tronapi_TronMessageInput) -> Bool { + if lhs.message != rhs.message {return false} + if lhs.isHex != rhs.isHex {return false} + if lhs.isTronHeader != rhs.isTronHeader {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Tronapi_TronMessageOutput: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".TronMessageOutput" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "signature"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularStringField(value: &self.signature) + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.signature.isEmpty { + try visitor.visitSingularStringField(value: self.signature, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Tronapi_TronMessageOutput, rhs: Tronapi_TronMessageOutput) -> Bool { + if lhs.signature != rhs.signature {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/proto/build.rs b/proto/build.rs index c1b5a5b6..4efb4267 100644 --- a/proto/build.rs +++ b/proto/build.rs @@ -37,6 +37,10 @@ fn main() { // subtrate env::set_var("OUT_DIR", "../wallet/coin-substrate/src"); prost_build::compile_protos(&["src/substrate.proto"], &["src/"]).unwrap(); + + // tron + env::set_var("OUT_DIR", "../wallet/coin-tron/src"); + prost_build::compile_protos(&["src/tron.proto"], &["src/"]).unwrap(); } #[cfg(test)] diff --git a/proto/src/tron.proto b/proto/src/tron.proto new file mode 100644 index 00000000..6c9c7448 --- /dev/null +++ b/proto/src/tron.proto @@ -0,0 +1,20 @@ +syntax = "proto3"; +package tronapi; + +message TronTxInput { + string raw_data = 2; +} + +message TronTxOutput { + string signature = 1; +} + +message TronMessageInput { + string message = 2; + bool is_hex =4; + bool is_tron_header=5; +} + +message TronMessageOutput { + string signature = 1; +} \ No newline at end of file diff --git a/wallet/coin-tron/src/signer.rs b/wallet/coin-tron/src/signer.rs index be8c9fb8..a221be6c 100644 --- a/wallet/coin-tron/src/signer.rs +++ b/wallet/coin-tron/src/signer.rs @@ -1,12 +1,12 @@ use crate::address::TronAddress; -use crate::tronapi::{TronMessageSignReq, TronMessageSignRes, TronTxReq, TronTxRes}; +use crate::tronapi::{TronMessageInput, TronMessageOutput, TronTxInput, TronTxOutput}; use crate::Result; use common::apdu::{Apdu, ApduCheck, CoinCommonApdu, Secp256k1Apdu}; use common::constants::TRON_AID; use common::error::CoinError; use common::path::check_path_validity; use common::utility::{is_valid_hex, secp256k1_sign, sha256_hash}; -use common::{constants, utility}; +use common::{constants, utility, SignParam}; use device::device_binding::KEY_MANAGER; use device::key_manager::KeyManager; use secp256k1::{self, Message as SecpMessage, Signature as SecpSignature}; @@ -16,8 +16,11 @@ use transport::message::{send_apdu, send_apdu_timeout}; pub struct TronSigner {} impl TronSigner { - pub fn sign_message(input: TronMessageSignReq) -> Result { - check_path_validity(&input.path).unwrap(); + pub fn sign_message( + input: TronMessageInput, + sign_param: &SignParam, + ) -> Result { + check_path_validity(&sign_param.path).unwrap(); let message = match input.is_hex { true => { @@ -44,7 +47,7 @@ impl TronSigner { data_pack.push(hash.len() as u8); data_pack.extend(&hash); - let path = input.path.as_bytes(); + let path = sign_param.path.as_bytes(); data_pack.push(0x02); data_pack.push(path.len() as u8); data_pack.extend(path); @@ -58,12 +61,17 @@ impl TronSigner { data_pack_with_sig.extend(&data_pack); drop(key_manager_obj); - let signature = TronSigner::sign(&input.path, &data_pack_with_sig, &hash, &input.address)?; - Ok(TronMessageSignRes { signature }) + let signature = TronSigner::sign( + &sign_param.path, + &data_pack_with_sig, + &hash, + &sign_param.sender, + )?; + Ok(TronMessageOutput { signature }) } - pub fn sign_transaction(input: TronTxReq) -> Result { - check_path_validity(&input.path).unwrap(); + pub fn sign_transaction(input: TronTxInput, sign_param: &SignParam) -> Result { + check_path_validity(&sign_param.path).unwrap(); let mut data_pack = Vec::new(); @@ -73,17 +81,17 @@ impl TronSigner { data_pack.push(hash.len() as u8); data_pack.extend(&hash); - let path = input.path.as_bytes(); + let path = sign_param.path.as_bytes(); data_pack.push(0x02); data_pack.push(path.len() as u8); data_pack.extend(path); - let payment = input.payment.as_bytes(); + let payment = sign_param.payment.as_bytes(); data_pack.push(0x07); data_pack.push(payment.len() as u8); data_pack.extend(payment); - let to = input.to.as_bytes(); + let to = sign_param.receiver.as_bytes(); data_pack.push(0x08); data_pack.push(to.len() as u8); data_pack.extend(to); @@ -98,8 +106,13 @@ impl TronSigner { data_pack_with_sig.extend(&data_pack_sig); data_pack_with_sig.extend(&data_pack); - let signature = TronSigner::sign(&input.path, &data_pack_with_sig, &hash, &input.address)?; - Ok(TronTxRes { signature }) + let signature = TronSigner::sign( + &sign_param.path, + &data_pack_with_sig, + &hash, + &sign_param.sender, + )?; + Ok(TronTxOutput { signature }) } pub fn sign(path: &str, data_pack: &[u8], hash: &[u8], sender: &str) -> Result { @@ -164,58 +177,69 @@ impl TronSigner { #[cfg(test)] mod tests { use crate::signer::TronSigner; - use crate::tronapi::{TronMessageSignReq, TronTxReq}; + use crate::tronapi::{TronMessageInput, TronTxInput}; use bitcoin::util::misc::hex_bytes; - use common::constants; + use common::{constants, SignParam}; use device::device_binding::bind_test; #[test] fn sign_message() { bind_test(); - let input = TronMessageSignReq { + let sign_param = SignParam { + chain_type: "TRON".to_string(), path: constants::TRON_PATH.to_string(), + network: "".to_string(), + input: None, + payment: "".to_string(), + receiver: "".to_string(), + sender: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), + fee: "".to_string(), + }; + + let input = TronMessageInput { message: "645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76".to_string(), - address: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), is_hex: true, is_tron_header: true, }; - let res = TronSigner::sign_message(input).unwrap(); + let res = TronSigner::sign_message(input, &sign_param).unwrap(); assert_eq!("16417c6489da3a88ef980bf0a42551b9e76181d03e7334548ab3cb36e7622a484482722882a29e2fe4587b95c739a68624ebf9ada5f013a9340d883f03fcf9af1b", &res.signature); - let input2 = TronMessageSignReq { - path: constants::TRON_PATH.to_string(), + let input2 = TronMessageInput { message: "645c0b7b58158babbfa6c6cd5a48aa7340a8749176b120e8516216787a13dc76".to_string(), - address: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), is_hex: true, is_tron_header: false, }; - let res = TronSigner::sign_message(input2).unwrap(); + let res = TronSigner::sign_message(input2, &sign_param).unwrap(); assert_eq!("06ff3c5f98b8e8e257f47a66ce8e953c7a7d0f96eb6687da6a98b66a36c2a725759cab3df94d014bd17760328adf860649303c68c4fa6644d9f307e2f32cc3311c", &res.signature); - let input = TronMessageSignReq { - path: constants::TRON_PATH.to_string(), + let input3 = TronMessageInput { message: "abcdef".to_string(), - address: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), is_hex: false, is_tron_header: true, }; - let res = TronSigner::sign_message(input).unwrap(); + let res = TronSigner::sign_message(input3, &sign_param).unwrap(); assert_eq!("a87eb6ae7e97621b6ba2e2f70db31fe0c744c6adcfdc005044026506b70ac11a33f415f4478b6cf84af32b3b5d70a13a77e53287613449b345bb16fe012c04081b", &res.signature); } #[test] fn sign_transaction() { bind_test(); - - let input = TronTxReq{ + let sign_param = SignParam { + chain_type: "TRON".to_string(), path: constants::TRON_PATH.to_string(), - raw_data: "0a0208312208b02efdc02638b61e40f083c3a7c92d5a65080112610a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412300a1541a1e81654258bf14f63feb2e8d1380075d45b0dac1215410b3e84ec677b3e63c99affcadb91a6b4e086798f186470a0bfbfa7c92d".to_string(), - address: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), + network: "".to_string(), + input: None, payment: "100 TRX".to_string(), - to: "TDQqJsFsStSy5fjG52KuiWW7HhJGAKGJLb".to_string() + receiver: "TDQqJsFsStSy5fjG52KuiWW7HhJGAKGJLb".to_string(), + sender: "TY2uroBeZ5trA9QT96aEWj32XLkAAhQ9R2".to_string(), + fee: "20 dd".to_string(), }; - let res = TronSigner::sign_transaction(input).unwrap(); + let input = TronTxInput{ + raw_data: "0a0208312208b02efdc02638b61e40f083c3a7c92d5a65080112610a2d747970652e676f6f676c65617069732e636f6d2f70726f746f636f6c2e5472616e73666572436f6e747261637412300a1541a1e81654258bf14f63feb2e8d1380075d45b0dac1215410b3e84ec677b3e63c99affcadb91a6b4e086798f186470a0bfbfa7c92d".to_string(), + }; + + let res = TronSigner::sign_transaction(input, &sign_param).unwrap(); assert_eq!("c65b4bde808f7fcfab7b0ef9c1e3946c83311f8ac0a5e95be2d8b6d2400cfe8b5e24dc8f0883132513e422f2aaad8a4ecc14438eae84b2683eefa626e3adffc61c", &res.signature); } } diff --git a/wallet/coin-tron/src/tronapi.rs b/wallet/coin-tron/src/tronapi.rs index a6e3b3a8..5e9a3828 100644 --- a/wallet/coin-tron/src/tronapi.rs +++ b/wallet/coin-tron/src/tronapi.rs @@ -1,46 +1,24 @@ #[derive(Clone, PartialEq, ::prost::Message)] -pub struct TronTxReq { - #[prost(string, tag = "1")] - pub path: std::string::String, +pub struct TronTxInput { #[prost(string, tag = "2")] pub raw_data: std::string::String, - #[prost(string, tag = "3")] - pub address: std::string::String, - #[prost(string, tag = "4")] - pub payment: std::string::String, - #[prost(string, tag = "5")] - pub to: std::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] -pub struct TronTxRes { +pub struct TronTxOutput { #[prost(string, tag = "1")] pub signature: std::string::String, } #[derive(Clone, PartialEq, ::prost::Message)] -pub struct TronAddressReq { - #[prost(string, tag = "1")] - pub path: std::string::String, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TronAddressRes { - #[prost(string, tag = "1")] - pub address: std::string::String, -} -#[derive(Clone, PartialEq, ::prost::Message)] -pub struct TronMessageSignReq { - #[prost(string, tag = "1")] - pub path: std::string::String, +pub struct TronMessageInput { #[prost(string, tag = "2")] pub message: std::string::String, - #[prost(string, tag = "3")] - pub address: std::string::String, #[prost(bool, tag = "4")] pub is_hex: bool, #[prost(bool, tag = "5")] pub is_tron_header: bool, } #[derive(Clone, PartialEq, ::prost::Message)] -pub struct TronMessageSignRes { +pub struct TronMessageOutput { #[prost(string, tag = "1")] pub signature: std::string::String, } From 0be4c8938445ed69b1b1df983955ef3c30c5af9c Mon Sep 17 00:00:00 2001 From: xiemener Date: Thu, 10 Dec 2020 19:18:45 +0800 Subject: [PATCH 4/7] test substrate sign tx on ios --- .../ios/iosExample.xcodeproj/project.pbxproj | 12 ++ examples/ios/iosExample/API.swift | 22 ++ examples/ios/iosExample/BIP44.swift | 1 + examples/ios/iosExample/Proto/common.pb.swift | 197 ++++++++++++++++++ .../ios/iosExample/Proto/substrate.pb.swift | 106 ++++++++++ examples/ios/iosExample/ViewController.swift | 3 +- tools/build-ios-example.sh | 2 + 7 files changed, 342 insertions(+), 1 deletion(-) create mode 100644 examples/ios/iosExample/Proto/common.pb.swift create mode 100644 examples/ios/iosExample/Proto/substrate.pb.swift diff --git a/examples/ios/iosExample.xcodeproj/project.pbxproj b/examples/ios/iosExample.xcodeproj/project.pbxproj index bb467a37..c45072c4 100644 --- a/examples/ios/iosExample.xcodeproj/project.pbxproj +++ b/examples/ios/iosExample.xcodeproj/project.pbxproj @@ -23,6 +23,9 @@ D86ADCF725473BBA008CBEAE /* Data+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86ADCF625473BBA008CBEAE /* Data+Extension.swift */; }; D86ADD1125473F94008CBEAE /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86ADD1025473F94008CBEAE /* String+Extension.swift */; }; D86ADD1325473FE4008CBEAE /* Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = D86ADD1225473FE4008CBEAE /* Hex.swift */; }; + D87733EC2582323F00037FFE /* substrate.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87733EA2582323E00037FFE /* substrate.pb.swift */; }; + D87733ED2582323F00037FFE /* tron.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87733EB2582323F00037FFE /* tron.pb.swift */; }; + D87733EF2582336500037FFE /* common.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87733EE2582336500037FFE /* common.pb.swift */; }; D87BBB912462068900F09CFB /* DeviceManageViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BBB902462068900F09CFB /* DeviceManageViewController.swift */; }; D87BBB9624621F2100F09CFB /* CosmosViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BBB9224621F2100F09CFB /* CosmosViewController.swift */; }; D87BBB9724621F2100F09CFB /* EOSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = D87BBB9324621F2100F09CFB /* EOSViewController.swift */; }; @@ -104,6 +107,9 @@ D86ADCF625473BBA008CBEAE /* Data+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Data+Extension.swift"; sourceTree = ""; }; D86ADD1025473F94008CBEAE /* String+Extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = ""; }; D86ADD1225473FE4008CBEAE /* Hex.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Hex.swift; sourceTree = ""; }; + D87733EA2582323E00037FFE /* substrate.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = substrate.pb.swift; sourceTree = ""; }; + D87733EB2582323F00037FFE /* tron.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = tron.pb.swift; sourceTree = ""; }; + D87733EE2582336500037FFE /* common.pb.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = common.pb.swift; sourceTree = ""; }; D87BBB902462068900F09CFB /* DeviceManageViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DeviceManageViewController.swift; sourceTree = ""; }; D87BBB9224621F2100F09CFB /* CosmosViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CosmosViewController.swift; sourceTree = ""; }; D87BBB9324621F2100F09CFB /* EOSViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EOSViewController.swift; sourceTree = ""; }; @@ -308,6 +314,9 @@ D8B0C355244A43EB009F1B06 /* proto */ = { isa = PBXGroup; children = ( + D87733EE2582336500037FFE /* common.pb.swift */, + D87733EA2582323E00037FFE /* substrate.pb.swift */, + D87733EB2582323F00037FFE /* tron.pb.swift */, D8B0C35A244A463F009F1B06 /* api.pb.swift */, D8B0C35B244A463F009F1B06 /* btc.pb.swift */, D8B0C357244A463F009F1B06 /* cosmos.pb.swift */, @@ -572,6 +581,8 @@ D8B0C362244A463F009F1B06 /* device.pb.swift in Sources */, D87BBBBE2462245B00F09CFB /* CosmosTest.swift in Sources */, D87BBB9824621F2100F09CFB /* BTCViewController.swift in Sources */, + D87733EC2582323F00037FFE /* substrate.pb.swift in Sources */, + D87733EF2582336500037FFE /* common.pb.swift in Sources */, D87BBB9624621F2100F09CFB /* CosmosViewController.swift in Sources */, D8B0C36A244A520F009F1B06 /* API.swift in Sources */, D8B0C35D244A463F009F1B06 /* cosmos.pb.swift in Sources */, @@ -587,6 +598,7 @@ D8B0C35E244A463F009F1B06 /* eos.pb.swift in Sources */, D86ADCF525473B62008CBEAE /* ImkeyError.swift in Sources */, D87BBBC32462250C00F09CFB /* FeatTest.swift in Sources */, + D87733ED2582323F00037FFE /* tron.pb.swift in Sources */, D8B0C360244A463F009F1B06 /* api.pb.swift in Sources */, D87BBB9724621F2100F09CFB /* EOSViewController.swift in Sources */, ); diff --git a/examples/ios/iosExample/API.swift b/examples/ios/iosExample/API.swift index a6db998b..7844f62d 100644 --- a/examples/ios/iosExample/API.swift +++ b/examples/ios/iosExample/API.swift @@ -513,6 +513,28 @@ public class API{ return bindResponse.bindResult } + public class func substrateSignTX(){ + var input = Substrateapi_SubstrateRawTxIn() + input.rawData = "0600ffd7568e5f0a7eda67a82691ff379ac4bba4f9c9b859fe779b5d46363b61ad2db9e56c0703d148e25901007b000000dcd1346701ca8396496e52aa2785b1748deb6db09551b72159dcb3e08991025bde8f69eeb5e065e18c6950ff708d7e551f68dc9bf59a07c52367c0280f805ec7" + var signParam = Common_SignParam() + signParam.chainType = "POLKADOT" + signParam.path = BIP44.polkadot + signParam.payment = "25 DOT" + signParam.receiver = "12pWV6LvG4iAfNpFNTvvkWy3H9H8wtCkjiXupAzo2BCmPViM" + signParam.sender = "147mvrDYhFpZzvFASKBDNVcxoyz8XCVNyyFKSZcpbQxN33TT" + signParam.fee = "15.4000 milli DOT" + signParam.input.value = try! input.serializedData() + + var action = Api_ImkeyAction() + action.param.value = try! signParam.serializedData() + action.method = "sign_tx" + + let paramHex = try! action.serializedData().toHexString() + let res = call_imkey_api(paramHex) +// let hexRes = String(cString:res!).toHexString() +// Log.d(hexRes) + } + public class func btcSignTX(){ Log.d("btc sign ...") var btcInput = Btcapi_BtcTxInput() diff --git a/examples/ios/iosExample/BIP44.swift b/examples/ios/iosExample/BIP44.swift index b0afbfd4..3c55eb40 100644 --- a/examples/ios/iosExample/BIP44.swift +++ b/examples/ios/iosExample/BIP44.swift @@ -18,5 +18,6 @@ public struct BIP44 { public static let eos = "m/44'/194'" public static let EOS_LEDGER = "m/44'/194'/0'/0/0" public static let cosmos = "m/44'/118'/0'/0/0" + public static let polkadot = "m/44'/354'/0'/0'/0'" } diff --git a/examples/ios/iosExample/Proto/common.pb.swift b/examples/ios/iosExample/Proto/common.pb.swift new file mode 100644 index 00000000..21ee779f --- /dev/null +++ b/examples/ios/iosExample/Proto/common.pb.swift @@ -0,0 +1,197 @@ +// DO NOT EDIT. +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: common.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that your are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +public struct Common_SignParam { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var chainType: String { + get {return _storage._chainType} + set {_uniqueStorage()._chainType = newValue} + } + + public var path: String { + get {return _storage._path} + set {_uniqueStorage()._path = newValue} + } + + public var network: String { + get {return _storage._network} + set {_uniqueStorage()._network = newValue} + } + + public var input: SwiftProtobuf.Google_Protobuf_Any { + get {return _storage._input ?? SwiftProtobuf.Google_Protobuf_Any()} + set {_uniqueStorage()._input = newValue} + } + /// Returns true if `input` has been explicitly set. + public var hasInput: Bool {return _storage._input != nil} + /// Clears the value of `input`. Subsequent reads from it will return its default value. + public mutating func clearInput() {_uniqueStorage()._input = nil} + + public var payment: String { + get {return _storage._payment} + set {_uniqueStorage()._payment = newValue} + } + + public var receiver: String { + get {return _storage._receiver} + set {_uniqueStorage()._receiver = newValue} + } + + public var sender: String { + get {return _storage._sender} + set {_uniqueStorage()._sender = newValue} + } + + public var fee: String { + get {return _storage._fee} + set {_uniqueStorage()._fee = newValue} + } + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} + + fileprivate var _storage = _StorageClass.defaultInstance +} + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "common" + +extension Common_SignParam: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SignParam" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "chainType"), + 2: .same(proto: "path"), + 3: .same(proto: "network"), + 4: .same(proto: "input"), + 5: .same(proto: "payment"), + 6: .same(proto: "receiver"), + 7: .same(proto: "sender"), + 8: .same(proto: "fee"), + ] + + fileprivate class _StorageClass { + var _chainType: String = String() + var _path: String = String() + var _network: String = String() + var _input: SwiftProtobuf.Google_Protobuf_Any? = nil + var _payment: String = String() + var _receiver: String = String() + var _sender: String = String() + var _fee: String = String() + + static let defaultInstance = _StorageClass() + + private init() {} + + init(copying source: _StorageClass) { + _chainType = source._chainType + _path = source._path + _network = source._network + _input = source._input + _payment = source._payment + _receiver = source._receiver + _sender = source._sender + _fee = source._fee + } + } + + fileprivate mutating func _uniqueStorage() -> _StorageClass { + if !isKnownUniquelyReferenced(&_storage) { + _storage = _StorageClass(copying: _storage) + } + return _storage + } + + public mutating func decodeMessage(decoder: inout D) throws { + _ = _uniqueStorage() + try withExtendedLifetime(_storage) { (_storage: _StorageClass) in + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularStringField(value: &_storage._chainType) + case 2: try decoder.decodeSingularStringField(value: &_storage._path) + case 3: try decoder.decodeSingularStringField(value: &_storage._network) + case 4: try decoder.decodeSingularMessageField(value: &_storage._input) + case 5: try decoder.decodeSingularStringField(value: &_storage._payment) + case 6: try decoder.decodeSingularStringField(value: &_storage._receiver) + case 7: try decoder.decodeSingularStringField(value: &_storage._sender) + case 8: try decoder.decodeSingularStringField(value: &_storage._fee) + default: break + } + } + } + } + + public func traverse(visitor: inout V) throws { + try withExtendedLifetime(_storage) { (_storage: _StorageClass) in + if !_storage._chainType.isEmpty { + try visitor.visitSingularStringField(value: _storage._chainType, fieldNumber: 1) + } + if !_storage._path.isEmpty { + try visitor.visitSingularStringField(value: _storage._path, fieldNumber: 2) + } + if !_storage._network.isEmpty { + try visitor.visitSingularStringField(value: _storage._network, fieldNumber: 3) + } + if let v = _storage._input { + try visitor.visitSingularMessageField(value: v, fieldNumber: 4) + } + if !_storage._payment.isEmpty { + try visitor.visitSingularStringField(value: _storage._payment, fieldNumber: 5) + } + if !_storage._receiver.isEmpty { + try visitor.visitSingularStringField(value: _storage._receiver, fieldNumber: 6) + } + if !_storage._sender.isEmpty { + try visitor.visitSingularStringField(value: _storage._sender, fieldNumber: 7) + } + if !_storage._fee.isEmpty { + try visitor.visitSingularStringField(value: _storage._fee, fieldNumber: 8) + } + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Common_SignParam, rhs: Common_SignParam) -> Bool { + if lhs._storage !== rhs._storage { + let storagesAreEqual: Bool = withExtendedLifetime((lhs._storage, rhs._storage)) { (_args: (_StorageClass, _StorageClass)) in + let _storage = _args.0 + let rhs_storage = _args.1 + if _storage._chainType != rhs_storage._chainType {return false} + if _storage._path != rhs_storage._path {return false} + if _storage._network != rhs_storage._network {return false} + if _storage._input != rhs_storage._input {return false} + if _storage._payment != rhs_storage._payment {return false} + if _storage._receiver != rhs_storage._receiver {return false} + if _storage._sender != rhs_storage._sender {return false} + if _storage._fee != rhs_storage._fee {return false} + return true + } + if !storagesAreEqual {return false} + } + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/examples/ios/iosExample/Proto/substrate.pb.swift b/examples/ios/iosExample/Proto/substrate.pb.swift new file mode 100644 index 00000000..efd08c6d --- /dev/null +++ b/examples/ios/iosExample/Proto/substrate.pb.swift @@ -0,0 +1,106 @@ +// DO NOT EDIT. +// +// Generated by the Swift generator plugin for the protocol buffer compiler. +// Source: substrate.proto +// +// For information on using the generated types, please see the documentation: +// https://github.com/apple/swift-protobuf/ + +import Foundation +import SwiftProtobuf + +// If the compiler emits an error on this type, it is because this file +// was generated by a version of the `protoc` Swift plug-in that is +// incompatible with the version of SwiftProtobuf to which you are linking. +// Please ensure that your are building against the same version of the API +// that was used to generate this file. +fileprivate struct _GeneratedWithProtocGenSwiftVersion: SwiftProtobuf.ProtobufAPIVersionCheck { + struct _2: SwiftProtobuf.ProtobufAPIVersion_2 {} + typealias Version = _2 +} + +public struct Substrateapi_SubstrateRawTxIn { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var rawData: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +public struct Substrateapi_SubstrateTxOut { + // SwiftProtobuf.Message conformance is added in an extension below. See the + // `Message` and `Message+*Additions` files in the SwiftProtobuf library for + // methods supported on all messages. + + public var signature: String = String() + + public var unknownFields = SwiftProtobuf.UnknownStorage() + + public init() {} +} + +// MARK: - Code below here is support for the SwiftProtobuf runtime. + +fileprivate let _protobuf_package = "substrateapi" + +extension Substrateapi_SubstrateRawTxIn: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SubstrateRawTxIn" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "rawData"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularStringField(value: &self.rawData) + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.rawData.isEmpty { + try visitor.visitSingularStringField(value: self.rawData, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Substrateapi_SubstrateRawTxIn, rhs: Substrateapi_SubstrateRawTxIn) -> Bool { + if lhs.rawData != rhs.rawData {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} + +extension Substrateapi_SubstrateTxOut: SwiftProtobuf.Message, SwiftProtobuf._MessageImplementationBase, SwiftProtobuf._ProtoNameProviding { + public static let protoMessageName: String = _protobuf_package + ".SubstrateTxOut" + public static let _protobuf_nameMap: SwiftProtobuf._NameMap = [ + 1: .same(proto: "signature"), + ] + + public mutating func decodeMessage(decoder: inout D) throws { + while let fieldNumber = try decoder.nextFieldNumber() { + switch fieldNumber { + case 1: try decoder.decodeSingularStringField(value: &self.signature) + default: break + } + } + } + + public func traverse(visitor: inout V) throws { + if !self.signature.isEmpty { + try visitor.visitSingularStringField(value: self.signature, fieldNumber: 1) + } + try unknownFields.traverse(visitor: &visitor) + } + + public static func ==(lhs: Substrateapi_SubstrateTxOut, rhs: Substrateapi_SubstrateTxOut) -> Bool { + if lhs.signature != rhs.signature {return false} + if lhs.unknownFields != rhs.unknownFields {return false} + return true + } +} diff --git a/examples/ios/iosExample/ViewController.swift b/examples/ios/iosExample/ViewController.swift index d7472aba..eb521bd9 100644 --- a/examples/ios/iosExample/ViewController.swift +++ b/examples/ios/iosExample/ViewController.swift @@ -119,12 +119,13 @@ class ViewController: UIViewController,BLEDelegate { print("ramSize:\(ramSize)") print("power:\(power)") print("lifeTime:\(lifeTime)") +// API.substrateSignTX() } @IBAction func bindCheck(_ sender: Any) { let status = API.bindCheck() print("status:\(status)") - let bindResult = API.bindAcquire(bindCode: "ydsgqpkx") + let bindResult = API.bindAcquire(bindCode: "u5qgen7u") print("bind result:\(bindResult)") } diff --git a/tools/build-ios-example.sh b/tools/build-ios-example.sh index 211770d3..cca24001 100755 --- a/tools/build-ios-example.sh +++ b/tools/build-ios-example.sh @@ -27,4 +27,6 @@ protoc --swift_opt=Visibility=Public --swift_out=../../examples/ios/iosExample/p protoc --swift_opt=Visibility=Public --swift_out=../../examples/ios/iosExample/proto eos.proto protoc --swift_opt=Visibility=Public --swift_out=../../examples/ios/iosExample/proto cosmos.proto protoc --swift_opt=Visibility=Public --swift_out=../../examples/ios/iosExample/proto tron.proto +protoc --swift_opt=Visibility=Public --swift_out=../../examples/ios/iosExample/proto substrate.proto +protoc --swift_opt=Visibility=Public --swift_out=../../examples/ios/iosExample/proto common.proto popd \ No newline at end of file From 511c3bbe3c17c20a431aff76450697a60d688f55 Mon Sep 17 00:00:00 2001 From: xiemener Date: Sat, 12 Dec 2020 14:41:20 +0800 Subject: [PATCH 5/7] add tron sign message in api --- api/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/api/src/lib.rs b/api/src/lib.rs index 7ab0dd96..5c1ce601 100644 --- a/api/src/lib.rs +++ b/api/src/lib.rs @@ -210,6 +210,7 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char param.clone().input.unwrap().value.as_slice(), ¶m, ), + "TRON" => tron_signer::sign_message(¶m.clone().input.unwrap().value, ¶m), _ => Err(format_err!( "sign message is not supported the chain {}", param.chain_type From 545efbd655d168ed84ff67937a7f8136bed84bf7 Mon Sep 17 00:00:00 2001 From: xiemener Date: Mon, 14 Dec 2020 15:20:45 +0800 Subject: [PATCH 6/7] fix fee not shown --- wallet/coin-tron/src/signer.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/wallet/coin-tron/src/signer.rs b/wallet/coin-tron/src/signer.rs index a221be6c..5f3d764c 100644 --- a/wallet/coin-tron/src/signer.rs +++ b/wallet/coin-tron/src/signer.rs @@ -96,6 +96,11 @@ impl TronSigner { data_pack.push(to.len() as u8); data_pack.extend(to); + let fee = sign_param.fee.as_bytes(); + data_pack.push(0x09); + data_pack.push(fee.len() as u8); + data_pack.extend(fee); + let key_manager_obj = KEY_MANAGER.lock().unwrap(); let data_pack_sig = secp256k1_sign(&key_manager_obj.pri_key, &data_pack)?; drop(key_manager_obj); From 341f1513bac1b9b4756595d917da8ef4ae39f5ad Mon Sep 17 00:00:00 2001 From: xiemener Date: Fri, 18 Dec 2020 00:55:08 +0800 Subject: [PATCH 7/7] add sender address verification --- wallet/coin-substrate/src/transaction.rs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/wallet/coin-substrate/src/transaction.rs b/wallet/coin-substrate/src/transaction.rs index 2835d7c5..812a889c 100644 --- a/wallet/coin-substrate/src/transaction.rs +++ b/wallet/coin-substrate/src/transaction.rs @@ -1,4 +1,4 @@ -use crate::address::AddressType; +use crate::address::{AddressType, SubstrateAddress}; use crate::substrateapi::{SubstrateRawTxIn, SubstrateTxOut}; use crate::{Result, PAYLOAD_HASH_THRESHOLD, SIGNATURE_TYPE_ED25519}; use common::apdu::{Apdu, ApduCheck, Ed25519Apdu}; @@ -38,6 +38,16 @@ impl Transaction { let select_result = send_apdu(select_apdu)?; ApduCheck::check_response(&select_result)?; + let address_type = match sign_param.chain_type.as_str() { + "POLKADOT" => AddressType::Polkadot, + "KUSAMA" => AddressType::Kusama, + _ => panic!("address type not support"), + }; + let addr = SubstrateAddress::get_address(&sign_param.path, &address_type)?; + if &sign_param.sender != &addr { + return Err(CoinError::ImkeyAddressMismatchWithPath.into()); + } + let raw_data_bytes = if tx.raw_data.starts_with("0x") { tx.raw_data[2..].to_string() } else { @@ -127,7 +137,7 @@ mod test { input: None, payment: "25 DOT".to_string(), receiver: "12pWV6LvG4iAfNpFNTvvkWy3H9H8wtCkjiXupAzo2BCmPViM".to_string(), - sender: "147mvrDYhFpZzvFASKBDNVcxoyz8XCVNyyFKSZcpbQxN33TT".to_string(), + sender: "16NhUkUTkYsYRjMD22Sop2DF8MAXUsjPcYtgHF3t1ccmohx1".to_string(), fee: "15.4000 milli DOT".to_string(), }; @@ -159,7 +169,7 @@ mod test { input: None, payment: "25 DOT".to_string(), receiver: "12pWV6LvG4iAfNpFNTvvkWy3H9H8wtCkjiXupAzo2BCmPViM".to_string(), - sender: "147mvrDYhFpZzvFASKBDNVcxoyz8XCVNyyFKSZcpbQxN33TT".to_string(), + sender: "Fde6T2hDvbvuQrRizcjPoQNZTxuVSbTp78zwFcxzUb86xXSZ".to_string(), fee: "15.4000 milli DOT".to_string(), };