Skip to content

Commit b7a7371

Browse files
authored
Feature/encrypt (#10)
* encrypt works * hash of conditions doesnt match * discrepancy between local access control condition identity hash and the one on the node - investigating * encryption works now * remove old tests * move rust imports * cargo fmt * Clippy fix * fix clippy
1 parent 75bdc60 commit b7a7371

File tree

7 files changed

+477
-2
lines changed

7 files changed

+477
-2
lines changed

lit-rust-sdk/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,8 @@ k256 = { version = "0.13", features = ["serde", "ecdsa", "arithmetic"] }
3636
serde_bare = "0.5.0"
3737
alloy = { version = "1.0.24", features = ["full"] }
3838
eyre = "0.6.12"
39+
sha2 = "0.10"
3940

4041
[dev-dependencies]
4142
tokio-test = "0.4"
42-
tracing-subscriber = "0.3"
43+
tracing-subscriber = { version = "0.3", features = ["env-filter"] }

lit-rust-sdk/src/client/encrypt.rs

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
use crate::client::LitNodeClient;
2+
use crate::types::{EncryptRequest, EncryptResponse};
3+
use alloy::providers::Provider as ProviderTrait;
4+
use eyre::{eyre, Result};
5+
use sha2::{Digest, Sha256};
6+
use tracing::debug;
7+
8+
impl<P> LitNodeClient<P>
9+
where
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+
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+
}

lit-rust-sdk/src/client/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ use reqwest::Client;
66
use std::sync::Arc;
77

88
mod connect;
9+
mod encrypt;
910
mod execute;
1011
mod pkp;
1112
mod session_sigs;

lit-rust-sdk/src/lib.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,4 +57,8 @@ pub mod types;
5757

5858
pub use client::LitNodeClient;
5959
pub use config::{LitNetwork, LitNodeClientConfig};
60-
pub use types::{AuthMethod, AuthSig, ExecuteJsParams, ExecuteJsResponse, SessionSignatures, PKP};
60+
pub use types::{
61+
AccessControlCondition, AuthMethod, AuthSig, EncryptRequest, EncryptResponse,
62+
EvmContractCondition, ExecuteJsParams, ExecuteJsResponse, ReturnValueTest, SessionSignatures,
63+
SolRpcCondition, UnifiedAccessControlCondition, UnifiedAccessControlConditionItem, PKP,
64+
};

lit-rust-sdk/src/types.rs

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -230,3 +230,112 @@ pub struct JsonSignSessionKeyResponseV1 {
230230
pub data_signed: String,
231231
pub bls_root_pubkey: String,
232232
}
233+
234+
// Access Control Condition types
235+
#[derive(Debug, Clone, Serialize, Deserialize)]
236+
#[serde(rename_all = "camelCase")]
237+
pub struct AccessControlCondition {
238+
pub contract_address: String,
239+
pub chain: String,
240+
pub standard_contract_type: String,
241+
pub method: String,
242+
pub parameters: Vec<String>,
243+
pub return_value_test: ReturnValueTest,
244+
}
245+
246+
// For unified access control conditions, we need a version with conditionType
247+
#[derive(Debug, Clone, Serialize, Deserialize)]
248+
#[serde(rename_all = "camelCase")]
249+
pub struct UnifiedAccessControlConditionItem {
250+
pub condition_type: String,
251+
pub contract_address: String,
252+
pub standard_contract_type: String,
253+
pub chain: String,
254+
pub method: String,
255+
pub parameters: Vec<String>,
256+
pub return_value_test: ReturnValueTest,
257+
}
258+
259+
#[derive(Debug, Clone, Serialize, Deserialize)]
260+
#[serde(rename_all = "camelCase")]
261+
pub struct EvmContractCondition {
262+
pub contract_address: String,
263+
pub function_name: String,
264+
pub function_params: Vec<serde_json::Value>,
265+
pub function_abi: serde_json::Value,
266+
pub chain: String,
267+
pub return_value_test: ReturnValueTest,
268+
}
269+
270+
// For unified access control conditions, we need a version with conditionType
271+
#[derive(Debug, Clone, Serialize, Deserialize)]
272+
#[serde(rename_all = "camelCase")]
273+
pub struct UnifiedEvmContractConditionItem {
274+
pub condition_type: String,
275+
pub contract_address: String,
276+
pub function_name: String,
277+
pub function_params: Vec<serde_json::Value>,
278+
pub function_abi: serde_json::Value,
279+
pub chain: String,
280+
pub return_value_test: ReturnValueTest,
281+
}
282+
283+
#[derive(Debug, Clone, Serialize, Deserialize)]
284+
#[serde(rename_all = "camelCase")]
285+
pub struct SolRpcCondition {
286+
pub method: String,
287+
pub params: Vec<serde_json::Value>,
288+
pub chain: String,
289+
pub return_value_test: ReturnValueTest,
290+
}
291+
292+
// For unified access control conditions, we need a version with conditionType
293+
#[derive(Debug, Clone, Serialize, Deserialize)]
294+
#[serde(rename_all = "camelCase")]
295+
pub struct UnifiedSolRpcConditionItem {
296+
pub condition_type: String,
297+
pub method: String,
298+
pub params: Vec<serde_json::Value>,
299+
pub chain: String,
300+
pub return_value_test: ReturnValueTest,
301+
}
302+
303+
#[derive(Debug, Clone, Serialize, Deserialize)]
304+
#[serde(untagged)]
305+
pub enum UnifiedAccessControlCondition {
306+
AccessControl(UnifiedAccessControlConditionItem),
307+
EvmContract(UnifiedEvmContractConditionItem),
308+
SolRpc(UnifiedSolRpcConditionItem),
309+
Operator(OperatorCondition),
310+
}
311+
312+
#[derive(Debug, Clone, Serialize, Deserialize)]
313+
pub struct OperatorCondition {
314+
pub operator: String,
315+
}
316+
317+
#[derive(Debug, Clone, Serialize, Deserialize)]
318+
pub struct ReturnValueTest {
319+
pub comparator: String,
320+
pub value: serde_json::Value,
321+
}
322+
323+
// Encryption related types
324+
#[derive(Debug, Clone, Serialize, Deserialize)]
325+
pub struct EncryptRequest {
326+
pub data_to_encrypt: Vec<u8>,
327+
#[serde(skip_serializing_if = "Option::is_none")]
328+
pub access_control_conditions: Option<Vec<AccessControlCondition>>,
329+
#[serde(skip_serializing_if = "Option::is_none")]
330+
pub evm_contract_conditions: Option<Vec<EvmContractCondition>>,
331+
#[serde(skip_serializing_if = "Option::is_none")]
332+
pub sol_rpc_conditions: Option<Vec<SolRpcCondition>>,
333+
#[serde(skip_serializing_if = "Option::is_none")]
334+
pub unified_access_control_conditions: Option<Vec<UnifiedAccessControlCondition>>,
335+
}
336+
337+
#[derive(Debug, Clone, Serialize, Deserialize)]
338+
pub struct EncryptResponse {
339+
pub ciphertext: String,
340+
pub data_to_encrypt_hash: String,
341+
}

0 commit comments

Comments
 (0)