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}