jwt_rustcrypto/
encode.rs

1use crate::{sign_es, sign_hmac, sign_rsa, Algorithm, Error, Header, SigningKey};
2use base64::Engine;
3use serde::Serialize;
4use serde_json::Value as JsonValue;
5
6/// Encodes a JWT using the provided header, signing key, and payload.
7/// Returns the encoded JWT.
8/// # Arguments
9/// * `header` - The header of the JWT.
10/// * `signing_key` - The signing key used to sign the JWT.
11/// * `payload` - The payload of the JWT.
12/// # Example
13/// ```
14/// use jwt_rustcrypto::{encode, Algorithm, Header, SigningKey};
15/// use serde_json::json;
16///
17/// let header = Header::new(Algorithm::HS256);
18/// let signing_key = SigningKey::from_secret(b"mysecret");
19/// let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 });
20///
21/// let encoded = encode(&header, &signing_key, &payload);
22/// assert!(encoded.is_ok());
23/// ```
24pub fn encode<T: Serialize>(
25    header: &Header,
26    signing_key: &SigningKey,
27    payload: &T,
28) -> Result<String, Error> {
29    let header_json = serde_json::to_value(header)?;
30    let payload_json = serde_json::to_value(payload)?;
31    let signing_input = get_signing_input(&payload_json, &header_json)?;
32
33    let signature = match header.alg {
34        Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => {
35            sign_hmac(&signing_input, signing_key, &header.alg)
36        }
37        Algorithm::RS256
38        | Algorithm::RS384
39        | Algorithm::RS512
40        | Algorithm::PS256
41        | Algorithm::PS384
42        | Algorithm::PS512 => sign_rsa(&signing_input, signing_key, &header.alg),
43        Algorithm::ES256 | Algorithm::ES256K | Algorithm::ES384 | Algorithm::ES512 => {
44            sign_es(&signing_input, signing_key, &header.alg)
45        }
46    }?;
47
48    Ok(format!("{}.{}", signing_input, signature))
49}
50
51fn get_signing_input(payload: &JsonValue, header: &JsonValue) -> Result<String, Error> {
52    let header_str = serde_json::to_string(header)?;
53    let payload_str = serde_json::to_string(payload)?;
54    let signing_input = format!(
55        "{}.{}",
56        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(header_str.as_bytes()),
57        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(payload_str.as_bytes())
58    );
59    Ok(signing_input)
60}
61
62#[cfg(test)]
63mod tests {
64    use super::*;
65    use crate::Algorithm;
66    use serde_json::json;
67    use std::fs;
68    use std::path::Path;
69
70    const TEST_KEYS_DIR: &str = "tests/keys";
71
72    fn load_key(file_name: &str) -> String {
73        let path = Path::new(TEST_KEYS_DIR).join(file_name);
74        fs::read_to_string(path).expect("Failed to read key file")
75    }
76
77    #[test]
78    fn test_get_signing_input_valid_input() {
79        let header = json!({ "alg": "HS256", "typ": "JWT" });
80        let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022 });
81
82        let result = get_signing_input(&payload, &header);
83        assert!(result.is_ok());
84        assert!(result.unwrap().contains('.'));
85    }
86
87    #[test]
88    fn test_sign_rsa_invalid_digest() {
89        let signing_key =
90            SigningKey::from_rsa_pem(load_key("rsa_private_key_pkcs8.pem").as_bytes()).unwrap();
91        let invalid_alg = Algorithm::ES512; // ES algorithms not supported in RSA signing
92
93        let result = sign_rsa("data", &signing_key, &invalid_alg);
94        assert!(result.is_err());
95        assert_eq!(result.unwrap_err().to_string(), "Unsupported algorithm");
96    }
97}