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