breez_sdk_spark/signer/external.rs
1use crate::error::SignerError;
2
3use super::external_types::{
4 EcdsaSignatureBytes, ExternalAggregateFrostRequest, ExternalEncryptedPrivateKey,
5 ExternalFrostCommitments, ExternalFrostSignature, ExternalFrostSignatureShare,
6 ExternalPrivateKeySource, ExternalSecretToSplit, ExternalSignFrostRequest, ExternalTreeNodeId,
7 ExternalVerifiableSecretShare, PrivateKeyBytes, PublicKeyBytes, RecoverableEcdsaSignatureBytes,
8 SchnorrSignatureBytes,
9};
10
11/// External signer trait that can be implemented by users and passed to the SDK.
12///
13/// This trait mirrors the `BreezSigner` trait but uses FFI-compatible types (bytes, strings)
14/// instead of Rust-specific types. This allows it to be exposed through FFI and WASM bindings.
15///
16/// All methods accept and return simple types:
17/// - Derivation paths as strings (e.g., "m/44'/0'/0'")
18/// - Public keys, signatures, and other crypto primitives as Vec<u8>
19/// - Spark-specific types as serialized representations
20///
21/// Errors are returned as `SignerError` for FFI compatibility.
22#[cfg_attr(feature = "uniffi", uniffi::export(with_foreign))]
23#[macros::async_trait]
24pub trait ExternalSigner: Send + Sync {
25 /// Returns the identity public key as 33 bytes (compressed secp256k1 key).
26 fn identity_public_key(&self) -> Result<PublicKeyBytes, SignerError>;
27
28 /// Derives a public key for the given BIP32 derivation path.
29 ///
30 /// # Arguments
31 /// * `path` - BIP32 derivation path as a string (e.g., "m/44'/0'/0'/0/0")
32 ///
33 /// # Returns
34 /// The derived public key as 33 bytes, or a `SignerError`
35 async fn derive_public_key(&self, path: String) -> Result<PublicKeyBytes, SignerError>;
36
37 /// Signs a message using ECDSA at the given derivation path.
38 ///
39 /// # Arguments
40 /// * `message` - The message to sign
41 /// * `path` - BIP32 derivation path as a string
42 ///
43 /// # Returns
44 /// 64-byte compact ECDSA signature, or a `SignerError`
45 async fn sign_ecdsa(
46 &self,
47 message: Vec<u8>,
48 path: String,
49 ) -> Result<EcdsaSignatureBytes, SignerError>;
50
51 /// Signs a message using recoverable ECDSA at the given derivation path.
52 ///
53 /// # Arguments
54 /// * `message` - The message to sign (will be double-SHA256 hashed)
55 /// * `path` - BIP32 derivation path as a string
56 ///
57 /// # Returns
58 /// 65 bytes: recovery ID (31 + `recovery_id`) + 64-byte signature, or a `SignerError`
59 async fn sign_ecdsa_recoverable(
60 &self,
61 message: Vec<u8>,
62 path: String,
63 ) -> Result<RecoverableEcdsaSignatureBytes, SignerError>;
64
65 /// Encrypts a message using ECIES at the given derivation path.
66 ///
67 /// # Arguments
68 /// * `message` - The message to encrypt
69 /// * `path` - BIP32 derivation path for the encryption key
70 ///
71 /// # Returns
72 /// Encrypted data, or a `SignerError`
73 async fn ecies_encrypt(&self, message: Vec<u8>, path: String) -> Result<Vec<u8>, SignerError>;
74
75 /// Decrypts a message using ECIES at the given derivation path.
76 ///
77 /// # Arguments
78 /// * `message` - The encrypted message
79 /// * `path` - BIP32 derivation path for the decryption key
80 ///
81 /// # Returns
82 /// Decrypted data, or a `SignerError`
83 async fn ecies_decrypt(&self, message: Vec<u8>, path: String) -> Result<Vec<u8>, SignerError>;
84
85 /// Signs a hash using Schnorr signature at the given derivation path.
86 ///
87 /// # Arguments
88 /// * `hash` - The 32-byte hash to sign (must be 32 bytes)
89 /// * `path` - BIP32 derivation path as a string
90 ///
91 /// # Returns
92 /// 64-byte Schnorr signature, or a `SignerError`
93 async fn sign_hash_schnorr(
94 &self,
95 hash: Vec<u8>,
96 path: String,
97 ) -> Result<SchnorrSignatureBytes, SignerError>;
98
99 /// Generates Frost signing commitments for multi-party signing.
100 ///
101 /// # Returns
102 /// Frost commitments with nonces, or a `SignerError`
103 async fn generate_frost_signing_commitments(
104 &self,
105 ) -> Result<ExternalFrostCommitments, SignerError>;
106
107 /// Gets the public key for a specific tree node in the Spark wallet.
108 ///
109 /// # Arguments
110 /// * `id` - The tree node identifier
111 ///
112 /// # Returns
113 /// The public key for the node, or an error string
114 async fn get_public_key_for_node(
115 &self,
116 id: ExternalTreeNodeId,
117 ) -> Result<PublicKeyBytes, SignerError>;
118
119 /// Generates a random private key.
120 ///
121 /// # Returns
122 /// A randomly generated private key source, or an error string
123 async fn generate_random_key(&self) -> Result<ExternalPrivateKeySource, SignerError>;
124
125 /// Gets a static deposit private key source by index.
126 ///
127 /// # Arguments
128 /// * `index` - The index of the static deposit key
129 ///
130 /// # Returns
131 /// The private key source, or an error string
132 async fn get_static_deposit_private_key_source(
133 &self,
134 index: u32,
135 ) -> Result<ExternalPrivateKeySource, SignerError>;
136
137 /// Gets a static deposit private key by index.
138 ///
139 /// # Arguments
140 /// * `index` - The index of the static deposit key
141 ///
142 /// # Returns
143 /// The 32-byte private key, or an error string
144 async fn get_static_deposit_private_key(
145 &self,
146 index: u32,
147 ) -> Result<PrivateKeyBytes, SignerError>;
148
149 /// Gets a static deposit public key by index.
150 ///
151 /// # Arguments
152 /// * `index` - The index of the static deposit key
153 ///
154 /// # Returns
155 /// The 33-byte public key, or an error string
156 async fn get_static_deposit_public_key(
157 &self,
158 index: u32,
159 ) -> Result<PublicKeyBytes, SignerError>;
160
161 /// Subtracts one private key from another.
162 ///
163 /// # Arguments
164 /// * `signing_key` - The first private key source
165 /// * `new_signing_key` - The second private key source to subtract
166 ///
167 /// # Returns
168 /// The resulting private key source, or an error string
169 async fn subtract_private_keys(
170 &self,
171 signing_key: ExternalPrivateKeySource,
172 new_signing_key: ExternalPrivateKeySource,
173 ) -> Result<ExternalPrivateKeySource, SignerError>;
174
175 /// Splits a secret with proofs using Shamir's Secret Sharing.
176 ///
177 /// # Arguments
178 /// * `secret` - The secret to split
179 /// * `threshold` - Minimum number of shares needed to reconstruct
180 /// * `num_shares` - Total number of shares to create
181 ///
182 /// # Returns
183 /// Vector of verifiable secret shares, or an error string
184 async fn split_secret(
185 &self,
186 secret: ExternalSecretToSplit,
187 threshold: u32,
188 num_shares: u32,
189 ) -> Result<Vec<ExternalVerifiableSecretShare>, SignerError>;
190
191 /// Encrypts a private key for a specific receiver's public key.
192 ///
193 /// # Arguments
194 /// * `private_key` - The encrypted private key to re-encrypt
195 /// * `receiver_public_key` - The receiver's 33-byte public key
196 ///
197 /// # Returns
198 /// Encrypted data for the receiver, or an error string
199 async fn encrypt_private_key_for_receiver(
200 &self,
201 private_key: ExternalEncryptedPrivateKey,
202 receiver_public_key: PublicKeyBytes,
203 ) -> Result<Vec<u8>, SignerError>;
204
205 /// Gets the public key from a private key source.
206 ///
207 /// # Arguments
208 /// * `private_key` - The private key source
209 ///
210 /// # Returns
211 /// The corresponding 33-byte public key, or an error string
212 async fn get_public_key_from_private_key_source(
213 &self,
214 private_key: ExternalPrivateKeySource,
215 ) -> Result<PublicKeyBytes, SignerError>;
216
217 /// Signs using Frost protocol (multi-party signing).
218 ///
219 /// # Arguments
220 /// * `request` - The Frost signing request
221 ///
222 /// # Returns
223 /// A signature share, or an error string
224 async fn sign_frost(
225 &self,
226 request: ExternalSignFrostRequest,
227 ) -> Result<ExternalFrostSignatureShare, SignerError>;
228
229 /// Aggregates Frost signature shares into a final signature.
230 ///
231 /// # Arguments
232 /// * `request` - The Frost aggregation request
233 ///
234 /// # Returns
235 /// The aggregated Frost signature, or an error string
236 async fn aggregate_frost_signatures(
237 &self,
238 request: ExternalAggregateFrostRequest,
239 ) -> Result<ExternalFrostSignature, SignerError>;
240}