jsonwebtoken/src/crypto.rs

116 lines
4.3 KiB
Rust
Raw Normal View History

2017-02-22 02:45:28 -05:00
use std::sync::Arc;
use base64;
2017-02-22 02:45:28 -05:00
use ring::{rand, digest, hmac, signature};
use ring::constant_time::verify_slices_are_equal;
2017-02-22 02:45:28 -05:00
use untrusted;
use errors::{Result, ErrorKind};
/// The algorithms supported for signing/verifying
#[derive(Debug, PartialEq, Copy, Clone, Serialize, Deserialize)]
pub enum Algorithm {
2017-04-12 21:08:07 -04:00
/// HMAC using SHA-256
HS256,
2017-04-12 21:08:07 -04:00
/// HMAC using SHA-384
HS384,
2017-04-12 21:08:07 -04:00
/// HMAC using SHA-512
2017-02-22 02:45:28 -05:00
HS512,
2017-04-12 21:08:07 -04:00
/// RSASSA-PKCS1-v1_5 using SHA-256
2017-02-22 02:45:28 -05:00
RS256,
2017-04-12 21:08:07 -04:00
/// RSASSA-PKCS1-v1_5 using SHA-384
2017-02-22 02:45:28 -05:00
RS384,
2017-04-12 21:08:07 -04:00
/// RSASSA-PKCS1-v1_5 using SHA-512
2017-02-22 02:45:28 -05:00
RS512,
}
2017-04-13 03:36:32 -04:00
/// Take the payload of a JWT, sign it using the algorithm given and return
/// the base64 url safe encoded of the result.
///
/// Only use this function if you want to do something other than JWT.
2017-02-22 02:45:28 -05:00
pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<String> {
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)?;
Ok(base64::encode_config(
signature.as_ref(),
base64::URL_SAFE_NO_PAD
))
},
}
}
2017-04-12 21:08:07 -04:00
/// Compares the signature given with a re-computed signature for HMAC or using the public key
2017-04-13 03:36:32 -04:00
/// for RSA.
///
/// Only use this function if you want to do something other than JWT.
2017-02-22 02:45:28 -05:00
///
/// `signature` is the signature part of a jwt (text after the second '.')
2017-04-12 21:08:07 -04:00
///
2017-02-22 02:45:28 -05:00
/// `signing_input` is base64(header) + "." + base64(claims)
pub fn verify(signature: &str, signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<bool> {
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 | 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)?;
2017-04-10 23:40:01 -04:00
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());
2017-02-22 02:45:28 -05:00
let res = signature::verify(
verification_alg,
2017-04-10 23:40:01 -04:00
public_key_der,
message,
expected_signature,
2017-02-22 02:45:28 -05:00
);
Ok(res.is_ok())
},
}
}