Skip to content

Commit

Permalink
deserialization support for some missing algorithms (#324)
Browse files Browse the repository at this point in the history
* Only deserialization support for missing Algorithm (Keycloak)

* linting fixes

* linting change leftover fix

* KeyAlgorithm moved to mod jwk, typo fixes

* Moved pub method to Jwk private

* Removed test
  • Loading branch information
nick9822 authored Oct 21, 2023
1 parent e7bb952 commit b32e0a3
Show file tree
Hide file tree
Showing 3 changed files with 112 additions and 11 deletions.
9 changes: 7 additions & 2 deletions examples/auth0.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
/// Example for the backend to backend implementation
use std::collections::HashMap;
use std::str::FromStr;

use jsonwebtoken::jwk::AlgorithmParameters;
use jsonwebtoken::{decode, decode_header, jwk, DecodingKey, Validation};
use jsonwebtoken::{decode, decode_header, jwk, Algorithm, DecodingKey, Validation};

const TOKEN: &str = "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6IjFaNTdkX2k3VEU2S1RZNTdwS3pEeSJ9.eyJpc3MiOiJodHRwczovL2Rldi1kdXp5YXlrNC5ldS5hdXRoMC5jb20vIiwic3ViIjoiNDNxbW44c281R3VFU0U1N0Fkb3BhN09jYTZXeVNidmRAY2xpZW50cyIsImF1ZCI6Imh0dHBzOi8vZGV2LWR1enlheWs0LmV1LmF1dGgwLmNvbS9hcGkvdjIvIiwiaWF0IjoxNjIzNTg1MzAxLCJleHAiOjE2MjM2NzE3MDEsImF6cCI6IjQzcW1uOHNvNUd1RVNFNTdBZG9wYTdPY2E2V3lTYnZkIiwic2NvcGUiOiJyZWFkOnVzZXJzIiwiZ3R5IjoiY2xpZW50LWNyZWRlbnRpYWxzIn0.0MpewU1GgvRqn4F8fK_-Eu70cUgWA5JJrdbJhkCPCxXP-8WwfI-qx1ZQg2a7nbjXICYAEl-Z6z4opgy-H5fn35wGP0wywDqZpqL35IPqx6d0wRvpPMjJM75zVXuIjk7cEhDr2kaf1LOY9auWUwGzPiDB_wM-R0uvUMeRPMfrHaVN73xhAuQWVjCRBHvNscYS5-i6qBQKDMsql87dwR72DgHzMlaC8NnaGREBC-xiSamesqhKPVyGzSkFSaF3ZKpGrSDapqmHkNW9RDBE3GQ9OHM33vzUdVKOjU1g9Leb9PDt0o1U4p3NQoGJPShQ6zgWSUEaqvUZTfkbpD_DoYDRxA";
const JWKS_REPLY: &str = r#"
Expand All @@ -21,7 +22,11 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
match &j.algorithm {
AlgorithmParameters::RSA(rsa) => {
let decoding_key = DecodingKey::from_rsa_components(&rsa.n, &rsa.e).unwrap();
let mut validation = Validation::new(j.common.algorithm.unwrap());

let mut validation = Validation::new(
Algorithm::from_str(j.common.key_algorithm.unwrap().to_string().as_str())
.unwrap(),
);
validation.validate_exp = false;
let decoded_token =
decode::<HashMap<String, serde_json::Value>>(TOKEN, &decoding_key, &validation)
Expand Down
2 changes: 2 additions & 0 deletions src/algorithms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,8 @@ impl Algorithm {

#[cfg(test)]
mod tests {
use crate::jwk::KeyAlgorithm;

use super::*;

#[test]
Expand Down
112 changes: 103 additions & 9 deletions src/jwk.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
#![allow(missing_docs)]
//! This crate contains types only for working JWK and JWK Sets
//! This is only meant to be used to deal with public JWK, not generate ones.
//! Most of the code in this file is taken from https://github.com/lawliet89/biscuit but
//! tweaked to remove the private bits as it's not the goal for this crate currently.
use crate::Algorithm;
///! This crate contains types only for working JWK and JWK Sets
///! This is only meant to be used to deal with public JWK, not generate ones.
///! Most of the code in this file is taken from https://github.com/lawliet89/biscuit but
/// tweaked to remove the private bits as it's not the goal for this crate currently.
///!
use crate::{
errors::{self, Error, ErrorKind},
Algorithm,
};
use serde::{de, Deserialize, Deserializer, Serialize, Serializer};
use std::fmt;
use std::{fmt, str::FromStr};

/// The intended usage of the public `KeyType`. This enum is serialized `untagged`
#[derive(Clone, Debug, Eq, PartialEq, Hash)]
Expand Down Expand Up @@ -141,6 +145,87 @@ impl<'de> Deserialize<'de> for KeyOperations {
}
}

/// The algorithms of the keys
#[allow(non_camel_case_types, clippy::upper_case_acronyms)]
#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone, Serialize, Deserialize)]
pub enum KeyAlgorithm {
/// HMAC using SHA-256
HS256,
/// HMAC using SHA-384
HS384,
/// HMAC using SHA-512
HS512,

/// ECDSA using SHA-256
ES256,
/// ECDSA using SHA-384
ES384,

/// RSASSA-PKCS1-v1_5 using SHA-256
RS256,
/// RSASSA-PKCS1-v1_5 using SHA-384
RS384,
/// RSASSA-PKCS1-v1_5 using SHA-512
RS512,

/// RSASSA-PSS using SHA-256
PS256,
/// RSASSA-PSS using SHA-384
PS384,
/// RSASSA-PSS using SHA-512
PS512,

/// Edwards-curve Digital Signature Algorithm (EdDSA)
EdDSA,

/// RSAES-PKCS1-V1_5
RSA1_5,

/// RSAES-OAEP using SHA-1
#[serde(rename = "RSA-OAEP")]
RSA_OAEP,

/// RSAES-OAEP-256 using SHA-2
#[serde(rename = "RSA-OAEP-256")]
RSA_OAEP_256,
}

impl FromStr for KeyAlgorithm {
type Err = Error;
fn from_str(s: &str) -> errors::Result<Self> {
match s {
"HS256" => Ok(KeyAlgorithm::HS256),
"HS384" => Ok(KeyAlgorithm::HS384),
"HS512" => Ok(KeyAlgorithm::HS512),
"ES256" => Ok(KeyAlgorithm::ES256),
"ES384" => Ok(KeyAlgorithm::ES384),
"RS256" => Ok(KeyAlgorithm::RS256),
"RS384" => Ok(KeyAlgorithm::RS384),
"PS256" => Ok(KeyAlgorithm::PS256),
"PS384" => Ok(KeyAlgorithm::PS384),
"PS512" => Ok(KeyAlgorithm::PS512),
"RS512" => Ok(KeyAlgorithm::RS512),
"EdDSA" => Ok(KeyAlgorithm::EdDSA),
"RSA1_5" => Ok(KeyAlgorithm::RSA1_5),
"RSA-OAEP" => Ok(KeyAlgorithm::RSA_OAEP),
"RSA-OAEP-256" => Ok(KeyAlgorithm::RSA_OAEP_256),
_ => Err(ErrorKind::InvalidAlgorithmName.into()),
}
}
}

impl fmt::Display for KeyAlgorithm {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{:?}", self)
}
}

impl KeyAlgorithm {
fn to_algorithm(self) -> errors::Result<Algorithm> {
Algorithm::from_str(self.to_string().as_str())
}
}

/// Common JWK parameters
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize, Default, Hash)]
pub struct CommonParameters {
Expand All @@ -158,9 +243,9 @@ pub struct CommonParameters {
#[serde(rename = "key_ops", skip_serializing_if = "Option::is_none", default)]
pub key_operations: Option<Vec<KeyOperations>>,

/// The algorithm intended for use with the key
/// The algorithm keys intended for use with the key.
#[serde(rename = "alg", skip_serializing_if = "Option::is_none", default)]
pub algorithm: Option<Algorithm>,
pub key_algorithm: Option<KeyAlgorithm>,

/// The case sensitive Key ID for the key
#[serde(rename = "kid", skip_serializing_if = "Option::is_none", default)]
Expand Down Expand Up @@ -326,6 +411,13 @@ pub struct Jwk {
pub algorithm: AlgorithmParameters,
}

impl Jwk {
/// Find whether the Algorithm is implmented and supported
pub fn is_supported(&self) -> bool {
self.common.key_algorithm.unwrap().to_algorithm().is_ok()
}
}

/// A JWK set
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct JwkSet {
Expand Down Expand Up @@ -366,7 +458,9 @@ mod tests {
assert_eq!(set.keys.len(), 1);
let key = &set.keys[0];
assert_eq!(key.common.key_id, Some("abc123".to_string()));
assert_eq!(key.common.algorithm, Some(Algorithm::HS256));
let algorithm = key.common.key_algorithm.unwrap().to_algorithm().unwrap();
assert_eq!(algorithm, Algorithm::HS256);

match &key.algorithm {
AlgorithmParameters::OctetKey(key) => {
assert_eq!(key.key_type, OctetKeyType::Octet);
Expand Down

0 comments on commit b32e0a3

Please sign in to comment.