1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

Multiple IANA codegen enhancement

- JWS/JWE algorithms are properly splitted
 - Enums now have a proper description
 - They implement FromStr and Display
 - mas-jose does not reexport mas-iana anymore
This commit is contained in:
Quentin Gliech
2022-01-12 10:58:27 +01:00
parent d9b1ef3ded
commit 2844706bb1
21 changed files with 401 additions and 497 deletions

1
Cargo.lock generated
View File

@ -1599,6 +1599,7 @@ dependencies = [
name = "mas-iana"
version = "0.1.0"
dependencies = [
"parse-display",
"schemars",
"serde",
]

View File

@ -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
});

View File

@ -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);

View File

@ -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")

View File

@ -27,8 +27,7 @@ struct File {
client: Arc<Client>,
registry_name: &'static str,
registry_url: &'static str,
sections: Vec<&'static str>,
sources: Vec<&'static str>,
sections: Vec<Section>,
items: HashMap<&'static str, Vec<EnumMember>>,
}
@ -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<T: EnumEntry>(mut self) -> anyhow::Result<Self> {
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, "}}")?;

View File

@ -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")

View File

@ -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<Section> {
Self::SECTIONS
.iter()
.map(|s| Section {
url: Some(Self::URL),
..*s
})
.collect()
}
fn key(&self) -> Option<&'static str>;
fn name(&self) -> &str;

View File

@ -8,3 +8,4 @@ license = "Apache-2.0"
[dependencies]
serde = "1.0.133"
schemars = { version = "0.8.8" }
parse-display = "0.5.3"

View File

@ -14,292 +14,396 @@
//! Enums from the "JSON Object Signing and Encryption" IANA registry
//! See <https://www.iana.org/assignments/jose/jose.xhtml>
//!
//! Generated from:
//! - <https://www.iana.org/assignments/jose/web-signature-encryption-algorithms.csv>
//! - <https://www.iana.org/assignments/jose/web-encryption-compression-algorithms.csv>
//! - <https://www.iana.org/assignments/jose/web-key-types.csv>
//! - <https://www.iana.org/assignments/jose/web-key-elliptic-curve.csv>
//! - <https://www.iana.org/assignments/jose/web-key-use.csv>
//! - <https://www.iana.org/assignments/jose/web-key-operations.csv>
// 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: <https://www.iana.org/assignments/jose/web-signature-encryption-algorithms.csv>
#[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: <https://www.iana.org/assignments/jose/web-signature-encryption-algorithms.csv>
#[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: <https://www.iana.org/assignments/jose/web-signature-encryption-algorithms.csv>
#[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: <https://www.iana.org/assignments/jose/web-encryption-compression-algorithms.csv>
#[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: <https://www.iana.org/assignments/jose/web-key-types.csv>
#[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: <https://www.iana.org/assignments/jose/web-key-elliptic-curve.csv>
#[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: <https://www.iana.org/assignments/jose/web-key-elliptic-curve.csv>
#[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: <https://www.iana.org/assignments/jose/web-key-use.csv>
#[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: <https://www.iana.org/assignments/jose/web-key-operations.csv>
#[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,
}

View File

@ -14,86 +14,121 @@
//! Enums from the "OAuth Parameters" IANA registry
//! See <https://www.iana.org/assignments/jose/jose.xhtml>
//!
//! Generated from:
//! - <https://www.iana.org/assignments/oauth-parameters/token-type-hint.csv>
//! - <https://www.iana.org/assignments/oauth-parameters/endpoint.csv>
//! - <https://www.iana.org/assignments/oauth-parameters/token-endpoint-auth-method.csv>
//! - <https://www.iana.org/assignments/oauth-parameters/pkce-code-challenge-method.csv>
// 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: <https://www.iana.org/assignments/oauth-parameters/token-type-hint.csv>
#[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: <https://www.iana.org/assignments/oauth-parameters/endpoint.csv>
#[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: <https://www.iana.org/assignments/oauth-parameters/token-endpoint-auth-method.csv>
#[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: <https://www.iana.org/assignments/oauth-parameters/pkce-code-challenge-method.csv>
#[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,
}

View File

@ -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
//!
//! <https://www.iana.org/assignments/jose/jose.xhtml>
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,
}

View File

@ -15,6 +15,10 @@
//! Ref: <https://www.rfc-editor.org/rfc/rfc7517.html>
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<Vec<JsonWebKeyOperation>>,
#[serde(default)]
alg: Option<JsonWebSignatureAlgorithm>,
alg: Option<JsonWebSignatureAlg>,
#[serde(default)]
kid: Option<String>,
@ -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
}

View File

