diff --git a/benches/jwt.rs b/benches/jwt.rs index 8d42936..dab789c 100644 --- a/benches/jwt.rs +++ b/benches/jwt.rs @@ -1,23 +1,20 @@ #![feature(test)] -extern crate test; extern crate jsonwebtoken as jwt; +extern crate test; #[macro_use] extern crate serde_derive; -use jwt::{encode, decode, Header, Validation}; +use jwt::{decode, encode, Header, Validation}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] struct Claims { sub: String, - company: String + company: String, } #[bench] fn bench_encode(b: &mut test::Bencher) { - let claim = Claims { - sub: "b@b.com".to_owned(), - company: "ACME".to_owned() - }; + let claim = Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned() }; b.iter(|| encode(&Header::default(), &claim, "secret".as_ref())); } diff --git a/examples/custom_header.rs b/examples/custom_header.rs index 38d2248..99d346d 100644 --- a/examples/custom_header.rs +++ b/examples/custom_header.rs @@ -2,9 +2,8 @@ extern crate jsonwebtoken as jwt; #[macro_use] extern crate serde_derive; -use jwt::{encode, decode, Header, Algorithm, Validation}; -use jwt::errors::{ErrorKind}; - +use jwt::errors::ErrorKind; +use jwt::{decode, encode, Algorithm, Header, Validation}; #[derive(Debug, Serialize, Deserialize)] struct Claims { @@ -14,11 +13,8 @@ struct Claims { } fn main() { - let my_claims = Claims { - sub: "b@b.com".to_owned(), - company: "ACME".to_owned(), - exp: 10000000000, - }; + let my_claims = + Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned(), exp: 10000000000 }; let key = "secret"; let mut header = Header::default(); @@ -27,17 +23,18 @@ fn main() { let token = match encode(&header, &my_claims, key.as_ref()) { Ok(t) => t, - Err(_) => panic!() // in practice you would return the error + Err(_) => panic!(), // in practice you would return the error }; println!("{:?}", token); - let token_data = match decode::(&token, key.as_ref(), &Validation::new(Algorithm::HS512)) { - Ok(c) => c, - Err(err) => match *err.kind() { - ErrorKind::InvalidToken => panic!(), // Example on how to handle a specific error - _ => panic!() - } - }; + let token_data = + match decode::(&token, key.as_ref(), &Validation::new(Algorithm::HS512)) { + Ok(c) => c, + Err(err) => match *err.kind() { + ErrorKind::InvalidToken => panic!(), // Example on how to handle a specific error + _ => panic!(), + }, + }; println!("{:?}", token_data.claims); println!("{:?}", token_data.header); } diff --git a/examples/validation.rs b/examples/validation.rs index 987cdbe..31fb7eb 100644 --- a/examples/validation.rs +++ b/examples/validation.rs @@ -2,9 +2,8 @@ extern crate jsonwebtoken as jwt; #[macro_use] extern crate serde_derive; -use jwt::{encode, decode, Header, Validation}; -use jwt::errors::{ErrorKind}; - +use jwt::errors::ErrorKind; +use jwt::{decode, encode, Header, Validation}; #[derive(Debug, Serialize, Deserialize)] struct Claims { @@ -14,28 +13,22 @@ struct Claims { } fn main() { - let my_claims = Claims { - sub: "b@b.com".to_owned(), - company: "ACME".to_owned(), - exp: 10000000000, - }; + let my_claims = + Claims { sub: "b@b.com".to_owned(), company: "ACME".to_owned(), exp: 10000000000 }; let key = "secret"; let token = match encode(&Header::default(), &my_claims, key.as_ref()) { Ok(t) => t, - Err(_) => panic!() // in practice you would return the error + Err(_) => panic!(), // in practice you would return the error }; - let validation = Validation { - sub: Some("b@b.com".to_string()), - ..Validation::default() - }; + let validation = Validation { sub: Some("b@b.com".to_string()), ..Validation::default() }; let token_data = match decode::(&token, key.as_ref(), &validation) { Ok(c) => c, Err(err) => match *err.kind() { ErrorKind::InvalidToken => panic!("Token is invalid"), // Example on how to handle a specific error ErrorKind::InvalidIssuer => panic!("Issuer is invalid"), // Example on how to handle a specific error - _ => panic!("Some other errors") - } + _ => panic!("Some other errors"), + }, }; println!("{:?}", token_data.claims); println!("{:?}", token_data.header); diff --git a/rustfmt.toml b/rustfmt.toml new file mode 100644 index 0000000..d45dc0e --- /dev/null +++ b/rustfmt.toml @@ -0,0 +1 @@ +use_small_heuristics = "max" diff --git a/src/crypto.rs b/src/crypto.rs index 81bbda1..207897b 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -1,13 +1,12 @@ use std::sync::Arc; use base64; -use ring::{rand, digest, hmac, signature}; use ring::constant_time::verify_slices_are_equal; -use untrusted; +use ring::{digest, hmac, rand, signature}; use std::str::FromStr; +use untrusted; -use errors::{Result, Error, ErrorKind, new_error}; - +use errors::{new_error, Error, ErrorKind, Result}; /// The algorithms supported for signing/verifying #[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)] @@ -53,9 +52,7 @@ fn sign_hmac(alg: &'static digest::Algorithm, key: &[u8], signing_input: &str) - let signing_key = hmac::SigningKey::new(alg, key); let digest = hmac::sign(&signing_key, signing_input.as_bytes()); - Ok( - base64::encode_config::(&digest, base64::URL_SAFE_NO_PAD) - ) + Ok(base64::encode_config::(&digest, base64::URL_SAFE_NO_PAD)) } /// The actual RSA signing + encoding @@ -70,18 +67,17 @@ fn sign_rsa(alg: Algorithm, key: &[u8], signing_input: &str) -> Result { let key_pair = Arc::new( signature::RSAKeyPair::from_der(untrusted::Input::from(key)) - .map_err(|_| ErrorKind::InvalidRsaKey)? + .map_err(|_| ErrorKind::InvalidRsaKey)?, ); - let mut signing_state = signature::RSASigningState::new(key_pair) - .map_err(|_| ErrorKind::InvalidRsaKey)?; + let mut signing_state = + signature::RSASigningState::new(key_pair).map_err(|_| ErrorKind::InvalidRsaKey)?; let mut signature = vec![0; signing_state.key_pair().public_modulus_len()]; let rng = rand::SystemRandom::new(); - signing_state.sign(ring_alg, &rng, signing_input.as_bytes(), &mut signature) + signing_state + .sign(ring_alg, &rng, signing_input.as_bytes(), &mut signature) .map_err(|_| ErrorKind::InvalidRsaKey)?; - Ok( - base64::encode_config::<[u8]>(&signature, base64::URL_SAFE_NO_PAD) - ) + Ok(base64::encode_config::<[u8]>(&signature, base64::URL_SAFE_NO_PAD)) } /// Take the payload of a JWT, sign it using the algorithm given and return @@ -94,12 +90,19 @@ pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result sign_hmac(&digest::SHA384, key, signing_input), Algorithm::HS512 => sign_hmac(&digest::SHA512, key, signing_input), - Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => sign_rsa(algorithm, key, signing_input), + Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => { + sign_rsa(algorithm, key, signing_input) + } } } /// See Ring RSA docs for more details -fn verify_rsa(alg: &signature::RSAParameters, signature: &str, signing_input: &str, key: &[u8]) -> Result { +fn verify_rsa( + alg: &signature::RSAParameters, + signature: &str, + signing_input: &str, + key: &[u8], +) -> Result { let signature_bytes = base64::decode_config(signature, base64::URL_SAFE_NO_PAD)?; let public_key_der = untrusted::Input::from(key); let message = untrusted::Input::from(signing_input.as_bytes()); @@ -118,15 +121,26 @@ fn verify_rsa(alg: &signature::RSAParameters, signature: &str, signing_input: &s /// `signature` is the signature part of a jwt (text after the second '.') /// /// `signing_input` is base64(header) + "." + base64(claims) -pub fn verify(signature: &str, signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result { +pub fn verify( + signature: &str, + signing_input: &str, + key: &[u8], + algorithm: Algorithm, +) -> Result { match algorithm { Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { // we just re-sign the data with the key and compare if they are equal let signed = sign(signing_input, key, algorithm)?; Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok()) - }, - Algorithm::RS256 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA256, signature, signing_input, key), - Algorithm::RS384 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA384, signature, signing_input, key), - Algorithm::RS512 => verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA512, signature, signing_input, key), + } + Algorithm::RS256 => { + verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA256, signature, signing_input, key) + } + Algorithm::RS384 => { + verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA384, signature, signing_input, key) + } + Algorithm::RS512 => { + verify_rsa(&signature::RSA_PKCS1_2048_8192_SHA512, signature, signing_input, key) + } } } diff --git a/src/errors.rs b/src/errors.rs index 6ff1f1d..d450b9a 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -10,7 +10,6 @@ pub(crate) fn new_error(kind: ErrorKind) -> Error { Error(Box::new(kind)) } - /// A type alias for `Result`. pub type Result = result::Result; @@ -43,7 +42,6 @@ pub enum ErrorKind { InvalidAlgorithmName, // validation error - /// When a token’s `exp` claim indicates that it has expired ExpiredSignature, /// When a token’s `iss` claim does not match the expected issuer @@ -60,7 +58,6 @@ pub enum ErrorKind { InvalidAlgorithm, // 3rd party errors - /// An error happened when decoding some base64 text Base64(base64::DecodeError), /// An error happened while serializing/deserializing JSON @@ -68,7 +65,6 @@ pub enum ErrorKind { /// Some of the text was invalid UTF-8 Utf8(::std::string::FromUtf8Error), - /// Hints that destructuring should not be exhaustive. /// /// This enum may grow additional variants, so this makes sure clients diff --git a/src/header.rs b/src/header.rs index 042149c..0db2614 100644 --- a/src/header.rs +++ b/src/header.rs @@ -1,6 +1,5 @@ use crypto::Algorithm; - /// A basic JWT header, the alg defaults to HS256 and typ is automatically /// set to `JWT`. All the other fields are optional. #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] diff --git a/src/lib.rs b/src/lib.rs index b99079d..592e565 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -6,37 +6,31 @@ #[macro_use] extern crate serde_derive; -extern crate serde_json; -extern crate serde; extern crate base64; -extern crate ring; -extern crate untrusted; extern crate chrono; +extern crate ring; +extern crate serde; +extern crate serde_json; +extern crate untrusted; +mod crypto; /// All the errors, generated using error-chain pub mod errors; mod header; -mod crypto; mod serialization; mod validation; +pub use crypto::{sign, verify, Algorithm}; pub use header::Header; -pub use crypto::{ - Algorithm, - sign, - verify, -}; -pub use validation::Validation; pub use serialization::TokenData; - +pub use validation::Validation; use serde::de::DeserializeOwned; use serde::ser::Serialize; -use errors::{Result, ErrorKind, new_error}; +use errors::{new_error, ErrorKind, Result}; use serialization::{from_jwt_part, from_jwt_part_claims, to_jwt_part}; -use validation::{validate}; - +use validation::validate; /// Encode the header and claims given and sign the payload using the algorithm from the header and the key /// @@ -76,9 +70,9 @@ macro_rules! expect_two { let mut i = $iter; match (i.next(), i.next(), i.next()) { (Some(first), Some(second), None) => (first, second), - _ => return Err(new_error(ErrorKind::InvalidToken)) + _ => return Err(new_error(ErrorKind::InvalidToken)), } - }} + }}; } /// Decode a token into a struct containing 2 fields: `claims` and `header`. @@ -100,7 +94,11 @@ macro_rules! expect_two { /// // Claims is a struct that implements Deserialize /// let token_data = decode::(&token, "secret", &Validation::new(Algorithm::HS256)); /// ``` -pub fn decode(token: &str, key: &[u8], validation: &Validation) -> Result> { +pub fn decode( + token: &str, + key: &[u8], + validation: &Validation, +) -> Result> { let (signature, signing_input) = expect_two!(token.rsplitn(2, '.')); let (claims, header) = expect_two!(signing_input.rsplitn(2, '.')); let header: Header = from_jwt_part(header)?; @@ -113,7 +111,7 @@ pub fn decode(token: &str, key: &[u8], validation: &Validat return Err(new_error(ErrorKind::InvalidAlgorithm)); } - let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?; + let (decoded_claims, claims_map): (T, _) = from_jwt_part_claims(claims)?; validate(&claims_map, validation)?; Ok(TokenData { header, claims: decoded_claims }) @@ -143,7 +141,7 @@ pub fn dangerous_unsafe_decode(token: &str) -> Result { /// The decoded JWT header pub header: Header, /// The decoded JWT claims - pub claims: T + pub claims: T, } /// Serializes to JSON and encodes to base64 @@ -32,11 +31,13 @@ pub fn from_jwt_part, T: DeserializeOwned>(encoded: B) -> Result, T: DeserializeOwned>(encoded: B) -> Result<(T, Map)> { +pub fn from_jwt_part_claims, T: DeserializeOwned>( + encoded: B, +) -> Result<(T, Map)> { let decoded = base64::decode_config(encoded.as_ref(), base64::URL_SAFE_NO_PAD)?; let s = String::from_utf8(decoded)?; let claims: T = from_str(&s)?; - let map: Map<_,_> = from_str(&s)?; + let map: Map<_, _> = from_str(&s)?; Ok((claims, map)) } diff --git a/src/validation.rs b/src/validation.rs index dc87301..3684823 100644 --- a/src/validation.rs +++ b/src/validation.rs @@ -1,11 +1,10 @@ use chrono::Utc; use serde::ser::Serialize; -use serde_json::{Value, from_value, to_value}; use serde_json::map::Map; +use serde_json::{from_value, to_value, Value}; -use errors::{Result, ErrorKind, new_error}; use crypto::Algorithm; - +use errors::{new_error, ErrorKind, Result}; /// Contains the various validations that are applied after decoding a token. /// @@ -107,8 +106,6 @@ impl Default for Validation { } } - - pub fn validate(claims: &Map, options: &Validation) -> Result<()> { let now = Utc::now().timestamp(); @@ -175,12 +172,11 @@ pub fn validate(claims: &Map, options: &Validation) -> Result<()> Ok(()) } - #[cfg(test)] mod tests { - use serde_json::{to_value}; - use serde_json::map::Map; use chrono::Utc; + use serde_json::map::Map; + use serde_json::to_value; use super::{validate, Validation}; @@ -190,7 +186,8 @@ mod tests { fn iat_in_past_ok() { let mut claims = Map::new(); claims.insert("iat".to_string(), to_value(Utc::now().timestamp() - 10000).unwrap()); - let validation = Validation { validate_exp: false, validate_iat: true, ..Validation::default() }; + let validation = + Validation { validate_exp: false, validate_iat: true, ..Validation::default() }; let res = validate(&claims, &validation); assert!(res.is_ok()); } @@ -199,7 +196,8 @@ mod tests { fn iat_in_future_fails() { let mut claims = Map::new(); claims.insert("iat".to_string(), to_value(Utc::now().timestamp() + 100000).unwrap()); - let validation = Validation { validate_exp: false, validate_iat: true, ..Validation::default() }; + let validation = + Validation { validate_exp: false, validate_iat: true, ..Validation::default() }; let res = validate(&claims, &validation); assert!(res.is_err()); @@ -248,10 +246,7 @@ mod tests { fn exp_in_past_but_in_leeway_ok() { let mut claims = Map::new(); claims.insert("exp".to_string(), to_value(Utc::now().timestamp() - 500).unwrap()); - let validation = Validation { - leeway: 1000 * 60, - ..Default::default() - }; + let validation = Validation { leeway: 1000 * 60, ..Default::default() }; let res = validate(&claims, &validation); assert!(res.is_ok()); } @@ -272,7 +267,8 @@ mod tests { fn nbf_in_past_ok() { let mut claims = Map::new(); claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() - 10000).unwrap()); - let validation = Validation { validate_exp: false, validate_nbf: true, ..Validation::default() }; + let validation = + Validation { validate_exp: false, validate_nbf: true, ..Validation::default() }; let res = validate(&claims, &validation); assert!(res.is_ok()); } @@ -281,7 +277,8 @@ mod tests { fn nbf_in_future_fails() { let mut claims = Map::new(); claims.insert("nbf".to_string(), to_value(Utc::now().timestamp() + 100000).unwrap()); - let validation = Validation { validate_exp: false, validate_nbf: true, ..Validation::default() }; + let validation = + Validation { validate_exp: false, validate_nbf: true, ..Validation::default() }; let res = validate(&claims, &validation); assert!(res.is_err()); @@ -405,10 +402,7 @@ mod tests { fn aud_string_ok() { let mut claims = Map::new(); claims.insert("aud".to_string(), to_value("Everyone").unwrap()); - let mut validation = Validation { - validate_exp: false, - ..Validation::default() - }; + let mut validation = Validation { validate_exp: false, ..Validation::default() }; validation.set_audience(&"Everyone"); let res = validate(&claims, &validation); assert!(res.is_ok()); @@ -418,10 +412,7 @@ mod tests { fn aud_array_of_string_ok() { let mut claims = Map::new(); claims.insert("aud".to_string(), to_value(["UserA", "UserB"]).unwrap()); - let mut validation = Validation { - validate_exp: false, - ..Validation::default() - }; + let mut validation = Validation { validate_exp: false, ..Validation::default() }; validation.set_audience(&["UserA", "UserB"]); let res = validate(&claims, &validation); assert!(res.is_ok()); @@ -431,10 +422,7 @@ mod tests { fn aud_type_mismatch_fails() { let mut claims = Map::new(); claims.insert("aud".to_string(), to_value("Everyone").unwrap()); - let mut validation = Validation { - validate_exp: false, - ..Validation::default() - }; + let mut validation = Validation { validate_exp: false, ..Validation::default() }; validation.set_audience(&["UserA", "UserB"]); let res = validate(&claims, &validation); assert!(res.is_err()); @@ -449,10 +437,7 @@ mod tests { fn aud_correct_type_not_matching_fails() { let mut claims = Map::new(); claims.insert("aud".to_string(), to_value("Everyone").unwrap()); - let mut validation = Validation { - validate_exp: false, - ..Validation::default() - }; + let mut validation = Validation { validate_exp: false, ..Validation::default() }; validation.set_audience(&"None"); let res = validate(&claims, &validation); assert!(res.is_err()); @@ -466,10 +451,7 @@ mod tests { #[test] fn aud_missing_fails() { let claims = Map::new(); - let mut validation = Validation { - validate_exp: false, - ..Validation::default() - }; + let mut validation = Validation { validate_exp: false, ..Validation::default() }; validation.set_audience(&"None"); let res = validate(&claims, &validation); assert!(res.is_err()); diff --git a/tests/lib.rs b/tests/lib.rs index 740d5c0..129ee3d 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -3,8 +3,11 @@ extern crate jsonwebtoken; extern crate serde_derive; extern crate chrono; -use jsonwebtoken::{encode, decode, decode_header, dangerous_unsafe_decode, Algorithm, Header, sign, verify, Validation}; use chrono::Utc; +use jsonwebtoken::{ + dangerous_unsafe_decode, decode, decode_header, encode, sign, verify, Algorithm, Header, + Validation, +}; use std::str::FromStr; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] @@ -75,7 +78,8 @@ fn decode_token_missing_parts() { #[test] #[should_panic(expected = "InvalidSignature")] fn decode_token_invalid_signature() { - let token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong"; + let token = + "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJiQGIuY29tIiwiY29tcGFueSI6IkFDTUUifQ.wrong"; let claims = decode::(token, "secret".as_ref(), &Validation::default()); claims.unwrap(); } @@ -141,7 +145,7 @@ fn does_validation_in_right_order() { exp: Utc::now().timestamp() + 10000, }; let token = encode(&Header::default(), &my_claims, "secret".as_ref()).unwrap(); - let v = Validation { + let v = Validation { leeway: 5, validate_exp: true, iss: Some("iss no check".to_string()), diff --git a/tests/rsa.rs b/tests/rsa.rs index 23bc5cf..def1f9f 100644 --- a/tests/rsa.rs +++ b/tests/rsa.rs @@ -3,8 +3,8 @@ extern crate jsonwebtoken; extern crate serde_derive; extern crate chrono; -use jsonwebtoken::{encode, decode, Algorithm, Header, sign, verify, Validation}; use chrono::Utc; +use jsonwebtoken::{decode, encode, sign, verify, Algorithm, Header, Validation}; #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] struct Claims { @@ -15,12 +15,14 @@ struct Claims { #[test] fn round_trip_sign_verification() { - let encrypted = sign("hello world", include_bytes!("private_rsa_key.der"), Algorithm::RS256).unwrap(); - let is_valid = verify(&encrypted, "hello world", include_bytes!("public_rsa_key.der"), Algorithm::RS256).unwrap(); + let encrypted = + sign("hello world", include_bytes!("private_rsa_key.der"), Algorithm::RS256).unwrap(); + let is_valid = + verify(&encrypted, "hello world", include_bytes!("public_rsa_key.der"), Algorithm::RS256) + .unwrap(); assert!(is_valid); } - #[test] fn round_trip_claim() { let my_claims = Claims { @@ -28,8 +30,15 @@ fn round_trip_claim() { company: "ACME".to_string(), exp: Utc::now().timestamp() + 10000, }; - let token = encode(&Header::new(Algorithm::RS256), &my_claims, include_bytes!("private_rsa_key.der")).unwrap(); - let token_data = decode::(&token, include_bytes!("public_rsa_key.der"), &Validation::new(Algorithm::RS256)).unwrap(); + let token = + encode(&Header::new(Algorithm::RS256), &my_claims, include_bytes!("private_rsa_key.der")) + .unwrap(); + let token_data = decode::( + &token, + include_bytes!("public_rsa_key.der"), + &Validation::new(Algorithm::RS256), + ) + .unwrap(); assert_eq!(my_claims, token_data.claims); assert!(token_data.header.kid.is_none()); }