lit_rust_sdk/client/
decrypt.rs

1use crate::client::LitNodeClient;
2use crate::types::{
3    DecryptRequest, DecryptResponse, EncryptionSignRequest, EncryptionSignResponse,
4};
5use crate::utils;
6use alloy::providers::Provider as ProviderTrait;
7use eyre::{eyre, Result};
8use sha2::{Digest, Sha256};
9use tracing::{debug, info};
10
11impl<P> LitNodeClient<P>
12where
13    P: ProviderTrait,
14{
15    /// Decrypt data using BLS decryption with access control conditions
16    /// This retrieves decryption shares from nodes and combines them client-side
17    ///
18    /// # Arguments
19    ///
20    /// * `params` - The decryption request containing the ciphertext and access control conditions
21    ///
22    /// # Returns
23    ///
24    /// * `Result<DecryptResponse>` - The decrypted data
25    pub async fn decrypt(&self, params: DecryptRequest) -> Result<DecryptResponse> {
26        // Validate that client is ready
27        if !self.ready {
28            return Err(eyre!(
29                "LitNodeClient is not ready. Please call await client.connect() first."
30            ));
31        }
32
33        // Validate that at least one type of access control condition is provided
34        let has_conditions = params.access_control_conditions.is_some()
35            || params.evm_contract_conditions.is_some()
36            || params.sol_rpc_conditions.is_some()
37            || params.unified_access_control_conditions.is_some();
38
39        if !has_conditions {
40            return Err(eyre!(
41                "You must provide either accessControlConditions or evmContractConditions or solRpcConditions or unifiedAccessControlConditions"
42            ));
43        }
44
45        // Get current epoch
46        let epoch = self.epoch.as_ref().ok_or_else(|| eyre!("Epoch not set"))?;
47        let epoch_number = epoch.number.try_into().unwrap_or(0);
48
49        // Retrieve decryption shares from nodes
50        let decryption_shares = self
51            .retrieve_decryption_shares(&params, epoch_number)
52            .await?;
53
54        info!(
55            "Retrieved {} decryption shares from nodes",
56            decryption_shares.len()
57        );
58
59        // Get the network public key for verification
60        let network_pub_key = self
61            .network_pub_key
62            .as_ref()
63            .ok_or_else(|| eyre!("network_pub_key not set"))?;
64
65        // Get identity parameter for decryption
66        let hash_of_conditions_str = self.hash_access_control_conditions(&params)?;
67        let identity_param = self.get_identity_param_for_encryption(
68            &hash_of_conditions_str,
69            &params.data_to_encrypt_hash,
70        );
71
72        debug!("Identity param for decryption: {}", identity_param);
73
74        // Combine shares and decrypt
75        let decrypted_data = self.verify_and_decrypt_shares(
76            network_pub_key,
77            &identity_param,
78            &params.ciphertext,
79            decryption_shares,
80        )?;
81
82        Ok(DecryptResponse { decrypted_data })
83    }
84
85    /// Retrieve decryption shares from all connected nodes
86    async fn retrieve_decryption_shares(
87        &self,
88        params: &DecryptRequest,
89        epoch: u64,
90    ) -> Result<Vec<EncryptionSignResponse>> {
91        let nodes: Vec<_> = self.connection_state.iter().collect();
92        let mut shares = Vec::new();
93
94        for node in nodes {
95            let url = node.url.clone();
96            let session_sig = params
97                .session_sigs
98                .get(&url)
99                .ok_or_else(|| eyre!("No session signature for node: {}", url))?;
100
101            // Convert SessionSignature to AuthSig
102            let auth_sig = crate::types::AuthSig {
103                sig: session_sig.sig.clone(),
104                derived_via: session_sig.derived_via.clone(),
105                signed_message: session_sig.signed_message.clone(),
106                address: session_sig.address.clone(),
107                algo: session_sig.algo.clone(),
108            };
109
110            let request = EncryptionSignRequest {
111                access_control_conditions: params.access_control_conditions.clone(),
112                evm_contract_conditions: params.evm_contract_conditions.clone(),
113                sol_rpc_conditions: params.sol_rpc_conditions.clone(),
114                unified_access_control_conditions: params.unified_access_control_conditions.clone(),
115                chain: params.chain.clone(),
116                data_to_encrypt_hash: params.data_to_encrypt_hash.clone(),
117                auth_sig,
118                epoch,
119            };
120
121            let response = self.send_encryption_sign_request(&url, request).await?;
122            shares.push(response);
123        }
124
125        Ok(shares)
126    }
127
128    /// Send encryption/sign request to a single node
129    async fn send_encryption_sign_request(
130        &self,
131        node_url: &str,
132        request: EncryptionSignRequest,
133    ) -> Result<EncryptionSignResponse> {
134        let url = format!("{}/web/encryption/sign", node_url);
135        debug!("Sending encryption sign request to: {}", url);
136
137        let response = self.http_client.post(&url).json(&request).send().await?;
138
139        if !response.status().is_success() {
140            let error_text = response.text().await?;
141            return Err(eyre!(
142                "Failed to get decryption share from {}: {}",
143                node_url,
144                error_text
145            ));
146        }
147
148        let result: EncryptionSignResponse = response.json().await?;
149        Ok(result)
150    }
151
152    /// Verify and decrypt using BLS shares
153    fn verify_and_decrypt_shares(
154        &self,
155        _network_pub_key: &str,
156        _identity_param: &str,
157        ciphertext: &str,
158        shares: Vec<EncryptionSignResponse>,
159    ) -> Result<Vec<u8>> {
160        // Decode base64 ciphertext
161        use base64::{engine::general_purpose::STANDARD, Engine as _};
162        let ciphertext_bytes = STANDARD.decode(ciphertext)?;
163
164        // Combine shares into a single signature
165        let signature_shares: Vec<blsful::SignatureShare<blsful::Bls12381G2Impl>> =
166            shares.into_iter().map(|s| s.signature_share).collect();
167
168        let combined_signature = blsful::Signature::from_shares(&signature_shares)?;
169
170        // Serialize the signature for decryption
171        let sig_bytes = serde_bare::to_vec(&combined_signature)?;
172
173        // Decrypt the ciphertext using the combined signature
174        let decrypted = crate::bls::decrypt(&ciphertext_bytes, &sig_bytes)?;
175
176        Ok(decrypted)
177    }
178
179    pub fn hash_access_control_conditions(&self, req: &DecryptRequest) -> Result<String> {
180        // hash the access control condition and thing to decrypt
181        let mut hasher = Sha256::new();
182
183        // we need to check if we got passed an access control condition or an evm contract condition
184        if let Some(access_control_conditions) = &req.access_control_conditions {
185            let stringified_access_control_conditions =
186                serde_json::to_string(access_control_conditions)?;
187            debug!(
188                "stringified_access_control_conditions: {:?}",
189                stringified_access_control_conditions
190            );
191            hasher.update(stringified_access_control_conditions.as_bytes());
192        } else if let Some(evm_contract_conditions) = &req.evm_contract_conditions {
193            let stringified_access_control_conditions =
194                serde_json::to_string(evm_contract_conditions)?;
195            debug!(
196                "stringified_access_control_conditions: {:?}",
197                stringified_access_control_conditions
198            );
199            hasher.update(stringified_access_control_conditions.as_bytes());
200        } else if req.sol_rpc_conditions.is_some() {
201            return Err(eyre!("SolRpcConditions are not supported for decryption"));
202        } else if let Some(unified_access_control_conditions) =
203            &req.unified_access_control_conditions
204        {
205            let stringified_access_control_conditions =
206                serde_json::to_string(unified_access_control_conditions)?;
207            debug!(
208                "stringified_access_control_conditions: {:?}",
209                stringified_access_control_conditions
210            );
211            hasher.update(stringified_access_control_conditions.as_bytes());
212        } else {
213            return Err(eyre!("Missing access control conditions"));
214        }
215
216        let hashed_access_control_conditions = utils::bytes_to_hex(hasher.finalize());
217        debug!(
218            "hashed access control conditions: {:?}",
219            hashed_access_control_conditions
220        );
221        Ok(hashed_access_control_conditions)
222    }
223}