1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::error::{Error, JWTError};


use rsa::{RsaPublicKey, pkcs1v15::{VerifyingKey, Signature}, sha2::Sha256, BigUint, signature::Verifier};
use base64::{Engine as _, engine::general_purpose};
use std::fmt::Display;

#[derive(Clone)]
/// Simple wrapper arround rsa::VerifyingKey
pub struct RS256 {
    key: VerifyingKey<Sha256>
}

impl RS256 {

    /// Creates a new public key from primitives (like the ones obtained from JWKS identity server's endpoint)
    pub fn new_from_primitives<A: AsRef<str>, B: AsRef<str>>(n: A, e: B) -> Result<Self,Error> {
        
        let n = general_purpose::URL_SAFE_NO_PAD.decode(n.as_ref()).map_err(|e| Error::Decode(e, "Unable to decode modulus 'n' for public key!"))?;
        let e = general_purpose::URL_SAFE_NO_PAD.decode(e.as_ref()).map_err(|e| Error::Decode(e, "Unable to decode exponent 'e' for public key!"))?;
        let n = BigUint::from_bytes_be(&n);
        let e = BigUint::from_bytes_be(&e);

        let public_key = RsaPublicKey::new(n,e)?;

        let verifying_key: VerifyingKey<Sha256> = VerifyingKey::<Sha256>::new(public_key);

        Ok(RS256 {
            key: verifying_key
        })

    }

    /// JWT verification starting from the string with format 'a.b.c'
    /// Returns a new instance of a JWT
    pub fn verify_jwt(&self, jwt: &str) -> Result<(),Error> {

        // Split jwt by '.'
        let jwt_parts = jwt.split('.').collect::<Vec<&str>>();

        if jwt_parts.len() != 3 {
            return Err(Error::JWT(JWTError::JWTParts))
        }

        // Obtain the url_safe b64 parts to not have to refernce the original vector
        let headerb64_str = &jwt_parts[0];
        let payloadb64_str = &jwt_parts[1];
        let signatureb64 = &jwt_parts[2];

        // Create unprotected jwt (i.e. 'a.b' without the signature)
        let unprotected_jwt = format!("{}.{}",headerb64_str,payloadb64_str);
        // Obtain signature without b64 encoding
        let signature = general_purpose::URL_SAFE_NO_PAD.decode(signatureb64).map_err(|e| Error::Decode(e, "Unable to decode signature from jwt"))?;
        let real_signature: Signature = signature.as_slice().try_into()?;

        self.key.verify(unprotected_jwt.as_bytes(), &real_signature)?;
        Ok(())


    }
}

impl Display for RS256 {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f,"rs256")
    }
}


#[cfg(test)]
mod test {

    // use std::io::Read;

    // use base64::{engine::general_purpose, Engine};
    // use ring::{signature::{RsaKeyPair, self}, rand};

    // use crate::{Error, sign_algorithms::RS256};

    // #[test]
    // fn from_primitives() -> Result<(),Error> {

    //     let n = "AKfNQkE4bI8xl9BSMH5WbsSBKAWM6C2F8hS6We3xDJCcqRtdUZEBCBiYo5kt3NIWrFjrcusSYYGXnvT8WRLZr0ERoaEwo-bcxHjBCYhDvgIpa1wIG8psgZmLjxxieKHIArcpkhM0Ly8ku8_dWhoSllH-49NANxKE6w8XLQ2R6CGK4x3KTwd0Wcb5nQaE5gfizZA91yZHoGgUL42BZg_s5RFi-U3XdT0Sw65mza-xZop10TO5xFwi1NFVphf-UeGgyB81sc2SRwufpqP6oZ1Ym6ncrWd-B6UdX5cnlredDUSdJpuhJqSXbPLNbd5qH1WNwO_f5jmi5UHsEEbbaDI2Wkk".to_string();
    //     let e = "AQAB".to_string();
    //     RS256::new_from_primitives(n, e)?;

    //     Ok(())
    // }

    // #[test]
    // fn sign_and_verify_asymmetric_signing() -> Result<(),Error> {

    //     let mut private_key_der = std::fs::File::open("./private.der")?;

    //     let mut contents: Vec<u8> = Vec::new();
    //     private_key_der.read_to_end(&mut contents)?;
    //     let key_pair = RsaKeyPair::from_der(&contents)?;

    //     let header = String::from("{\"Animal\": \"perrito\"}");
    //     let payload = String::from("{\"Nombre\": \"Caloncho\"}");

    //     // Encodes them without pading
    //     let header_str = general_purpose::URL_SAFE_NO_PAD.encode(header);
    //     let payload_str = general_purpose::URL_SAFE_NO_PAD.encode(payload);

    //     let unsecure_jwt = format!("{}.{}",header_str,payload_str);

    //     let rng = rand::SystemRandom::new();
    //     let mut signature_vec = vec![0; key_pair.public_modulus_len()];
    //     // Obtain signature
    //     key_pair.sign(&signature::RSA_PKCS1_SHA256, &rng, unsecure_jwt.as_bytes(), &mut signature_vec)?;
    //     // Returns signed jwt
    //     let secured_jwt = format!("{}.{}",unsecure_jwt, general_purpose::URL_SAFE_NO_PAD.encode(signature_vec));
        
    //     let mut public_key_der = std::fs::File::open("./public.der")?;
    //     let mut contents: Vec<u8> = Vec::new();
    //     public_key_der.read_to_end(&mut contents)?;

    //     let verifying_asym_key = RS256::new(contents)?;

    //     verifying_asym_key.verify_jwt(&secured_jwt)

    // }
}