Skip to main content

alibi_plugins/sso/
signing_algorithms.rs

1//! Application policies for configured SAML signing and digest algorithms.
2#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
3pub enum SsoDeprecatedAlgorithmBehavior {
4    Reject,
5    #[default]
6    Warn,
7    Allow,
8}
9/// Admission policy for configured algorithms at provider registration and update.
10#[derive(Clone, Debug, Default)]
11pub struct SsoSamlSigningPolicy {
12    pub on_deprecated: SsoDeprecatedAlgorithmBehavior,
13    pub allowed_signature_algorithms: Option<Vec<String>>,
14    pub allowed_digest_algorithms: Option<Vec<String>>,
15}
16
17use super::{AuthError, AuthResult, Value};
18const SIGNATURES: &[(&str, &str)] = &[
19    ("sha1", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"),
20    (
21        "sha256",
22        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
23    ),
24    (
25        "sha384",
26        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
27    ),
28    (
29        "sha512",
30        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
31    ),
32    ("rsa-sha1", "http://www.w3.org/2000/09/xmldsig#rsa-sha1"),
33    (
34        "rsa-sha256",
35        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256",
36    ),
37    (
38        "rsa-sha384",
39        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha384",
40    ),
41    (
42        "rsa-sha512",
43        "http://www.w3.org/2001/04/xmldsig-more#rsa-sha512",
44    ),
45    (
46        "ecdsa-sha256",
47        "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256",
48    ),
49    (
50        "ecdsa-sha384",
51        "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha384",
52    ),
53    (
54        "ecdsa-sha512",
55        "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha512",
56    ),
57];
58const DIGESTS: &[(&str, &str)] = &[
59    ("sha1", "http://www.w3.org/2000/09/xmldsig#sha1"),
60    ("sha256", "http://www.w3.org/2001/04/xmlenc#sha256"),
61    ("sha384", "http://www.w3.org/2001/04/xmldsig-more#sha384"),
62    ("sha512", "http://www.w3.org/2001/04/xmlenc#sha512"),
63];
64fn normalize<'a>(algorithm: &'a str, names: &[(&str, &'static str)]) -> &'a str {
65    names
66        .iter()
67        .find(|(name, _)| name.eq_ignore_ascii_case(algorithm))
68        .map_or(algorithm, |(_, uri)| *uri)
69}
70fn failure(code: &str, message: String) -> AuthError {
71    AuthError::Api {
72        status: 400,
73        code: Some(code.into()),
74        message,
75    }
76}
77pub(super) fn validate(config: &Value, policy: &SsoSamlSigningPolicy) -> AuthResult<()> {
78    for (field, label, names, allowed) in [
79        (
80            "signatureAlgorithm",
81            "signature",
82            SIGNATURES,
83            &policy.allowed_signature_algorithms,
84        ),
85        (
86            "digestAlgorithm",
87            "digest",
88            DIGESTS,
89            &policy.allowed_digest_algorithms,
90        ),
91    ] {
92        let Some(algorithm) = config
93            .get(field)
94            .and_then(Value::as_str)
95            .filter(|value| !value.is_empty())
96        else {
97            continue;
98        };
99        let normalized = normalize(algorithm, names);
100        if let Some(allowed) = allowed {
101            if !allowed
102                .iter()
103                .any(|value| normalize(value, names) == normalized)
104            {
105                return Err(failure(
106                    "SAML_ALGORITHM_NOT_ALLOWED",
107                    format!("SAML {label} algorithm not in allow-list: {algorithm}"),
108                ));
109            }
110            continue;
111        }
112        if names.first().is_some_and(|(_, uri)| normalized == *uri) {
113            let message = format!(
114                "SAML config uses deprecated {label} algorithm: {algorithm}. Consider using SHA-256 or stronger."
115            );
116            match policy.on_deprecated {
117                SsoDeprecatedAlgorithmBehavior::Reject => {
118                    return Err(failure("SAML_DEPRECATED_CONFIG_ALGORITHM", message));
119                }
120                SsoDeprecatedAlgorithmBehavior::Warn => tracing::warn!("{message}"),
121                SsoDeprecatedAlgorithmBehavior::Allow => {}
122            }
123        } else if !names.iter().any(|(_, uri)| *uri == normalized) {
124            return Err(failure(
125                "SAML_UNKNOWN_ALGORITHM",
126                format!("SAML {label} algorithm not recognized: {algorithm}"),
127            ));
128        }
129    }
130    Ok(())
131}