1use ed25519_dalek::{Signature, Signer, SigningKey};
2use serde_json::json;
3
4use crate::proof::{base64url_decode, base64url_encode, build_proof_input, sha256};
5use crate::TrellisClientError;
6
7pub struct SessionAuth {
9 pub session_key: String,
11 signing_key: SigningKey,
12}
13
14impl SessionAuth {
15 pub fn from_seed_base64url(seed_b64url: &str) -> Result<Self, TrellisClientError> {
17 let seed = base64url_decode(seed_b64url)?;
18 if seed.len() != 32 {
19 return Err(TrellisClientError::InvalidSeedLen(seed.len()));
20 }
21 let mut seed32 = [0u8; 32];
22 seed32.copy_from_slice(&seed);
23 let signing_key = SigningKey::from_bytes(&seed32);
24 let public = signing_key.verifying_key().to_bytes();
25 let session_key = base64url_encode(&public);
26 Ok(Self {
27 session_key,
28 signing_key,
29 })
30 }
31
32 pub fn sign_sha256_domain(&self, prefix: &str, value: &str) -> String {
34 let digest = sha256(format!("{prefix}:{value}").as_bytes());
35 let signature: Signature = self.signing_key.sign(&digest);
36 base64url_encode(&signature.to_bytes())
37 }
38
39 pub fn nats_connect_token(&self, iat: u64) -> String {
41 let signature = self.sign_sha256_domain("nats-connect", &iat.to_string());
42 serde_json::to_string(&json!({
43 "v": 1,
44 "sessionKey": self.session_key,
45 "iat": iat,
46 "sig": signature,
47 }))
48 .expect("nats auth token json")
49 }
50
51 pub fn nats_connect_binding_token(&self, binding_token: &str) -> String {
53 let signature = self.sign_sha256_domain("nats-connect", binding_token);
54 serde_json::to_string(&json!({
55 "v": 1,
56 "sessionKey": self.session_key,
57 "bindingToken": binding_token,
58 "sig": signature,
59 }))
60 .expect("nats auth token json")
61 }
62
63 pub fn inbox_prefix(&self) -> String {
65 format!(
66 "_INBOX.{}",
67 &self.session_key[..16.min(self.session_key.len())]
68 )
69 }
70
71 pub fn create_proof(&self, subject: &str, payload: &[u8]) -> String {
73 let payload_hash = sha256(payload);
74 let input = build_proof_input(&self.session_key, subject, &payload_hash);
75 let digest = sha256(&input);
76 let signature: Signature = self.signing_key.sign(&digest);
77 base64url_encode(&signature.to_bytes())
78 }
79}