diff --git a/Cargo.lock b/Cargo.lock index b0893b4c..158e5921 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1599,6 +1599,7 @@ dependencies = [ name = "mas-iana" version = "0.1.0" dependencies = [ + "parse-display", "schemars", "serde", ] diff --git a/crates/handlers/src/oauth2/discovery.rs b/crates/handlers/src/oauth2/discovery.rs index 73642afc..87b7bb40 100644 --- a/crates/handlers/src/oauth2/discovery.rs +++ b/crates/handlers/src/oauth2/discovery.rs @@ -15,7 +15,8 @@ use std::collections::HashSet; use mas_config::OAuth2Config; -use mas_jose::{JsonWebSignatureAlgorithm, SigningKeystore}; +use mas_iana::jose::JsonWebSignatureAlg; +use mas_jose::SigningKeystore; use oauth2_types::{ oidc::{ClaimType, Metadata, SubjectType}, pkce::CodeChallengeMethod, @@ -43,12 +44,12 @@ pub(super) fn filter( let client_auth_signing_alg_values_supported = Some({ let mut s = HashSet::new(); - s.insert(JsonWebSignatureAlgorithm::Hs256); - s.insert(JsonWebSignatureAlgorithm::Hs384); - s.insert(JsonWebSignatureAlgorithm::Hs512); - s.insert(JsonWebSignatureAlgorithm::Rs256); - s.insert(JsonWebSignatureAlgorithm::Rs384); - s.insert(JsonWebSignatureAlgorithm::Rs512); + s.insert(JsonWebSignatureAlg::Hs256); + s.insert(JsonWebSignatureAlg::Hs384); + s.insert(JsonWebSignatureAlg::Hs512); + s.insert(JsonWebSignatureAlg::Rs256); + s.insert(JsonWebSignatureAlg::Rs384); + s.insert(JsonWebSignatureAlg::Rs512); s }); diff --git a/crates/handlers/src/oauth2/token.rs b/crates/handlers/src/oauth2/token.rs index ea4be8d2..128798e5 100644 --- a/crates/handlers/src/oauth2/token.rs +++ b/crates/handlers/src/oauth2/token.rs @@ -21,9 +21,10 @@ use headers::{CacheControl, Pragma}; use hyper::StatusCode; use mas_config::{OAuth2ClientConfig, OAuth2Config}; use mas_data_model::{AuthorizationGrantStage, TokenType}; +use mas_iana::jose::JsonWebSignatureAlg; use mas_jose::{ claims::{AT_HASH, AUD, AUTH_TIME, C_HASH, EXP, IAT, ISS, NONCE, SUB}, - DecodedJsonWebToken, JsonWebSignatureAlgorithm, SigningKeystore, StaticKeystore, + DecodedJsonWebToken, SigningKeystore, StaticKeystore, }; use mas_storage::{ oauth2::{ @@ -288,7 +289,7 @@ async fn authorization_code_grant( .wrap_error()?; let header = key_store - .prepare_header(JsonWebSignatureAlgorithm::Rs256) + .prepare_header(JsonWebSignatureAlg::Rs256) .await .wrap_error()?; let id_token = DecodedJsonWebToken::new(header, claims); diff --git a/crates/iana-codegen/src/jose.rs b/crates/iana-codegen/src/jose.rs index 8fd0d9f7..84185bb7 100644 --- a/crates/iana-codegen/src/jose.rs +++ b/crates/iana-codegen/src/jose.rs @@ -14,7 +14,10 @@ use serde::Deserialize; -use crate::EnumEntry; +use crate::{ + traits::{s, Section}, + EnumEntry, +}; #[derive(Debug, Deserialize, PartialEq, Eq)] enum Usage { @@ -60,13 +63,41 @@ pub struct WebEncryptionSignatureAlgorithm { impl EnumEntry for WebEncryptionSignatureAlgorithm { const URL: &'static str = "https://www.iana.org/assignments/jose/web-signature-encryption-algorithms.csv"; - const SECTIONS: &'static [&'static str] = - &["JsonWebSignatureAlgorithm", "JsonWebEncryptionAlgorithm"]; + const SECTIONS: &'static [Section] = &[ + s( + "JsonWebSignatureAlg", + r#"JSON Web Signature "alg" parameter"#, + ), + s( + "JsonWebEncryptionAlg", + r#"JSON Web Encryption "alg" parameter"#, + ), + s( + "JsonWebEncryptionEnc", + r#"JSON Web Encryption "enc" parameter"#, + ), + ]; fn key(&self) -> Option<&'static str> { match self.usage { - Usage::Alg => Some("JsonWebSignatureAlgorithm"), - Usage::Enc => Some("JsonWebEncryptionAlgorithm"), + Usage::Alg => { + // RFC7518 has one for signature algs and one for encryption algs. The other two + // RFCs are additional Elliptic curve signature algs + if self.reference.contains("RFC7518, Section 3") + || self.reference.contains("RFC8037") + || self.reference.contains("RFC8812") + { + Some("JsonWebSignatureAlg") + } else if self.reference.contains("RFC7518, Section 4") + || self.reference.contains("WebCryptoAPI") + { + Some("JsonWebEncryptionAlg") + } else { + tracing::warn!("Unknown reference {} for JWA", self.reference); + None + } + } + Usage::Enc => Some("JsonWebEncryptionEnc"), _ => None, } } @@ -96,7 +127,10 @@ pub struct WebEncryptionCompressionAlgorithm { impl EnumEntry for WebEncryptionCompressionAlgorithm { const URL: &'static str = "https://www.iana.org/assignments/jose/web-encryption-compression-algorithms.csv"; - const SECTIONS: &'static [&'static str] = &["JsonWebEncryptionCompressionAlgorithm"]; + const SECTIONS: &'static [Section] = &[s( + "JsonWebEncryptionCompressionAlgorithm", + "JSON Web Encryption Compression Algorithm", + )]; fn key(&self) -> Option<&'static str> { Some("JsonWebEncryptionCompressionAlgorithm") @@ -128,7 +162,7 @@ pub struct WebKeyType { impl EnumEntry for WebKeyType { const URL: &'static str = "https://www.iana.org/assignments/jose/web-key-types.csv"; - const SECTIONS: &'static [&'static str] = &["JsonWebKeyType"]; + const SECTIONS: &'static [Section] = &[s("JsonWebKeyType", "JSON Web Key Type")]; fn key(&self) -> Option<&'static str> { Some("JsonWebKeyType") @@ -160,8 +194,16 @@ pub struct WebKeyEllipticCurve { impl EnumEntry for WebKeyEllipticCurve { const URL: &'static str = "https://www.iana.org/assignments/jose/web-key-elliptic-curve.csv"; - const SECTIONS: &'static [&'static str] = - &["JsonWebKeyEcEllipticCurve", "JsonWebKeyOkpEllipticCurve"]; + const SECTIONS: &'static [Section] = &[ + s( + "JsonWebKeyEcEllipticCurve", + "JSON Web Key EC Elliptic Curve", + ), + s( + "JsonWebKeyOkpEllipticCurve", + "JSON Web Key OKP Elliptic Curve", + ), + ]; fn key(&self) -> Option<&'static str> { if self.name.starts_with("P-") || self.name == "secp256k1" { @@ -195,7 +237,7 @@ pub struct WebKeyUse { impl EnumEntry for WebKeyUse { const URL: &'static str = "https://www.iana.org/assignments/jose/web-key-use.csv"; - const SECTIONS: &'static [&'static str] = &["JsonWebKeyUse"]; + const SECTIONS: &'static [Section] = &[s("JsonWebKeyUse", "JSON Web Key Use")]; fn key(&self) -> Option<&'static str> { Some("JsonWebKeyUse") @@ -225,7 +267,7 @@ pub struct WebKeyOperation { impl EnumEntry for WebKeyOperation { const URL: &'static str = "https://www.iana.org/assignments/jose/web-key-operations.csv"; - const SECTIONS: &'static [&'static str] = &["JsonWebKeyOperation"]; + const SECTIONS: &'static [Section] = &[s("JsonWebKeyOperation", "JSON Web Key Operation")]; fn key(&self) -> Option<&'static str> { Some("JsonWebKeyOperation") diff --git a/crates/iana-codegen/src/main.rs b/crates/iana-codegen/src/main.rs index affd93d5..abf4f0f5 100644 --- a/crates/iana-codegen/src/main.rs +++ b/crates/iana-codegen/src/main.rs @@ -27,8 +27,7 @@ struct File { client: Arc, registry_name: &'static str, registry_url: &'static str, - sections: Vec<&'static str>, - sources: Vec<&'static str>, + sections: Vec
, items: HashMap<&'static str, Vec>, } @@ -46,7 +45,6 @@ impl File { client, registry_name, registry_url, - sources: Vec::new(), sections: Vec::new(), items: HashMap::new(), } @@ -55,8 +53,7 @@ impl File { #[tracing::instrument(skip_all, fields(url))] async fn load(mut self) -> anyhow::Result { tracing::Span::current().record("url", &T::URL); - self.sections.extend_from_slice(T::SECTIONS); - self.sources.push(T::URL); + self.sections.extend(T::sections()); for (key, value) in T::fetch(&self.client).await? { self.items.entry(key).or_default().push(value); } @@ -99,43 +96,43 @@ impl Display for File { //! Enums from the {:?} IANA registry //! See <{}> -//! -//! Generated from:"#, + +// Do not edit this file manually + +use parse_display::{{Display, FromStr}}; +use schemars::JsonSchema; +use serde::{{Deserialize, Serialize}};"#, self.registry_name, self.registry_url, )?; - for source in &self.sources { - writeln!(f, "//! - <{}>", source)?; - } - - writeln!( - f, - r#" -// Do not edit this file manually - -use schemars::JsonSchema; -use serde::{{Deserialize, Serialize}};"# - )?; - - for key in &self.sections { - let list = if let Some(list) = self.items.get(key) { + for section in &self.sections { + let list = if let Some(list) = self.items.get(section.key) { list } else { continue; }; - writeln!(f)?; - writeln!( + write!( f, - "#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)]" + r#" +/// {} +/// +/// Source: <{}> +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] +pub enum {} {{"#, + section.doc, + section.url.unwrap(), + section.key, )?; - write!(f, "pub enum {} {{", key)?; for member in list { writeln!(f)?; if let Some(description) = &member.description { writeln!(f, " /// {}", description)?; } writeln!(f, " #[serde(rename = \"{}\")]", member.value)?; + writeln!(f, " #[display(\"{}\")]", member.value)?; writeln!(f, " {},", member.enum_name)?; } writeln!(f, "}}")?; diff --git a/crates/iana-codegen/src/oauth.rs b/crates/iana-codegen/src/oauth.rs index 13206ea6..13a893b7 100644 --- a/crates/iana-codegen/src/oauth.rs +++ b/crates/iana-codegen/src/oauth.rs @@ -14,7 +14,10 @@ use serde::Deserialize; -use crate::EnumEntry; +use crate::{ + traits::{s, Section}, + EnumEntry, +}; #[allow(dead_code)] #[derive(Debug, Deserialize)] @@ -30,7 +33,7 @@ pub struct TokenTypeHint { impl EnumEntry for TokenTypeHint { const URL: &'static str = "https://www.iana.org/assignments/oauth-parameters/token-type-hint.csv"; - const SECTIONS: &'static [&'static str] = &["OAuthTokenTypeHint"]; + const SECTIONS: &'static [Section] = &[s("OAuthTokenTypeHint", "OAuth Token Type Hint")]; fn key(&self) -> Option<&'static str> { Some("OAuthTokenTypeHint") @@ -54,7 +57,10 @@ pub struct AuthorizationEndpointResponseType { impl EnumEntry for AuthorizationEndpointResponseType { const URL: &'static str = "https://www.iana.org/assignments/oauth-parameters/endpoint.csv"; - const SECTIONS: &'static [&'static str] = &["OAuthAuthorizationEndpointResponseType"]; + const SECTIONS: &'static [Section] = &[s( + "OAuthAuthorizationEndpointResponseType", + "OAuth Authorization Endpoint Response Type", + )]; fn key(&self) -> Option<&'static str> { Some("OAuthAuthorizationEndpointResponseType") @@ -79,7 +85,10 @@ pub struct TokenEndpointAuthenticationMethod { impl EnumEntry for TokenEndpointAuthenticationMethod { const URL: &'static str = "https://www.iana.org/assignments/oauth-parameters/token-endpoint-auth-method.csv"; - const SECTIONS: &'static [&'static str] = &["OAuthTokenEndpointAuthenticationMethod"]; + const SECTIONS: &'static [Section] = &[s( + "OAuthTokenEndpointAuthenticationMethod", + "OAuth Token Endpoint Authentication Method", + )]; fn key(&self) -> Option<&'static str> { Some("OAuthTokenEndpointAuthenticationMethod") @@ -104,7 +113,8 @@ pub struct PkceCodeChallengeMethod { impl EnumEntry for PkceCodeChallengeMethod { const URL: &'static str = "https://www.iana.org/assignments/oauth-parameters/pkce-code-challenge-method.csv"; - const SECTIONS: &'static [&'static str] = &["PkceCodeChallengeMethod"]; + const SECTIONS: &'static [Section] = + &[s("PkceCodeChallengeMethod", "PKCE Code Challenge Method")]; fn key(&self) -> Option<&'static str> { Some("PkceCodeChallengeMethod") diff --git a/crates/iana-codegen/src/traits.rs b/crates/iana-codegen/src/traits.rs index ddde5858..cc66f152 100644 --- a/crates/iana-codegen/src/traits.rs +++ b/crates/iana-codegen/src/traits.rs @@ -18,6 +18,21 @@ use convert_case::{Case, Casing}; use reqwest::Client; use serde::de::DeserializeOwned; +#[derive(Debug, Clone)] +pub struct Section { + pub key: &'static str, + pub doc: &'static str, + pub url: Option<&'static str>, +} + +pub const fn s(key: &'static str, doc: &'static str) -> Section { + Section { + key, + doc, + url: None, + } +} + #[derive(Debug)] pub struct EnumMember { pub value: String, @@ -28,7 +43,17 @@ pub struct EnumMember { #[async_trait] pub trait EnumEntry: DeserializeOwned + Send + Sync { const URL: &'static str; - const SECTIONS: &'static [&'static str]; + const SECTIONS: &'static [Section]; + + fn sections() -> Vec
{ + Self::SECTIONS + .iter() + .map(|s| Section { + url: Some(Self::URL), + ..*s + }) + .collect() + } fn key(&self) -> Option<&'static str>; fn name(&self) -> &str; diff --git a/crates/iana/Cargo.toml b/crates/iana/Cargo.toml index 69165869..736747dd 100644 --- a/crates/iana/Cargo.toml +++ b/crates/iana/Cargo.toml @@ -8,3 +8,4 @@ license = "Apache-2.0" [dependencies] serde = "1.0.133" schemars = { version = "0.8.8" } +parse-display = "0.5.3" diff --git a/crates/iana/src/jose.rs b/crates/iana/src/jose.rs index d02934e3..688ba17b 100644 --- a/crates/iana/src/jose.rs +++ b/crates/iana/src/jose.rs @@ -14,292 +14,396 @@ //! Enums from the "JSON Object Signing and Encryption" IANA registry //! See -//! -//! Generated from: -//! - -//! - -//! - -//! - -//! - -//! - // Do not edit this file manually +use parse_display::{Display, FromStr}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum JsonWebSignatureAlgorithm { +/// JSON Web Signature "alg" parameter +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] +pub enum JsonWebSignatureAlg { /// HMAC using SHA-256 #[serde(rename = "HS256")] + #[display("HS256")] Hs256, /// HMAC using SHA-384 #[serde(rename = "HS384")] + #[display("HS384")] Hs384, /// HMAC using SHA-512 #[serde(rename = "HS512")] + #[display("HS512")] Hs512, /// RSASSA-PKCS1-v1_5 using SHA-256 #[serde(rename = "RS256")] + #[display("RS256")] Rs256, /// RSASSA-PKCS1-v1_5 using SHA-384 #[serde(rename = "RS384")] + #[display("RS384")] Rs384, /// RSASSA-PKCS1-v1_5 using SHA-512 #[serde(rename = "RS512")] + #[display("RS512")] Rs512, /// ECDSA using P-256 and SHA-256 #[serde(rename = "ES256")] + #[display("ES256")] Es256, /// ECDSA using P-384 and SHA-384 #[serde(rename = "ES384")] + #[display("ES384")] Es384, /// ECDSA using P-521 and SHA-512 #[serde(rename = "ES512")] + #[display("ES512")] Es512, /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256 #[serde(rename = "PS256")] + #[display("PS256")] Ps256, /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384 #[serde(rename = "PS384")] + #[display("PS384")] Ps384, /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512 #[serde(rename = "PS512")] + #[display("PS512")] Ps512, /// No digital signature or MAC performed #[serde(rename = "none")] + #[display("none")] None, + /// EdDSA signature algorithms + #[serde(rename = "EdDSA")] + #[display("EdDSA")] + EdDsa, + + /// ECDSA using secp256k1 curve and SHA-256 + #[serde(rename = "ES256K")] + #[display("ES256K")] + Es256K, +} + +/// JSON Web Encryption "alg" parameter +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] +pub enum JsonWebEncryptionAlg { /// RSAES-PKCS1-v1_5 #[serde(rename = "RSA1_5")] + #[display("RSA1_5")] Rsa15, /// RSAES OAEP using default parameters #[serde(rename = "RSA-OAEP")] + #[display("RSA-OAEP")] RsaOaep, /// RSAES OAEP using SHA-256 and MGF1 with SHA-256 #[serde(rename = "RSA-OAEP-256")] + #[display("RSA-OAEP-256")] RsaOaep256, /// AES Key Wrap using 128-bit key #[serde(rename = "A128KW")] + #[display("A128KW")] A128Kw, /// AES Key Wrap using 192-bit key #[serde(rename = "A192KW")] + #[display("A192KW")] A192Kw, /// AES Key Wrap using 256-bit key #[serde(rename = "A256KW")] + #[display("A256KW")] A256Kw, /// Direct use of a shared symmetric key #[serde(rename = "dir")] + #[display("dir")] Dir, /// ECDH-ES using Concat KDF #[serde(rename = "ECDH-ES")] + #[display("ECDH-ES")] EcdhEs, /// ECDH-ES using Concat KDF and "A128KW" wrapping #[serde(rename = "ECDH-ES+A128KW")] + #[display("ECDH-ES+A128KW")] EcdhEsA128Kw, /// ECDH-ES using Concat KDF and "A192KW" wrapping #[serde(rename = "ECDH-ES+A192KW")] + #[display("ECDH-ES+A192KW")] EcdhEsA192Kw, /// ECDH-ES using Concat KDF and "A256KW" wrapping #[serde(rename = "ECDH-ES+A256KW")] + #[display("ECDH-ES+A256KW")] EcdhEsA256Kw, /// Key wrapping with AES GCM using 128-bit key #[serde(rename = "A128GCMKW")] + #[display("A128GCMKW")] A128Gcmkw, /// Key wrapping with AES GCM using 192-bit key #[serde(rename = "A192GCMKW")] + #[display("A192GCMKW")] A192Gcmkw, /// Key wrapping with AES GCM using 256-bit key #[serde(rename = "A256GCMKW")] + #[display("A256GCMKW")] A256Gcmkw, /// PBES2 with HMAC SHA-256 and "A128KW" wrapping #[serde(rename = "PBES2-HS256+A128KW")] + #[display("PBES2-HS256+A128KW")] Pbes2Hs256A128Kw, /// PBES2 with HMAC SHA-384 and "A192KW" wrapping #[serde(rename = "PBES2-HS384+A192KW")] + #[display("PBES2-HS384+A192KW")] Pbes2Hs384A192Kw, /// PBES2 with HMAC SHA-512 and "A256KW" wrapping #[serde(rename = "PBES2-HS512+A256KW")] + #[display("PBES2-HS512+A256KW")] Pbes2Hs512A256Kw, - /// EdDSA signature algorithms - #[serde(rename = "EdDSA")] - EdDsa, - /// RSA-OAEP using SHA-384 and MGF1 with SHA-384 #[serde(rename = "RSA-OAEP-384")] + #[display("RSA-OAEP-384")] RsaOaep384, /// RSA-OAEP using SHA-512 and MGF1 with SHA-512 #[serde(rename = "RSA-OAEP-512")] + #[display("RSA-OAEP-512")] RsaOaep512, - - /// ECDSA using secp256k1 curve and SHA-256 - #[serde(rename = "ES256K")] - Es256K, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum JsonWebEncryptionAlgorithm { +/// JSON Web Encryption "enc" parameter +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] +pub enum JsonWebEncryptionEnc { /// AES_128_CBC_HMAC_SHA_256 authenticated encryption algorithm #[serde(rename = "A128CBC-HS256")] + #[display("A128CBC-HS256")] A128CbcHs256, /// AES_192_CBC_HMAC_SHA_384 authenticated encryption algorithm #[serde(rename = "A192CBC-HS384")] + #[display("A192CBC-HS384")] A192CbcHs384, /// AES_256_CBC_HMAC_SHA_512 authenticated encryption algorithm #[serde(rename = "A256CBC-HS512")] + #[display("A256CBC-HS512")] A256CbcHs512, /// AES GCM using 128-bit key #[serde(rename = "A128GCM")] + #[display("A128GCM")] A128Gcm, /// AES GCM using 192-bit key #[serde(rename = "A192GCM")] + #[display("A192GCM")] A192Gcm, /// AES GCM using 256-bit key #[serde(rename = "A256GCM")] + #[display("A256GCM")] A256Gcm, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// JSON Web Encryption Compression Algorithm +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum JsonWebEncryptionCompressionAlgorithm { /// DEFLATE #[serde(rename = "DEF")] + #[display("DEF")] Def, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// JSON Web Key Type +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum JsonWebKeyType { /// Elliptic Curve #[serde(rename = "EC")] + #[display("EC")] Ec, /// RSA #[serde(rename = "RSA")] + #[display("RSA")] Rsa, /// Octet sequence #[serde(rename = "oct")] + #[display("oct")] Oct, /// Octet string key pairs #[serde(rename = "OKP")] + #[display("OKP")] Okp, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// JSON Web Key EC Elliptic Curve +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum JsonWebKeyEcEllipticCurve { /// P-256 Curve #[serde(rename = "P-256")] + #[display("P-256")] P256, /// P-384 Curve #[serde(rename = "P-384")] + #[display("P-384")] P384, /// P-521 Curve #[serde(rename = "P-521")] + #[display("P-521")] P521, /// SECG secp256k1 curve #[serde(rename = "secp256k1")] + #[display("secp256k1")] Secp256K1, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// JSON Web Key OKP Elliptic Curve +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum JsonWebKeyOkpEllipticCurve { /// Ed25519 signature algorithm key pairs #[serde(rename = "Ed25519")] + #[display("Ed25519")] Ed25519, /// Ed448 signature algorithm key pairs #[serde(rename = "Ed448")] + #[display("Ed448")] Ed448, /// X25519 function key pairs #[serde(rename = "X25519")] + #[display("X25519")] X25519, /// X448 function key pairs #[serde(rename = "X448")] + #[display("X448")] X448, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// JSON Web Key Use +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum JsonWebKeyUse { /// Digital Signature or MAC #[serde(rename = "sig")] + #[display("sig")] Sig, /// Encryption #[serde(rename = "enc")] + #[display("enc")] Enc, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// JSON Web Key Operation +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum JsonWebKeyOperation { /// Compute digital signature or MAC #[serde(rename = "sign")] + #[display("sign")] Sign, /// Verify digital signature or MAC #[serde(rename = "verify")] + #[display("verify")] Verify, /// Encrypt content #[serde(rename = "encrypt")] + #[display("encrypt")] Encrypt, /// Decrypt content and validate decryption, if applicable #[serde(rename = "decrypt")] + #[display("decrypt")] Decrypt, /// Encrypt key #[serde(rename = "wrapKey")] + #[display("wrapKey")] WrapKey, /// Decrypt key and validate decryption, if applicable #[serde(rename = "unwrapKey")] + #[display("unwrapKey")] UnwrapKey, /// Derive key #[serde(rename = "deriveKey")] + #[display("deriveKey")] DeriveKey, /// Derive bits not to be used as a key #[serde(rename = "deriveBits")] + #[display("deriveBits")] DeriveBits, } diff --git a/crates/iana/src/oauth.rs b/crates/iana/src/oauth.rs index ead4b141..e3e70ada 100644 --- a/crates/iana/src/oauth.rs +++ b/crates/iana/src/oauth.rs @@ -14,86 +14,121 @@ //! Enums from the "OAuth Parameters" IANA registry //! See -//! -//! Generated from: -//! - -//! - -//! - -//! - // Do not edit this file manually +use parse_display::{Display, FromStr}; use schemars::JsonSchema; use serde::{Deserialize, Serialize}; -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// OAuth Token Type Hint +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum OAuthTokenTypeHint { #[serde(rename = "access_token")] + #[display("access_token")] AccessToken, #[serde(rename = "refresh_token")] + #[display("refresh_token")] RefreshToken, #[serde(rename = "pct")] + #[display("pct")] Pct, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// OAuth Authorization Endpoint Response Type +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum OAuthAuthorizationEndpointResponseType { #[serde(rename = "code")] + #[display("code")] Code, #[serde(rename = "code id_token")] + #[display("code id_token")] CodeIdToken, #[serde(rename = "code id_token token")] + #[display("code id_token token")] CodeIdTokenToken, #[serde(rename = "code token")] + #[display("code token")] CodeToken, #[serde(rename = "id_token")] + #[display("id_token")] IdToken, #[serde(rename = "id_token token")] + #[display("id_token token")] IdTokenToken, #[serde(rename = "none")] + #[display("none")] None, #[serde(rename = "token")] + #[display("token")] Token, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// OAuth Token Endpoint Authentication Method +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum OAuthTokenEndpointAuthenticationMethod { #[serde(rename = "none")] + #[display("none")] None, #[serde(rename = "client_secret_post")] + #[display("client_secret_post")] ClientSecretPost, #[serde(rename = "client_secret_basic")] + #[display("client_secret_basic")] ClientSecretBasic, #[serde(rename = "client_secret_jwt")] + #[display("client_secret_jwt")] ClientSecretJwt, #[serde(rename = "private_key_jwt")] + #[display("private_key_jwt")] PrivateKeyJwt, #[serde(rename = "tls_client_auth")] + #[display("tls_client_auth")] TlsClientAuth, #[serde(rename = "self_signed_tls_client_auth")] + #[display("self_signed_tls_client_auth")] SelfSignedTlsClientAuth, } -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] +/// PKCE Code Challenge Method +/// +/// Source: +#[derive( + Debug, Clone, Copy, PartialEq, Eq, Hash, Display, FromStr, Serialize, Deserialize, JsonSchema, +)] pub enum PkceCodeChallengeMethod { #[serde(rename = "plain")] + #[display("plain")] Plain, #[serde(rename = "S256")] + #[display("S256")] S256, } diff --git a/crates/jose/src/iana.rs b/crates/jose/src/iana.rs deleted file mode 100644 index c31d3ac8..00000000 --- a/crates/jose/src/iana.rs +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2022 The Matrix.org Foundation C.I.C. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -//! Generated enums from the IANA JOSE registry -//! -//! - -use schemars::JsonSchema; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum JsonWebSignatureAlgorithm { - /// HMAC using SHA-256 - #[serde(rename = "HS256")] - Hs256, - - /// HMAC using SHA-384 - #[serde(rename = "HS384")] - Hs384, - - /// HMAC using SHA-512 - #[serde(rename = "HS512")] - Hs512, - - /// RSASSA-PKCS1-v1_5 using SHA-256 - #[serde(rename = "RS256")] - Rs256, - - /// RSASSA-PKCS1-v1_5 using SHA-384 - #[serde(rename = "RS384")] - Rs384, - - /// RSASSA-PKCS1-v1_5 using SHA-512 - #[serde(rename = "RS512")] - Rs512, - - /// ECDSA using P-256 and SHA-256 - #[serde(rename = "ES256")] - Es256, - - /// ECDSA using P-384 and SHA-384 - #[serde(rename = "ES384")] - Es384, - - /// ECDSA using P-521 and SHA-512 - #[serde(rename = "ES512")] - Es512, - - /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256 - #[serde(rename = "PS256")] - Ps256, - - /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384 - #[serde(rename = "PS384")] - Ps384, - - /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512 - #[serde(rename = "PS512")] - Ps512, - - /// No digital signature or MAC performed - #[serde(rename = "none")] - None, - - /// RSAES-PKCS1-v1_5 - #[serde(rename = "RSA1_5")] - Rsa15, - - /// RSAES OAEP using default parameters - #[serde(rename = "RSA-OAEP")] - RsaOaep, - - /// RSAES OAEP using SHA-256 and MGF1 with SHA-256 - #[serde(rename = "RSA-OAEP-256")] - RsaOaep256, - - /// AES Key Wrap using 128-bit key - #[serde(rename = "A128KW")] - A128Kw, - - /// AES Key Wrap using 192-bit key - #[serde(rename = "A192KW")] - A192Kw, - - /// AES Key Wrap using 256-bit key - #[serde(rename = "A256KW")] - A256Kw, - - /// Direct use of a shared symmetric key - #[serde(rename = "dir")] - Dir, - - /// ECDH-ES using Concat KDF - #[serde(rename = "ECDH-ES")] - EcdhEs, - - /// ECDH-ES using Concat KDF and "A128KW" wrapping - #[serde(rename = "ECDH-ES+A128KW")] - EcdhEsA128Kw, - - /// ECDH-ES using Concat KDF and "A192KW" wrapping - #[serde(rename = "ECDH-ES+A192KW")] - EcdhEsA192Kw, - - /// ECDH-ES using Concat KDF and "A256KW" wrapping - #[serde(rename = "ECDH-ES+A256KW")] - EcdhEsA256Kw, - - /// Key wrapping with AES GCM using 128-bit key - #[serde(rename = "A128GCMKW")] - A128Gcmkw, - - /// Key wrapping with AES GCM using 192-bit key - #[serde(rename = "A192GCMKW")] - A192Gcmkw, - - /// Key wrapping with AES GCM using 256-bit key - #[serde(rename = "A256GCMKW")] - A256Gcmkw, - - /// PBES2 with HMAC SHA-256 and "A128KW" wrapping - #[serde(rename = "PBES2-HS256+A128KW")] - Pbes2Hs256A128Kw, - - /// PBES2 with HMAC SHA-384 and "A192KW" wrapping - #[serde(rename = "PBES2-HS384+A192KW")] - Pbes2Hs384A192Kw, - - /// PBES2 with HMAC SHA-512 and "A256KW" wrapping - #[serde(rename = "PBES2-HS512+A256KW")] - Pbes2Hs512A256Kw, - - /// EdDSA signature algorithms - #[serde(rename = "EdDSA")] - EdDsa, - - /// RSA-OAEP using SHA-384 and MGF1 with SHA-384 - #[serde(rename = "RSA-OAEP-384")] - RsaOaep384, - - /// RSA-OAEP using SHA-512 and MGF1 with SHA-512 - #[serde(rename = "RSA-OAEP-512")] - RsaOaep512, - - /// ECDSA using secp256k1 curve and SHA-256 - #[serde(rename = "ES256K")] - Es256K, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum JsonWebEncryptionAlgorithm { - /// AES_128_CBC_HMAC_SHA_256 authenticated encryption algorithm - #[serde(rename = "A128CBC-HS256")] - A128CbcHs256, - - /// AES_192_CBC_HMAC_SHA_384 authenticated encryption algorithm - #[serde(rename = "A192CBC-HS384")] - A192CbcHs384, - - /// AES_256_CBC_HMAC_SHA_512 authenticated encryption algorithm - #[serde(rename = "A256CBC-HS512")] - A256CbcHs512, - - /// AES GCM using 128-bit key - #[serde(rename = "A128GCM")] - A128Gcm, - - /// AES GCM using 192-bit key - #[serde(rename = "A192GCM")] - A192Gcm, - - /// AES GCM using 256-bit key - #[serde(rename = "A256GCM")] - A256Gcm, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum JsonWebEncryptionCompressionAlgorithm { - /// DEFLATE - #[serde(rename = "DEF")] - Def, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)] -pub enum JsonWebKeyType { - /// Elliptic Curve - #[serde(rename = "EC")] - Ec, - - /// RSA - #[serde(rename = "RSA")] - Rsa, - - /// Octet sequence - #[serde(rename = "oct")] - Oct, - - /// Octet string key pairs - #[serde(rename = "OKP")] - Okp, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum JsonWebKeyEcEllipticCurve { - /// P-256 Curve - #[serde(rename = "P-256")] - P256, - - /// P-384 Curve - #[serde(rename = "P-384")] - P384, - - /// P-521 Curve - #[serde(rename = "P-521")] - P521, - - /// SECG secp256k1 curve - #[serde(rename = "secp256k1")] - Secp256K1, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum JsonWebKeyOkpEllipticCurve { - /// Ed25519 signature algorithm key pairs - #[serde(rename = "Ed25519")] - Ed25519, - - /// Ed448 signature algorithm key pairs - #[serde(rename = "Ed448")] - Ed448, - - /// X25519 function key pairs - #[serde(rename = "X25519")] - X25519, - - /// X448 function key pairs - #[serde(rename = "X448")] - X448, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum JsonWebKeyUse { - /// Digital Signature or MAC - #[serde(rename = "sig")] - Sig, - - /// Encryption - #[serde(rename = "enc")] - Enc, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, JsonSchema)] -pub enum JsonWebKeyOperation { - /// Compute digital signature or MAC - #[serde(rename = "sign")] - Sign, - - /// Verify digital signature or MAC - #[serde(rename = "verify")] - Verify, - - /// Encrypt content - #[serde(rename = "encrypt")] - Encrypt, - - /// Decrypt content and validate decryption, if applicable - #[serde(rename = "decrypt")] - Decrypt, - - /// Encrypt key - #[serde(rename = "wrapKey")] - WrapKey, - - /// Decrypt key and validate decryption, if applicable - #[serde(rename = "unwrapKey")] - UnwrapKey, - - /// Derive key - #[serde(rename = "deriveKey")] - DeriveKey, - - /// Derive bits not to be used as a key - #[serde(rename = "deriveBits")] - DeriveBits, -} diff --git a/crates/jose/src/jwk.rs b/crates/jose/src/jwk.rs index 754bd2ac..3ead54b5 100644 --- a/crates/jose/src/jwk.rs +++ b/crates/jose/src/jwk.rs @@ -15,6 +15,10 @@ //! Ref: use anyhow::bail; +use mas_iana::jose::{ + JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyOperation, JsonWebKeyType, + JsonWebKeyUse, JsonWebSignatureAlg, +}; use p256::NistP256; use rsa::{BigUint, PublicKeyParts}; use schemars::JsonSchema; @@ -26,14 +30,6 @@ use serde_with::{ }; use url::Url; -use crate::{ - iana::{ - JsonWebKeyEcEllipticCurve, JsonWebKeyOkpEllipticCurve, JsonWebKeyOperation, JsonWebKeyUse, - JsonWebSignatureAlgorithm, - }, - JsonWebKeyType, -}; - #[serde_as] #[skip_serializing_none] #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)] @@ -48,7 +44,7 @@ pub struct JsonWebKey { key_ops: Option>, #[serde(default)] - alg: Option, + alg: Option, #[serde(default)] kid: Option, @@ -102,7 +98,7 @@ impl JsonWebKey { } #[must_use] - pub fn with_alg(mut self, alg: JsonWebSignatureAlgorithm) -> Self { + pub fn with_alg(mut self, alg: JsonWebSignatureAlg) -> Self { self.alg = Some(alg); self } diff --git a/crates/jose/src/jwt.rs b/crates/jose/src/jwt.rs index 57d97f1c..0b3fdd5f 100644 --- a/crates/jose/src/jwt.rs +++ b/crates/jose/src/jwt.rs @@ -15,6 +15,9 @@ use std::str::FromStr; use base64ct::{Base64UrlUnpadded, Encoding}; +use mas_iana::jose::{ + JsonWebEncryptionCompressionAlgorithm, JsonWebEncryptionEnc, JsonWebSignatureAlg, +}; use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde_with::{ base64::{Base64, Standard, UrlSafe}, @@ -23,23 +26,16 @@ use serde_with::{ }; use url::Url; -use crate::{ - iana::{ - JsonWebEncryptionAlgorithm, JsonWebEncryptionCompressionAlgorithm, - JsonWebSignatureAlgorithm, - }, - jwk::JsonWebKey, - SigningKeystore, VerifyingKeystore, -}; +use crate::{jwk::JsonWebKey, SigningKeystore, VerifyingKeystore}; #[serde_as] #[skip_serializing_none] #[derive(Debug, Serialize, Deserialize)] pub struct JwtHeader { - alg: JsonWebSignatureAlgorithm, + alg: JsonWebSignatureAlg, #[serde(default)] - enc: Option, + enc: Option, #[serde(default)] jku: Option, @@ -85,7 +81,7 @@ impl JwtHeader { } #[must_use] - pub fn new(alg: JsonWebSignatureAlgorithm) -> Self { + pub fn new(alg: JsonWebSignatureAlg) -> Self { Self { alg, enc: None, @@ -104,7 +100,7 @@ impl JwtHeader { } #[must_use] - pub fn alg(&self) -> JsonWebSignatureAlgorithm { + pub fn alg(&self) -> JsonWebSignatureAlg { self.alg } @@ -254,7 +250,7 @@ mod tests { jwt.decode_and_verify(&store).await.unwrap(); assert_eq!(jwt.header.typ, Some("JWT".to_string())); - assert_eq!(jwt.header.alg, JsonWebSignatureAlgorithm::Hs256); + assert_eq!(jwt.header.alg, JsonWebSignatureAlg::Hs256); assert_eq!( jwt.payload, serde_json::json!({ diff --git a/crates/jose/src/keystore/jwks.rs b/crates/jose/src/keystore/jwks.rs index b2ad3724..10ba1546 100644 --- a/crates/jose/src/keystore/jwks.rs +++ b/crates/jose/src/keystore/jwks.rs @@ -18,15 +18,13 @@ use anyhow::bail; use async_trait::async_trait; use chrono::{DateTime, Duration, Utc}; use digest::Digest; +use mas_iana::jose::{JsonWebKeyType, JsonWebSignatureAlg}; use rsa::{PublicKey, RsaPublicKey}; use sha2::{Sha256, Sha384, Sha512}; use signature::{Signature, Verifier}; use tokio::sync::RwLock; -use crate::{ - ExportJwks, JsonWebKeySet, JsonWebKeyType, JsonWebSignatureAlgorithm, JwtHeader, - VerifyingKeystore, -}; +use crate::{ExportJwks, JsonWebKeySet, JwtHeader, VerifyingKeystore}; pub struct StaticJwksStore { key_set: JsonWebKeySet, @@ -96,7 +94,7 @@ impl VerifyingKeystore for &StaticJwksStore { .ok_or_else(|| anyhow::anyhow!("missing kid"))? .to_string(); match header.alg() { - JsonWebSignatureAlgorithm::Rs256 => { + JsonWebSignatureAlg::Rs256 => { let key = self.find_rsa_key(kid)?; let digest = { @@ -112,7 +110,7 @@ impl VerifyingKeystore for &StaticJwksStore { )?; } - JsonWebSignatureAlgorithm::Rs384 => { + JsonWebSignatureAlg::Rs384 => { let key = self.find_rsa_key(kid)?; let digest = { @@ -128,7 +126,7 @@ impl VerifyingKeystore for &StaticJwksStore { )?; } - JsonWebSignatureAlgorithm::Rs512 => { + JsonWebSignatureAlg::Rs512 => { let key = self.find_rsa_key(kid)?; let digest = { @@ -144,7 +142,7 @@ impl VerifyingKeystore for &StaticJwksStore { )?; } - JsonWebSignatureAlgorithm::Es256 => { + JsonWebSignatureAlg::Es256 => { let key = self.find_ecdsa_key(kid)?; let signature = ecdsa::Signature::from_bytes(signature)?; diff --git a/crates/jose/src/keystore/shared_secret.rs b/crates/jose/src/keystore/shared_secret.rs index af53445d..e045d3d3 100644 --- a/crates/jose/src/keystore/shared_secret.rs +++ b/crates/jose/src/keystore/shared_secret.rs @@ -17,10 +17,11 @@ use std::collections::HashSet; use anyhow::bail; use async_trait::async_trait; use hmac::{Hmac, Mac}; +use mas_iana::jose::JsonWebSignatureAlg; use sha2::{Sha256, Sha384, Sha512}; use super::{SigningKeystore, VerifyingKeystore}; -use crate::{JsonWebSignatureAlgorithm, JwtHeader}; +use crate::JwtHeader; pub struct SharedSecret<'a> { inner: &'a [u8], @@ -36,22 +37,20 @@ impl<'a> SharedSecret<'a> { #[async_trait] impl<'a> SigningKeystore for &SharedSecret<'a> { - fn supported_algorithms(self) -> HashSet { + fn supported_algorithms(self) -> HashSet { let mut algorithms = HashSet::with_capacity(3); - algorithms.insert(JsonWebSignatureAlgorithm::Hs256); - algorithms.insert(JsonWebSignatureAlgorithm::Hs384); - algorithms.insert(JsonWebSignatureAlgorithm::Hs512); + algorithms.insert(JsonWebSignatureAlg::Hs256); + algorithms.insert(JsonWebSignatureAlg::Hs384); + algorithms.insert(JsonWebSignatureAlg::Hs512); algorithms } - async fn prepare_header(self, alg: JsonWebSignatureAlgorithm) -> anyhow::Result { + async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result { if !matches!( alg, - JsonWebSignatureAlgorithm::Hs256 - | JsonWebSignatureAlgorithm::Hs384 - | JsonWebSignatureAlgorithm::Hs512, + JsonWebSignatureAlg::Hs256 | JsonWebSignatureAlg::Hs384 | JsonWebSignatureAlg::Hs512, ) { bail!("unsupported algorithm") } @@ -63,19 +62,19 @@ impl<'a> SigningKeystore for &SharedSecret<'a> { // TODO: do the signing in a blocking task // TODO: should we bail out if the key is too small? let signature = match header.alg() { - JsonWebSignatureAlgorithm::Hs256 => { + JsonWebSignatureAlg::Hs256 => { let mut mac = Hmac::::new_from_slice(self.inner)?; mac.update(msg); mac.finalize().into_bytes().to_vec() } - JsonWebSignatureAlgorithm::Hs384 => { + JsonWebSignatureAlg::Hs384 => { let mut mac = Hmac::::new_from_slice(self.inner)?; mac.update(msg); mac.finalize().into_bytes().to_vec() } - JsonWebSignatureAlgorithm::Hs512 => { + JsonWebSignatureAlg::Hs512 => { let mut mac = Hmac::::new_from_slice(self.inner)?; mac.update(msg); mac.finalize().into_bytes().to_vec() @@ -98,19 +97,19 @@ impl<'a> VerifyingKeystore for &SharedSecret<'a> { ) -> anyhow::Result<()> { // TODO: do the verification in a blocking task match header.alg() { - JsonWebSignatureAlgorithm::Hs256 => { + JsonWebSignatureAlg::Hs256 => { let mut mac = Hmac::::new_from_slice(self.inner)?; mac.update(payload); mac.verify(signature.try_into()?)?; } - JsonWebSignatureAlgorithm::Hs384 => { + JsonWebSignatureAlg::Hs384 => { let mut mac = Hmac::::new_from_slice(self.inner)?; mac.update(payload); mac.verify(signature.try_into()?)?; } - JsonWebSignatureAlgorithm::Hs512 => { + JsonWebSignatureAlg::Hs512 => { let mut mac = Hmac::::new_from_slice(self.inner)?; mac.update(payload); mac.verify(signature.try_into()?)?; @@ -133,9 +132,9 @@ mod tests { let message = "this is the message to sign".as_bytes(); let store = SharedSecret::new(&secret); for alg in [ - JsonWebSignatureAlgorithm::Hs256, - JsonWebSignatureAlgorithm::Hs384, - JsonWebSignatureAlgorithm::Hs512, + JsonWebSignatureAlg::Hs256, + JsonWebSignatureAlg::Hs384, + JsonWebSignatureAlg::Hs512, ] { let header = store.prepare_header(alg).await.unwrap(); assert_eq!(header.alg(), alg); diff --git a/crates/jose/src/keystore/static_keystore.rs b/crates/jose/src/keystore/static_keystore.rs index 2151f2eb..ea25b510 100644 --- a/crates/jose/src/keystore/static_keystore.rs +++ b/crates/jose/src/keystore/static_keystore.rs @@ -19,6 +19,7 @@ use async_trait::async_trait; use base64ct::{Base64UrlUnpadded, Encoding}; use digest::Digest; use ecdsa::{SigningKey, VerifyingKey}; +use mas_iana::jose::{JsonWebKeyUse, JsonWebSignatureAlg}; use p256::{NistP256, PublicKey}; use pkcs1::{DecodeRsaPrivateKey, EncodeRsaPublicKey}; use pkcs8::{DecodePrivateKey, EncodePublicKey}; @@ -27,7 +28,7 @@ use sha2::{Sha256, Sha384, Sha512}; use signature::{Signature, Signer, Verifier}; use super::{ExportJwks, SigningKeystore, VerifyingKeystore}; -use crate::{iana::JsonWebSignatureAlgorithm, JsonWebKey, JsonWebKeySet, JwtHeader}; +use crate::{JsonWebKey, JsonWebKeySet, JwtHeader}; // Generate with // openssl genrsa 2048 @@ -126,7 +127,7 @@ impl StaticKeystore { #[async_trait] impl SigningKeystore for &StaticKeystore { - fn supported_algorithms(self) -> HashSet { + fn supported_algorithms(self) -> HashSet { let has_rsa = !self.rsa_keys.is_empty(); let has_es256 = !self.es256_keys.is_empty(); @@ -134,30 +135,30 @@ impl SigningKeystore for &StaticKeystore { let mut algorithms = HashSet::with_capacity(capacity); if has_rsa { - algorithms.insert(JsonWebSignatureAlgorithm::Rs256); - algorithms.insert(JsonWebSignatureAlgorithm::Rs384); - algorithms.insert(JsonWebSignatureAlgorithm::Rs512); + algorithms.insert(JsonWebSignatureAlg::Rs256); + algorithms.insert(JsonWebSignatureAlg::Rs384); + algorithms.insert(JsonWebSignatureAlg::Rs512); } if has_es256 { - algorithms.insert(JsonWebSignatureAlgorithm::Es256); + algorithms.insert(JsonWebSignatureAlg::Es256); } algorithms } - async fn prepare_header(self, alg: JsonWebSignatureAlgorithm) -> anyhow::Result { + async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result { let header = JwtHeader::new(alg); let kid = match alg { - JsonWebSignatureAlgorithm::Rs256 - | JsonWebSignatureAlgorithm::Rs384 - | JsonWebSignatureAlgorithm::Rs512 => self + JsonWebSignatureAlg::Rs256 + | JsonWebSignatureAlg::Rs384 + | JsonWebSignatureAlg::Rs512 => self .rsa_keys .keys() .next() .ok_or_else(|| anyhow::anyhow!("no RSA keys in keystore"))?, - JsonWebSignatureAlgorithm::Es256 => self + JsonWebSignatureAlg::Es256 => self .es256_keys .keys() .next() @@ -175,7 +176,7 @@ impl SigningKeystore for &StaticKeystore { // TODO: do the signing in a blocking task let signature = match header.alg() { - JsonWebSignatureAlgorithm::Rs256 => { + JsonWebSignatureAlg::Rs256 => { let key = self .rsa_keys .get(kid) @@ -193,7 +194,7 @@ impl SigningKeystore for &StaticKeystore { )? } - JsonWebSignatureAlgorithm::Rs384 => { + JsonWebSignatureAlg::Rs384 => { let key = self .rsa_keys .get(kid) @@ -211,7 +212,7 @@ impl SigningKeystore for &StaticKeystore { )? } - JsonWebSignatureAlgorithm::Rs512 => { + JsonWebSignatureAlg::Rs512 => { let key = self .rsa_keys .get(kid) @@ -229,7 +230,7 @@ impl SigningKeystore for &StaticKeystore { )? } - JsonWebSignatureAlgorithm::Es256 => { + JsonWebSignatureAlg::Es256 => { let key = self .es256_keys .get(kid) @@ -261,7 +262,7 @@ impl VerifyingKeystore for &StaticKeystore { // TODO: do the verification in a blocking task match header.alg() { - JsonWebSignatureAlgorithm::Rs256 => { + JsonWebSignatureAlg::Rs256 => { let key = self .rsa_keys .get(kid) @@ -282,7 +283,7 @@ impl VerifyingKeystore for &StaticKeystore { )?; } - JsonWebSignatureAlgorithm::Rs384 => { + JsonWebSignatureAlg::Rs384 => { let key = self .rsa_keys .get(kid) @@ -303,7 +304,7 @@ impl VerifyingKeystore for &StaticKeystore { )?; } - JsonWebSignatureAlgorithm::Rs512 => { + JsonWebSignatureAlg::Rs512 => { let key = self .rsa_keys .get(kid) @@ -324,7 +325,7 @@ impl VerifyingKeystore for &StaticKeystore { )?; } - JsonWebSignatureAlgorithm::Es256 => { + JsonWebSignatureAlg::Es256 => { let key = self .es256_keys .get(kid) @@ -349,15 +350,15 @@ impl ExportJwks for StaticKeystore { let pubkey = RsaPublicKey::from(key); JsonWebKey::new(pubkey.into()) .with_kid(kid) - .with_use(crate::JsonWebKeyUse::Sig) + .with_use(JsonWebKeyUse::Sig) }); let es256 = self.es256_keys.iter().map(|(kid, key)| { let pubkey = ecdsa::VerifyingKey::from(key); JsonWebKey::new(pubkey.into()) .with_kid(kid) - .with_use(crate::JsonWebKeyUse::Sig) - .with_alg(JsonWebSignatureAlgorithm::Es256) + .with_use(JsonWebKeyUse::Sig) + .with_alg(JsonWebSignatureAlg::Es256) }); let keys = rsa.chain(es256).collect(); @@ -380,10 +381,10 @@ mod tests { }; for alg in [ - JsonWebSignatureAlgorithm::Rs256, - JsonWebSignatureAlgorithm::Rs384, - JsonWebSignatureAlgorithm::Rs512, - JsonWebSignatureAlgorithm::Es256, + JsonWebSignatureAlg::Rs256, + JsonWebSignatureAlg::Rs384, + JsonWebSignatureAlg::Rs512, + JsonWebSignatureAlg::Es256, ] { let header = store.prepare_header(alg).await.unwrap(); assert_eq!(header.alg(), alg); diff --git a/crates/jose/src/keystore/traits.rs b/crates/jose/src/keystore/traits.rs index dfd55493..fc4b67ef 100644 --- a/crates/jose/src/keystore/traits.rs +++ b/crates/jose/src/keystore/traits.rs @@ -15,14 +15,15 @@ use std::collections::HashSet; use async_trait::async_trait; +use mas_iana::jose::JsonWebSignatureAlg; -use crate::{iana::JsonWebSignatureAlgorithm, JsonWebKeySet, JwtHeader}; +use crate::{JsonWebKeySet, JwtHeader}; #[async_trait] pub trait SigningKeystore { - fn supported_algorithms(self) -> HashSet; + fn supported_algorithms(self) -> HashSet; - async fn prepare_header(self, alg: JsonWebSignatureAlgorithm) -> anyhow::Result; + async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result; async fn sign(self, header: &JwtHeader, msg: &[u8]) -> anyhow::Result>; } diff --git a/crates/jose/src/lib.rs b/crates/jose/src/lib.rs index 16c7177b..66bf8195 100644 --- a/crates/jose/src/lib.rs +++ b/crates/jose/src/lib.rs @@ -19,18 +19,12 @@ #![allow(clippy::missing_errors_doc)] #![allow(clippy::module_name_repetitions)] -pub(crate) use mas_iana::jose as iana; - pub mod claims; pub(crate) mod jwk; pub(crate) mod jwt; mod keystore; pub use self::{ - iana::{ - JsonWebEncryptionAlgorithm, JsonWebEncryptionCompressionAlgorithm, JsonWebKeyOperation, - JsonWebKeyType, JsonWebKeyUse, JsonWebSignatureAlgorithm, - }, jwk::{JsonWebKey, JsonWebKeySet}, jwt::{DecodedJsonWebToken, JsonWebTokenParts, JwtHeader}, keystore::{ diff --git a/crates/oauth2-types/src/oidc.rs b/crates/oauth2-types/src/oidc.rs index d6aa1bb1..9baef27c 100644 --- a/crates/oauth2-types/src/oidc.rs +++ b/crates/oauth2-types/src/oidc.rs @@ -14,7 +14,7 @@ use std::collections::HashSet; -use mas_iana::jose::{JsonWebEncryptionAlgorithm, JsonWebSignatureAlgorithm}; +use mas_iana::jose::{JsonWebEncryptionAlg, JsonWebEncryptionEnc, JsonWebSignatureAlg}; use serde::Serialize; use serde_with::skip_serializing_none; use url::Url; @@ -83,8 +83,7 @@ pub struct Metadata { /// JSON array containing a list of the JWS signing algorithms supported by /// the token endpoint for the signature on the JWT used to authenticate the /// client at the token endpoint. - pub token_endpoint_auth_signing_alg_values_supported: - Option>, + pub token_endpoint_auth_signing_alg_values_supported: Option>, /// URL of a page containing human-readable information that developers /// might want or need to know when using the authorization server. @@ -115,8 +114,7 @@ pub struct Metadata { /// JSON array containing a list of the JWS signing algorithms supported by /// the revocation endpoint for the signature on the JWT used to /// authenticate the client at the revocation endpoint. - pub revocation_endpoint_auth_signing_alg_values_supported: - Option>, + pub revocation_endpoint_auth_signing_alg_values_supported: Option>, /// URL of the authorization server's OAuth 2.0 introspection endpoint. pub introspection_endpoint: Option, @@ -129,7 +127,7 @@ pub struct Metadata { /// the introspection endpoint for the signature on the JWT used to /// authenticate the client at the introspection endpoint. pub introspection_endpoint_auth_signing_alg_values_supported: - Option>, + Option>, /// PKCE code challenge methods supported by this authorization server. pub code_challenge_methods_supported: Option>, @@ -147,39 +145,39 @@ pub struct Metadata { /// JSON array containing a list of the JWS "alg" values supported by the OP /// for the ID Token. - pub id_token_signing_alg_values_supported: Option>, + pub id_token_signing_alg_values_supported: Option>, /// JSON array containing a list of the JWE "alg" values supported by the OP /// for the ID Token. - pub id_token_encryption_alg_values_supported: Option>, + pub id_token_encryption_alg_values_supported: Option>, /// JSON array containing a list of the JWE "enc" values supported by the OP /// for the ID Token. - pub id_token_encryption_enc_values_supported: Option>, + pub id_token_encryption_enc_values_supported: Option>, /// JSON array containing a list of the JWS "alg" values supported by the /// UserInfo Endpoint. - pub userinfo_signing_alg_values_supported: Option>, + pub userinfo_signing_alg_values_supported: Option>, /// JSON array containing a list of the JWE "alg" values supported by the /// UserInfo Endpoint. - pub userinfo_encryption_alg_values_supported: Option>, + pub userinfo_encryption_alg_values_supported: Option>, /// JSON array containing a list of the JWE "enc" values supported by the /// UserInfo Endpoint. - pub userinfo_encryption_enc_values_supported: Option>, + pub userinfo_encryption_enc_values_supported: Option>, /// JSON array containing a list of the JWS "alg" values supported by the OP /// for Request Objects. - pub request_object_signing_alg_values_supported: Option>, + pub request_object_signing_alg_values_supported: Option>, /// JSON array containing a list of the JWE "alg" values supported by the OP /// for Request Objects. - pub request_object_encryption_alg_values_supported: Option>, + pub request_object_encryption_alg_values_supported: Option>, /// JSON array containing a list of the JWE "enc" values supported by the OP /// for Request Objects. - pub request_object_encryption_enc_values_supported: Option>, + pub request_object_encryption_enc_values_supported: Option>, /// JSON array containing a list of the "display" parameter values that the /// OpenID Provider supports. diff --git a/crates/oauth2-types/src/pkce.rs b/crates/oauth2-types/src/pkce.rs index d90f830c..76bbc238 100644 --- a/crates/oauth2-types/src/pkce.rs +++ b/crates/oauth2-types/src/pkce.rs @@ -33,16 +33,14 @@ use sha2::{Digest, Sha256}; Serialize, Deserialize, )] -#[cfg_attr(feature = "sqlx_type", derive(sqlx::Type))] -#[repr(i8)] pub enum CodeChallengeMethod { #[serde(rename = "plain")] #[display("plain")] - Plain = 0, + Plain, #[serde(rename = "S256")] #[display("S256")] - S256 = 1, + S256, } impl CodeChallengeMethod { diff --git a/crates/warp-utils/src/filters/client.rs b/crates/warp-utils/src/filters/client.rs index 88a77d08..d07041af 100644 --- a/crates/warp-utils/src/filters/client.rs +++ b/crates/warp-utils/src/filters/client.rs @@ -290,7 +290,7 @@ struct ClientAuthForm { mod tests { use headers::authorization::Credentials; use mas_config::{ConfigurationSection, OAuth2ClientAuthMethodConfig}; - use mas_jose::{ExportJwks, JsonWebSignatureAlgorithm, SigningKeystore, StaticKeystore}; + use mas_jose::{ExportJwks, SigningKeystore, StaticKeystore}; use serde_json::json; use super::*; @@ -364,17 +364,17 @@ mod tests { #[tokio::test] async fn client_secret_jwt_hs256() { - client_secret_jwt(JsonWebSignatureAlgorithm::Hs256).await; + client_secret_jwt("HS256").await; } #[tokio::test] async fn client_secret_jwt_hs384() { - client_secret_jwt(JsonWebSignatureAlgorithm::Hs384).await; + client_secret_jwt("HS384").await; } #[tokio::test] async fn client_secret_jwt_hs512() { - client_secret_jwt(JsonWebSignatureAlgorithm::Hs512).await; + client_secret_jwt("HS512").await; } fn client_claims( @@ -395,7 +395,8 @@ mod tests { claims } - async fn client_secret_jwt(alg: JsonWebSignatureAlgorithm) { + async fn client_secret_jwt(alg: &str) { + let alg = alg.parse().unwrap(); let audience = "https://example.com/token"; let filter = client_authentication::
(&oauth2_config().await, audience.to_string()); @@ -463,25 +464,26 @@ mod tests { #[tokio::test] async fn client_secret_jwt_rs256() { - private_key_jwt(JsonWebSignatureAlgorithm::Rs256).await; + private_key_jwt("RS256").await; } #[tokio::test] async fn client_secret_jwt_rs384() { - private_key_jwt(JsonWebSignatureAlgorithm::Rs384).await; + private_key_jwt("RS384").await; } #[tokio::test] async fn client_secret_jwt_rs512() { - private_key_jwt(JsonWebSignatureAlgorithm::Rs512).await; + private_key_jwt("RS512").await; } #[tokio::test] async fn client_secret_jwt_es256() { - private_key_jwt(JsonWebSignatureAlgorithm::Es256).await; + private_key_jwt("ES256").await; } - async fn private_key_jwt(alg: JsonWebSignatureAlgorithm) { + async fn private_key_jwt(alg: &str) { + let alg = alg.parse().unwrap(); let audience = "https://example.com/token"; let filter = client_authentication::(&oauth2_config().await, audience.to_string());