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}