breez_sdk_spark/signer/external.rs
1use crate::error::SignerError;
2
3use super::external_types::{
4 EcdsaSignatureBytes, HashedMessageBytes, MessageBytes, PublicKeyBytes,
5 RecoverableEcdsaSignatureBytes, SchnorrSignatureBytes,
6};
7
8/// External signer trait that can be implemented by users and passed to the SDK.
9///
10/// This trait mirrors the `BreezSigner` trait but uses FFI-compatible types (bytes, strings)
11/// instead of Rust-specific types. This allows it to be exposed through FFI and WASM bindings.
12///
13/// All methods accept and return simple types:
14/// - Derivation paths as strings (e.g., "m/44'/0'/0'")
15/// - Public keys, signatures, and other crypto primitives as Vec<u8>
16/// - Spark-specific types as serialized representations
17///
18/// Errors are returned as `SignerError` for FFI compatibility.
19#[cfg_attr(
20 feature = "uniffi",
21 uniffi::export(with_foreign, async_runtime = "tokio")
22)]
23#[macros::async_trait]
24pub trait ExternalBreezSigner: Send + Sync {
25 /// Derives a public key for the given BIP32 derivation path.
26 ///
27 /// # Arguments
28 /// * `path` - BIP32 derivation path as a string (e.g., "m/44'/0'/0'/0/0")
29 ///
30 /// # Returns
31 /// The derived public key as 33 bytes, or a `SignerError`
32 ///
33 /// See also: [JavaScript `getPublicKeyFromDerivation`](https://docs.spark.money/wallets/spark-signer#get-public-key-from-derivation)
34 async fn derive_public_key(&self, path: String) -> Result<PublicKeyBytes, SignerError>;
35
36 /// Signs a message using ECDSA at the given derivation path.
37 ///
38 /// The message should be a 32-byte digest (typically a hash of the original data).
39 ///
40 /// # Arguments
41 /// * `message` - The 32-byte message digest to sign
42 /// * `path` - BIP32 derivation path as a string
43 ///
44 /// # Returns
45 /// 64-byte compact ECDSA signature, or a `SignerError`
46 async fn sign_ecdsa(
47 &self,
48 message: MessageBytes,
49 path: String,
50 ) -> Result<EcdsaSignatureBytes, SignerError>;
51
52 /// Signs a message using recoverable ECDSA at the given derivation path.
53 ///
54 /// The message should be a 32-byte digest (typically a hash of the original data).
55 ///
56 /// # Arguments
57 /// * `message` - The 32-byte message digest to sign
58 /// * `path` - BIP32 derivation path as a string
59 ///
60 /// # Returns
61 /// 65 bytes: recovery ID (31 + `recovery_id`) + 64-byte signature, or a `SignerError`
62 async fn sign_ecdsa_recoverable(
63 &self,
64 message: MessageBytes,
65 path: String,
66 ) -> Result<RecoverableEcdsaSignatureBytes, SignerError>;
67
68 /// Encrypts a message using ECIES at the given derivation path.
69 ///
70 /// # Arguments
71 /// * `message` - The message to encrypt
72 /// * `path` - BIP32 derivation path for the encryption key
73 ///
74 /// # Returns
75 /// Encrypted data, or a `SignerError`
76 async fn encrypt_ecies(&self, message: Vec<u8>, path: String) -> Result<Vec<u8>, SignerError>;
77
78 /// Decrypts a message using ECIES at the given derivation path.
79 ///
80 /// # Arguments
81 /// * `message` - The encrypted message
82 /// * `path` - BIP32 derivation path for the decryption key
83 ///
84 /// # Returns
85 /// Decrypted data, or a `SignerError`
86 ///
87 /// See also: [JavaScript `decryptEcies`](https://docs.spark.money/wallets/spark-signer#decrypt-ecies)
88 async fn decrypt_ecies(&self, message: Vec<u8>, path: String) -> Result<Vec<u8>, SignerError>;
89
90 /// Signs a hash using Schnorr signature at the given derivation path.
91 ///
92 /// # Arguments
93 /// * `hash` - The 32-byte hash to sign (must be 32 bytes)
94 /// * `path` - BIP32 derivation path as a string
95 ///
96 /// # Returns
97 /// 64-byte Schnorr signature, or a `SignerError`
98 async fn sign_hash_schnorr(
99 &self,
100 hash: Vec<u8>,
101 path: String,
102 ) -> Result<SchnorrSignatureBytes, SignerError>;
103
104 /// HMAC-SHA256 of a message at the given derivation path.
105 ///
106 /// # Arguments
107 /// * `message` - The message to hash
108 /// * `path` - BIP32 derivation path as a string
109 ///
110 /// # Returns
111 /// 32-byte HMAC-SHA256, or a `SignerError`
112 ///
113 /// See also: [JavaScript `htlcHMAC`](https://docs.spark.money/wallets/spark-signer#generate-htlc-hmac)
114 async fn hmac_sha256(
115 &self,
116 message: Vec<u8>,
117 path: String,
118 ) -> Result<HashedMessageBytes, SignerError>;
119}
120
121/// External signer that provides signing only, without the SDK's local
122/// ECIES/HMAC operations.
123///
124/// Implement this instead of [`ExternalBreezSigner`] for a signer that can't
125/// release key material for local encryption (for example a policy-restricted
126/// enclave). The capability is declared by the type: the SDK keeps session
127/// tokens in plaintext and the features that rely on ECIES/HMAC are unavailable.
128#[cfg_attr(
129 feature = "uniffi",
130 uniffi::export(with_foreign, async_runtime = "tokio")
131)]
132#[macros::async_trait]
133pub trait ExternalSigningSigner: Send + Sync {
134 /// Derives a public key for the given BIP32 derivation path.
135 ///
136 /// # Arguments
137 /// * `path` - BIP32 derivation path as a string (e.g., "m/44'/0'/0'/0/0")
138 ///
139 /// # Returns
140 /// The derived public key as 33 bytes, or a `SignerError`
141 ///
142 /// See also: [JavaScript `getPublicKeyFromDerivation`](https://docs.spark.money/wallets/spark-signer#get-public-key-from-derivation)
143 async fn derive_public_key(&self, path: String) -> Result<PublicKeyBytes, SignerError>;
144
145 /// Signs a message using ECDSA at the given derivation path.
146 ///
147 /// The message should be a 32-byte digest (typically a hash of the original data).
148 ///
149 /// # Arguments
150 /// * `message` - The 32-byte message digest to sign
151 /// * `path` - BIP32 derivation path as a string
152 ///
153 /// # Returns
154 /// 64-byte compact ECDSA signature, or a `SignerError`
155 async fn sign_ecdsa(
156 &self,
157 message: MessageBytes,
158 path: String,
159 ) -> Result<EcdsaSignatureBytes, SignerError>;
160
161 /// Signs a message using recoverable ECDSA at the given derivation path.
162 ///
163 /// The message should be a 32-byte digest (typically a hash of the original data).
164 ///
165 /// # Arguments
166 /// * `message` - The 32-byte message digest to sign
167 /// * `path` - BIP32 derivation path as a string
168 ///
169 /// # Returns
170 /// 65 bytes: recovery ID (31 + `recovery_id`) + 64-byte signature, or a `SignerError`
171 async fn sign_ecdsa_recoverable(
172 &self,
173 message: MessageBytes,
174 path: String,
175 ) -> Result<RecoverableEcdsaSignatureBytes, SignerError>;
176
177 /// Signs a hash using Schnorr signature at the given derivation path.
178 ///
179 /// # Arguments
180 /// * `hash` - The 32-byte hash to sign (must be 32 bytes)
181 /// * `path` - BIP32 derivation path as a string
182 ///
183 /// # Returns
184 /// 64-byte Schnorr signature, or a `SignerError`
185 async fn sign_hash_schnorr(
186 &self,
187 hash: Vec<u8>,
188 path: String,
189 ) -> Result<SchnorrSignatureBytes, SignerError>;
190}