Skip to main content

volga_oauth_core/
algorithm.rs

1//! JWS signing and verification algorithm identifiers
2
3use serde::{Deserialize, Serialize};
4use std::fmt::{Display, Formatter};
5
6/// A JWS signing / verification algorithm (RFC 7518 Section 3.1)
7///
8/// The `alg` header value of a JWT, shared by everything in the framework
9/// that signs or verifies one: bearer authentication on the server side,
10/// and `private_key_jwt` client assertions on the client side. The mapping
11/// onto the underlying JWT implementation stays private to each crate, so
12/// no consumer has to name it.
13/// The variant names are the registered `alg` values verbatim, so the
14/// derived serde impls are the wire form (`JwsAlgorithm::EdDSA` <-> `"EdDSA"`).
15#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Default, Serialize, Deserialize)]
16#[non_exhaustive]
17pub enum JwsAlgorithm {
18    /// HMAC using SHA-256
19    #[default]
20    HS256,
21    /// HMAC using SHA-384
22    HS384,
23    /// HMAC using SHA-512
24    HS512,
25    /// ECDSA using P-256 and SHA-256
26    ES256,
27    /// ECDSA using P-384 and SHA-384
28    ES384,
29    /// RSASSA-PKCS1-v1_5 using SHA-256
30    RS256,
31    /// RSASSA-PKCS1-v1_5 using SHA-384
32    RS384,
33    /// RSASSA-PKCS1-v1_5 using SHA-512
34    RS512,
35    /// RSASSA-PSS using SHA-256
36    PS256,
37    /// RSASSA-PSS using SHA-384
38    PS384,
39    /// RSASSA-PSS using SHA-512
40    PS512,
41    /// Edwards-curve Digital Signature Algorithm (EdDSA)
42    EdDSA,
43}
44
45impl JwsAlgorithm {
46    /// Returns the registered `alg` header value of this algorithm
47    ///
48    /// Use it to match against the algorithm lists a server advertises,
49    /// such as `token_endpoint_auth_signing_alg_values_supported`.
50    pub const fn as_str(&self) -> &'static str {
51        match self {
52            Self::HS256 => "HS256",
53            Self::HS384 => "HS384",
54            Self::HS512 => "HS512",
55            Self::ES256 => "ES256",
56            Self::ES384 => "ES384",
57            Self::RS256 => "RS256",
58            Self::RS384 => "RS384",
59            Self::RS512 => "RS512",
60            Self::PS256 => "PS256",
61            Self::PS384 => "PS384",
62            Self::PS512 => "PS512",
63            Self::EdDSA => "EdDSA",
64        }
65    }
66
67    /// Returns `true` for the HMAC algorithms, where signer and verifier
68    /// hold the same secret
69    ///
70    /// Anything built on proving possession of a private key - a
71    /// `private_key_jwt` client assertion, a key published in a JWKS -
72    /// must refuse these: a shared secret proves nothing about who signed.
73    pub const fn is_symmetric(&self) -> bool {
74        matches!(self, Self::HS256 | Self::HS384 | Self::HS512)
75    }
76}
77
78impl Display for JwsAlgorithm {
79    #[inline]
80    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
81        f.write_str(self.as_str())
82    }
83}
84
85#[cfg(test)]
86mod tests {
87    use super::*;
88
89    const ALL: [JwsAlgorithm; 12] = [
90        JwsAlgorithm::HS256,
91        JwsAlgorithm::HS384,
92        JwsAlgorithm::HS512,
93        JwsAlgorithm::ES256,
94        JwsAlgorithm::ES384,
95        JwsAlgorithm::RS256,
96        JwsAlgorithm::RS384,
97        JwsAlgorithm::RS512,
98        JwsAlgorithm::PS256,
99        JwsAlgorithm::PS384,
100        JwsAlgorithm::PS512,
101        JwsAlgorithm::EdDSA,
102    ];
103
104    #[test]
105    fn it_defaults_to_hs256() {
106        assert_eq!(JwsAlgorithm::default(), JwsAlgorithm::HS256);
107    }
108
109    #[test]
110    fn it_renders_the_registered_alg_names() {
111        // the `alg` name is compared against server-advertised strings, so
112        // it must match the RFC 7518 spelling exactly, `EdDSA` included
113        for alg in ALL {
114            assert_eq!(alg.to_string(), alg.as_str());
115            assert_eq!(format!("{alg:?}"), alg.as_str());
116        }
117        assert_eq!(JwsAlgorithm::EdDSA.as_str(), "EdDSA");
118    }
119
120    #[test]
121    fn it_separates_symmetric_from_asymmetric() {
122        for alg in ALL {
123            assert_eq!(alg.is_symmetric(), alg.as_str().starts_with("HS"));
124        }
125    }
126}