Skip to main content

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}