Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ur of BTC #81

Merged
merged 5 commits into from
Mar 18, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions include/URRegistryFFI/lib_ur_registry_ffi.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ const char* parse_hd_path(struct ExternError*, const char* hd_path);
// BTC
const char* generate_crypto_psbt(struct ExternError*, const char* psbt_hex);
const char* parse_crypto_psbt(struct ExternError*, const char* ur_type, const char* cbor_hex);
const char* generate_btc_sign_request(struct ExternError*, const char* request_id, const char* sign_data, const int data_type, const char* accounts, const char* origin);
const char* parse_btc_signature(struct ExternError*, const char* ur_type, const char* cbor_hex);

// ETH
const char* generate_eth_sign_request(struct ExternError*, const char* request_id, const char* sign_data, const int data_type, const int chain_id, const char* path, const char* xfp, const char* address, const char* origin);
Expand Down
161 changes: 161 additions & 0 deletions libs/ur-registry-ffi/src/bitcoin/btc_sign_request.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
use hex;
use serde::Deserialize;
use serde_json::json;
use ur_registry::bitcoin::btc_sign_request::{BtcSignRequest, DataType};
use ur_registry::crypto_key_path::CryptoKeyPath;
use uuid::Uuid;

use crate::export;
use crate::util_internal::string_helper::remove_prefix_0x;

#[derive(Deserialize)]
struct Account {
path: String,
xfp: String,
address: Option<String>,
}

