Skip to content

Commit

Permalink
feat: imKey support subaddress feature
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaoguang1010 committed Jan 9, 2024
1 parent 9897492 commit 629feb4
Show file tree
Hide file tree
Showing 29 changed files with 1,462 additions and 258 deletions.
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions imkey-core/ikc-common/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ aes-soft = "=0.6.4"
block-modes = "=0.7.0"
parking_lot = "=0.12.1"
bitcoin = "=0.29.2"
byteorder = "=1.4.3"
49 changes: 48 additions & 1 deletion imkey-core/ikc-common/src/path.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
use crate::error::CommonError;
use crate::Result;
use regex::Regex;
use std::str::FromStr;

pub fn check_path_validity(path: &str) -> Result<()> {
//check depth and length
let strings: Vec<&str> = path.split("/").collect();
let depth = strings.len();
if depth < 2 || depth > 10 || path.len() > 100 {
if !(2..=10).contains(&depth) || path.len() > 100 {
return Err(CommonError::ImkeyPathIllegal.into());
}
//regx check
Expand All @@ -17,6 +18,50 @@ pub fn check_path_validity(path: &str) -> Result<()> {
Ok(())
}

pub fn check_path_max_five_depth(path: &str) -> Result<()> {
//check depth and length
let strings: Vec<&str> = path.split("/").collect();
let depth = strings.len();
if !(2..=6).contains(&depth) || path.len() > 100 {
return Err(CommonError::ImkeyPathIllegal.into());
}
//regx check
let re = Regex::new(r"^m/[0-9'/]+$").unwrap();
if !re.is_match(path) {
return Err(CommonError::ImkeyPathIllegal.into());
}
Ok(())
}

pub fn get_account_path(path: &str) -> Result<String> {
// example: m/44'/60'/0'/0/0
let _ = bitcoin::util::bip32::DerivationPath::from_str(path)?;
let mut children: Vec<&str> = path.split('/').collect();

ensure!(children.len() >= 4, format!("{} path is too short", path));

while children.len() > 4 {
children.remove(children.len() - 1);
}
Ok(children.join("/"))
}

/**
get parent public key path
*/
pub fn get_parent_path(path: &str) -> Result<&str> {
if path.is_empty() {
return Err(CommonError::ImkeyPathIllegal.into());
}

let mut end_flg = path.rfind("/").unwrap();
if path.ends_with("/") {
let path = &path[..path.len() - 1];
end_flg = path.rfind("/").unwrap();
}
Ok(&path[..end_flg])
}

#[cfg(test)]
mod test {
use crate::path::check_path_validity;
Expand All @@ -25,5 +70,7 @@ mod test {
fn check_path_validity_test() {
assert!(check_path_validity("m/44'/0'/0'").is_ok());
assert!(check_path_validity("m/44a'/0'/0'").is_err());
assert!(check_path_validity("m/44'/0'/0'/0'/0'").is_ok());
assert!(check_path_validity("m/44'/0'/0'/0'/0'/0'").is_err());
}
}
45 changes: 36 additions & 9 deletions imkey-core/ikc-common/src/utility.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
use crate::Result;
use bitcoin::util::base58;
use bitcoin::util::bip32::ExtendedPubKey;
use byteorder::{BigEndian, ByteOrder};
use num_bigint::BigInt;
use num_integer::Integer;
use num_traits::{FromPrimitive, Num, Zero};
Expand Down Expand Up @@ -118,17 +121,41 @@ pub fn retrieve_recid(msg: &[u8], sign_compact: &[u8], pubkey: &Vec<u8>) -> Resu
Ok(rec_id)
}

pub fn get_account_path(path: &str) -> Result<String> {
// example: m/44'/60'/0'/0/0
let _ = bitcoin::util::bip32::DerivationPath::from_str(path)?;
let mut children: Vec<&str> = path.split('/').collect();
pub fn to_ss58check_with_version(extended_key: ExtendedPubKey, version: &[u8]) -> String {
let mut ret = [0; 78];
// let extended_key = self.0;
ret[0..4].copy_from_slice(version);
ret[4] = extended_key.depth;
ret[5..9].copy_from_slice(&extended_key.parent_fingerprint[..]);

ensure!(children.len() >= 4, format!("{} path is too short", path));
BigEndian::write_u32(&mut ret[9..13], u32::from(extended_key.child_number));

while children.len() > 4 {
children.remove(children.len() - 1);
}
Ok(children.join("/"))
ret[13..45].copy_from_slice(&extended_key.chain_code[..]);
ret[45..78].copy_from_slice(&extended_key.public_key.serialize()[..]);
base58::check_encode_slice(&ret[..])
}

pub fn get_ext_version(network: &str, derivation_path: &str) -> Result<Vec<u8>> {
let ret = if derivation_path.starts_with("m/49'") {
if network == "MAINNET" {
hex_to_bytes("049d7cb2")?
} else {
hex_to_bytes("044a5262")?
}
} else if derivation_path.starts_with("m/84'") {
if network == "MAINNET" {
hex_to_bytes("04b24746")?
} else {
hex_to_bytes("045f1cf6")?
}
} else {
if network == "MAINNET" {
hex_to_bytes("0488b21e").unwrap()
} else {
hex_to_bytes("043587cf").unwrap()
}
};
Ok(ret)
}

#[cfg(test)]
Expand Down
2 changes: 1 addition & 1 deletion imkey-core/ikc-device/src/device_binding.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "7FVRAJJ7";

#[cfg(test)]
mod test {
Expand Down
2 changes: 1 addition & 1 deletion imkey-core/ikc-proto/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ extern crate prost_build;
fn main() {
// tcx-api
env::set_var("OUT_DIR", "../ikc/src");
prost_build::compile_protos(&["src/ikc.proto"], &["src/"]).unwrap();
prost_build::compile_protos(&["src/api.proto"], &["src/"]).unwrap();

// common
env::set_var("OUT_DIR", "../ikc-common/src");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
syntax = "proto3";
package ikc;
package api;

import "google/protobuf/any.proto";

Expand Down Expand Up @@ -100,4 +100,31 @@ message BtcForkWallet {
string chainType = 2;
string address = 3;
string encXPub = 4;
}

message DeriveAccountsParam {
message Derivation {
string chainType = 1;
string path = 2;
string network = 3;
string segWit = 4;
string chainId = 5;
string curve = 6;
string bech32Prefix = 7;
}
repeated Derivation derivations= 3;
}

message AccountResponse {
string chainType = 1;
string address = 2;
string path = 3;
string curve = 4;
string publicKey = 5;
string extendedPublicKey = 6;
string encryptedExtendedPublicKey = 7;
}

message DeriveAccountsResult {
repeated AccountResponse accounts = 1;
}
11 changes: 11 additions & 0 deletions imkey-core/ikc-wallet/coin-bitcoin/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,17 @@ impl BtcAddress {
Ok(Address::p2shwpkh(&pub_key_obj, network)?.to_string())
}

pub fn get_pub_key(path: &str) -> Result<String> {
//path check
check_path_validity(path)?;

//get xpub
let xpub_data = get_xpub_data(path, true)?;
let pub_key = &xpub_data[..130];

Ok(pub_key.to_string())
}

/**
get parent public key path
*/
Expand Down
14 changes: 14 additions & 0 deletions imkey-core/ikc-wallet/coin-btc-fork/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use ikc_common::error::{CoinError, CommonError};
use ikc_common::path::check_path_validity;

use bech32::{u5, ToBase32, Variant};
use ikc_common::utility::uncompress_pubkey_2_compress;
use std::fmt::{Display, Formatter};
use std::str::FromStr;

Expand Down Expand Up @@ -172,6 +173,19 @@ impl BtcForkAddress {
addr.network.network == coin.network
}
}

pub fn get_pub_key(path: &str) -> Result<String> {
check_path_validity(path)?;

let xpub_data = get_xpub_data(path, true)?;
let pub_key = uncompress_pubkey_2_compress(&xpub_data[..130]);

if pub_key.starts_with("0x") {
Ok(pub_key)
} else {
Ok(format!("0x{}", pub_key))
}
}
}

impl FromStr for BtcForkAddress {
Expand Down
2 changes: 1 addition & 1 deletion imkey-core/ikc-wallet/coin-ckb/src/address.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl CkbAddress {
}

pub fn get_public_key(path: &str) -> Result<String> {
check_path_validity(path).expect("check path error");
check_path_validity(path)?;

let select_apdu = Apdu::select_applet(NERVOS_AID);
let select_response = send_apdu(select_apdu)?;
Expand Down
67 changes: 58 additions & 9 deletions imkey-core/ikc-wallet/coin-cosmos/src/address.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
use crate::Result;
use bech32::{encode, ToBase32, Variant};
use bitcoin::util::bip32::{ChainCode, ChildNumber, DerivationPath, ExtendedPubKey, Fingerprint};
use bitcoin::Network;
use bitcoin_hashes::hex::{FromHex, ToHex};
use bitcoin_hashes::{hash160, Hash};
use hex;
use ikc_common::apdu::{ApduCheck, CoinCommonApdu, CosmosApdu};
use ikc_common::error::CoinError;
use ikc_common::path;
use ikc_common::path::{check_path_validity, get_parent_path};
use ikc_common::utility;
use ikc_device::device_binding::KEY_MANAGER;
use ikc_transport::message;
use secp256k1::PublicKey;
use std::str::FromStr;

#[derive(Debug)]
pub struct CosmosAddress {}
Expand Down Expand Up @@ -40,17 +45,13 @@ impl CosmosAddress {
return Err(CoinError::ImkeySignatureVerifyFail.into());
}

let uncomprs_pubkey: String = res_msg_pubkey
.chars()
.take(res_msg_pubkey.len() - 4)
.collect();
let comprs_pubkey = utility::uncompress_pubkey_2_compress(&uncomprs_pubkey);

Ok(comprs_pubkey)
Ok(sign_source_val.to_string())
}

pub fn get_address(path: &str) -> Result<String> {
let comprs_pubkey = CosmosAddress::get_pub_key(path).unwrap();
// let comprs_pubkey = CosmosAddress::get_pub_key(path)?;
let comprs_pubkey =
utility::uncompress_pubkey_2_compress(&CosmosAddress::get_pub_key(path)?);
//hash160
let pub_key_bytes = hex::decode(comprs_pubkey).unwrap();
let pub_key_hash = hash160::Hash::hash(&pub_key_bytes).to_hex();
Expand All @@ -66,6 +67,54 @@ impl CosmosAddress {
ApduCheck::check_response(&res_reg)?;
Ok(address)
}

pub fn get_xpub(path: &str) -> Result<String> {
//path check
check_path_validity(path)?;

//get xpub data
let xpub_data = Self::get_pub_key(path)?;
let xpub_data = &xpub_data[..194];

//get public key and chain code
let pub_key = &xpub_data[..130];
let sub_chain_code = &xpub_data[130..];
let pub_key_obj = PublicKey::from_str(pub_key)?;

//build parent public key obj
let parent_xpub_data = Self::get_pub_key(get_parent_path(path)?)?;
let parent_xpub_data = &parent_xpub_data[..194];
let parent_pub_key = &parent_xpub_data[..130];
let parent_chain_code = &parent_xpub_data[130..];
let parent_pub_key_obj = PublicKey::from_str(parent_pub_key)?;

//get parent public key fingerprint
let parent_chain_code = ChainCode::from(hex::decode(parent_chain_code)?.as_slice());
let parent_ext_pub_key = ExtendedPubKey {
network: Network::Bitcoin,
depth: 0 as u8,
parent_fingerprint: Fingerprint::default(),
child_number: ChildNumber::from_normal_idx(0).unwrap(),
public_key: parent_pub_key_obj,
chain_code: parent_chain_code,
};
let fingerprint_obj = parent_ext_pub_key.fingerprint();

//build extend public key obj
let sub_chain_code_obj = ChainCode::from(hex::decode(sub_chain_code)?.as_slice());

let chain_number_vec: Vec<ChildNumber> = DerivationPath::from_str(path)?.into();
let extend_public_key = ExtendedPubKey {
network: Network::Bitcoin,
depth: chain_number_vec.len() as u8,
parent_fingerprint: fingerprint_obj,
child_number: *chain_number_vec.get(chain_number_vec.len() - 1).unwrap(),
public_key: pub_key_obj,
chain_code: sub_chain_code_obj,
};
//get and return xpub
Ok(extend_public_key.to_string())
}
}

#[cfg(test)]
Expand All @@ -82,7 +131,7 @@ mod tests {
let comprs_pubkey = CosmosAddress::get_pub_key(constants::COSMOS_PATH).unwrap();
assert_eq!(
&comprs_pubkey,
"0232C1EF21D73C19531B0AA4E863CF397C2B982B2F958F60CDB62969824C096D65"
"0432C1EF21D73C19531B0AA4E863CF397C2B982B2F958F60CDB62969824C096D658AEDE012F4A4B2E3A893B71A787617FEB04F19D2E3BAC5CEE989AA55E8057458CCAAB803B2556DC264D2EE7836AC20B3E2FADB725DA9167F87BD10013D9E48F3"
);
}

Expand Down
Loading

0 comments on commit 629feb4

Please sign in to comment.