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:
parent
b2d57edc72
commit
c26bdf7e06
|
@ -115,6 +115,9 @@ This library currently supports the following:
|
||||||
- RS256
|
- RS256
|
||||||
- RS384
|
- RS384
|
||||||
- RS512
|
- RS512
|
||||||
|
- PS256
|
||||||
|
- PS384
|
||||||
|
- PS512
|
||||||
- ES256
|
- ES256
|
||||||
- ES384
|
- ES384
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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());
|
||||||
}
|
}
|
||||||
|
|
41
tests/rsa.rs
41
tests/rsa.rs
|
@ -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());
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue