diff --git a/Cargo.toml b/Cargo.toml index 28f1517..cff552d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,7 @@ repository = "https://github.com/Keats/rust-jwt" keywords = ["jwt", "web", "api", "token", "json"] [dependencies] -rustc-serialize = "^0.3" -error-chain = "0.10" +error-chain = { version = "0.10", default-features = false } serde_json = "0.9" serde_derive = "0.9" serde = "0.9" diff --git a/src/crypto.rs b/src/crypto.rs index c432d35..3414c6c 100644 --- a/src/crypto.rs +++ b/src/crypto.rs @@ -26,6 +26,42 @@ pub enum Algorithm { RS512, } +/// The actual HS signing + encoding +fn sign_hmac(alg: &'static digest::Algorithm, key: &[u8], signing_input: &str) -> Result { + let signing_key = hmac::SigningKey::new(&alg, key); + Ok(base64::encode_config( + hmac::sign(&signing_key, signing_input.as_bytes()).as_ref(), + base64::URL_SAFE_NO_PAD + )) +} + +/// The actual RSA signing + encoding +/// Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html +fn sign_rsa(alg: Algorithm, key: &[u8], signing_input: &str) -> Result { + let ring_alg = match alg { + Algorithm::RS256 => &signature::RSA_PKCS1_SHA256, + Algorithm::RS384 => &signature::RSA_PKCS1_SHA384, + Algorithm::RS512 => &signature::RSA_PKCS1_SHA512, + _ => unreachable!(), + }; + + let key_pair = Arc::new( + signature::RSAKeyPair::from_der( + untrusted::Input::from(key) + ).map_err(|_| ErrorKind::InvalidKey)? + ); + let mut signing_state = signature::RSASigningState::new(key_pair) + .map_err(|_| ErrorKind::InvalidKey)?; + 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) + .map_err(|_| ErrorKind::InvalidKey)?; + + Ok(base64::encode_config( + signature.as_ref(), + base64::URL_SAFE_NO_PAD + )) +} /// Take the payload of a JWT, sign it using the algorithm given and return /// the base64 url safe encoded of the result. @@ -33,47 +69,30 @@ pub enum Algorithm { /// Only use this function if you want to do something other than JWT. pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result { match algorithm { - Algorithm::HS256 | Algorithm::HS384 | Algorithm::HS512 => { - let digest = match algorithm { - Algorithm::HS256 => &digest::SHA256, - Algorithm::HS384 => &digest::SHA384, - Algorithm::HS512 => &digest::SHA512, - _ => unreachable!(), - }; - let key = hmac::SigningKey::new(digest, key); - Ok(base64::encode_config( - hmac::sign(&key, signing_input.as_bytes()).as_ref(), - base64::URL_SAFE_NO_PAD - )) - }, - Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => { - let ring_alg = match algorithm { - Algorithm::RS256 => &signature::RSA_PKCS1_SHA256, - Algorithm::RS384 => &signature::RSA_PKCS1_SHA384, - Algorithm::RS512 => &signature::RSA_PKCS1_SHA512, - _ => unreachable!(), - }; - // Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html - let key_pair = Arc::new( - signature::RSAKeyPair::from_der( - untrusted::Input::from(key) - ).map_err(|_| ErrorKind::InvalidKey)? - ); - let mut signing_state = signature::RSASigningState::new(key_pair) - .map_err(|_| ErrorKind::InvalidKey)?; - 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) - .map_err(|_| ErrorKind::InvalidKey)?; + Algorithm::HS256 => sign_hmac(&digest::SHA256, key, signing_input), + Algorithm::HS384 => sign_hmac(&digest::SHA384, key, signing_input), + Algorithm::HS512 => sign_hmac(&digest::SHA512, key, signing_input), - Ok(base64::encode_config( - signature.as_ref(), - base64::URL_SAFE_NO_PAD - )) - }, + Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => sign_rsa(algorithm, key, signing_input), +// TODO: if PKCS1 is made prublic, remove the line above and uncomment below +// Algorithm::RS256 => sign_rsa(&signature::RSA_PKCS1_SHA256, key, signing_input), +// Algorithm::RS384 => sign_rsa(&signature::RSA_PKCS1_SHA384, key, signing_input), +// Algorithm::RS512 => sign_rsa(&signature::RSA_PKCS1_SHA512, key, signing_input), } } +/// See Ring RSA docs for more details +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()); + let expected_signature = untrusted::Input::from(signature_bytes.as_slice()); + + let res = signature::verify(alg, public_key_der, message, expected_signature); + + Ok(res.is_ok()) +} + /// Compares the signature given with a re-computed signature for HMAC or using the public key /// for RSA. /// @@ -89,27 +108,8 @@ pub fn verify(signature: &str, signing_input: &str, key: &[u8], algorithm: Algor let signed = sign(signing_input, key, algorithm)?; Ok(verify_slices_are_equal(signature.as_ref(), signed.as_ref()).is_ok()) }, - Algorithm::RS256 | Algorithm::RS384 | Algorithm::RS512 => { - // we use ring to verify using the public key given - let verification_alg = match algorithm { - Algorithm::RS256 => &signature::RSA_PKCS1_2048_8192_SHA256, - Algorithm::RS384 => &signature::RSA_PKCS1_2048_8192_SHA384, - Algorithm::RS512 => &signature::RSA_PKCS1_2048_8192_SHA512, - _ => unreachable!(), - }; - 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()); - let expected_signature = untrusted::Input::from(signature_bytes.as_slice()); - - let res = signature::verify( - verification_alg, - public_key_der, - message, - expected_signature, - ); - - Ok(res.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), } }