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    /// This is a lower-level primitive used as part of key tweaking operations.
204    ///
205    /// # Arguments
206    /// * `signing_key` - The first secret
207    /// * `new_signing_key` - The second secret to subtract
208    ///
209    /// # Returns
210    /// The resulting secret, or a `SignerError`
211    ///
212    /// See also: [JavaScript `subtractSplitAndEncrypt`](https://docs.spark.money/wallets/spark-signer#subtract,-split,-and-encrypt)
213    /// (this method provides the subtraction step of that higher-level operation)
214    async fn subtract_secrets(
215        &self,
216        signing_key: ExternalSecretSource,
217        new_signing_key: ExternalSecretSource,
218    ) -> Result<ExternalSecretSource, SignerError>;
219
220    /// Splits a secret with proofs using Shamir's Secret Sharing.
221    ///
222    /// # Arguments
223    /// * `secret` - The secret to split
224    /// * `threshold` - Minimum number of shares needed to reconstruct
225    /// * `num_shares` - Total number of shares to create
226    ///
227    /// # Returns
228    /// Vector of verifiable secret shares, or a `SignerError`
229    ///
230    /// See also: [JavaScript `splitSecretWithProofs`](https://docs.spark.money/wallets/spark-signer#split-secret-with-proofs)
231    async fn split_secret_with_proofs(
232        &self,
233        secret: ExternalSecretToSplit,
234        threshold: u32,
235        num_shares: u32,
236    ) -> Result<Vec<ExternalVerifiableSecretShare>, SignerError>;
237
238    /// Encrypts a secret for a specific receiver's public key.
239    ///
240    /// # Arguments
241    /// * `encrypted_secret` - The encrypted secret to re-encrypt
242    /// * `receiver_public_key` - The receiver's 33-byte public key
243    ///
244    /// # Returns
245    /// Encrypted data for the receiver, or a `SignerError`
246    async fn encrypt_secret_for_receiver(
247        &self,
248        encrypted_secret: ExternalEncryptedSecret,
249        receiver_public_key: PublicKeyBytes,
250    ) -> Result<Vec<u8>, SignerError>;
251
252    /// Gets the public key from a secret.
253    ///
254    /// # Arguments
255    /// * `secret` - The secret
256    ///
257    /// # Returns
258    /// The corresponding 33-byte public key, or a `SignerError`
259    ///
260    /// See also: [JavaScript `getPublicKeyFromDerivation`](https://docs.spark.money/wallets/spark-signer#get-public-key-from-derivation)
261    async fn public_key_from_secret(
262        &self,
263        secret: ExternalSecretSource,
264    ) -> Result<PublicKeyBytes, SignerError>;
265
266    /// Signs using Frost protocol (multi-party signing).
267    ///
268    /// # Arguments
269    /// * `request` - The Frost signing request
270    ///
271    /// # Returns
272    /// A signature share, or a `SignerError`
273    ///
274    /// See also: [JavaScript `signFrost`](https://docs.spark.money/wallets/spark-signer#frost-signing)
275    async fn sign_frost(
276        &self,
277        request: ExternalSignFrostRequest,
278    ) -> Result<ExternalFrostSignatureShare, SignerError>;
279
280    /// Aggregates Frost signature shares into a final signature.
281    ///
282    /// # Arguments
283    /// * `request` - The Frost aggregation request
284    ///
285    /// # Returns
286    /// The aggregated Frost signature, or a `SignerError`
287    ///
288    /// See also: [JavaScript `aggregateFrost`](https://docs.spark.money/wallets/spark-signer#aggregate-frost-signatures)
289    async fn aggregate_frost(
290        &self,
291        request: ExternalAggregateFrostRequest,
292    ) -> Result<ExternalFrostSignature, SignerError>;
293}