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}