Support for RSASSA-PSS signing algorithm

As specified in https://tools.ietf.org/html/rfc7518#section-3.5

- PS256 - RSASSA-PSS using SHA-256 hash algorithm
- PS384 - RSASSA-PSS using SHA-384 hash algorithm
- PS512 - RSASSA-PSS using SHA-512 hash algorithm
This commit is contained in:
Kan-Ru Chen 2019-06-08 03:08:54 +09:00
parent b2d57edc72
commit c26bdf7e06
5 changed files with 70 additions and 22 deletions

View File

@ -115,6 +115,9 @@ This library currently supports the following:
- RS256 - RS256
- RS384 - RS384
- RS512 - RS512
- PS256
- PS384
- PS512
- ES256 - ES256
- ES384 - ES384

View File

@ -30,6 +30,13 @@ pub enum Algorithm {
RS384, RS384,
/// RSASSA-PKCS1-v1_5 using SHA-512 /// RSASSA-PKCS1-v1_5 using SHA-512
RS512, RS512,
/// RSASSA-PSS using SHA-256
PS256,
/// RSASSA-PSS using SHA-384
PS384,
/// RSASSA-PSS using SHA-512
PS512,
} }
impl Default for Algorithm { impl Default for Algorithm {
@ -50,6 +57,9 @@ impl FromStr for Algorithm {
"RS256" => Ok(Algorithm::RS256), "RS256" => Ok(Algorithm::RS256),
"RS384" => Ok(Algorithm::RS384), "RS384" => Ok(Algorithm::RS384),
"RS512" => Ok(Algorithm::RS512), "RS512" => Ok(Algorithm::RS512),
"PS256" => Ok(Algorithm::PS256),
"PS384" => Ok(Algorithm::PS384),
"PS512" => Ok(Algorithm::PS512),
_ => Err(new_error(ErrorKind::InvalidAlgorithmName)), _ => Err(new_error(ErrorKind::InvalidAlgorithmName)),
} }
} }
@ -64,7 +74,11 @@ fn sign_hmac(alg: &'static digest::Algorithm, key: &[u8], signing_input: &str) -
} }
/// The actual ECDSA signing + encoding /// The actual ECDSA signing + encoding
fn sign_ecdsa(alg: &'static signature::EcdsaSigningAlgorithm, key: &[u8], signing_input: &str) -> Result<String> { fn sign_ecdsa(
alg: &'static signature::EcdsaSigningAlgorithm,
key: &[u8],
signing_input: &str,
) -> Result<String> {
let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, untrusted::Input::from(key))?; let signing_key = signature::EcdsaKeyPair::from_pkcs8(alg, untrusted::Input::from(key))?;
let rng = rand::SystemRandom::new(); let rng = rand::SystemRandom::new();
let sig = signing_key.sign(&rng, untrusted::Input::from(signing_input.as_bytes()))?; let sig = signing_key.sign(&rng, untrusted::Input::from(signing_input.as_bytes()))?;
@ -73,7 +87,11 @@ fn sign_ecdsa(alg: &'static signature::EcdsaSigningAlgorithm, key: &[u8], signin
/// The actual RSA signing + encoding /// The actual RSA signing + encoding
/// Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html /// Taken from Ring doc https://briansmith.org/rustdoc/ring/signature/index.html
fn sign_rsa(alg: &'static signature::RsaEncoding, key: &[u8], signing_input: &str) -> Result<String> { fn sign_rsa(
alg: &'static dyn signature::RsaEncoding,
key: &[u8],
signing_input: &str,
) -> Result<String> {
let key_pair = Arc::new( let key_pair = Arc::new(
signature::RsaKeyPair::from_der(untrusted::Input::from(key)) signature::RsaKeyPair::from_der(untrusted::Input::from(key))
.map_err(|_| ErrorKind::InvalidRsaKey)?, .map_err(|_| ErrorKind::InvalidRsaKey)?,
@ -97,12 +115,20 @@ pub fn sign(signing_input: &str, key: &[u8], algorithm: Algorithm) -> Result<Str
Algorithm::HS384 => sign_hmac(&digest::SHA384, key, signing_input), Algorithm::HS384 => sign_hmac(&digest::SHA384, key, signing_input),
Algorithm::HS512 => sign_hmac(&digest::SHA512, key, signing_input), Algorithm::HS512 => sign_hmac(&digest::SHA512, key, signing_input),
Algorithm::ES256 => sign_ecdsa(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, key, signing_input), Algorithm::ES256 => {
Algorithm::ES384 => sign_ecdsa(&signature::ECDSA_P384_SHA384_FIXED_SIGNING, key, signing_input), sign_ecdsa(&signature::ECDSA_P256_SHA256_FIXED_SIGNING, key, signing_input)
}
Algorithm::ES384 => {
sign_ecdsa(&signature::ECDSA_P384_SHA384_FIXED_SIGNING, key, signing_input)
}
Algorithm::RS256 => sign_rsa(&signature::RSA_PKCS1_SHA256, key, signing_input), Algorithm::RS256 => sign_rsa(&signature::RSA_PKCS1_SHA256, key, signing_input),
Algorithm::RS384 => sign_rsa(&signature::RSA_PKCS1_SHA384, 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), Algorithm::RS512 => sign_rsa(&signature::RSA_PKCS1_SHA512, key, signing_input),
Algorithm::PS256 => sign_rsa(&signature::RSA_PSS_SHA256, key, signing_input),
Algorithm::PS384 => sign_rsa(&signature::RSA_PSS_SHA384, key, signing_input),
Algorithm::PS512 => sign_rsa(&signature::RSA_PSS_SHA512, key, signing_input),
} }
} }
@ -158,5 +184,14 @@ pub fn verify(
Algorithm::RS512 => { Algorithm::RS512 => {
verify_ring(&signature::RSA_PKCS1_2048_8192_SHA512, signature, signing_input, key) verify_ring(&signature::RSA_PKCS1_2048_8192_SHA512, signature, signing_input, key)
} }
Algorithm::PS256 => {
verify_ring(&signature::RSA_PSS_2048_8192_SHA256, signature, signing_input, key)
}
Algorithm::PS384 => {
verify_ring(&signature::RSA_PSS_2048_8192_SHA384, signature, signing_input, key)
}
Algorithm::PS512 => {
verify_ring(&signature::RSA_PSS_2048_8192_SHA512, signature, signing_input, key)
}
} }
} }

