breez_sdk_spark/signer/external.rs
1use crate::error::SignerError;
2
3use super::external_types::{
4 EcdsaSignatureBytes, ExternalAggregateFrostRequest, ExternalEncryptedSecret,
5 ExternalFrostCommitments, ExternalFrostSignature, ExternalFrostSignatureShare,
6 ExternalSecretSource, ExternalSecretToSplit, ExternalSignFrostRequest, ExternalTreeNodeId,
7 ExternalVerifiableSecretShare, HashedMessageBytes, MessageBytes, PublicKeyBytes,
8 RecoverableEcdsaSignatureBytes, SchnorrSignatureBytes, SecretBytes,
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 ///
27 /// See also: [JavaScript `getIdentityPublicKey`](https://docs.spark.money/wallets/spark-signer#get-identity-public-key)
28 fn identity_public_key(&self) -> Result<PublicKeyBytes, SignerError>;
29
30 /// Derives a public key for the given BIP32 derivation path.
31 ///
32 /// # Arguments
33 /// * `path` - BIP32 derivation path as a string (e.g., "m/44'/0'/0'/0/0")
34 ///
35 /// # Returns
36 /// The derived public key as 33 bytes, or a `SignerError`
37 ///
38 /// See also: [JavaScript `getPublicKeyFromDerivation`](https://docs.spark.money/wallets/spark-signer#get-public-key-from-derivation)
39 async fn derive_public_key(&self, path: String) -> Result<PublicKeyBytes, SignerError>;
40
41 /// Signs a message using ECDSA at the given derivation path.
42 ///
43 /// The message should be a 32-byte digest (typically a hash of the original data).
44 ///
45 /// # Arguments
46 /// * `message` - The 32-byte message digest to sign
47 /// * `path` - BIP32 derivation path as a string
48 ///
49 /// # Returns
50 /// 64-byte compact ECDSA signature, or a `SignerError`
51 async fn sign_ecdsa(
52 &self,
53 message: MessageBytes,
54 path: String,
55 ) -> Result<EcdsaSignatureBytes, SignerError>;
56
57 /// Signs a message using recoverable ECDSA at the given derivation path.
58 ///
59 /// The message should be a 32-byte digest (typically a hash of the original data).
60 ///
61 /// # Arguments
62 /// * `message` - The 32-byte message digest to sign
63 /// * `path` - BIP32 derivation path as a string
64 ///
65 /// # Returns
66 /// 65 bytes: recovery ID (31 + `recovery_id`) + 64-byte signature, or a `SignerError`
67 async fn sign_ecdsa_recoverable(
68 &self,
69 message: MessageBytes,
70 path: String,
71 ) -> Result<RecoverableEcdsaSignatureBytes, SignerError>;
72
73 /// Encrypts a message using ECIES at the given derivation path.
74 ///
75 /// # Arguments
76 /// * `message` - The message to encrypt
77 /// * `path` - BIP32 derivation path for the encryption key
78 ///
79 /// # Returns
80 /// Encrypted data, or a `SignerError`
81 async fn encrypt_ecies(&self, message: Vec<u8>, path: String) -> Result<Vec<u8>, SignerError>;
82
83 /// Decrypts a message using ECIES at the given derivation path.
84 ///
85 /// # Arguments
86 /// * `message` - The encrypted message
87 /// * `path` - BIP32 derivation path for the decryption key
88 ///
89 /// # Returns
90 /// Decrypted data, or a `SignerError`
91 ///
92 /// See also: [JavaScript `decryptEcies`](https://docs.spark.money/wallets/spark-signer#decrypt-ecies)
93 async fn decrypt_ecies(&self, message: Vec<u8>, path: String) -> Result<Vec<u8>, SignerError>;
94
95 /// Signs a hash using Schnorr signature at the given derivation path.
96 ///
97 /// # Arguments
98 /// * `hash` - The 32-byte hash to sign (must be 32 bytes)
99 /// * `path` - BIP32 derivation path as a string
100 ///
101 /// # Returns
102 /// 64-byte Schnorr signature, or a `SignerError`
103 async fn sign_hash_schnorr(
104 &self,
105 hash: Vec<u8>,
106 path: String,
107 ) -> Result<SchnorrSignatureBytes, SignerError>;
108
109 /// HMAC-SHA256 of a message at the given derivation path.
110 ///
111 /// # Arguments
112 /// * `message` - The message to hash
113 /// * `path` - BIP32 derivation path as a string
114 ///
115 /// # Returns
116 /// 32-byte HMAC-SHA256, or a `SignerError`
117 ///
118 /// See also: [JavaScript `htlcHMAC`](https://docs.spark.money/wallets/spark-signer#generate-htlc-hmac)
119 async fn hmac_sha256(
120 &self,
121 message: Vec<u8>,
122 path: String,
123 ) -> Result<HashedMessageBytes, SignerError>;
124 /// Generates Frost signing commitments for multi-party signing.
125 ///
126 /// # Returns
127 /// Frost commitments with nonces, or a `SignerError`
128 ///
129 /// See also: [JavaScript `getRandomSigningCommitment`](https://docs.spark.money/wallets/spark-signer#get-random-signing-commitment)
130 async fn generate_random_signing_commitment(
131 &self,
132 ) -> Result<ExternalFrostCommitments, SignerError>;
133
134 /// Gets the public key for a specific tree node in the Spark wallet.
135 ///
136 /// # Arguments
137 /// * `id` - The tree node identifier
138 ///
139 /// # Returns
140 /// The public key for the node, or a `SignerError`
141 async fn get_public_key_for_node(
142 &self,
143 id: ExternalTreeNodeId,
144 ) -> Result<PublicKeyBytes, SignerError>;
145
146 /// Generates a random secret that is encrypted and known only to the signer.
147 ///
148 /// This method creates a new random secret and returns it in encrypted form.
149 /// The plaintext secret never leaves the signer boundary, providing a secure way
150 /// to create secrets that can be referenced in subsequent operations without
151 /// exposing them.
152 ///
153 /// This is conceptually similar to Spark's key derivation system where secrets
154 /// are represented by opaque references (like tree node IDs or Random) rather than raw values.
155 /// The encrypted secret can be passed to other signer methods that need to operate
156 /// on it, while keeping the actual secret material protected within the signer.
157 ///
158 /// # Returns
159 /// An encrypted secret that can be used in subsequent signer operations,
160 /// or a `SignerError` if generation fails.
161 ///
162 /// See also: [Key Derivation System](https://docs.spark.money/wallets/spark-signer#the-keyderivation-system)
163 async fn generate_random_secret(&self) -> Result<ExternalEncryptedSecret, SignerError>;
164
165 /// Gets an encrypted static deposit secret by index.
166 ///
167 /// # Arguments
168 /// * `index` - The index of the static deposit secret
169 ///
170 /// # Returns
171 /// The encrypted secret, or a `SignerError`
172 ///
173 /// This is the encrypted version of: [JavaScript `getStaticDepositSecretKey`](https://docs.spark.money/wallets/spark-signer#get-static-deposit-secret-key)
174 async fn static_deposit_secret_encrypted(
175 &self,
176 index: u32,
177 ) -> Result<ExternalSecretSource, SignerError>;
178
179 /// Gets a static deposit secret by index.
180 ///
181 /// # Arguments
182 /// * `index` - The index of the static deposit secret
183 ///
184 /// # Returns
185 /// The 32-byte secret, or a `SignerError`
186 ///
187 /// See also: [JavaScript `getStaticDepositSecretKey`](https://docs.spark.money/wallets/spark-signer#get-static-deposit-secret-key)
188 async fn static_deposit_secret(&self, index: u32) -> Result<SecretBytes, SignerError>;
189
190 /// Gets a static deposit signing public key by index.
191 ///
192 /// # Arguments
193 /// * `index` - The index of the static deposit public signing key
194 ///
195 /// # Returns
196 /// The 33-byte public key, or a `SignerError`
197 ///
198 /// See also: [JavaScript `getStaticDepositSigningKey`](https://docs.spark.money/wallets/spark-signer#get-static-deposit-signing-key)
199 async fn static_deposit_signing_key(&self, index: u32) -> Result<PublicKeyBytes, SignerError>;
200
201 /// Subtracts one secret from another.
202 ///
203 /// # Arguments
204 /// * `signing_key` - The first secret
205 /// * `new_signing_key` - The second secret to subtract
206 ///
207 /// # Returns
208 /// The resulting secret, or a `SignerError`
209 ///
210 /// See also: [JavaScript `subtractSplitAndEncrypt`](https://docs.spark.money/wallets/spark-signer#subtract,-split,-and-encrypt)
211 /// (this method provides the subtraction step of that higher-level operation)
212 async fn subtract_secrets(
213 &self,
214 signing_key: ExternalSecretSource,
215 new_signing_key: ExternalSecretSource,
216 ) -> Result<ExternalSecretSource, SignerError>;
217
218 /// Splits a secret with proofs using Shamir's Secret Sharing.
219 ///
220 /// # Arguments
221 /// * `secret` - The secret to split
222 /// * `threshold` - Minimum number of shares needed to reconstruct
223 /// * `num_shares` - Total number of shares to create
224 ///
225 /// # Returns
226 /// Vector of verifiable secret shares, or a `SignerError`
227 ///
228 /// See also: [JavaScript `splitSecretWithProofs`](https://docs.spark.money/wallets/spark-signer#split-secret-with-proofs)
229 async fn split_secret_with_proofs(
230 &self,
231 secret: ExternalSecretToSplit,
232 threshold: u32,
233 num_shares: u32,
234 ) -> Result<Vec<ExternalVerifiableSecretShare>, SignerError>;
235
236 /// Encrypts a secret for a specific receiver's public key.
237 ///
238 /// # Arguments
239 /// * `encrypted_secret` - The encrypted secret to re-encrypt
240 /// * `receiver_public_key` - The receiver's 33-byte public key
241 ///
242 /// # Returns
243 /// Encrypted data for the receiver, or a `SignerError`
244 async fn encrypt_secret_for_receiver(
245 &self,
246 encrypted_secret: ExternalEncryptedSecret,
247 receiver_public_key: PublicKeyBytes,
248 ) -> Result<Vec<u8>, SignerError>;
249
250 /// Gets the public key from a secret.
251 ///
252 /// # Arguments
253 /// * `secret` - The secret
254 ///
255 /// # Returns
256 /// The corresponding 33-byte public key, or a `SignerError`
257 ///
258 /// See also: [JavaScript `getPublicKeyFromDerivation`](https://docs.spark.money/wallets/spark-signer#get-public-key-from-derivation)
259 async fn public_key_from_secret(
260 &self,
261 secret: ExternalSecretSource,
262 ) -> Result<PublicKeyBytes, SignerError>;
263
264 /// Signs using Frost protocol (multi-party signing).
265 ///
266 /// # Arguments
267 /// * `request` - The Frost signing request
268 ///
269 /// # Returns
270 /// A signature share, or a `SignerError`
271 ///
272 /// See also: [JavaScript `signFrost`](https://docs.spark.money/wallets/spark-signer#frost-signing)
273 async fn sign_frost(
274 &self,
275 request: ExternalSignFrostRequest,
276 ) -> Result<ExternalFrostSignatureShare, SignerError>;
277
278 /// Aggregates Frost signature shares into a final signature.
279 ///
280 /// # Arguments
281 /// * `request` - The Frost aggregation request
282 ///
283 /// # Returns
284 /// The aggregated Frost signature, or a `SignerError`
285 ///
286 /// See also: [JavaScript `aggregateFrost`](https://docs.spark.money/wallets/spark-signer#aggregate-frost-signatures)
287 async fn aggregate_frost(
288 &self,
289 request: ExternalAggregateFrostRequest,
290 ) -> Result<ExternalFrostSignature, SignerError>;
291}