Skip to main content

schul_cloud_api/
state.rs

1#![allow(dead_code)]
2use base64::Engine;
3use base64::engine::general_purpose::STANDARD as BASE64;
4use openssl::pkey::{PKey, Private, Public};
5use openssl::rsa::{Padding, Rsa};
6use crate::Result;
7use rand::distr::Alphanumeric;
8use rand::Rng;
9use crate::errors::Errors;
10
11#[derive(Debug, Clone)]
12pub struct EncryptionState {
13    private_key: Rsa<Private>,
14    public_key: Rsa<Public>,
15    private_signing_key: Rsa<Private>,
16    public_signing_key: Rsa<Public>,
17}
18impl EncryptionState {
19    pub fn new(private_encryption_key: Rsa<Private>, public_encryption_key: Rsa<Public>, private_signing_key: Rsa<Private>, public_signing_key: Rsa<Public>) -> Self {
20        Self {
21            private_key: private_encryption_key,
22            public_key: public_encryption_key,
23            private_signing_key,
24            public_signing_key
25        }
26    }
27    pub fn decrypt(&self, key: String) -> Result<Vec<u8>> {
28        let encrypted_data = BASE64.decode(key).map_err(|e| Errors::Base64Error(e))?;
29        let mut result = vec![0; encrypted_data.len()];
30        let decrypted_len = self.private_key.private_decrypt(&*encrypted_data, &mut *result, Padding::PKCS1_OAEP).map_err(|e| Errors::EncryptionError(e))?;
31        result.truncate(decrypted_len);
32        Ok(result)
33    }
34    pub fn encrypt(&self, data: Vec<u8>) -> Result<Vec<u8>> {
35        let mut result = vec![0; self.private_key.size() as usize];
36        let encrypted_len = self.private_key.private_encrypt(&*data, &mut *result, Padding::PKCS1_OAEP).map_err(|e| Errors::EncryptionError(e))?;
37        result.truncate(encrypted_len);
38        Ok(result)
39    }
40    #[cfg(feature = "experimental")]
41    pub fn sign(&self, data: Vec<u8>) -> Result<String> {
42        let key = PKey::from_rsa(self.private_key.clone())?;
43        let mut signer = openssl::sign::Signer::new(openssl::hash::MessageDigest::sha256(), &key)?;
44        signer.update(&*data)?;
45        Ok(BASE64.encode(signer.sign_to_vec()?))
46    }
47}
48#[derive(Clone, Debug)]
49pub struct State {
50    pub(crate) base_url: String,
51    pub device_id: String,
52    pub client_key: Option<String>,
53}
54impl State {
55    pub fn build_url(&self, path: impl ToString) -> String {
56        let path = path.to_string();
57        let base = if self.base_url.ends_with('/') {
58            self.base_url.clone()
59        } else {
60            self.base_url.clone() + "/"
61        };
62        base + path.trim_start_matches("/")
63    }
64    pub(crate) fn expect_client_key(&self) -> Result<String> {
65        self.client_key.clone().ok_or(Errors::NotAuthenticated)
66    }
67    pub(crate) fn get_device_id(&self) -> String {self.device_id.clone()}
68}
69impl State {
70    pub fn new(base_url: String, device_id: String, client_key: Option<String>) -> Self {
71        let base_url = if base_url.starts_with("http://") || base_url.starts_with("https://") {
72            base_url
73        } else {
74            "https://".to_string() + &base_url
75        };
76        Self {
77            base_url,
78            device_id,
79            client_key,
80        }
81    }
82}
83impl Default for State {
84    fn default() -> Self {
85        Self {
86            base_url: "https://api.stashcat.com/".into(),
87            device_id: rand::rng().sample_iter(&Alphanumeric).take(32).map(char::from).collect(),
88            client_key: None,
89        }
90    }
91}