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). Verification accepts `s` and `n - s`
38    /// alike, as JOSE allows, so a JWS's bytes do not identify it; see
39    /// [`DefaultVerifier`](super::DefaultVerifier).
40    ES256,
41    /// ECDSA with secp256k1 and SHA-256 (RFC 8812 §3.2). Verification accepts only the lower of
42    /// `s` and `n - s`.
43    ES256K,
44    /// ECDSA with P-384 and SHA-384 (RFC 7518 §3.4).
45    ES384,
46    /// RSASSA-PKCS1-v1_5 with SHA-256 (RFC 7518 §3.3), with a key of at least 2048 bits.
47    RS256,
48    /// RSASSA-PSS with SHA-256 and MGF1 with SHA-256 (RFC 7518 §3.5), with a key of at least 2048 bits.
49    PS256,
50}
51
52impl JwsAlgorithm {
53    /// Every algorithm this type knows, in declaration order.
54    pub const ALL: &'static [JwsAlgorithm] = &[
55        Self::EdDSA,
56        Self::ES256,
57        Self::ES256K,
58        Self::ES384,
59        Self::RS256,
60        Self::PS256,
61    ];
62
63    /// The IANA name, for example `ES256`.
64    pub const fn as_str(self) -> &'static str {
65        match self {
66            Self::EdDSA => "EdDSA",
67            Self::ES256 => "ES256",
68            Self::ES256K => "ES256K",
69            Self::ES384 => "ES384",
70            Self::RS256 => "RS256",
71            Self::PS256 => "PS256",
72        }
73    }
74
75    /// Whether this is an ECDSA algorithm, whose JWS signature is the raw `r || s` concatenation.
76    pub const fn is_ecdsa(self) -> bool {
77        matches!(self, Self::ES256 | Self::ES256K | Self::ES384)
78    }
79}
80
81impl fmt::Display for JwsAlgorithm {
82    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
83        f.write_str(self.as_str())
84    }
85}
86
87/// An algorithm name that is not a [`JwsAlgorithm`].
88#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
89#[error("unknown or unsupported JWS algorithm `{0}`")]
90pub struct UnknownAlgorithm(String);
91
92impl UnknownAlgorithm {
93    /// The rejected name.
94    pub fn name(&self) -> &str {
95        &self.0
96    }
97}
98
99impl FromStr for JwsAlgorithm {
100    type Err = UnknownAlgorithm;
101
102    fn from_str(s: &str) -> Result<Self, Self::Err> {
103        Self::ALL
104            .iter()
105            .copied()
106            .find(|alg| alg.as_str() == s)
107            .ok_or_else(|| UnknownAlgorithm(s.to_owned()))
108    }
109}
110
111impl TryFrom<jsonwebtoken::Algorithm> for JwsAlgorithm {
112    type Error = UnknownAlgorithm;
113
114    /// Fails for the HMAC algorithms and the SHA-384/SHA-512 RSA variants.
115    fn try_from(alg: jsonwebtoken::Algorithm) -> Result<Self, Self::Error> {
116        use jsonwebtoken::Algorithm as A;
117        match alg {
118            A::EdDSA => Ok(Self::EdDSA),
119            A::ES256 => Ok(Self::ES256),
120            A::ES384 => Ok(Self::ES384),
121            A::RS256 => Ok(Self::RS256),
122            A::PS256 => Ok(Self::PS256),
123            other => Err(UnknownAlgorithm(format!("{other:?}"))),
124        }
125    }
126}
127
128impl TryFrom<JwsAlgorithm> for jsonwebtoken::Algorithm {
129    type Error = UnknownAlgorithm;
130
131    /// Fails for [`JwsAlgorithm::ES256K`], which `jsonwebtoken` does not implement.
132    fn try_from(alg: JwsAlgorithm) -> Result<Self, Self::Error> {
133        match alg {
134            JwsAlgorithm::EdDSA => Ok(Self::EdDSA),
135            JwsAlgorithm::ES256 => Ok(Self::ES256),
136            JwsAlgorithm::ES384 => Ok(Self::ES384),
137            JwsAlgorithm::RS256 => Ok(Self::RS256),
138            JwsAlgorithm::PS256 => Ok(Self::PS256),
139            other => Err(UnknownAlgorithm(other.as_str().to_owned())),
140        }
141    }
142}