use base64::Engine; use jwt_rustcrypto::{ decode, decode_only, encode, Algorithm, Error, Header, SigningKey, ValidationOptions, VerifyingKey, }; use rsa::pkcs8::DecodePublicKey; use rsa::traits::PublicKeyParts; use rsa::RsaPublicKey; use serde_json::{json, to_value, value::Value as JsonValue}; use std::fs; use std::path::Path; use std::time::{SystemTime, UNIX_EPOCH}; const TEST_KEYS_DIR: &str = "tests/keys"; fn load_key(file_name: &str) -> String { let path = Path::new(TEST_KEYS_DIR).join(file_name); fs::read_to_string(path).expect("Failed to read key file") } fn current_timestamp() -> u64 { SystemTime::now() .duration_since(UNIX_EPOCH) .expect("SystemTime before UNIX EPOCH") .as_secs() } #[test] fn test_decode_hs256_valid_signature() { let header = Header::new(Algorithm::HS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": to_value(current_timestamp() + 3600).unwrap() }); let signing_key = SigningKey::from_secret(b"mysecret"); let verifying_key = VerifyingKey::from_secret(b"mysecret"); let encoded = encode(&header, &signing_key, &payload).unwrap(); let validation_options = ValidationOptions::default().with_algorithm(Algorithm::HS256); let result = decode(&encoded, &verifying_key, &validation_options); assert!(result.is_ok(), "HS256 decoding failed: {:?}", result.err()); let decoded = result.unwrap(); assert_eq!(decoded.header.alg, Algorithm::HS256); assert_eq!(decoded.payload["sub"], "1234567890"); } #[test] fn test_decode_hs256_invalid_signature() { let header = Header::new(Algorithm::HS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": to_value(current_timestamp() + 3600).unwrap() }); let signing_key = SigningKey::from_secret(b"mysecret"); let verifying_key = VerifyingKey::from_secret(b"mysecret"); let encoded = encode(&header, &signing_key, &payload).unwrap(); // Modify the JWT to invalidate the signature let mut parts: Vec<&str> = encoded.split('.').collect(); parts[2] = "SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; let tampered_token = parts.join("."); let validation_options = ValidationOptions::default().with_algorithm(Algorithm::HS256); let result = decode(&tampered_token, &verifying_key, &validation_options); assert!(result.is_err()); assert!(matches!(result.unwrap_err(), Error::InvalidSignature)); } #[test] fn test_decode_rsa_valid_signature() { let rsa_private_key = load_key("rsa_private_key_pkcs8.pem"); let rsa_public_key = load_key("rsa_public_key_pkcs8.pem"); let signing_key = SigningKey::from_rsa_pem(rsa_private_key.as_bytes()).unwrap(); let verifying_key = VerifyingKey::from_rsa_pem(rsa_public_key.as_bytes()).unwrap(); let header = Header::new(Algorithm::RS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": to_value(current_timestamp() + 3600).unwrap() }); let encoded = encode(&header, &signing_key, &payload).unwrap(); println!("THAT JWT {}", encoded); let validation_options = ValidationOptions::default().with_algorithm(Algorithm::RS256); let result = decode(&encoded, &verifying_key, &validation_options); assert!(result.is_ok(), "RSA256 decoding failed: {:?}", result.err()); let decoded = result.unwrap(); assert_eq!(decoded.header.alg, Algorithm::RS256); assert_eq!(decoded.payload["sub"], "1234567890"); } #[test] fn test_decode_rsa_invalid_signature() { let rsa_public_key = load_key("rsa_public_key_pkcs8.pem"); let verifying_key = VerifyingKey::from_rsa_pem(rsa_public_key.as_bytes()).unwrap(); let tampered_token = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.NHVaYe26MbtOYhSKkoKYdFVomg4i8ZJd8_-RU8VNbftc4TSMb4bXP3l3YlNWACwyXPGffz5aXHc6lty1Y2t4SWRqGteragsVdZufDn5BlnJl9pdR_kdVFUsra2rWKEofkZeIC4yWytE58sMIihvo9H1ScmmVwBcQP6XETqYd0aSHp1gOa9RdUPDvoXQ5oqygTqVtxaDr6wUFKrKItgBMzWIdNZ6y7O9E0DhEPTbE9rfBo6KTFsHAZnMg4k68CDp2woYIaXbmYTWcvbzIuHO7_37GT79XdIwkm95QJ7hYC9RiwrV7mesbY4PAahERJawntho0my942XheVLmGwLMBkQ"; let validation_options = ValidationOptions::default().with_algorithm(Algorithm::RS256); let result = decode(tampered_token, &verifying_key, &validation_options); assert!(result.is_err()); assert!(matches!(result.unwrap_err(), Error::InvalidSignature)); } #[test] fn test_decode_ec_valid_signature() { let ec_private_key = load_key("ec_private_key_p256_pkcs8.pem"); let ec_public_key = load_key("ec_public_key_p256_pkcs8.pem"); let signing_key = SigningKey::from_ec_pem(ec_private_key.as_bytes()).unwrap(); let verifying_key = VerifyingKey::from_ec_pem(ec_public_key.as_bytes()).unwrap(); let header = Header::new(Algorithm::ES256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": to_value(current_timestamp() + 3600).unwrap() }); let encoded = encode(&header, &signing_key, &payload).unwrap(); println!("THIS JWT {}", encoded); let validation_options = ValidationOptions::default().with_algorithm(Algorithm::ES256); let result = decode(&encoded, &verifying_key, &validation_options); assert!(result.is_ok(), "ES256 decoding failed: {:?}", result.err()); let decoded = result.unwrap(); assert_eq!(decoded.header.alg, Algorithm::ES256); assert_eq!(decoded.payload["sub"], "1234567890"); } #[test] fn test_decode_with_expired_validation() { let rsa_private_key = load_key("rsa_private_key_pkcs8.pem"); let rsa_public_key = load_key("rsa_public_key_pkcs8.pem"); let signing_key = SigningKey::from_rsa_pem(rsa_private_key.as_bytes()).unwrap(); let verifying_key = VerifyingKey::from_rsa_pem(rsa_public_key.as_bytes()).unwrap(); let header = Header::new(Algorithm::RS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": to_value(current_timestamp() - 3600).unwrap() }); let encoded = encode(&header, &signing_key, &payload).unwrap(); let validation_options = ValidationOptions::default().with_algorithm(Algorithm::RS256); let result = decode(&encoded, &verifying_key, &validation_options); assert!(matches!(result, Err(Error::ExpiredSignature))); } #[test] fn test_decode_with_issuer_validation() { let rsa_private_key = load_key("rsa_private_key_pkcs8.pem"); let rsa_public_key = load_key("rsa_public_key_pkcs8.pem"); let signing_key = SigningKey::from_rsa_pem(rsa_private_key.as_bytes()).unwrap(); let verifying_key = VerifyingKey::from_rsa_pem(rsa_public_key.as_bytes()).unwrap(); let header = Header::new(Algorithm::RS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "iss": "trusted_issuer", "exp": to_value(current_timestamp() + 3600).unwrap() }); let encoded = encode(&header, &signing_key, &payload).unwrap(); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::RS256) .with_issuer("trusted_issuer"); let result = decode(&encoded, &verifying_key, &validation_options); assert!( result.is_ok(), "Issuer validation failed: {:?}", result.err() ); } #[test] fn test_decode_with_issuer_validation_fail() { let rsa_private_key = load_key("rsa_private_key_pkcs8.pem"); let rsa_public_key = load_key("rsa_public_key_pkcs8.pem"); let signing_key = SigningKey::from_rsa_pem(rsa_private_key.as_bytes()).unwrap(); let verifying_key = VerifyingKey::from_rsa_pem(rsa_public_key.as_bytes()).unwrap(); let header = Header::new(Algorithm::RS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "iss": "untrusted_issuer", "exp": to_value(current_timestamp() + 3600).unwrap() }); let encoded = encode(&header, &signing_key, &payload).unwrap(); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::RS256) .with_issuer("trusted_issuer"); let result = decode(&encoded, &verifying_key, &validation_options); assert!(matches!(result, Err(Error::InvalidIssuer))); } #[test] fn test_decode_with_audience_validation() { let rsa_private_key = load_key("rsa_private_key_pkcs8.pem"); let rsa_public_key = load_key("rsa_public_key_pkcs8.pem"); let signing_key = SigningKey::from_rsa_pem(rsa_private_key.as_bytes()).unwrap(); let verifying_key = VerifyingKey::from_rsa_pem(rsa_public_key.as_bytes()).unwrap(); let header = Header::new(Algorithm::RS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "aud": "trusted_audience", "exp": to_value(current_timestamp() + 3600).unwrap() }); let encoded = encode(&header, &signing_key, &payload).unwrap(); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::RS256) .with_audience("trusted_audience"); let result = decode(&encoded, &verifying_key, &validation_options); assert!( result.is_ok(), "Audience validation failed: {:?}", result.err() ); } #[test] fn test_decode_with_audience_validation_fail() { let rsa_private_key = load_key("rsa_private_key_pkcs8.pem"); let rsa_public_key = load_key("rsa_public_key_pkcs8.pem"); let signing_key = SigningKey::from_rsa_pem(rsa_private_key.as_bytes()).unwrap(); let verifying_key = VerifyingKey::from_rsa_pem(rsa_public_key.as_bytes()).unwrap(); let header = Header::new(Algorithm::RS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "aud": "untrusted_audience", "exp": to_value(current_timestamp() + 3600).unwrap() }); let encoded = encode(&header, &signing_key, &payload).unwrap(); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::RS256) .with_audience("trusted_audience"); let result = decode(&encoded, &verifying_key, &validation_options); assert!(matches!(result, Err(Error::InvalidAudience))); } #[test] fn test_decode_only() { let header = Header::new(Algorithm::HS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": to_value(current_timestamp() + 3600).unwrap() }); let signing_key = SigningKey::from_secret(b"mysecret"); let encoded = encode(&header, &signing_key, &payload).unwrap(); let result = decode_only(&encoded); assert!(result.is_ok(), "Decoding failed: {:?}", result.err()); let decoded = result.unwrap(); assert_eq!(decoded.header.alg, Algorithm::HS256); assert_eq!(decoded.payload["sub"], "1234567890"); } #[test] fn test_decode_with_rsa_from_components() { let jwk_pub_key = |pem: &str| -> JsonValue { let rsa_public_key = RsaPublicKey::from_public_key_pem(pem).unwrap(); let n = rsa_public_key.n().to_bytes_be(); let e = rsa_public_key.e().to_bytes_be(); json!({ "kty": "RSA", "n": base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(n), "e": base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(e), "alg": "RS256", "use": "sig", }) }; let rsa_private_key = load_key("rsa_private_key_pkcs8.pem"); let rsa_public_key = load_key("rsa_public_key_pkcs8.pem"); let jwk = jwk_pub_key(&rsa_public_key); let signing_key = SigningKey::from_rsa_pem(rsa_private_key.as_bytes()).unwrap(); let n = base64::engine::general_purpose::URL_SAFE_NO_PAD .decode(jwk["n"].as_str().unwrap()) .unwrap(); let e = base64::engine::general_purpose::URL_SAFE_NO_PAD .decode(jwk["e"].as_str().unwrap()) .unwrap(); let verifying_key = VerifyingKey::from_rsa_components(&n, &e).unwrap(); let header = Header::new(Algorithm::RS256); let payload = json!({ "sub": "1234567890", "name": "John Doe", "iat": 1516239022, "exp": to_value(current_timestamp() + 3600).unwrap() }); let encoded = encode(&header, &signing_key, &payload).unwrap(); let validation_options = ValidationOptions::default().with_algorithm(Algorithm::RS256); let result = decode(&encoded, &verifying_key, &validation_options); assert!(result.is_ok(), "RSA256 decoding failed: {:?}", result.err()); let decoded = result.unwrap(); assert_eq!(decoded.header.alg, Algorithm::RS256); assert_eq!(decoded.payload["sub"], "1234567890"); } #[test] fn test_existing_rsa_signed_jwt() { let pubkey_str = load_key("test_pub_rsa_pkcs1.pem"); let test_jwt: &str = "eyJ0eXAiOiJzZC1qd3QiLCJhbGciOiJSUzI1NiJ9.eyJfc2QiOlsiVFhsUEt1RjM1cDQ3ZW9XTlpEcklxS0w0R0JFaDBFWXJEQnBjNmFCWjUyQSIsIkdYWlpyVUlsdnBtaDB4b0h4WURadzFOZ211WXJrd1VVS09rNG1XTHZKYUEiXSwiX3NkX2FsZyI6InNoYS0yNTYiLCJhZGRyZXNzIjp7Il9zZCI6WyJiUjVKM21ULXQ0a05pZ0V0dDJ5RVd1MU92b0hVMzBmSTZ1RVdJd2ozZWJBIiwiczhicTVKeUtJaFFwcVR1Vl9hcVNtd090UVN5UHV1TUlUU2xINXg1UWI5RSJdLCJjb3VudHJ5IjoiVVMiLCJyZWdpb24iOiJBbnlzdGF0ZSJ9LCJiaXJ0aGRhdGUiOiIxOTQwLTAxLTAxIiwiY25mIjp7ImFsZyI6IlJTMjU2IiwiZSI6IkFRQUIiLCJrdHkiOiJSU0EiLCJuIjoiNS1EZDU0WHNNQU5UWm9KMllCcHVpWmFfYXpyMzJIcEJ3MUZjanA1d1UwWFBqbW9NQTdKVllDSk4wU05maDZ0dFhyWHhhYWhFNXdmUzd4S1E0N1ZvWXhYTjlLa3kxMzdDSUx0Q0xPWUJDZkdULWFRRXJKS0FJWUVORWtzbVNpU3k0VnVWRk1yTzlMOV9KTzViZk02QjZ6X3pickJYX2MxU2s0UFRLTnBqRTcxcTJHenU4ak5GdTR0c0JaOFFSdmtJVldxNGdxVklQNTFQQmZEcmNfTm53dk1aallGN2pfc0Z5eGg2ZExTVV96QkRrZjJOVWo4VXQ0M25vcW9YMGJoaE96aGdyTlpadGpFMTlrZGFlZTJYbjBweG0td3QzRjBxUjZxd2F2TFRJT21LVHE0OFdXSGxvUk5QWXpGbEo4OHNOaVNLeW9Ta0hXMG9SVDlscUhGX3ZRIiwidXNlIjoic2lnIn0sImVtYWlsIjoiam9obmRvZUBleGFtcGxlLmNvbSIsIm5hdGlvbmFsaXRpZXMiOlt7Ii4uLiI6InhnU2FMYS1CNk03OWpwVWZtaE9Hb0pkSHdNS0RNR0s3eUVKdC0tX0xScDAifSx7Ii4uLiI6Im5vNWxNSkVJSmRWdHozS3lDMVRXVkk2T2tsQnZIMjFCOExOOVEzWkxWRmMifV0sInBob25lX251bWJlciI6IisxLTIwMi01NTUtMDEwMSIsInBob25lX251bWJlcl92ZXJpZmllZCI6dHJ1ZSwic3ViIjoidXNlcl80MiIsInVwZGF0ZWRfYXQiOjE1NzAwMDAwMDB9.K2h-DNDgnq6q61tSxm1Gv-Hfo46SD8rEcP7yLFxcAlQNKBY-l1-bpXCJcqVZ7jugs2lqng0Cf9e34tM1OPkU3R6Pi5kUMGSyJ2y2ifsaZhGLCgxzNKk5W2ZxdkehzZQ6nHy6iu4flbT92Szv0eBR0hmS3hYTCtHlE4xib9G2dKWTQigB4ylPMkoRzbiKjgkucGkxSLN5ZQRXdxkez19bk5Q9BwuNLQMKG0lanq4ZJWq1C4LPt_K0WhEntyTL6SxVxGfR5HaUSxeYPCCOWSz9AVyZ46DWZGRx48PbuXGgLDH1UJYIsMej2F89CU-3QkWUrFq9b-DCYCQMxbBBekeLog"; let verifying_key = VerifyingKey::from_rsa_pem(pubkey_str.as_bytes()).unwrap(); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::RS256) .without_expiry(); let result = decode(test_jwt, &verifying_key, &validation_options); assert!(result.is_ok(), "RSA256 decoding failed: {:?}", result.err()); let decoded = result.unwrap(); assert_eq!(decoded.header.alg, Algorithm::RS256); } #[test] fn test_existing_hmac_signed_jwt() { let verifying_key = VerifyingKey::from_secret(b"your-256-bit-secret"); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::HS256) .without_expiry(); let test_jwt: &str = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; let result = decode(test_jwt, &verifying_key, &validation_options); assert!(result.is_ok(), "RSA256 decoding failed: {:?}", result.err()); let decoded = result.unwrap(); assert_eq!(decoded.header.alg, Algorithm::HS256); } #[test] fn test_existing_es256_signed_jwt() { let pubkey_str = load_key("test_pub_es256_pkcs8.pem"); let test_jwt: &str = "eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.tyh-VfuzIxCyGYDlkBA7DfyjrqmSHu6pQ2hoZuFqUSLPNY2N0mpHb3nk5K17HWP_3cYHBw7AhHale5wky6-sVA"; let verifying_key = VerifyingKey::from_ec_pem(pubkey_str.as_bytes()).unwrap(); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::ES256) .without_expiry(); let result = decode(test_jwt, &verifying_key, &validation_options); assert!(result.is_ok(), "ES256 decoding failed: {:?}", result.err()); } #[test] fn test_existing_es384_signed_jwt() { let pubkey_str = load_key("test_pub_es384_pkcs8.pem"); let test_jwt: &str = "eyJhbGciOiJFUzM4NCIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.VUPWQZuClnkFbaEKCsPy7CZVMh5wxbCSpaAWFLpnTe9J0--PzHNeTFNXCrVHysAa3eFbuzD8_bLSsgTKC8SzHxRVSj5eN86vBPo_1fNfE7SHTYhWowjY4E_wuiC13yoj"; let verifying_key = VerifyingKey::from_ec_pem(pubkey_str.as_bytes()).unwrap(); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::ES384) .without_expiry(); let result = decode(test_jwt, &verifying_key, &validation_options); assert!(result.is_ok(), "ES384 decoding failed: {:?}", result.err()); } #[test] fn test_existing_es512_signed_jwt() { let pubkey_str = load_key("test_pub_es512_pkcs8.pem"); let test_jwt: &str = "eyJhbGciOiJFUzUxMiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiYWRtaW4iOnRydWUsImlhdCI6MTUxNjIzOTAyMn0.AbVUinMiT3J_03je8WTOIl-VdggzvoFgnOsdouAs-DLOtQzau9valrq-S6pETyi9Q18HH-EuwX49Q7m3KC0GuNBJAc9Tksulgsdq8GqwIqZqDKmG7hNmDzaQG1Dpdezn2qzv-otf3ZZe-qNOXUMRImGekfQFIuH_MjD2e8RZyww6lbZk"; let verifying_key = VerifyingKey::from_ec_pem(pubkey_str.as_bytes()).unwrap(); let validation_options = ValidationOptions::default() .with_algorithm(Algorithm::ES512) .without_expiry(); let result = decode(test_jwt, &verifying_key, &validation_options); assert!(result.is_ok(), "ES512 decoding failed: {:?}", result.err()); }