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
6pub 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; 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}