View File

@ -97,7 +97,7 @@ impl StdError for Error {
} }
} }
fn cause(&self) -> Option<&StdError> { fn cause(&self) -> Option<&dyn StdError> {
match *self.0 { match *self.0 {
ErrorKind::InvalidToken => None, ErrorKind::InvalidToken => None,
ErrorKind::InvalidSignature => None, ErrorKind::InvalidSignature => None,

View File

@ -166,5 +166,8 @@ fn generate_algorithm_enum_from_str() {
assert!(Algorithm::from_str("RS256").is_ok()); assert!(Algorithm::from_str("RS256").is_ok());
assert!(Algorithm::from_str("RS384").is_ok()); assert!(Algorithm::from_str("RS384").is_ok());
assert!(Algorithm::from_str("RS512").is_ok()); assert!(Algorithm::from_str("RS512").is_ok());
assert!(Algorithm::from_str("PS256").is_ok());
assert!(Algorithm::from_str("PS384").is_ok());
assert!(Algorithm::from_str("PS512").is_ok());
assert!(Algorithm::from_str("").is_err()); assert!(Algorithm::from_str("").is_err());
} }

View File

@ -6,6 +6,15 @@ extern crate chrono;
use chrono::Utc; use chrono::Utc;
use jsonwebtoken::{decode, encode, sign, verify, Algorithm, Header, Validation}; use jsonwebtoken::{decode, encode, sign, verify, Algorithm, Header, Validation};
const RSA_ALGORITHMS: &[Algorithm] = &[
Algorithm::RS256,
Algorithm::RS384,
Algorithm::RS512,
Algorithm::PS256,
Algorithm::PS384,
Algorithm::PS512,
];
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)] #[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
struct Claims { struct Claims {
sub: String, sub: String,
@ -15,12 +24,12 @@ struct Claims {
#[test] #[test]
fn round_trip_sign_verification() { fn round_trip_sign_verification() {
let encrypted = for &alg in RSA_ALGORITHMS {
sign("hello world", include_bytes!("private_rsa_key.der"), Algorithm::RS256).unwrap(); let encrypted = sign("hello world", include_bytes!("private_rsa_key.der"), alg).unwrap();
let is_valid = let is_valid =
verify(&encrypted, "hello world", include_bytes!("public_rsa_key.der"), Algorithm::RS256) verify(&encrypted, "hello world", include_bytes!("public_rsa_key.der"), alg).unwrap();
.unwrap(); assert!(is_valid);
assert!(is_valid); }
} }
#[test] #[test]
@ -30,15 +39,13 @@ fn round_trip_claim() {
company: "ACME".to_string(), company: "ACME".to_string(),
exp: Utc::now().timestamp() + 10000, exp: Utc::now().timestamp() + 10000,
}; };
let token = for &alg in RSA_ALGORITHMS {
encode(&Header::new(Algorithm::RS256), &my_claims, include_bytes!("private_rsa_key.der")) let token =
.unwrap(); encode(&Header::new(alg), &my_claims, include_bytes!("private_rsa_key.der")).unwrap();
let token_data = decode::<Claims>( let token_data =
&token, decode::<Claims>(&token, include_bytes!("public_rsa_key.der"), &Validation::new(alg))
include_bytes!("public_rsa_key.der"), .unwrap();
&Validation::new(Algorithm::RS256), assert_eq!(my_claims, token_data.claims);
) assert!(token_data.header.kid.is_none());
.unwrap(); }
assert_eq!(my_claims, token_data.claims);
assert!(token_data.header.kid.is_none());
} }