Skip to main content

identity_kit/jose/
algorithm.rs

1//! `JwsAlgorithm`: the digital signature algorithms this crate speaks.
2
3use core::fmt;
4use core::str::FromStr;
5
6use serde::{Deserialize, Serialize};
7
8/// A JWS digital signature algorithm (RFC 7518 §3.1, RFC 8037 §3.1, RFC 8812 §3.2).
9///
10/// Only asymmetric algorithms are listed: a DID controller signs with a private key and anyone
11/// resolves the public key, so HMAC and `none` have no place here. Conversions to and from
12/// [`jsonwebtoken::Algorithm`] are fallible in both directions because `jsonwebtoken` has HMAC
13/// variants this type lacks and lacks [`ES256K`](Self::ES256K).
14///
15/// The `Display` and `FromStr` forms are the IANA "JSON Web Signature and Encryption Algorithms"
16/// names, and the type serializes as that string.
17///
18/// # Examples
19///
20/// ```
21/// use identity_kit::jose::JwsAlgorithm;
22///
23/// let alg: JwsAlgorithm = "ES256".parse()?;
24/// assert_eq!(alg, JwsAlgorithm::ES256);
25/// assert_eq!(alg.to_string(), "ES256");
26/// assert_eq!(serde_json::to_string(&alg)?, "\"ES256\"");
27///
28/// assert!("HS256".parse::<JwsAlgorithm>().is_err());
29/// assert!("none".parse::<JwsAlgorithm>().is_err());
30/// # Ok::<(), Box<dyn std::error::Error>>(())
31/// ```
32#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
33#[non_exhaustive]
34pub enum JwsAlgorithm {
35    /// EdDSA with Ed25519 (RFC 8037).
36    EdDSA,
37    /// ECDSA with P-256 and SHA-256 (RFC 7518 §3.4).
38    ES256,
39    /// ECDSA with secp256k1 and SHA-256 (RFC 8812 §3.2).
40    ES256K,
41    /// ECDSA with P-384 and SHA-384 (RFC 7518 §3.4).
42    ES384,
43    /// RSASSA-PKCS1-v1_5 with SHA-256 (RFC 7518 §3.3).
44    RS256,
45    /// RSASSA-PSS with SHA-256 and MGF1 with SHA-256 (RFC 7518 §3.5).
46    PS256,
47}
48
49impl JwsAlgorithm {
50    /// Every algorithm this type knows, in declaration order.
51    pub const ALL: &'static [JwsAlgorithm] = &[
52        Self::EdDSA,
53        Self::ES256,
54        Self::ES256K,
55        Self::ES384,
56        Self::RS256,
57        Self::PS256,
58    ];
59
60    /// The IANA name, for example `ES256`.
61    pub const fn as_str(self) -> &'static str {
62        match self {
63            Self::EdDSA => "EdDSA",
64            Self::ES256 => "ES256",
65            Self::ES256K => "ES256K",
66            Self::ES384 => "ES384",
67            Self::RS256 => "RS256",
68            Self::PS256 => "PS256",
69        }
70    }
71
72    /// Whether this is an ECDSA algorithm, whose JWS signature is the raw `r || s` concatenation.
73    pub const fn is_ecdsa(self) -> bool {
74        matches!(self, Self::ES256 | Self::ES256K | Self::ES384)
75    }
76}
77
78impl fmt::Display for JwsAlgorithm {
79    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
80        f.write_str(self.as_str())
81    }
82}
83
84/// An algorithm name that is not a [`JwsAlgorithm`].
85#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
86#[error("unknown or unsupported JWS algorithm `{0}`")]
87pub struct UnknownAlgorithm(String);
88
89impl UnknownAlgorithm {
90    /// The rejected name.
91    pub fn name(&self) -> &str {
92        &self.0
93    }
94}
95
96impl FromStr for JwsAlgorithm {
97    type Err = UnknownAlgorithm;
98
99    fn from_str(s: &str) -> Result<Self, Self::Err> {
100        Self::ALL
101            .iter()
102            .copied()
103            .find(|alg| alg.as_str() == s)
104            .ok_or_else(|| UnknownAlgorithm(s.to_owned()))
105    }
106}
107
108impl TryFrom<jsonwebtoken::Algorithm> for JwsAlgorithm {
109    type Error = UnknownAlgorithm;
110
111    /// Fails for the HMAC algorithms and the SHA-384/SHA-512 RSA variants.
112    fn try_from(alg: jsonwebtoken::Algorithm) -> Result<Self, Self::Error> {
113        use jsonwebtoken::Algorithm as A;
114        match alg {
115            A::EdDSA => Ok(Self::EdDSA),
116            A::ES256 => Ok(Self::ES256),
117            A::ES384 => Ok(Self::ES384),
118            A::RS256 => Ok(Self::RS256),
119            A::PS256 => Ok(Self::PS256),
120            other => Err(UnknownAlgorithm(format!("{other:?}"))),
121        }
122    }
123}
124
125impl TryFrom<JwsAlgorithm> for jsonwebtoken::Algorithm {
126    type Error = UnknownAlgorithm;
127
128    /// Fails for [`JwsAlgorithm::ES256K`], which `jsonwebtoken` does not implement.
129    fn try_from(alg: JwsAlgorithm) -> Result<Self, Self::Error> {
130        match alg {
131            JwsAlgorithm::EdDSA => Ok(Self::EdDSA),
132            JwsAlgorithm::ES256 => Ok(Self::ES256),
133            JwsAlgorithm::ES384 => Ok(Self::ES384),
134            JwsAlgorithm::RS256 => Ok(Self::RS256),
135            JwsAlgorithm::PS256 => Ok(Self::PS256),
136            other => Err(UnknownAlgorithm(other.as_str().to_owned())),
137        }
138    }
139}