From 52c9351cfc11b85cf54e521104283433cd02e775 Mon Sep 17 00:00:00 2001 From: Cao Xi <1060010408@qq.com> Date: Wed, 29 May 2024 11:19:02 +0800 Subject: [PATCH 1/3] feat: add stellar --- libs/ur-registry-ffi/src/lib.rs | 2 + libs/ur-registry-ffi/src/stellar/mod.rs | 2 + .../src/stellar/stellar_sign_request.rs | 127 ++++++++ .../src/stellar/stellar_signature.rs | 74 +++++ libs/ur-registry/src/lib.rs | 1 + libs/ur-registry/src/macros_impl.rs | 3 + libs/ur-registry/src/registry_types.rs | 6 + libs/ur-registry/src/stellar/mod.rs | 2 + .../src/stellar/stellar_sign_request.rs | 290 ++++++++++++++++++ .../src/stellar/stellar_signature.rs | 161 ++++++++++ 10 files changed, 668 insertions(+) create mode 100644 libs/ur-registry-ffi/src/stellar/mod.rs create mode 100644 libs/ur-registry-ffi/src/stellar/stellar_sign_request.rs create mode 100644 libs/ur-registry-ffi/src/stellar/stellar_signature.rs create mode 100644 libs/ur-registry/src/stellar/mod.rs create mode 100644 libs/ur-registry/src/stellar/stellar_sign_request.rs create mode 100644 libs/ur-registry/src/stellar/stellar_signature.rs diff --git a/libs/ur-registry-ffi/src/lib.rs b/libs/ur-registry-ffi/src/lib.rs index ac03637..f8a12cf 100644 --- a/libs/ur-registry-ffi/src/lib.rs +++ b/libs/ur-registry-ffi/src/lib.rs @@ -15,5 +15,7 @@ mod util_internal; pub mod utils; pub mod sui; pub mod ton; +pub mod stellar; + ffi_support::define_string_destructor!(keystone_sdk_destroy_string); diff --git a/libs/ur-registry-ffi/src/stellar/mod.rs b/libs/ur-registry-ffi/src/stellar/mod.rs new file mode 100644 index 0000000..8e85660 --- /dev/null +++ b/libs/ur-registry-ffi/src/stellar/mod.rs @@ -0,0 +1,2 @@ +pub mod stellar_sign_request; +pub mod stellar_signature; diff --git a/libs/ur-registry-ffi/src/stellar/stellar_sign_request.rs b/libs/ur-registry-ffi/src/stellar/stellar_sign_request.rs new file mode 100644 index 0000000..722d3e9 --- /dev/null +++ b/libs/ur-registry-ffi/src/stellar/stellar_sign_request.rs @@ -0,0 +1,127 @@ +use hex; +use serde_json::json; +use ur_registry::crypto_key_path::CryptoKeyPath; +use ur_registry::stellar::stellar_sign_request::{SignType, StellarSignRequest}; +use ur_registry::traits::To; +use uuid::Uuid; + +use crate::export; +use crate::util_internal::string_helper::remove_prefix_0x; + +export! { + @Java_com_keystone_sdk_KeystoneNativeSDK_generateStellarSignRequest + fn generate_stellar_sign_request( + request_id: &str, + sign_data: &str, + path: &str, + xfp: &str, + address: &str, + origin: &str, + sign_type: u32 + ) -> String { + let xfp_bytes = match hex::decode(xfp) { + Ok(v) => v, + Err(_) => return json!({"error": "xfp is invalid"}).to_string(), + }; + let xfp_slice: [u8; 4] = match xfp_bytes.as_slice().try_into() { + Ok(v) => v, + Err(_) => return json!({"error": "length of xfp must be exactly 8"}).to_string(), + }; + let derivation_path = match CryptoKeyPath::from_path(path.to_string(), Some(xfp_slice)) { + Ok(v) => v, + Err(_) => return json!({"error": "path is invalid"}).to_string(), + }; + let sign_type = match SignType::from_u32(sign_type) { + Ok(v) => v, + Err(_) => return json!({"error": "sign_type is invalid"}).to_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 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 address = if address.len() == 0 { None } else { Some(address.as_bytes().to_vec()) }; + let origin = if origin.len() == 0 { None } else { Some(origin.to_string()) }; + + let result = StellarSignRequest::new( + Some(request_id), + sign_date_bytes, + derivation_path, + address, + origin, + sign_type + ); + + let cbor = match result.to_bytes() { + Ok(v) => v, + Err(_) => return json!({"error": "cbor serialization failed"}).to_string(), + }; + let cbor = hex::encode(cbor); + let ur_type = "stellar-sign-request"; + let ur = json!({ + "type": ur_type, + "cbor": cbor, + }); + ur.to_string() + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_generate_stellar_sign_request() { + let request_id = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"; + let sign_data = "01000103c8d842a2f17fd7aab608ce2ea535a6e958dffa20caf669b347b911c4171965530f957620b228bae2b94c82ddd4c093983a67365555b737ec7ddc1117e61c72e0000000000000000000000000000000000000000000000000000000000000000010295cc2f1f39f3604718496ea00676d6a72ec66ad09d926e3ece34f565f18d201020200010c0200000000e1f50500000000"; + let path = "m/44'/501'/0'/0'"; + let xfp = "12121212"; + let address = ""; + let sign_type = 1; + + let expect_result = "{\"cbor\":\"a501d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d02589601000103c8d842a2f17fd7aab608ce2ea535a6e958dffa20caf669b347b911c4171965530f957620b228bae2b94c82ddd4c093983a67365555b737ec7ddc1117e61c72e0000000000000000000000000000000000000000000000000000000000000000010295cc2f1f39f3604718496ea00676d6a72ec66ad09d926e3ece34f565f18d201020200010c0200000000e1f5050000000003d90130a20188182cf51901f5f500f500f5021a121212120568736f6c666c6172650601\",\"type\":\"stellar-sign-request\"}"; + + assert_eq!( + expect_result, + generate_stellar_sign_request(request_id, sign_data, path, xfp, address, sign_type) + ); + } + + #[test] + fn test_generate_stellar_sign_request_path_error() { + let request_id = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"; + let sign_data = "01000103c8d842a2f17fd7aab608ce2ea535a6e958dffa20caf669b347b911c4171965530f957620b228bae2b94c82ddd4c093983a67365555b737ec7ddc1117e61c72e0000000000000000000000000000000000000000000000000000000000000000010295cc2f1f39f3604718496ea00676d6a72ec66ad09d926e3ece34f565f18d201020200010c0200000000e1f50500000000"; + let path = ""; + let xfp = "1212120"; + let address = ""; + let sign_type = 1; + + let err_result_derivation_path = "{\"error\":\"xfp is invalid\"}"; + + assert_eq!( + err_result_derivation_path, + generate_stellar_sign_request(request_id, sign_data, path, xfp, address, sign_type) + ); + } + + #[test] + fn test_generate_stellar_sign_request_err_sign_data() { + let request_id = "9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d"; + let sign_data = "0123h"; + let path = "m/44'/501'/0'/0'"; + let xfp = "12121212"; + let address = ""; + let sign_type = 1; + + let err_result = "{\"error\":\"sign data is invalid\"}"; + + assert_eq!( + err_result, + generate_stellar_sign_request(request_id, sign_data, path, xfp, address, sign_type) + generate_stellar_sign_request(request_id, sign_data, path, xfp, address, sign_type) + ); + } +} diff --git a/libs/ur-registry-ffi/src/stellar/stellar_signature.rs b/libs/ur-registry-ffi/src/stellar/stellar_signature.rs new file mode 100644 index 0000000..4336cd6 --- /dev/null +++ b/libs/ur-registry-ffi/src/stellar/stellar_signature.rs @@ -0,0 +1,74 @@ +use anyhow::format_err; +use anyhow::Error; +use hex; +use serde_json::json; +use ur_registry::registry_types::STELLAR_SIGNATURE; +use ur_registry::stellar::stellar_signature::StellarSignature; +use ur_registry::traits::From; +use uuid::Uuid; + +use crate::export; + +export! { + @Java_com_keystone_sdk_KeystoneNativeSDK_parseStellarSignature + fn parse_stellar_signature(ur_type: &str, cbor_hex: &str) -> String { + if STELLAR_SIGNATURE.get_type() != ur_type { + return json!({"error": "type not match"}).to_string(); + } + + let parse_signature = || -> Result<(String, String), Error> { + let cbor = hex::decode(cbor_hex.to_string())?; + let stellar_signature = StellarSignature::from_cbor(cbor).map_err(|_| format_err!(""))?; + let uuid = stellar_signature.get_request_id().ok_or(format_err!(""))?; + let uuid_hex = hex::encode(uuid); + let request_id = Uuid::parse_str(&uuid_hex)?.to_string(); + let signature = hex::encode(stellar_signature.get_signature()); + Ok((request_id, signature)) + }; + match parse_signature() { + Ok((request_id, signature)) => json!({ + "request_id": request_id, + "signature": signature, + }).to_string(), + Err(_) => json!({"error": "signature is invalid"}).to_string(), + } + } +} + +#[cfg(test)] +mod tests { + + use super::*; + #[test] + fn test_parse_stellar_signature() { + let stellar_signature_cbor = "a201d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d025840d4f0a7bcd95bba1fbb1051885054730e3f47064288575aacc102fbbf6a9a14daa066991e360d3e3406c20c00a40973eff37c7d641e5b351ec4a99bfe86f335f7"; + let expect_result = "{\"request_id\":\"9b1deb4d-3b7d-4bad-9bdd-2b0d7b3dcb6d\",\"signature\":\"d4f0a7bcd95bba1fbb1051885054730e3f47064288575aacc102fbbf6a9a14daa066991e360d3e3406c20c00a40973eff37c7d641e5b351ec4a99bfe86f335f7\"}"; + + assert_eq!( + expect_result, + parse_stellar_signature("stellar-signature", stellar_signature_cbor) + ); + } + + #[test] + fn test_parse_stellar_signature_type_error() { + let stellar_signature_cbor = "a301d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d025841d4f0a7bcd95bba1fbb1051885054730e3f47064288575aacc102fbbf6a9a14daa066991e360d3e3406c20c00a40973eff37c7d641e5b351ec4a99bfe86f335f71303686b657973746f6e65"; + let expect_result = "{\"error\":\"type not match\"}"; + + assert_eq!( + expect_result, + parse_stellar_signature("eth-signature", stellar_signature_cbor) + ); + } + + #[test] + fn test_parse_stellar_signature_error() { + let stellar_signature_cbor = "a201"; + let expect_result = "{\"error\":\"signature is invalid\"}"; + + assert_eq!( + expect_result, + parse_stellar_signature("stellar-signature", stellar_signature_cbor) + ); + } +} diff --git a/libs/ur-registry/src/lib.rs b/libs/ur-registry/src/lib.rs index 9c2cc8e..8e6c946 100644 --- a/libs/ur-registry/src/lib.rs +++ b/libs/ur-registry/src/lib.rs @@ -30,6 +30,7 @@ pub mod pb; pub mod registry_types; pub mod script_expression; pub mod solana; +pub mod stellar; pub mod sui; pub mod ton; pub mod traits; diff --git a/libs/ur-registry/src/macros_impl.rs b/libs/ur-registry/src/macros_impl.rs index 9be34cc..524335d 100644 --- a/libs/ur-registry/src/macros_impl.rs +++ b/libs/ur-registry/src/macros_impl.rs @@ -30,6 +30,7 @@ use crate::keystone::{ }; use crate::near::{near_sign_request::NearSignRequest, near_signature::NearSignature}; use crate::solana::{sol_sign_request::SolSignRequest, sol_signature::SolSignature}; +use crate::stellar::{stellar_sign_request::StellarSignRequest, stellar_signature::StellarSignature}; use crate::sui::sui_sign_request::SuiSignRequest; use crate::sui::sui_signature::SuiSignature; use crate::ton::{ton_signature::TonSignature, ton_sign_request::TonSignRequest}; @@ -68,6 +69,8 @@ impl_cbor_bytes!( NearSignature, SolSignRequest, SolSignature, + StellarSignRequest, + StellarSignature, SuiSignRequest, SuiSignature, TonSignature, diff --git a/libs/ur-registry/src/registry_types.rs b/libs/ur-registry/src/registry_types.rs index 8ef54de..e012d55 100644 --- a/libs/ur-registry/src/registry_types.rs +++ b/libs/ur-registry/src/registry_types.rs @@ -8,6 +8,7 @@ pub enum URType { CryptoAccount(String), EthSignRequest(String), SolSignRequest(String), + StellarSignRequest(String), NearSignRequest(String), ArweaveSignRequest(String), AptosSignRequest(String), @@ -33,6 +34,7 @@ impl URType { "keystone-sign-request" => Ok(URType::KeystoneSignRequest(type_str.to_string())), "eth-sign-request" => Ok(URType::EthSignRequest(type_str.to_string())), "sol-sign-request" => Ok(URType::SolSignRequest(type_str.to_string())), + "stellar-sign-request" => Ok(URType::StellarSignRequest(type_str.to_string())), "arweave-sign-request" => Ok(URType::ArweaveSignRequest(type_str.to_string())), "cosmos-sign-request" => Ok(URType::CosmosSignRequest(type_str.to_string())), "evm-sign-request" => Ok(URType::EvmSignRequest(type_str.to_string())), @@ -56,6 +58,7 @@ impl URType { URType::KeystoneSignRequest(type_str) => type_str.to_string(), URType::EthSignRequest(type_str) => type_str.to_string(), URType::SolSignRequest(type_str) => type_str.to_string(), + URType::StellarSignRequest(type_str) => type_str.to_string(), URType::NearSignRequest(type_str) => type_str.to_string(), URType::ArweaveSignRequest(type_str) => type_str.to_string(), URType::AptosSignRequest(type_str) => type_str.to_string(), @@ -141,3 +144,6 @@ pub const TON_SIGNATURE: RegistryType = RegistryType("ton-signature", Some(7202) // BTC pub const BTC_SIGN_REQUEST: RegistryType = RegistryType("btc-sign-request", Some(8101)); pub const BTC_SIGNATURE: RegistryType = RegistryType("btc-signature", Some(8102)); +// Stellar +pub const STELLAR_SIGN_REQUEST: RegistryType = RegistryType("stellar-sign-request", Some(8201)); +pub const STELLAR_SIGNATURE: RegistryType = RegistryType("stellar-signature", Some(8202)); diff --git a/libs/ur-registry/src/stellar/mod.rs b/libs/ur-registry/src/stellar/mod.rs new file mode 100644 index 0000000..54ea905 --- /dev/null +++ b/libs/ur-registry/src/stellar/mod.rs @@ -0,0 +1,2 @@ +pub mod stellar_sign_request; +pub mod stellar_signature; \ No newline at end of file diff --git a/libs/ur-registry/src/stellar/stellar_sign_request.rs b/libs/ur-registry/src/stellar/stellar_sign_request.rs new file mode 100644 index 0000000..a055f7f --- /dev/null +++ b/libs/ur-registry/src/stellar/stellar_sign_request.rs @@ -0,0 +1,290 @@ +use crate::cbor::cbor_map; +use crate::crypto_key_path::CryptoKeyPath; +use crate::error::{URError, URResult}; +use crate::registry_types::{RegistryType, CRYPTO_KEYPATH, STELLAR_SIGN_REQUEST, UUID}; +use crate::traits::{From as FromCbor, RegistryItem, To}; +use crate::types::Bytes; +use alloc::format; +use alloc::string::{String, ToString}; +use alloc::vec::Vec; +use minicbor::data::{Int, Tag}; +use minicbor::encode::Write; +use minicbor::{Decoder, Encoder}; + +const REQUEST_ID: u8 = 1; +const SIGN_DATA: u8 = 2; +const DERIVATION_PATH: u8 = 3; +const ADDRESS: u8 = 4; +const ORIGIN: u8 = 5; +const SIGN_TYPE: u8 = 6; + +#[derive(Clone, Debug, PartialEq)] +#[derive(Default)] +pub enum SignType { + #[default] + Transaction = 1, + Message, +} + + + +impl SignType { + pub fn from_u32(i: u32) -> Result { + match i { + 1 => Ok(SignType::Transaction), + 2 => Ok(SignType::Message), + x => Err(format!( + "invalid value for sign_type in stellar-sign-request, expected 1 or 2, received {:?}", + x + )), + } + } +} + +#[derive(Clone, Debug, Default)] +pub struct StellarSignRequest { + request_id: Option, + sign_data: Bytes, + derivation_path: CryptoKeyPath, + address: Option, + origin: Option, + sign_type: SignType, +} + +impl StellarSignRequest { + pub fn default() -> Self { + Default::default() + } + + pub fn set_request_id(&mut self, id: Bytes) { + self.request_id = Some(id); + } + + pub fn set_sign_data(&mut self, data: Bytes) { + self.sign_data = data; + } + + pub fn set_derivation_path(&mut self, derivation_path: CryptoKeyPath) { + self.derivation_path = derivation_path; + } + + pub fn set_address(&mut self, address: Bytes) { + self.address = Some(address) + } + + pub fn set_origin(&mut self, origin: String) { + self.origin = Some(origin) + } + + pub fn set_sign_type(&mut self, sign_type: SignType) { + self.sign_type = sign_type + } + + pub fn new( + request_id: Option, + sign_data: Bytes, + derivation_path: CryptoKeyPath, + address: Option, + origin: Option, + sign_type: SignType, + ) -> StellarSignRequest { + StellarSignRequest { + request_id, + sign_data, + derivation_path, + address, + origin, + sign_type, + } + } + pub fn get_request_id(&self) -> Option { + self.request_id.clone() + } + pub fn get_sign_data(&self) -> Bytes { + self.sign_data.clone() + } + pub fn get_derivation_path(&self) -> CryptoKeyPath { + self.derivation_path.clone() + } + pub fn get_address(&self) -> Option { + self.address.clone() + } + pub fn get_origin(&self) -> Option { + self.origin.clone() + } + pub fn get_sign_type(&self) -> SignType { + self.sign_type.clone() + } + + fn get_map_size(&self) -> u64 { + let mut size = 3; + if self.request_id.is_some() { + size += 1; + } + if self.address.is_some() { + size += 1; + } + if self.origin.is_some() { + size += 1; + } + size + } +} + +impl RegistryItem for StellarSignRequest { + fn get_registry_type() -> RegistryType<'static> { + STELLAR_SIGN_REQUEST + } +} + +impl minicbor::Encode for StellarSignRequest { + fn encode( + &self, + e: &mut Encoder, + _ctx: &mut C, + ) -> Result<(), minicbor::encode::Error> { + e.map(self.get_map_size())?; + + if let Some(request_id) = &self.request_id { + e.int(Int::from(REQUEST_ID))? + .tag(Tag::Unassigned(UUID.get_tag()))? + .bytes(request_id)?; + } + + e.int(Int::from(SIGN_DATA))?.bytes(&self.sign_data)?; + + e.int(Int::from(DERIVATION_PATH))?; + e.tag(Tag::Unassigned(CRYPTO_KEYPATH.get_tag()))?; + CryptoKeyPath::encode(&self.derivation_path, e, _ctx)?; + + if let Some(address) = &self.address { + e.int(Int::from(ADDRESS))?.bytes(address)?; + } + + if let Some(origin) = &self.origin { + e.int(Int::from(ORIGIN))?.str(origin)?; + } + + e.int(Int::from(SIGN_TYPE))? + .int(Int::from(self.sign_type.clone() as u8))?; + + Ok(()) + } +} + +impl<'b, C> minicbor::Decode<'b, C> for StellarSignRequest { + fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result { + let mut result = StellarSignRequest::default(); + cbor_map(d, &mut result, |key, obj, d| { + let key = + u8::try_from(key).map_err(|e| minicbor::decode::Error::message(e.to_string()))?; + match key { + REQUEST_ID => { + d.tag()?; + obj.request_id = Some(d.bytes()?.to_vec()); + } + SIGN_DATA => { + obj.sign_data = d.bytes()?.to_vec(); + } + DERIVATION_PATH => { + d.tag()?; + obj.derivation_path = CryptoKeyPath::decode(d, _ctx)?; + } + ADDRESS => { + obj.address = Some(d.bytes()?.to_vec()); + } + ORIGIN => { + obj.origin = Some(d.str()?.to_string()); + } + SIGN_TYPE => { + obj.sign_type = SignType::from_u32( + u32::try_from(d.int()?) + .map_err(|e| minicbor::decode::Error::message(e.to_string()))?, + ) + .map_err(minicbor::decode::Error::message)?; + } + _ => {} + } + Ok(()) + })?; + Ok(result) + } +} + +impl To for StellarSignRequest { + fn to_bytes(&self) -> URResult> { + minicbor::to_vec(self.clone()).map_err(|e| URError::CborEncodeError(e.to_string())) + } +} + +impl FromCbor for StellarSignRequest { + fn from_cbor(bytes: Vec) -> URResult { + minicbor::decode(&bytes).map_err(|e| URError::CborDecodeError(e.to_string())) + } +} + +#[cfg(test)] +mod tests { + use crate::crypto_key_path::{CryptoKeyPath, PathComponent}; + use crate::stellarana::stellar_sign_request::{SignType, StellarSignRequest}; + use crate::traits::{From as FromCbor, To}; + use alloc::string::ToString; + use alloc::vec; + use alloc::vec::Vec; + use hex::FromHex; + + #[test] + fn test_encode() { + let path1 = PathComponent::new(Some(44), true).unwrap(); + let path2 = PathComponent::new(Some(501), true).unwrap(); + let path3 = PathComponent::new(Some(0), true).unwrap(); + let path4 = PathComponent::new(Some(0), true).unwrap(); + + let source_fingerprint: [u8; 4] = [18, 18, 18, 18]; + let components = vec![path1, path2, path3, path4]; + let crypto_key_path = CryptoKeyPath::new(components, Some(source_fingerprint), None); + + let request_id = Some( + [ + 155, 29, 235, 77, 59, 125, 75, 173, 155, 221, 43, 13, 123, 61, 203, 109, + ] + .to_vec(), + ); + let sign_data = [ + 1, 0, 1, 3, 200, 216, 66, 162, 241, 127, 215, 170, 182, 8, 206, 46, 165, 53, 166, 233, + 88, 223, 250, 32, 202, 246, 105, 179, 71, 185, 17, 196, 23, 25, 101, 83, 15, 149, 118, + 32, 178, 40, 186, 226, 185, 76, 130, 221, 212, 192, 147, 152, 58, 103, 54, 85, 85, 183, + 55, 236, 125, 220, 17, 23, 230, 28, 114, 224, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 41, 92, 194, 241, 243, 159, + 54, 4, 113, 132, 150, 234, 0, 103, 109, 106, 114, 236, 102, 173, 9, 217, 38, 227, 236, + 227, 79, 86, 95, 24, 210, 1, 2, 2, 0, 1, 12, 2, 0, 0, 0, 0, 225, 245, 5, 0, 0, 0, 0, + ] + .to_vec(); + let stellar_sign_request = StellarSignRequest::new( + request_id, + sign_data, + crypto_key_path, + None, + Some("solflare".to_string()), + SignType::Transaction, + ); + assert_eq!( + "a501d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d02589601000103c8d842a2f17fd7aab608ce2ea535a6e958dffa20caf669b347b911c4171965530f957620b228bae2b94c82ddd4c093983a67365555b737ec7ddc1117e61c72e0000000000000000000000000000000000000000000000000000000000000000010295cc2f1f39f3604718496ea00676d6a72ec66ad09d926e3ece34f565f18d201020200010c0200000000e1f5050000000003d90130a20188182cf51901f5f500f500f5021a121212120568736f6c666c6172650601", + hex::encode(stellar_sign_request.to_bytes().unwrap()).to_lowercase() + ); + } + + #[test] + fn test_decode() { + let bytes = Vec::from_hex( + "a501d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d02589601000103c8d842a2f17fd7aab608ce2ea535a6e958dffa20caf669b347b911c4171965530f957620b228bae2b94c82ddd4c093983a67365555b737ec7ddc1117e61c72e0000000000000000000000000000000000000000000000000000000000000000010295cc2f1f39f3604718496ea00676d6a72ec66ad09d926e3ece34f565f18d201020200010c0200000000e1f5050000000003d90130a20188182cf51901f5f500f500f5021a121212120568736f6c666c6172650601", + ) + .unwrap(); + let stellar_sign_request = StellarSignRequest::from_cbor(bytes).unwrap(); + assert_eq!( + "44'/501'/0'/0'", + stellar_sign_request.derivation_path.get_path().unwrap() + ); + assert_eq!(SignType::Transaction, stellar_sign_request.get_sign_type()); + } +} diff --git a/libs/ur-registry/src/stellar/stellar_signature.rs b/libs/ur-registry/src/stellar/stellar_signature.rs new file mode 100644 index 0000000..063ce00 --- /dev/null +++ b/libs/ur-registry/src/stellar/stellar_signature.rs @@ -0,0 +1,161 @@ +use crate::cbor::cbor_map; +use crate::error::{URError, URResult}; +use crate::registry_types::{RegistryType, STELLAR_SIGNATURE, UUID}; +use crate::traits::{From as FromCbor, RegistryItem, To}; +use crate::types::Bytes; +use alloc::string::ToString; +use alloc::vec::Vec; +use minicbor::data::{Int, Tag}; +use minicbor::encode::Write; +use minicbor::{Decoder, Encoder}; + +const REQUEST_ID: u8 = 1; +const SIGNATURE: u8 = 2; + +#[derive(Clone, Debug, Default)] +pub struct StellarSignature { + request_id: Option, + signature: Bytes, +} + +impl StellarSignature { + pub fn default() -> Self { + Default::default() + } + + pub fn set_request_id(&mut self, id: Bytes) { + self.request_id = Some(id); + } + + pub fn set_signature(&mut self, signature: Bytes) { + self.signature = signature; + } + + pub fn new(request_id: Option, signature: Bytes) -> Self { + StellarSignature { + request_id, + signature, + } + } + + pub fn get_request_id(&self) -> Option { + self.request_id.clone() + } + pub fn get_signature(&self) -> Bytes { + self.signature.clone() + } +} + +impl RegistryItem for StellarSignature { + fn get_registry_type() -> RegistryType<'static> { + STELLAR_SIGNATURE + } +} + +impl minicbor::Encode for StellarSignature { + fn encode( + &self, + e: &mut Encoder, + _ctx: &mut C, + ) -> Result<(), minicbor::encode::Error> { + let mut size = 1; + if self.request_id.is_some() { + size += 1; + } + e.map(size)?; + if let Some(request_id) = &self.request_id { + e.int(Int::from(REQUEST_ID))? + .tag(Tag::Unassigned(UUID.get_tag()))? + .bytes(request_id)?; + } + e.int(Int::from(SIGNATURE))?.bytes(&self.signature)?; + Ok(()) + } +} + +impl<'b, C> minicbor::Decode<'b, C> for StellarSignature { + fn decode(d: &mut Decoder<'b>, _ctx: &mut C) -> Result { + let mut result = StellarSignature::default(); + cbor_map(d, &mut result, |key, obj, d| { + let key = + u8::try_from(key).map_err(|e| minicbor::decode::Error::message(e.to_string()))?; + match key { + REQUEST_ID => { + d.tag()?; + obj.request_id = Some(d.bytes()?.to_vec()); + } + SIGNATURE => { + obj.signature = d.bytes()?.to_vec(); + } + _ => {} + } + Ok(()) + })?; + Ok(result) + } +} + +impl To for StellarSignature { + fn to_bytes(&self) -> URResult> { + minicbor::to_vec(self.clone()).map_err(|e| URError::CborEncodeError(e.to_string())) + } +} + +impl FromCbor for StellarSignature { + fn from_cbor(bytes: Vec) -> URResult { + minicbor::decode(&bytes).map_err(|e| URError::CborDecodeError(e.to_string())) + } +} + +#[cfg(test)] +mod tests { + use crate::stellar::stellar_signature::StellarSignature; + use crate::traits::{From as FromCbor, To}; + use alloc::vec::Vec; + use hex::FromHex; + + #[test] + fn test_encode() { + let request_id = Some( + [ + 155, 29, 235, 77, 59, 125, 75, 173, 155, 221, 43, 13, 123, 61, 203, 109, + ] + .to_vec(), + ); + let signature = [ + 212, 240, 167, 188, 217, 91, 186, 31, 187, 16, 81, 136, 80, 84, 115, 14, 63, 71, 6, 66, + 136, 87, 90, 172, 193, 2, 251, 191, 106, 154, 20, 218, 160, 102, 153, 30, 54, 13, 62, + 52, 6, 194, 12, 0, 164, 9, 115, 239, 243, 124, 125, 100, 30, 91, 53, 30, 196, 169, 155, + 254, 134, 243, 53, 247, + ] + .to_vec(); + let stellar_signature = StellarSignature::new(request_id, signature); + assert_eq!( + "a201d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d025840d4f0a7bcd95bba1fbb1051885054730e3f47064288575aacc102fbbf6a9a14daa066991e360d3e3406c20c00a40973eff37c7d641e5b351ec4a99bfe86f335f7", + hex::encode(stellar_signature.to_bytes().unwrap()).to_lowercase() + ); + } + + #[test] + fn test_decode() { + let bytes = Vec::from_hex( + "a201d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d025840d4f0a7bcd95bba1fbb1051885054730e3f47064288575aacc102fbbf6a9a14daa066991e360d3e3406c20c00a40973eff37c7d641e5b351ec4a99bfe86f335f7", + ) + .unwrap(); + let stellar_signature = StellarSignature::from_cbor(bytes).unwrap(); + assert_eq!( + [155, 29, 235, 77, 59, 125, 75, 173, 155, 221, 43, 13, 123, 61, 203, 109].to_vec(), + stellar_signature.get_request_id().unwrap() + ); + assert_eq!( + [ + 212, 240, 167, 188, 217, 91, 186, 31, 187, 16, 81, 136, 80, 84, 115, 14, 63, 71, 6, + 66, 136, 87, 90, 172, 193, 2, 251, 191, 106, 154, 20, 218, 160, 102, 153, 30, 54, + 13, 62, 52, 6, 194, 12, 0, 164, 9, 115, 239, 243, 124, 125, 100, 30, 91, 53, 30, + 196, 169, 155, 254, 134, 243, 53, 247 + ] + .to_vec(), + stellar_signature.get_signature() + ); + } +} From 73bd016e6f78678ae0c72f7fb530e120a4a4d72e Mon Sep 17 00:00:00 2001 From: Charon-Fan Date: Thu, 30 May 2024 10:00:16 +0700 Subject: [PATCH 2/3] fix: generate_stellar_sign_request missing origin parameter --- .../src/stellar/stellar_sign_request.rs | 12 +++++++----- libs/ur-registry/src/stellar/stellar_sign_request.rs | 6 +----- 2 files changed, 8 insertions(+), 10 deletions(-) diff --git a/libs/ur-registry-ffi/src/stellar/stellar_sign_request.rs b/libs/ur-registry-ffi/src/stellar/stellar_sign_request.rs index 722d3e9..29bc7f3 100644 --- a/libs/ur-registry-ffi/src/stellar/stellar_sign_request.rs +++ b/libs/ur-registry-ffi/src/stellar/stellar_sign_request.rs @@ -81,12 +81,13 @@ mod tests { let xfp = "12121212"; let address = ""; let sign_type = 1; + let origin = ""; - let expect_result = "{\"cbor\":\"a501d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d02589601000103c8d842a2f17fd7aab608ce2ea535a6e958dffa20caf669b347b911c4171965530f957620b228bae2b94c82ddd4c093983a67365555b737ec7ddc1117e61c72e0000000000000000000000000000000000000000000000000000000000000000010295cc2f1f39f3604718496ea00676d6a72ec66ad09d926e3ece34f565f18d201020200010c0200000000e1f5050000000003d90130a20188182cf51901f5f500f500f5021a121212120568736f6c666c6172650601\",\"type\":\"stellar-sign-request\"}"; + let expect_result = "{\"cbor\":\"a401d825509b1deb4d3b7d4bad9bdd2b0d7b3dcb6d02589601000103c8d842a2f17fd7aab608ce2ea535a6e958dffa20caf669b347b911c4171965530f957620b228bae2b94c82ddd4c093983a67365555b737ec7ddc1117e61c72e0000000000000000000000000000000000000000000000000000000000000000010295cc2f1f39f3604718496ea00676d6a72ec66ad09d926e3ece34f565f18d201020200010c0200000000e1f5050000000003d90130a20188182cf51901f5f500f500f5021a121212120601\",\"type\":\"stellar-sign-request\"}"; assert_eq!( expect_result, - generate_stellar_sign_request(request_id, sign_data, path, xfp, address, sign_type) + generate_stellar_sign_request(request_id, sign_data, path, xfp, address, origin, sign_type) ); } @@ -98,12 +99,13 @@ mod tests { let xfp = "1212120"; let address = ""; let sign_type = 1; + let origin = "keystone"; let err_result_derivation_path = "{\"error\":\"xfp is invalid\"}"; assert_eq!( err_result_derivation_path, - generate_stellar_sign_request(request_id, sign_data, path, xfp, address, sign_type) + generate_stellar_sign_request(request_id, sign_data, path, xfp, address, origin, sign_type) ); } @@ -115,13 +117,13 @@ mod tests { let xfp = "12121212"; let address = ""; let sign_type = 1; + let origin = "keystone"; let err_result = "{\"error\":\"sign data is invalid\"}"; assert_eq!( err_result, - generate_stellar_sign_request(request_id, sign_data, path, xfp, address, sign_type) - generate_stellar_sign_request(request_id, sign_data, path, xfp, address, sign_type) + generate_stellar_sign_request(request_id, sign_data, path, xfp, address, origin, sign_type) ); } } diff --git a/libs/ur-registry/src/stellar/stellar_sign_request.rs b/libs/ur-registry/src/stellar/stellar_sign_request.rs index a055f7f..73221e9 100644 --- a/libs/ur-registry/src/stellar/stellar_sign_request.rs +++ b/libs/ur-registry/src/stellar/stellar_sign_request.rs @@ -26,8 +26,6 @@ pub enum SignType { Message, } - - impl SignType { pub fn from_u32(i: u32) -> Result { match i { @@ -225,10 +223,8 @@ impl FromCbor for StellarSignRequest { #[cfg(test)] mod tests { + use super::*; use crate::crypto_key_path::{CryptoKeyPath, PathComponent}; - use crate::stellarana::stellar_sign_request::{SignType, StellarSignRequest}; - use crate::traits::{From as FromCbor, To}; - use alloc::string::ToString; use alloc::vec; use alloc::vec::Vec; use hex::FromHex; From 986adef0473abba991af19025dcc5e403e46faaf Mon Sep 17 00:00:00 2001 From: Charon-Fan Date: Fri, 31 May 2024 13:45:50 +0700 Subject: [PATCH 3/3] feat: Add support for TransactionHash sign type --- libs/ur-registry/src/stellar/stellar_sign_request.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/libs/ur-registry/src/stellar/stellar_sign_request.rs b/libs/ur-registry/src/stellar/stellar_sign_request.rs index 73221e9..3714331 100644 --- a/libs/ur-registry/src/stellar/stellar_sign_request.rs +++ b/libs/ur-registry/src/stellar/stellar_sign_request.rs @@ -23,6 +23,7 @@ const SIGN_TYPE: u8 = 6; pub enum SignType { #[default] Transaction = 1, + TransactionHash, Message, } @@ -30,7 +31,8 @@ impl SignType { pub fn from_u32(i: u32) -> Result { match i { 1 => Ok(SignType::Transaction), - 2 => Ok(SignType::Message), + 2 => Ok(SignType::TransactionHash), + 3 => Ok(SignType::Message), x => Err(format!( "invalid value for sign_type in stellar-sign-request, expected 1 or 2, received {:?}", x