@ -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<JsonWebEncryptionAlgorithm>,
enc: Option<JsonWebEncryptionEnc>,
#[serde(default)]
jku: Option<Url>,
@ -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!({

View File

@ -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)?;

View File

@ -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<JsonWebSignatureAlgorithm> {
fn supported_algorithms(self) -> HashSet<JsonWebSignatureAlg> {
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<JwtHeader> {
async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result<JwtHeader> {
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::<Sha256>::new_from_slice(self.inner)?;
mac.update(msg);
mac.finalize().into_bytes().to_vec()
}
JsonWebSignatureAlgorithm::Hs384 => {
JsonWebSignatureAlg::Hs384 => {
let mut mac = Hmac::<Sha384>::new_from_slice(self.inner)?;
mac.update(msg);
mac.finalize().into_bytes().to_vec()
}
JsonWebSignatureAlgorithm::Hs512 => {
JsonWebSignatureAlg::Hs512 => {
let mut mac = Hmac::<Sha512>::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::<Sha256>::new_from_slice(self.inner)?;
mac.update(payload);
mac.verify(signature.try_into()?)?;
}
JsonWebSignatureAlgorithm::Hs384 => {
JsonWebSignatureAlg::Hs384 => {
let mut mac = Hmac::<Sha384>::new_from_slice(self.inner)?;
mac.update(payload);
mac.verify(signature.try_into()?)?;
}
JsonWebSignatureAlgorithm::Hs512 => {
JsonWebSignatureAlg::Hs512 => {
let mut mac = Hmac::<Sha512>::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);

View File

@ -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<JsonWebSignatureAlgorithm> {
fn supported_algorithms(self) -> HashSet<JsonWebSignatureAlg> {
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<JwtHeader> {
async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result<JwtHeader> {
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);

View File

@ -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<JsonWebSignatureAlgorithm>;
fn supported_algorithms(self) -> HashSet<JsonWebSignatureAlg>;
async fn prepare_header(self, alg: JsonWebSignatureAlgorithm) -> anyhow::Result<JwtHeader>;
async fn prepare_header(self, alg: JsonWebSignatureAlg) -> anyhow::Result<JwtHeader>;
async fn sign(self, header: &JwtHeader, msg: &[u8]) -> anyhow::Result<Vec<u8>>;
}

View File

@ -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::{

View File

@ -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<HashSet<JsonWebSignatureAlgorithm>>,
pub token_endpoint_auth_signing_alg_values_supported: Option<HashSet<JsonWebSignatureAlg>>,
/// 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<HashSet<JsonWebSignatureAlgorithm>>,
pub revocation_endpoint_auth_signing_alg_values_supported: Option<HashSet<JsonWebSignatureAlg>>,
/// URL of the authorization server's OAuth 2.0 introspection endpoint.
pub introspection_endpoint: Option<Url>,
@ -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<HashSet<JsonWebSignatureAlgorithm>>,
Option<HashSet<JsonWebSignatureAlg>>,
/// PKCE code challenge methods supported by this authorization server.
pub code_challenge_methods_supported: Option<HashSet<CodeChallengeMethod>>,
@ -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<HashSet<JsonWebSignatureAlgorithm>>,
pub id_token_signing_alg_values_supported: Option<HashSet<JsonWebSignatureAlg>>,
/// 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<HashSet<JsonWebSignatureAlgorithm>>,
pub id_token_encryption_alg_values_supported: Option<HashSet<JsonWebEncryptionAlg>>,
/// 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<HashSet<JsonWebEncryptionAlgorithm>>,
pub id_token_encryption_enc_values_supported: Option<HashSet<JsonWebEncryptionEnc>>,
/// JSON array containing a list of the JWS "alg" values supported by the
/// UserInfo Endpoint.
pub userinfo_signing_alg_values_supported: Option<HashSet<JsonWebSignatureAlgorithm>>,
pub userinfo_signing_alg_values_supported: Option<HashSet<JsonWebSignatureAlg>>,
/// JSON array containing a list of the JWE "alg" values supported by the
/// UserInfo Endpoint.
pub userinfo_encryption_alg_values_supported: Option<HashSet<JsonWebSignatureAlgorithm>>,
pub userinfo_encryption_alg_values_supported: Option<HashSet<JsonWebEncryptionAlg>>,
/// JSON array containing a list of the JWE "enc" values supported by the
/// UserInfo Endpoint.
pub userinfo_encryption_enc_values_supported: Option<HashSet<JsonWebEncryptionAlgorithm>>,
pub userinfo_encryption_enc_values_supported: Option<HashSet<JsonWebEncryptionEnc>>,
/// 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<HashSet<JsonWebSignatureAlgorithm>>,
pub request_object_signing_alg_values_supported: Option<HashSet<JsonWebSignatureAlg>>,
/// 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<HashSet<JsonWebSignatureAlgorithm>>,
pub request_object_encryption_alg_values_supported: Option<HashSet<JsonWebEncryptionAlg>>,
/// 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<HashSet<JsonWebEncryptionAlgorithm>>,
pub request_object_encryption_enc_values_supported: Option<HashSet<JsonWebEncryptionEnc>>,
/// JSON array containing a list of the "display" parameter values that the
/// OpenID Provider supports.

View File

@ -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 {

View File

@ -290,7 +290,7 @@ struct ClientAuthForm<T> {
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::<Form>(&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::<Form>(&oauth2_config().await, audience.to_string());