lit_rust_sdk/client/
encrypt.rs

1use crate::client::LitNodeClient;
2use crate::types::{EncryptRequest, EncryptResponse};
3use alloy::providers::Provider as ProviderTrait;
4use eyre::{eyre, Result};
5use sha2::{Digest, Sha256};
6use tracing::debug;
7
8impl<P> LitNodeClient<P>
9where
10    P: ProviderTrait,
11{
12    /// Encrypt data using BLS encryption with access control conditions
13    ///
14    /// # Arguments
15    ///
16    /// * `params` - The encryption request containing the data and access control conditions
17    ///
18    /// # Returns
19    ///
20    /// * `Result<EncryptResponse>` - The encrypted ciphertext and hash of the data
21    pub async fn encrypt(&self, params: EncryptRequest) -> Result<EncryptResponse> {
22        // Validate that client is ready
23        if !self.ready {
24            return Err(eyre!(
25                "LitNodeClient is not ready. Please call await client.connect() first."
26            ));
27        }
28
29        // Validate that subnet_pub_key exists
30        let subnet_pub_key = self
31            .subnet_pub_key
32            .as_ref()
33            .ok_or_else(|| eyre!("subnet_pub_key cannot be null"))?;
34
35        // Validate that at least one type of access control condition is provided
36        let has_conditions = params.access_control_conditions.is_some()
37            || params.evm_contract_conditions.is_some()
38            || params.sol_rpc_conditions.is_some()
39            || params.unified_access_control_conditions.is_some();
40
41        if !has_conditions {
42            return Err(eyre!(
43                "You must provide either accessControlConditions or evmContractConditions or solRpcConditions or unifiedAccessControlConditions"
44            ));
45        }
46
47        // Hash the access control conditions
48        let hash_of_conditions = self.get_hashed_access_control_conditions(&params)?;
49        let hash_of_conditions_str = hex::encode(&hash_of_conditions);
50
51        debug!("hashOfConditionsStr: {}", hash_of_conditions_str);
52
53        // Hash the private data
54        let mut hasher = Sha256::new();
55        hasher.update(&params.data_to_encrypt);
56        let hash_of_private_data = hasher.finalize();
57        let hash_of_private_data_str = hex::encode(hash_of_private_data);
58
59        debug!("hashOfPrivateDataStr: {}", hash_of_private_data_str);
60
61        // Assemble identity parameter
62        let identity_param = self
63            .get_identity_param_for_encryption(&hash_of_conditions_str, &hash_of_private_data_str);
64
65        debug!("identityParam: {}", identity_param);
66
67        // Remove 0x prefix from subnet_pub_key if present
68        let clean_pub_key = subnet_pub_key.strip_prefix("0x").unwrap_or(subnet_pub_key);
69
70        // Encrypt using BLS
71        let ciphertext_bytes = crate::bls::encrypt(
72            &hex::decode(clean_pub_key)?,
73            &params.data_to_encrypt,
74            identity_param.as_bytes(),
75        )?;
76
77        // Convert to base64
78        use base64::{engine::general_purpose::STANDARD, Engine as _};
79        let ciphertext = STANDARD.encode(&ciphertext_bytes);
80
81        Ok(EncryptResponse {
82            ciphertext,
83            data_to_encrypt_hash: hash_of_private_data_str,
84        })
85    }
86
87    /// Hash the access control conditions to match lit-node implementation
88    fn get_hashed_access_control_conditions(&self, params: &EncryptRequest) -> Result<Vec<u8>> {
89        // Serialize the conditions to JSON exactly like lit-node does
90        let conditions_json = if let Some(ref conditions) = params.unified_access_control_conditions
91        {
92            serde_json::to_string(conditions)?
93        } else if let Some(ref conditions) = params.access_control_conditions {
94            serde_json::to_string(conditions)?
95        } else if let Some(ref conditions) = params.evm_contract_conditions {
96            serde_json::to_string(conditions)?
97        } else if let Some(ref conditions) = params.sol_rpc_conditions {
98            serde_json::to_string(conditions)?
99        } else {
100            return Err(eyre!("No access control conditions provided"));
101        };
102
103        tracing::debug!(
104            "stringified_access_control_conditions: {:?}",
105            conditions_json
106        );
107
108        // Hash the JSON string exactly like lit-node does
109        let mut hasher = Sha256::new();
110        hasher.update(conditions_json.as_bytes());
111        Ok(hasher.finalize().to_vec())
112    }
113
114    /// Generate the identity parameter for encryption
115    pub(crate) fn get_identity_param_for_encryption(
116        &self,
117        hash_of_conditions: &str,
118        hash_of_private_data: &str,
119    ) -> String {
120        format!(
121            "lit-accesscontrolcondition://{}/{}",
122            hash_of_conditions, hash_of_private_data
123        )
124    }
125}