export! {
@Java_com_keystone_sdk_KeystoneNativeSDK_generateBTCSignRequest
fn generate_btc_sign_request(
request_id: &str,
sign_data: &str,
data_type: u32,
accounts: &str,
origin: &str
) -> String {
let request_id = match Uuid::parse_str(request_id) {
Ok(v) => v,
Err(_) => return json!({"error": "uuid is invalid"}).to_string(),
}.as_bytes().to_vec();

let mut is_accounts_err = false;
let mut addresses: Vec<String> = vec![];
let derivation_paths: Vec<CryptoKeyPath> = match serde_json::from_str::<Vec<Account>>(accounts) {
Ok(v) => v,
Err(_) => return json!({"error": "accounts is invalid"}).to_string(),
}.iter().map(|account| {
addresses.push(if account.address.is_none() { "".to_string() } else { account.address.clone().unwrap() });
let xfp = match hex::decode(account.xfp.clone()) {
Ok(v) => Some(v),
Err(_) => {
is_accounts_err = true;
None
},
};
if xfp.is_some() && xfp.as_ref().unwrap().len() != 4 {
is_accounts_err = true;
return None
}
match CryptoKeyPath::from_path(account.path.to_string(), xfp.map(|v| v.as_slice().try_into().ok()).flatten()) {
Ok(v) => Some(v),
Err(_) => {
is_accounts_err = true;
None
},
}
}).filter_map(|x| x).collect();
if is_accounts_err || derivation_paths.len() == 0 {
return json!({"error": "accounts is invalid"}).to_string();
}

let data_type = match DataType::from_u32(data_type) {
Ok(v) => v,
Err(_) => return json!({"error": "data type is invalid"}).to_string(),
};
let sign_date_bytes = match hex::decode(remove_prefix_0x(sign_data)) {
Ok(v) => v,
Err(_) => return json!({"error": "sign data is invalid"}).to_string(),
};

let origin = if origin.len() == 0 { None } else { Some(origin.to_string()) };

let cbor_bytes: Vec<u8> = match BtcSignRequest::new(
request_id,
sign_date_bytes,
data_type,
derivation_paths,
Some(addresses),
origin
).try_into() {
Ok(v) => v,
Err(_) => return json!({"error": "sign data is invalid"}).to_string(),
};
let cbor_hex = hex::encode(cbor_bytes);
let ur_type = "btc-sign-request";
let ur = json!({
"type": ur_type,
"cbor": cbor_hex,
});
ur.to_string()
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_generate_btc_sign_request() {
let request_id = "7AFD5E09-9267-43FB-A02E-08C4A09417EC";
let sign_data = "48656c6c6f2063727970746f20776f726c6421";
let data_type = 1;
let accounts = r#"
[
{
"path": "m/44'/0'/0'/0/0",
"xfp": "f23f9fd2",
"address": "1X5vtf4FeK8e8nvhVuvBg7Khkez7Sp3bd"
}
]
"#;
let origin = "BTC Wallet";

let expect_result = "{\"cbor\":\"a601d825507afd5e09926743fba02e08c4a09417ec025348656c6c6f2063727970746f20776f726c642103010481d90130a2018a182cf500f500f500f400f4021af23f9fd2058178213158357674663446654b3865386e76685675764267374b686b657a375370336264066a4254432057616c6c6574\",\"type\":\"btc-sign-request\"}";

assert_eq!(
expect_result,
generate_btc_sign_request(request_id, sign_data, data_type, accounts, origin)
);
}

#[test]
fn test_generate_btc_sign_request_path_error() {
let request_id = "7AFD5E09-9267-43FB-A02E-08C4A09417EC";
let sign_data = "48656c6c6f2063727970746f20776f726c6421";
let data_type = 1;
let accounts = "[]";
let origin = "BTC Wallet";

let expect_result = "{\"error\":\"accounts is invalid\"}";

assert_eq!(
expect_result,
generate_btc_sign_request(request_id, sign_data, data_type, accounts, origin)
);
}

#[test]
fn test_generate_btc_sign_request_err_sign_data() {
let request_id = "7AFD5E09-9267-43FB-A02E-08C4A09417EC";
let sign_data = "123412341";
let data_type = 1;
let accounts = r#"
[
{
"path": "m/44'/0'/0'/0/0",
"xfp": "f23f9fd2",
"address": "4c2a59190413dff36aba8e6ac130c7a691cfb79f"
}
]
"#;
let origin = "BTC Wallet";

let expect_result = "{\"error\":\"sign data is invalid\"}";

assert_eq!(
expect_result,
generate_btc_sign_request(request_id, sign_data, data_type, accounts, origin)
);
}
}
72 changes: 72 additions & 0 deletions libs/ur-registry-ffi/src/bitcoin/btc_signature.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
use anyhow::Error;
use hex;
use serde_json::json;
use ur_registry::{bitcoin::btc_signature::BtcSignature, registry_types::BTC_SIGNATURE};
use uuid::Uuid;

use crate::export;

export! {
@Java_com_keystone_sdk_KeystoneNativeSDK_parseBTCSignature
fn parse_btc_signature(ur_type: &str, cbor_hex: &str) -> String {
if BTC_SIGNATURE.get_type() != ur_type {
return json!({"error": "type not match"}).to_string();
}

let parse_signature = || -> Result<(String, String, String), Error> {
let cbor = hex::decode(cbor_hex.to_string())?;
let sig = BtcSignature::try_from(cbor)?;
let uuid_hex = hex::encode(sig.get_request_id());
let request_id = Uuid::parse_str(&uuid_hex)?.to_string();
let signature = hex::encode(sig.get_signature());
let public_key = hex::encode(sig.get_public_key());
Ok((request_id, signature, public_key))
};
match parse_signature() {
Ok((request_id, signature, public_key)) => json!({
"request_id": request_id,
"signature": signature,
"public_key": public_key,
}).to_string(),
Err(_) => json!({"error": "signature is invalid"}).to_string(),
}
}
}

#[cfg(test)]
mod tests {

use super::*;
#[test]
fn test_parse_btc_signature() {
let signature_cbor = "A301D825509B1DEB4D3B7D4BAD9BDD2B0D7B3DCB6D025840F4B79835417490958C72492723409289B444F3AF18274BA484A9EEACA9E760520E453776E5975DF058B537476932A45239685F694FC6362FE5AF6BA714DA6505035820AEB28ECACE5C664C080E71B9EFD3D071B3DAC119A26F4E830DD6BD06712ED93F";
let expect_result = "{\"public_key\":\"aeb28ecace5c664c080e71b9efd3d071b3dac119a26f4e830dd6bd06712ed93f\",\"request_id\":\"9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d\",\"signature\":\"f4b79835417490958c72492723409289b444f3af18274ba484a9eeaca9e760520e453776e5975df058b537476932a45239685f694fc6362fe5af6ba714da6505\"}";

assert_eq!(
expect_result,
parse_btc_signature("btc-signature", signature_cbor)
);
}

#[test]
fn test_parse_btc_signature_type_error() {
let signature_cbor = "A301D825509B1DEB4D3B7D4BAD9BDD2B0D7B3DCB6D025840B93921DB17F2F1D50BDA37B510F543151DF222E80946FEFBACFADFB2D4A79FDA4FACF0AE5B41D71EA3A7EBEA6AA88DE9577A788AEAB195B99B6A633C20E055030358207BAC671050FCBA0DD54F3930601C42AD36CC11BC0589ED8D3CEF3EFF1C49EF6E";
let expect_result = "{\"error\":\"type not match\"}";

assert_eq!(
expect_result,
parse_btc_signature("eth-signature", signature_cbor)
);
}

#[test]
fn test_parse_btc_signature_error() {
let signature_cbor = "a201";
let expect_result = "{\"error\":\"signature is invalid\"}";

assert_eq!(
expect_result,
parse_btc_signature("btc-signature", signature_cbor)
);
}
}
2 changes: 2 additions & 0 deletions libs/ur-registry-ffi/src/bitcoin/mod.rs
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
pub mod btc_sign_request;
pub mod btc_signature;
pub mod crypto_psbt;
Loading
Loading