lit_rust_sdk/client/
session_sigs.rs1use crate::types::{
2 AuthSig, LitResourceAbilityRequest, SessionKeySignedMessage, SessionSignature,
3 SessionSignatures,
4};
5use alloy::signers::local::PrivateKeySigner;
6use ed25519_dalek::Signer;
7use eyre::Result;
8use rand::Rng;
9use serde_json::Value;
10use std::collections::HashMap;
11use std::ops::{Add, Sub};
12use tracing::info;
13
14impl<P: alloy::providers::Provider> super::LitNodeClient<P> {
15 pub async fn get_local_session_sigs(
16 &self,
17 wallet: &PrivateKeySigner,
18 resource_ability_requests: Vec<LitResourceAbilityRequest>,
19 expiration: &str,
20 capability_auth_sigs: Vec<AuthSig>,
21 ) -> Result<SessionSignatures> {
22 if !self.ready {
23 return Err(eyre::eyre!("Lit Node Client not connected"));
24 }
25
26 let session_keypair = {
28 let mut secret_bytes = [0u8; 32];
29 rand::rngs::OsRng.fill(&mut secret_bytes);
30 ed25519_dalek::SigningKey::from_bytes(&secret_bytes)
31 };
32 let session_verifying_key = session_keypair.verifying_key();
33 let session_public_key = hex::encode(session_verifying_key.to_bytes());
34 info!("Generated session key: {}", session_public_key);
35
36 let auth_sig = self
38 .create_auth_sig_for_session_sig(
39 wallet,
40 &session_public_key,
41 &resource_ability_requests,
42 )
43 .await?;
44 info!("Created auth sig for session: {:?}", auth_sig);
45
46 let mut session_sigs = HashMap::new();
48 let now = chrono::Utc::now();
49 let issued_at = now
50 .sub(chrono::Duration::days(1))
51 .to_rfc3339_opts(chrono::SecondsFormat::Millis, true);
52
53 let mut capabilities = vec![auth_sig];
54 capabilities.extend(capability_auth_sigs);
55
56 for node_url in self.connected_nodes() {
57 let session_key_signed_message = SessionKeySignedMessage {
58 session_key: session_public_key.clone(),
59 resource_ability_requests: resource_ability_requests.clone(),
60 capabilities: capabilities.clone(),
61 issued_at: issued_at.clone(),
62 expiration: expiration.to_string(),
63 node_address: node_url.to_owned(),
64 };
65
66 let message = serde_json::to_string(&session_key_signed_message)?;
68
69 let signature = session_keypair.sign(message.as_bytes());
71
72 let session_sig = SessionSignature {
73 sig: signature.to_string(),
74 derived_via: "litSessionSignViaNacl".to_string(),
75 signed_message: message,
76 address: session_public_key.clone(),
77 algo: Some("ed25519".to_string()),
78 };
79
80 session_sigs.insert(node_url.clone(), session_sig);
81 }
82
83 if session_sigs.is_empty() {
84 return Err(eyre::eyre!(
85 "Failed to create session signatures for any node"
86 ));
87 }
88
89 Ok(session_sigs)
90 }
91
92 async fn create_auth_sig_for_session_sig(
93 &self,
94 wallet: &PrivateKeySigner,
95 session_public_key: &str,
96 resource_ability_requests: &[LitResourceAbilityRequest],
97 ) -> Result<AuthSig> {
98 use alloy::signers::Signer;
99 use siwe::Message;
100 use siwe_recap::Capability;
101
102 let wallet_address = wallet.address();
103
104 let mut resources = vec![];
106 let mut resource_prefixes = vec![];
107
108 for resource_ability_request in resource_ability_requests.iter() {
109 let (resource, resource_prefix) = (
110 "*/*".to_string(),
111 format!(
112 "{}://*",
113 resource_ability_request.resource.resource_prefix.clone()
114 ),
115 );
116 resources.push(resource);
117 resource_prefixes.push(resource_prefix);
118 }
119
120 let mut capabilities = Capability::<Value>::default();
121 for (resource, resource_prefix) in resources.iter().zip(resource_prefixes.iter()) {
122 let _ = capabilities
123 .with_actions_convert(resource_prefix.clone(), [(resource.clone(), [])]);
124 }
125
126 let nonce = self.get_latest_ethereum_blockhash().await?;
128
129 let now = chrono::Utc::now();
130 let siwe_issued_at = now.sub(chrono::Duration::days(1));
131 let siwe_expiration_time = now.add(chrono::Duration::days(7));
132
133 let siwe_message = capabilities
135 .build_message(Message {
136 domain: "localhost:3000".parse().unwrap(),
137 address: wallet_address.into_array(),
138 statement: Some(format!(
139 "I am creating a session for {}.",
140 session_public_key
141 )),
142 uri: format!("lit:session:{}", session_public_key)
143 .parse()
144 .unwrap(),
145 version: siwe::Version::V1,
146 chain_id: 1,
147 nonce,
148 issued_at: siwe_issued_at
149 .to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
150 .parse()
151 .unwrap(),
152 expiration_time: Some(
153 siwe_expiration_time
154 .to_rfc3339_opts(chrono::SecondsFormat::Millis, true)
155 .parse()
156 .unwrap(),
157 ),
158 not_before: None,
159 request_id: None,
160 resources: vec![],
161 })
162 .map_err(|e| eyre::eyre!("Could not create SIWE message: {}", e))?;
163
164 let message_str = siwe_message.to_string();
165 info!("Created SIWE message for auth sig: {}", message_str);
166
167 let signature = wallet.sign_message(message_str.as_bytes()).await?;
169 let sig_hex = format!("0x{}", hex::encode(signature.as_bytes()));
170
171 Ok(AuthSig {
172 sig: sig_hex,
173 derived_via: "web3.eth.personal.sign".to_string(),
174 signed_message: message_str,
175 address: wallet_address.to_checksum(None),
176 algo: None,
177 })
178 }
179}