Skip to main content

cataclysm_jwt/sign_algorithms/
hs256.rs

1use std::fmt::Display;
2
3use base64::{Engine as _, engine::general_purpose};
4use ring::hmac::{self, Key};
5
6use crate::error::{Error, JWTError, KeyError};
7
8/// Simple wrapper over HMAC_SHA256 key from ring
9#[derive(Clone)]
10pub struct HS256 {
11    key: Key
12}
13
14impl HS256 {
15    
16    /// New instance from a priori known secret
17    pub fn new<A: AsRef<str>>(secret: A) -> Self {
18        
19        let key = hmac::Key::new(hmac::HMAC_SHA256, secret.as_ref().as_bytes());
20        Self {
21            key
22        }
23
24    }
25
26    /// Sign created JWT with shared secret.
27    /// This function can be used in a context where the server is acting as an authorization server and not a
28    /// resource server.
29    pub fn sign_jwt(&self, headers: &str, payload: &str) -> String {
30
31        // Encodes them without pading
32        let header_str = general_purpose::URL_SAFE_NO_PAD.encode(headers);
33        let payload_str = general_purpose::URL_SAFE_NO_PAD.encode(payload);
34
35        // Creates no-signature jwt
36        let unsecure_jwt = format!("{}.{}",header_str,payload_str);
37
38        // Verifies that internal key exists
39        let sign = general_purpose::URL_SAFE_NO_PAD.encode(hmac::sign(&self.key, unsecure_jwt.as_bytes()).as_ref());
40        // Returs signed jwt
41        format!("{}.{}",unsecure_jwt,sign)
42        
43    }
44
45    /// JWT verification starting from the string with format 'a.b.c'
46    /// Returns a new instance of a JWT
47    pub fn verify_jwt(&self, jwt: &str) -> Result<(),Error> {
48
49        // Split jwt by '.'
50        let jwt_parts = jwt.split('.').collect::<Vec<&str>>();
51
52        if jwt_parts.len() != 3 {
53            return Err(Error::JWT(JWTError::JWTParts))
54        }
55
56        // Obtain the url_safe b64 parts to not have to reference the original vector
57        let headerb64_str = &jwt_parts[0];
58        let payloadb64_str = &jwt_parts[1];
59        let signatureb64 = &jwt_parts[2];
60
61        // Create unprotected jwt (i.e. 'a.b' without the signature)
62        let unprotected_jwt = format!("{}.{}",headerb64_str,payloadb64_str);
63        // Obtain signature without b64 encoding
64        let signature = general_purpose::URL_SAFE_NO_PAD.decode(signatureb64).map_err(|e| Error::Decode(e,"Unable to decode signature from jwt"))?;
65
66        // Signature verifiying based on unprotected jwt and signature.
67        // If it's incorrect, the function ends here
68        hmac::verify(&self.key, unprotected_jwt.as_bytes(), signature.as_ref()).map_err(|e| Error::Key(KeyError::Verification(e)))
69
70    }
71
72}
73
74impl Display for HS256 {
75    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76        write!(f,"hs256")
77    }
78}
79
80#[cfg(test)]
81mod test {
82    use crate::{Error, sign_algorithms::HS256};
83
84    #[test]
85    fn verify_signing_hs256() -> Result<(),Error> {
86
87        let header = String::from("{\"Animal\": \"perrito\"}");
88        let payload = String::from("{\"Nombre\": \"Milaneso\"}");
89        let secret = "Doggies";
90
91        let sym_key = HS256::new(secret);
92        
93        let secured_jwt = sym_key.sign_jwt(&header,&payload);
94        
95        sym_key.verify_jwt(&secured_jwt)
96
97    }
98
99}