jwt_kenji/
lib.rs

1#[cfg(test)]
2mod tests;
3
4use base64_kenji::base64_encode;
5
6pub struct JWT {
7    headers: Vec<[String;2]>,
8    payload: Vec<[String;2]>,
9    secret: String
10}
11
12impl JWT {
13    pub fn new(secret: String) -> Self {
14        JWT {
15            headers: Vec::new(),
16            payload: Vec::new(),
17            secret
18        }
19    }
20
21    pub fn verify(jwt: String, secret: String) -> bool {
22        let mut parts = jwt.split(".");
23        let headers = match parts.next() {
24            Some(headers) => headers.to_string(),
25            None => return false
26        };
27        let payload = match parts.next() {
28            Some(payload) => payload.to_string(),
29            None => return false
30        };
31
32        let correct_jwt = Self::jwt_to_string(headers.clone(), payload, secret);
33
34        jwt == correct_jwt
35    }
36
37    pub fn build(self) -> String {
38        let headers = base64_encode(Self::build_json(self.headers));
39        let payload = base64_encode(Self::build_json(self.payload));
40
41        Self::jwt_to_string(headers, payload, self.secret)
42    }
43
44    pub fn add_header(mut self, key: &str, value: &str) -> Self {
45        self.headers.push([key.to_string(), value.to_string()]);
46        self
47    }
48    pub fn add_payload(mut self, key: &str, value: &str) -> Self {
49        self.payload.push([key.to_string(), value.to_string()]);
50        self
51    }
52
53    fn jwt_to_string(headers: String, payload: String, secret: String) -> String {
54        let hash_input = format!("{}.{}.{}", headers, payload, secret);
55
56        format!("{}.{}.{}", headers, payload, Self::hash(hash_input, secret))
57    }
58
59    fn build_json(values: Vec<[String;2]>) -> String {
60        let mut res: String = values.into_iter().map(|[key, value]| {
61            format!(r#""{}":"{}","#, key, value)
62        }).collect::<String>();
63
64        res.pop();
65
66        format!("{{{}}}", res)
67    }
68
69    fn hash(input: String, secret: String) -> String {
70        let options: [char;69] = ['A','B','C','D','E','F','G','H','I','J','K','L','M',
71            'N','O','P','Q','R','S','T','U','V','W','X','Y','Z','a','b','c',
72            'd','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s',
73            't','u','v','w','x','y','z','0','1','2','3','4','5','6','7','8'
74            ,'9','+','/','%','/','*','"','='];
75
76        let mut hash_bytes: [char;30] = ['_';30];
77        let input_bytes = input.into_bytes();
78        let secret_bytes = secret.into_bytes();
79
80        let mut input_idx = 0;
81        let mut secret_idx = 0;
82
83        for i in 0..30 {
84            if input_idx >= input_bytes.len() {
85                input_idx = 0;
86            }
87            if secret_idx >= secret_bytes.len() {
88                secret_idx = 0;
89            }
90
91            hash_bytes[i] = options[((input_bytes[input_idx] + secret_bytes[secret_idx]) % 69) as usize];
92
93            input_idx += 1;
94            secret_idx += 1;
95        }
96
97        hash_bytes.into_iter().collect()
98    }
99}
100