From c5d3006c46ae167a99e11cf5dddbc3a1f17398b9 Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Thu, 7 Sep 2023 15:51:03 +0800 Subject: [PATCH 1/8] feat: add eth batch persona sign api --- imkey-core/ikc-common/Cargo.toml | 1 + imkey-core/ikc-common/src/apdu.rs | 10 ++ imkey-core/ikc-common/src/utility.rs | 9 ++ imkey-core/ikc-device/src/device_binding.rs | 2 +- imkey-core/ikc-proto/src/eth.proto | 9 ++ imkey-core/ikc-transport/src/message.rs | 8 +- .../ikc-wallet/coin-ethereum/src/ethapi.rs | 12 ++ .../coin-ethereum/src/transaction.rs | 145 +++++++++++++++++- 8 files changed, 186 insertions(+), 10 deletions(-) diff --git a/imkey-core/ikc-common/Cargo.toml b/imkey-core/ikc-common/Cargo.toml index 1f9882db..8f931439 100644 --- a/imkey-core/ikc-common/Cargo.toml +++ b/imkey-core/ikc-common/Cargo.toml @@ -32,3 +32,4 @@ aes-soft = "0.6.4" block-modes = "0.7.0" parking_lot = "0.12.1" bitcoin = "0.29.2" +tiny-keccak = { version = "2.0.2", features = ["keccak"] } diff --git a/imkey-core/ikc-common/src/apdu.rs b/imkey-core/ikc-common/src/apdu.rs index 259f433d..41a17826 100644 --- a/imkey-core/ikc-common/src/apdu.rs +++ b/imkey-core/ikc-common/src/apdu.rs @@ -151,6 +151,16 @@ impl EthApdu { pub fn personal_sign(path: &str) -> String { Apdu::sign_digest(0x55, 0x00, 0x00, path) } + + pub fn batch_personal_sign(p1: u8, p2: u8, data: Vec) -> String { + if data.len() as u32 > LC_MAX { + panic!("data to long"); + } + let mut apdu = ApduHeader::new(0x80, 0x57, p1, p2, data.len() as u8).to_array(); + apdu.extend(data.iter()); + apdu.push(0x00); + apdu.to_hex().to_uppercase() + } } pub struct EosApdu(); diff --git a/imkey-core/ikc-common/src/utility.rs b/imkey-core/ikc-common/src/utility.rs index 7c7a248f..50f83236 100644 --- a/imkey-core/ikc-common/src/utility.rs +++ b/imkey-core/ikc-common/src/utility.rs @@ -7,6 +7,7 @@ use ring::digest; use secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; use secp256k1::{Message, PublicKey as PublicKey2, Secp256k1, SecretKey, Signature}; use std::str::FromStr; +use tiny_keccak::Hasher; pub fn hex_to_bytes(value: &str) -> Result> { let ret_data; @@ -129,6 +130,14 @@ pub fn get_account_path(path: &str) -> Result { Ok(children.join("/")) } +pub fn keccak_256_hash(value: &[u8]) -> Vec { + let mut keccak256 = tiny_keccak::Keccak::v256(); + keccak256.update(&value); + let mut hash = [0u8; 256 / 8]; + keccak256.finalize(&mut hash); + hash.to_vec() +} + #[cfg(test)] mod tests { use crate::utility; diff --git a/imkey-core/ikc-device/src/device_binding.rs b/imkey-core/ikc-device/src/device_binding.rs index bd77e879..6560e9a2 100644 --- a/imkey-core/ikc-device/src/device_binding.rs +++ b/imkey-core/ikc-device/src/device_binding.rs @@ -243,7 +243,7 @@ pub fn bind_test() { // pub const TEST_KEY_PATH: &str = "/tmp/"; // pub const TEST_BIND_CODE: &str = "MCYNK5AH"; pub const TEST_KEY_PATH: &str = "/tmp/"; -pub const TEST_BIND_CODE: &str = "DJKP4NUR"; +pub const TEST_BIND_CODE: &str = "B97Q5QB6"; #[cfg(test)] mod test { diff --git a/imkey-core/ikc-proto/src/eth.proto b/imkey-core/ikc-proto/src/eth.proto index 2face9ed..c4c65fb1 100644 --- a/imkey-core/ikc-proto/src/eth.proto +++ b/imkey-core/ikc-proto/src/eth.proto @@ -32,4 +32,13 @@ message EthMessageInput { message EthMessageOutput { string signature = 1; +} + +message EthBatchMessageInput { + repeated string messages = 1; + bool isPersonalSign = 2; +} + +message EthBatchMessageOutput { + repeated string signatures = 1; } \ No newline at end of file diff --git a/imkey-core/ikc-transport/src/message.rs b/imkey-core/ikc-transport/src/message.rs index c5f09023..4ebe87aa 100644 --- a/imkey-core/ikc-transport/src/message.rs +++ b/imkey-core/ikc-transport/src/message.rs @@ -158,15 +158,15 @@ pub fn send_apdu_timeout(apdu: String, timeout: i32) -> Result { #[test] fn test_rwlock() { - let r1 = TEST.read().unwrap(); + let r1 = TEST.read(); println!("test:{}", *r1); - let r2 = TEST.read().unwrap(); + let r2 = TEST.read(); println!("test:{}", *r2); drop(r1); drop(r2); - let mut w = TEST.write().unwrap(); + let mut w = TEST.write(); *w = "haha".to_string(); println!("test:{}", *w); drop(w); @@ -174,7 +174,7 @@ fn test_rwlock() { #[test] fn test_callback() { - let callback = CALLBACK.lock().unwrap(); + let callback = CALLBACK.lock(); let ptr = callback( CString::new("00A4040000".to_owned()).unwrap().into_raw(), 20, diff --git a/imkey-core/ikc-wallet/coin-ethereum/src/ethapi.rs b/imkey-core/ikc-wallet/coin-ethereum/src/ethapi.rs index ebdc1b50..35b781bd 100644 --- a/imkey-core/ikc-wallet/coin-ethereum/src/ethapi.rs +++ b/imkey-core/ikc-wallet/coin-ethereum/src/ethapi.rs @@ -49,3 +49,15 @@ pub struct EthMessageOutput { #[prost(string, tag = "1")] pub signature: std::string::String, } +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EthBatchMessageInput { + #[prost(string, repeated, tag = "1")] + pub messages: ::std::vec::Vec, + #[prost(bool, tag = "2")] + pub is_personal_sign: bool, +} +#[derive(Clone, PartialEq, ::prost::Message)] +pub struct EthBatchMessageOutput { + #[prost(string, repeated, tag = "1")] + pub signatures: ::std::vec::Vec, +} diff --git a/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs b/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs index acf93d30..5074c1c2 100644 --- a/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs +++ b/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs @@ -1,20 +1,21 @@ use crate::address::EthAddress; -use crate::ethapi::{EthMessageInput, EthMessageOutput, EthTxOutput}; +use crate::ethapi::{ + EthBatchMessageInput, EthBatchMessageOutput, EthMessageInput, EthMessageOutput, EthTxOutput, +}; use crate::types::{Action, Signature}; use crate::Result as EthResult; use ethereum_types::{Address, H256, U256}; use ikc_common::apdu::{ApduCheck, CoinCommonApdu, EthApdu}; use ikc_common::error::CoinError; use ikc_common::path::check_path_validity; -use ikc_common::utility::{hex_to_bytes, is_valid_hex, secp256k1_sign}; +use ikc_common::utility::{hex_to_bytes, is_valid_hex, keccak_256_hash, secp256k1_sign}; use ikc_common::{constants, utility, SignParam}; use ikc_device::device_binding::KEY_MANAGER; use ikc_transport::message::{send_apdu, send_apdu_timeout}; use keccak_hash::keccak; use lazy_static::lazy_static; -use rlp::{self, DecoderError, Encodable, Rlp, RlpStream}; -use secp256k1::ecdsa::{RecoverableSignature, RecoveryId}; -use secp256k1::{self, Message as SecpMessage, Signature as SecpSignature}; +use rlp::{self, Encodable, RlpStream}; +use secp256k1::{self, Signature as SecpSignature}; use tiny_keccak::Hasher; lazy_static! { @@ -351,6 +352,108 @@ impl Transaction { Ok(EthMessageOutput { signature }) } + + pub fn batch_message_sign( + input: EthBatchMessageInput, + sign_param: &SignParam, + ) -> EthResult { + check_path_validity(&sign_param.path)?; + + let select_apdu = EthApdu::select_applet(); + let select_result = send_apdu(select_apdu)?; + ApduCheck::check_response(&select_result)?; + + //sender address check + let msg_pubkey = EthApdu::get_xpub(&sign_param.path, false); + let res_msg_pubkey = send_apdu(msg_pubkey)?; + let pubkey_raw = hex_to_bytes(&res_msg_pubkey[..130]).unwrap(); + let address_main = EthAddress::address_from_pubkey(pubkey_raw.clone()).unwrap(); + let address_checksummed = EthAddress::address_checksummed(&address_main); + if &address_checksummed != &sign_param.sender { + return Err(CoinError::ImkeyAddressMismatchWithPath.into()); + } + //path + let path_bytes = sign_param.path.as_bytes(); + let mut tlv_path = vec![]; + tlv_path.push(0x02); + tlv_path.push(path_bytes.len() as u8); + tlv_path.extend(path_bytes); + + //total number + let sign_number = input.messages.len(); + let mut tlv_total_number = vec![]; + tlv_total_number.push(0x04); + tlv_total_number.push(0x02); + tlv_total_number.extend(hex::decode(format!("{:04X}", sign_number))?); + + let mut singatures = vec![]; + for (index, msg) in input.messages.iter().enumerate() { + let message = if is_valid_hex(&msg) { + let value = if msg.to_lowercase().starts_with("0x") { + &msg[2..] + } else { + msg.as_str() + }; + hex::decode(value)? + } else { + msg.as_bytes().to_vec() + }; + + let mut sign_data = vec![]; + if input.is_personal_sign { + sign_data + .extend(format!("Ethereum Signed Message:\n{}", &message.len()).as_bytes()); + } + sign_data.extend(message); + let hash = keccak_256_hash(&sign_data.as_ref()); + + let key_manager_obj = KEY_MANAGER.lock(); + let sdk_prikey_signature = secp256k1_sign(&key_manager_obj.pri_key, &hash)?; + + let mut data: Vec = Vec::new(); + //signature + data.push(0x00); + data.push(sdk_prikey_signature.len() as u8); + data.extend(sdk_prikey_signature.as_slice()); + //hash + data.push(0x01); + data.push(hash.len() as u8); + data.extend(hash.iter()); + //path + data.extend(tlv_path.clone()); + //index + data.push(0x03); + data.push(0x02); + data.extend(hex::decode(format!("{:04X}", index + 1))?); + //total number + data.extend(tlv_total_number.clone()); + + let p1 = if index == 0 { 0x00 } else { 0x80 }; + let p2 = if index == sign_number - 1 { 0x80 } else { 0x00 }; + let sign_apdu = EthApdu::batch_personal_sign(p1, p2, data); + println!("sign_apdu:{}", sign_apdu); + + let sign_response = send_apdu(sign_apdu)?; + ApduCheck::check_response(&sign_response)?; + + let sign_compact = hex::decode(&sign_response[2..130]).unwrap(); + let mut signature_obj = SecpSignature::from_compact(sign_compact.as_slice()).unwrap(); + signature_obj.normalize_s(); + let normalizes_sig_vec = signature_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)); + + singatures.push(signature); + } + Ok(EthBatchMessageOutput { + signatures: singatures, + }) + } } #[derive(Debug, Clone, PartialEq)] @@ -1267,4 +1370,36 @@ mod tests { "0x09fa41c4d6b92482506c8c56f65b217cc3398821caec7695683110997426db01".to_string() ); } + + #[test] + fn test_batch_personal_sign() { + bind_test(); + + let sign_param = SignParam { + chain_type: "ETHEREUM".to_string(), + path: constants::ETH_PATH.to_string(), + network: "".to_string(), + input: None, + payment: "".to_string(), + receiver: "".to_string(), + sender: "0x6031564e7b2F5cc33737807b2E58DaFF870B590b".to_string(), + fee: "".to_string(), + }; + let mut messages = vec![]; + messages.push(hex::encode("Hello imKey".as_bytes())); + messages.push("0x8d61d40bb0761526fe24d84199321d5e9f6542e56c52018c401b963d64ef21678c18563a3eba889229ab078a8a1baed22226913f".to_string()); + let input = EthBatchMessageInput { + is_personal_sign: true, + messages, + }; + let output = Transaction::batch_message_sign(input, &sign_param).unwrap(); + assert_eq!( + output.signatures[0], + "d928f76ad80d63003c189b095078d94ae068dc2f18a5cafd97b3a630d7bc47465bd6f1e74de2e88c05b271e1c5a8b93564d9d8842c207482b20634d68f2d54e51b".to_string() + ); + assert_eq!( + output.signatures[1], + "35a94616ce12ddb79f6d351c2644c0fa2f496bd152b17102a5672359f583373b6dd5d2a60f5d9909cf84e6af7dc40176179c819a7cbd9b199f4c2e868530293f1b".to_string() + ); + } } From fe49d4ca8edad0448fa4f8984aada054ded43acd Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Thu, 7 Sep 2023 17:17:51 +0800 Subject: [PATCH 2/8] feat: code optimization --- .../coin-ethereum/src/transaction.rs | 42 ++++++++++--------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs b/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs index 5074c1c2..b100b1b4 100644 --- a/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs +++ b/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs @@ -373,18 +373,14 @@ impl Transaction { return Err(CoinError::ImkeyAddressMismatchWithPath.into()); } //path - let path_bytes = sign_param.path.as_bytes(); - let mut tlv_path = vec![]; - tlv_path.push(0x02); - tlv_path.push(path_bytes.len() as u8); - tlv_path.extend(path_bytes); + let tlv_path = gen_tlv_data(0x02, sign_param.path.as_bytes())?; //total number let sign_number = input.messages.len(); - let mut tlv_total_number = vec![]; - tlv_total_number.push(0x04); - tlv_total_number.push(0x02); - tlv_total_number.extend(hex::decode(format!("{:04X}", sign_number))?); + let tlv_total_number = gen_tlv_data( + 0x04, + hex::decode(format!("{:04X}", sign_number))?.as_slice(), + )?; let mut singatures = vec![]; for (index, msg) in input.messages.iter().enumerate() { @@ -412,26 +408,22 @@ impl Transaction { let mut data: Vec = Vec::new(); //signature - data.push(0x00); - data.push(sdk_prikey_signature.len() as u8); - data.extend(sdk_prikey_signature.as_slice()); + let tlv_signature = gen_tlv_data(0x00, sdk_prikey_signature.as_slice())?; + data.extend(tlv_signature); //hash - data.push(0x01); - data.push(hash.len() as u8); - data.extend(hash.iter()); + data.extend(gen_tlv_data(0x01, &hash)?); //path data.extend(tlv_path.clone()); //index - data.push(0x03); - data.push(0x02); - data.extend(hex::decode(format!("{:04X}", index + 1))?); + let tlv_index = + gen_tlv_data(0x03, hex::decode(format!("{:04X}", index + 1))?.as_slice())?; + data.extend(tlv_index); //total number data.extend(tlv_total_number.clone()); let p1 = if index == 0 { 0x00 } else { 0x80 }; let p2 = if index == sign_number - 1 { 0x80 } else { 0x00 }; let sign_apdu = EthApdu::batch_personal_sign(p1, p2, data); - println!("sign_apdu:{}", sign_apdu); let sign_response = send_apdu(sign_apdu)?; ApduCheck::check_response(&sign_response)?; @@ -456,6 +448,17 @@ impl Transaction { } } +fn gen_tlv_data(tag: u8, data: &[u8]) -> EthResult> { + if data.len() > 255 { + return Err(CoinError::InvalidParam.into()); + } + let mut tlv_data = vec![]; + tlv_data.push(tag); + tlv_data.push(data.len() as u8); + tlv_data.extend(data); + Ok(tlv_data) +} + #[derive(Debug, Clone, PartialEq)] pub struct UnverifiedTransaction { /// Plain Transaction. @@ -487,7 +490,6 @@ impl UnverifiedTransaction { } let hash = keccak(&rlp_bytes); self.hash = hash; - println!("hash:{}", &hex::encode(&hash)); self } From 415d3230e0bd5dee9907df219005d9d6932f9060 Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Fri, 8 Sep 2023 09:30:04 +0800 Subject: [PATCH 3/8] test: add eth batch sign test case --- imkey-core/ikc-common/src/constants.rs | 2 + imkey-core/ikc-common/src/error.rs | 2 + .../coin-ethereum/src/transaction.rs | 115 ++++++++++++++++-- imkey-core/ikc/src/ethereum_signer.rs | 40 +++++- imkey-core/ikc/src/lib.rs | 17 ++- 5 files changed, 162 insertions(+), 14 deletions(-) diff --git a/imkey-core/ikc-common/src/constants.rs b/imkey-core/ikc-common/src/constants.rs index 436f9c8c..7dd82b41 100644 --- a/imkey-core/ikc-common/src/constants.rs +++ b/imkey-core/ikc-common/src/constants.rs @@ -117,3 +117,5 @@ pub const ETH_TRANSACTION_TYPE_EIP2718: &str = "01"; pub const ETH_TRANSACTION_TYPE_EIP1559: &str = "02"; pub const ETH_MAX_SUPPORT_PAYMENT_LEN: usize = 255; + +pub const ETH_BATCH_SIGN_MAX_MESSAGE_NUMBER: usize = 1000; diff --git a/imkey-core/ikc-common/src/error.rs b/imkey-core/ikc-common/src/error.rs index b686ef0e..ceda3e72 100644 --- a/imkey-core/ikc-common/src/error.rs +++ b/imkey-core/ikc-common/src/error.rs @@ -82,4 +82,6 @@ pub enum CoinError { InvalidVersion, #[fail(display = "invalid addr length")] InvalidAddrLength, + #[fail(display = "imkey_exceeded_message_number")] + ImkeyExceededMessageNumber, } diff --git a/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs b/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs index b100b1b4..aabd271f 100644 --- a/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs +++ b/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs @@ -6,6 +6,7 @@ use crate::types::{Action, Signature}; use crate::Result as EthResult; use ethereum_types::{Address, H256, U256}; use ikc_common::apdu::{ApduCheck, CoinCommonApdu, EthApdu}; +use ikc_common::constants::ETH_BATCH_SIGN_MAX_MESSAGE_NUMBER; use ikc_common::error::CoinError; use ikc_common::path::check_path_validity; use ikc_common::utility::{hex_to_bytes, is_valid_hex, keccak_256_hash, secp256k1_sign}; @@ -357,6 +358,9 @@ impl Transaction { input: EthBatchMessageInput, sign_param: &SignParam, ) -> EthResult { + if input.messages.len() > ETH_BATCH_SIGN_MAX_MESSAGE_NUMBER { + return Err(CoinError::ImkeyExceededMessageNumber.into()); + } check_path_validity(&sign_param.path)?; let select_apdu = EthApdu::select_applet(); @@ -1376,17 +1380,7 @@ mod tests { #[test] fn test_batch_personal_sign() { bind_test(); - - let sign_param = SignParam { - chain_type: "ETHEREUM".to_string(), - path: constants::ETH_PATH.to_string(), - network: "".to_string(), - input: None, - payment: "".to_string(), - receiver: "".to_string(), - sender: "0x6031564e7b2F5cc33737807b2E58DaFF870B590b".to_string(), - fee: "".to_string(), - }; + let sign_param = gen_sign_param(); let mut messages = vec![]; messages.push(hex::encode("Hello imKey".as_bytes())); messages.push("0x8d61d40bb0761526fe24d84199321d5e9f6542e56c52018c401b963d64ef21678c18563a3eba889229ab078a8a1baed22226913f".to_string()); @@ -1404,4 +1398,103 @@ mod tests { "35a94616ce12ddb79f6d351c2644c0fa2f496bd152b17102a5672359f583373b6dd5d2a60f5d9909cf84e6af7dc40176179c819a7cbd9b199f4c2e868530293f1b".to_string() ); } + + #[test] + fn test_batch_message_sign() { + bind_test(); + let sign_param = gen_sign_param(); + let mut messages = vec![]; + messages.push(hex::encode("Hello imKey".as_bytes())); + messages.push("0x8d61d40bb0761526fe24d84199321d5e9f6542e56c52018c401b963d64ef21678c18563a3eba889229ab078a8a1baed22226913f".to_string()); + let input = EthBatchMessageInput { + is_personal_sign: false, + messages, + }; + let output = Transaction::batch_message_sign(input, &sign_param).unwrap(); + assert_eq!( + output.signatures[0], + "57c976d1fa15c7e833fd340bcb3a96974060ed555369d443449ac4429c1933433afa5304d1cfcb6799403f2b97a1e83309b98fae8ad5fade62335664d90e819f1b".to_string() + ); + assert_eq!( + output.signatures[1], + "3d8ba5e7375900476d715b479938e48a2e46e59f8e2e12673adb5e3df78a622050053ae0183f5e555e5db34ff43293de255f384709bd3fe6e00b8239c7f1a3561c".to_string() + ); + } + + #[test] + fn test_batch_personal_sign_bignumber() { + bind_test(); + let sign_param = gen_sign_param(); + let mut messages = vec![]; + for _i in 0..500 { + messages.push(hex::encode("Hello imKey".as_bytes())); + } + let input = EthBatchMessageInput { + is_personal_sign: true, + messages, + }; + let start_time = std::time::SystemTime::now(); + let output = Transaction::batch_message_sign(input, &sign_param); + let end_time = std::time::SystemTime::now(); + let elapsed_time = end_time.duration_since(start_time).unwrap(); + let total_seconds = elapsed_time.as_secs(); + let hours = total_seconds / 3600; + let minutes = (total_seconds % 3600) / 60; + let seconds = total_seconds % 60; + + println!( + "Elapsed time: {} hours, {} minutes, {} seconds", + hours, minutes, seconds + ); + assert!(output.is_ok()); + let output = output.unwrap(); + assert_eq!( + output.signatures[0], + "d928f76ad80d63003c189b095078d94ae068dc2f18a5cafd97b3a630d7bc47465bd6f1e74de2e88c05b271e1c5a8b93564d9d8842c207482b20634d68f2d54e51b".to_string() + ); + assert_eq!(output.signatures.len(), 100); + } + + #[test] + #[should_panic(expected = "imkey_address_mismatch_with_path")] + fn test_batch_message_sign_error_sender() { + let mut sign_param = gen_sign_param(); + sign_param.sender = "1111111111111111111111111111111111111111".to_string(); + let messages = vec![hex::encode("Hello imKey".as_bytes())]; + let input = EthBatchMessageInput { + is_personal_sign: true, + messages, + }; + let output = Transaction::batch_message_sign(input, &sign_param); + assert!(output.is_err()); + } + + #[test] + #[should_panic(expected = "imkey_exceeded_message_number")] + fn test_batch_message_sign_exceeded_message_number() { + let sign_param = gen_sign_param(); + let mut messages = vec![]; + for _i in 0..1001 { + messages.push(hex::encode("Hello imKey".as_bytes())); + } + let input = EthBatchMessageInput { + is_personal_sign: true, + messages, + }; + let output = Transaction::batch_message_sign(input, &sign_param); + assert!(output.is_err()); + } + + fn gen_sign_param() -> SignParam { + SignParam { + chain_type: "ETHEREUM".to_string(), + path: constants::ETH_PATH.to_string(), + network: "".to_string(), + input: None, + payment: "".to_string(), + receiver: "".to_string(), + sender: "0x6031564e7b2F5cc33737807b2E58DaFF870B590b".to_string(), + fee: "".to_string(), + } + } } diff --git a/imkey-core/ikc/src/ethereum_signer.rs b/imkey-core/ikc/src/ethereum_signer.rs index f5df84bf..9a01c00a 100644 --- a/imkey-core/ikc/src/ethereum_signer.rs +++ b/imkey-core/ikc/src/ethereum_signer.rs @@ -1,6 +1,6 @@ use crate::error_handling::Result; use crate::message_handler::encode_message; -use coin_ethereum::ethapi::{EthMessageInput, EthTxInput}; +use coin_ethereum::ethapi::{EthBatchMessageInput, EthMessageInput, EthTxInput}; use coin_ethereum::transaction::{AccessListItem, Transaction}; use coin_ethereum::types::Action; use ethereum_types::{Address, H256, U256, U64}; @@ -118,11 +118,18 @@ pub fn sign_eth_message(data: &[u8], sign_param: &SignParam) -> Result> encode_message(signed) } +pub fn batch_eth_message_sign(data: &[u8], sign_param: &SignParam) -> Result> { + let input: EthBatchMessageInput = + EthBatchMessageInput::decode(data).expect("imkey_illegal_param"); + let signed = Transaction::batch_message_sign(input, sign_param)?; + encode_message(signed) +} + #[cfg(test)] mod tests { use super::*; use crate::ethereum_signer::sign_eth_transaction; - use coin_ethereum::ethapi::{AccessList, EthTxInput, EthTxOutput}; + use coin_ethereum::ethapi::{AccessList, EthBatchMessageOutput, EthTxInput, EthTxOutput}; use ethereum_types::{Address, U256}; use hex; use ikc_common::constants; @@ -481,4 +488,33 @@ mod tests { "0x09fa41c4d6b92482506c8c56f65b217cc3398821caec7695683110997426db01".to_string() ); } + + #[test] + fn test_eth_batch_message_sign() { + bind_test(); + let sign_param = SignParam { + chain_type: "ETHEREUM".to_string(), + path: constants::ETH_PATH.to_string(), + network: "".to_string(), + input: None, + payment: "".to_string(), + receiver: "".to_string(), + sender: "0x6031564e7b2F5cc33737807b2E58DaFF870B590b".to_string(), + fee: "".to_string(), + }; + let mut messages = vec![]; + messages.push(hex::encode("Hello imKey".as_bytes())); + let input = EthBatchMessageInput { + is_personal_sign: true, + messages, + }; + let data = encode_message(input.to_owned()).unwrap(); + let res = batch_eth_message_sign(&data.as_ref(), &sign_param); + let output: EthBatchMessageOutput = + EthBatchMessageOutput::decode(res.unwrap().as_ref()).expect("imkey_illegal_param"); + assert_eq!( + output.signatures[0], + "d928f76ad80d63003c189b095078d94ae068dc2f18a5cafd97b3a630d7bc47465bd6f1e74de2e88c05b271e1c5a8b93564d9d8842c207482b20634d68f2d54e51b".to_string() + ); + } } diff --git a/imkey-core/ikc/src/lib.rs b/imkey-core/ikc/src/lib.rs index 761b6b77..4315eb98 100644 --- a/imkey-core/ikc/src/lib.rs +++ b/imkey-core/ikc/src/lib.rs @@ -253,6 +253,21 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char } }), + "batch_sign_message" => landingpad(|| { + let param: SignParam = SignParam::decode(action.param.unwrap().value.as_slice()) + .expect("unpack sign_message param error"); + match param.chain_type.as_str() { + "ETHEREUM" => ethereum_signer::sign_eth_message( + param.clone().input.unwrap().value.as_slice(), + ¶m, + ), + _ => Err(format_err!( + "batch sign message is not supported the chain {}", + param.chain_type + )), + } + }), + // btc "calc_external_address" => landingpad(|| { let param: ExternalAddressParam = @@ -405,7 +420,7 @@ mod tests { DeviceManage::bind_check(&"../test-data".to_string()).unwrap_or_default(); // DeviceManage::bind_acquire(&"".to_string()).unwrap(); // device::device_manager::app_delete("BCH"); - device::device_manager::app_download("BTC"); + // device::device_manager::app_download("BTC"); let ret_hex = unsafe { _to_str(call_imkey_api(_to_c_char(&"0a077369676e5f747812e6030a10636f6d6d6f6e2e5369676e506172616d12d1030a0b424954434f494e4341534812116d2f3434272f313435272f30272f302f301a074d41494e4e455422b2020a19627463666f726b6170692e427463466f726b5478496e7075741294020a2a71707a36376763776139616738346c6a6d6d6e33753774636a6d39726b63326a66636a6b32717a66637510a08d061aaa010a4061346439666561373337636236633030326337613833666235383531613366373566306163646437626237663137373232633162323465653765306232336461100018c09a0c222a7171687979616a75323270637967783870683035716a6e787978616c686d65736b796371706d67786e302a323736613931343265343237363563353238333832323063373064646634303461363632316262666265663330623138386163320020c6032801322a7171687979616a75323270637967783870683035716a6e787978616c686d65736b796371706d67786e303a044e4f4e452a09302e30303120424348322a71707a36376763776139616738346c6a6d6d6e33753774636a6d39726b63326a66636a6b32717a6663753a2a7171687979616a75323270637967783870683035716a6e787978616c686d65736b796371706d67786e30420e302e303030303034353420424348"))) }; From a7c014367442328a18354955d6ee492cecbc029f Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Fri, 8 Sep 2023 12:31:32 +0800 Subject: [PATCH 4/8] chore: modify version --- VERSION | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/VERSION b/VERSION index 57cf282e..338a5b5d 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.6.5 +2.6.6 From 5e6e0d364868464cab09bf70bd415cd743dd163d Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Fri, 8 Sep 2023 16:03:07 +0800 Subject: [PATCH 5/8] test: modify imKey server url to dev environment --- imkey-core/ikc-common/src/constants.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imkey-core/ikc-common/src/constants.rs b/imkey-core/ikc-common/src/constants.rs index 7dd82b41..8d342523 100644 --- a/imkey-core/ikc-common/src/constants.rs +++ b/imkey-core/ikc-common/src/constants.rs @@ -1,6 +1,6 @@ pub const VERSION: &str = "2.10.3"; -pub const URL: &str = "https://imkey.online:1000/imkey"; -// pub const URL: &str = "https://imkeyserver.com:10444/imkey"; +// pub const URL: &str = "https://imkey.online:1000/imkey"; +pub const URL: &str = "https://imkeyserver.com:10443/imkey"; pub const TSM_ACTION_SE_SECURE_CHECK: &str = "/seSecureCheck"; pub const TSM_ACTION_APP_DOWNLOAD: &str = "/appDownload"; From 46fe33d7d512f7168ef46f9fc063e3c5753c2cfa Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Tue, 12 Sep 2023 11:42:06 +0800 Subject: [PATCH 6/8] fix: fix the problem of eth batch message signature interface calling --- imkey-core/ikc/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imkey-core/ikc/src/lib.rs b/imkey-core/ikc/src/lib.rs index 4315eb98..f05166c2 100644 --- a/imkey-core/ikc/src/lib.rs +++ b/imkey-core/ikc/src/lib.rs @@ -257,7 +257,7 @@ pub unsafe extern "C" fn call_imkey_api(hex_str: *const c_char) -> *const c_char let param: SignParam = SignParam::decode(action.param.unwrap().value.as_slice()) .expect("unpack sign_message param error"); match param.chain_type.as_str() { - "ETHEREUM" => ethereum_signer::sign_eth_message( + "ETHEREUM" => ethereum_signer::batch_eth_message_sign( param.clone().input.unwrap().value.as_slice(), ¶m, ), From 0f4cb3368396f788b12c1795b09a0618f6dd38a2 Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Thu, 14 Sep 2023 18:52:34 +0800 Subject: [PATCH 7/8] feat: eth batch message signing interface code optimization --- imkey-core/ikc-common/src/apdu.rs | 1 + imkey-core/ikc-common/src/constants.rs | 4 +-- imkey-core/ikc-common/src/error.rs | 2 ++ .../coin-ethereum/src/transaction.rs | 31 +++++++------------ 4 files changed, 16 insertions(+), 22 deletions(-) diff --git a/imkey-core/ikc-common/src/apdu.rs b/imkey-core/ikc-common/src/apdu.rs index 41a17826..a3ac4c9e 100644 --- a/imkey-core/ikc-common/src/apdu.rs +++ b/imkey-core/ikc-common/src/apdu.rs @@ -572,6 +572,7 @@ impl ApduCheck { "F080" => Err(ApduError::ImkeyInMenuPage.into()), "F081" => Err(ApduError::ImkeyPinNotVerified.into()), "6F01" => Err(ApduError::ImkeyBluetoothChannelError.into()), + "6944" => Err(ApduError::ImkeySignatureCancelled.into()), _ => Err(format_err!("imkey_command_execute_fail_{}", response_data)), //Err(ApduError::ImkeyCommandExecuteFail.into()) } } diff --git a/imkey-core/ikc-common/src/constants.rs b/imkey-core/ikc-common/src/constants.rs index 8d342523..7dd82b41 100644 --- a/imkey-core/ikc-common/src/constants.rs +++ b/imkey-core/ikc-common/src/constants.rs @@ -1,6 +1,6 @@ pub const VERSION: &str = "2.10.3"; -// pub const URL: &str = "https://imkey.online:1000/imkey"; -pub const URL: &str = "https://imkeyserver.com:10443/imkey"; +pub const URL: &str = "https://imkey.online:1000/imkey"; +// pub const URL: &str = "https://imkeyserver.com:10444/imkey"; pub const TSM_ACTION_SE_SECURE_CHECK: &str = "/seSecureCheck"; pub const TSM_ACTION_APP_DOWNLOAD: &str = "/appDownload"; diff --git a/imkey-core/ikc-common/src/error.rs b/imkey-core/ikc-common/src/error.rs index ceda3e72..5c8a4b74 100644 --- a/imkey-core/ikc-common/src/error.rs +++ b/imkey-core/ikc-common/src/error.rs @@ -34,6 +34,8 @@ pub enum ApduError { ImkeyInMenuPage, #[fail(display = "imkey_pin_not_verified")] ImkeyPinNotVerified, + #[fail(display = "imkey_signature_cancelled")] + ImkeySignatureCancelled, } #[derive(Fail, Debug, PartialOrd, PartialEq)] diff --git a/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs b/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs index aabd271f..fc748327 100644 --- a/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs +++ b/imkey-core/ikc-wallet/coin-ethereum/src/transaction.rs @@ -407,13 +407,7 @@ impl Transaction { sign_data.extend(message); let hash = keccak_256_hash(&sign_data.as_ref()); - let key_manager_obj = KEY_MANAGER.lock(); - let sdk_prikey_signature = secp256k1_sign(&key_manager_obj.pri_key, &hash)?; - let mut data: Vec = Vec::new(); - //signature - let tlv_signature = gen_tlv_data(0x00, sdk_prikey_signature.as_slice())?; - data.extend(tlv_signature); //hash data.extend(gen_tlv_data(0x01, &hash)?); //path @@ -425,9 +419,18 @@ impl Transaction { //total number data.extend(tlv_total_number.clone()); + let key_manager_obj = KEY_MANAGER.lock(); + let sdk_prikey_signature = secp256k1_sign(&key_manager_obj.pri_key, &data)?; + + let mut req_data = vec![]; + //signature + let tlv_signature = gen_tlv_data(0x00, sdk_prikey_signature.as_slice())?; + req_data.extend(tlv_signature); + req_data.extend(data); + let p1 = if index == 0 { 0x00 } else { 0x80 }; let p2 = if index == sign_number - 1 { 0x80 } else { 0x00 }; - let sign_apdu = EthApdu::batch_personal_sign(p1, p2, data); + let sign_apdu = EthApdu::batch_personal_sign(p1, p2, req_data); let sign_response = send_apdu(sign_apdu)?; ApduCheck::check_response(&sign_response)?; @@ -1433,26 +1436,14 @@ mod tests { is_personal_sign: true, messages, }; - let start_time = std::time::SystemTime::now(); let output = Transaction::batch_message_sign(input, &sign_param); - let end_time = std::time::SystemTime::now(); - let elapsed_time = end_time.duration_since(start_time).unwrap(); - let total_seconds = elapsed_time.as_secs(); - let hours = total_seconds / 3600; - let minutes = (total_seconds % 3600) / 60; - let seconds = total_seconds % 60; - - println!( - "Elapsed time: {} hours, {} minutes, {} seconds", - hours, minutes, seconds - ); assert!(output.is_ok()); let output = output.unwrap(); assert_eq!( output.signatures[0], "d928f76ad80d63003c189b095078d94ae068dc2f18a5cafd97b3a630d7bc47465bd6f1e74de2e88c05b271e1c5a8b93564d9d8842c207482b20634d68f2d54e51b".to_string() ); - assert_eq!(output.signatures.len(), 100); + assert_eq!(output.signatures.len(), 500); } #[test] From ea467f5e42368ad4a61a760d11886b92eb60025e Mon Sep 17 00:00:00 2001 From: xiaoguang Date: Mon, 25 Sep 2023 11:11:18 +0800 Subject: [PATCH 8/8] test: switch imkey server to development environment --- imkey-core/ikc-common/src/constants.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/imkey-core/ikc-common/src/constants.rs b/imkey-core/ikc-common/src/constants.rs index 7dd82b41..8d342523 100644 --- a/imkey-core/ikc-common/src/constants.rs +++ b/imkey-core/ikc-common/src/constants.rs @@ -1,6 +1,6 @@ pub const VERSION: &str = "2.10.3"; -pub const URL: &str = "https://imkey.online:1000/imkey"; -// pub const URL: &str = "https://imkeyserver.com:10444/imkey"; +// pub const URL: &str = "https://imkey.online:1000/imkey"; +pub const URL: &str = "https://imkeyserver.com:10443/imkey"; pub const TSM_ACTION_SE_SECURE_CHECK: &str = "/seSecureCheck"; pub const TSM_ACTION_APP_DOWNLOAD: &str = "/appDownload";