ssi_jwk/
algorithm.rs

1use core::fmt;
2use serde::{Deserialize, Serialize};
3use ssi_crypto::{
4    algorithm::{ES256OrES384, SignatureAlgorithmInstance, SignatureAlgorithmType},
5    UnsupportedAlgorithm,
6};
7
8macro_rules! algorithms {
9    ($(
10        $(#[doc = $doc:tt])*
11        $(#[doc($doc_tag:ident)])?
12        $(#[serde $serde:tt])?
13        $id:ident: $name:literal
14    ),*) => {
15        /// Signature algorithm.
16        #[derive(Debug, Serialize, Deserialize, Clone, Copy, PartialEq, Hash, Eq)]
17        pub enum Algorithm {
18            $(
19                $(#[doc = $doc])*
20                $(#[doc($doc_tag)])?
21                $(#[serde $serde])?
22                #[serde(rename = $name)]
23                $id,
24            )*
25            /// No signature.
26            ///
27            /// Per the specs it should only be `none` but `None` is kept for backwards
28            /// compatibility.
29            #[serde(alias = "None", rename = "none")]
30            None
31        }
32
33        impl Algorithm {
34            pub fn as_str(&self) -> &'static str {
35                match self {
36                    $(
37                        Self::$id => $name,
38                    )*
39                    Self::None => "none"
40                }
41            }
42
43            pub fn into_str(self) -> &'static str {
44                match self {
45                    $(
46                        Self::$id => $name,
47                    )*
48                    Self::None => "none"
49                }
50            }
51        }
52
53        impl From<Algorithm> for ssi_crypto::Algorithm {
54            fn from(a: Algorithm) -> Self {
55                match a {
56                    $(Algorithm::$id => Self::$id,)*
57                    Algorithm::None => Self::None
58                }
59            }
60        }
61
62        impl From<Algorithm> for ssi_crypto::AlgorithmInstance {
63            fn from(a: Algorithm) -> Self {
64                match a {
65                    $(Algorithm::$id => Self::$id,)*
66                    Algorithm::None => Self::None
67                }
68            }
69        }
70
71        $(
72            impl From<ssi_crypto::algorithm::$id> for Algorithm {
73                fn from(_: ssi_crypto::algorithm::$id) -> Self {
74                    Self::$id
75                }
76            }
77
78            impl TryFrom<Algorithm> for ssi_crypto::algorithm::$id {
79                type Error = UnsupportedAlgorithm;
80
81                fn try_from(value: Algorithm) -> Result<Self, Self::Error> {
82                    match value {
83                        Algorithm::$id => Ok(Self),
84                        other => Err(UnsupportedAlgorithm(other.into()))
85                    }
86                }
87            }
88        )*
89
90        impl TryFrom<ssi_crypto::Algorithm> for Algorithm {
91            type Error = UnsupportedAlgorithm;
92
93            fn try_from(a: ssi_crypto::Algorithm) -> Result<Self, Self::Error> {
94                match a {
95                    $(ssi_crypto::Algorithm::$id => Ok(Self::$id),)*
96                    ssi_crypto::Algorithm::None => Ok(Self::None),
97                    other => Err(UnsupportedAlgorithm(other))
98                }
99            }
100        }
101
102        impl TryFrom<ssi_crypto::AlgorithmInstance> for Algorithm {
103            type Error = UnsupportedAlgorithm;
104
105            fn try_from(a: ssi_crypto::AlgorithmInstance) -> Result<Self, Self::Error> {
106                match a {
107                    $(ssi_crypto::AlgorithmInstance::$id => Ok(Self::$id),)*
108                    ssi_crypto::AlgorithmInstance::None => Ok(Self::None),
109                    other => Err(UnsupportedAlgorithm(other.algorithm()))
110                }
111            }
112        }
113    };
114}
115
116algorithms! {
117    /// HMAC using SHA-256.
118    ///
119    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
120    HS256: "HS256",
121
122    /// HMAC using SHA-384.
123    ///
124    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
125    HS384: "HS384",
126
127    /// HMAC using SHA-512.
128    ///
129    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
130    HS512: "HS512",
131
132    /// RSASSA-PKCS1-v1_5 using SHA-256.
133    ///
134    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
135    RS256: "RS256",
136
137    /// RSASSA-PKCS1-v1_5 using SHA-384.
138    ///
139    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
140    RS384: "RS384",
141
142    /// RSASSA-PKCS1-v1_5 using SHA-512.
143    ///
144    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
145    RS512: "RS512",
146
147    /// RSASSA-PSS using SHA-256 and MGF1 with SHA-256.
148    ///
149    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
150    PS256: "PS256",
151
152    /// RSASSA-PSS using SHA-384 and MGF1 with SHA-384.
153    ///
154    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
155    PS384: "PS384",
156
157    /// RSASSA-PSS using SHA-512 and MGF1 with SHA-512.
158    ///
159    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
160    PS512: "PS512",
161
162    /// Edwards-curve Digital Signature Algorithm (EdDSA) using SHA-256.
163    ///
164    /// The following curves are defined for use with `EdDSA`:
165    ///  - `Ed25519`
166    ///  - `Ed448`
167    ///
168    /// See: <https://www.rfc-editor.org/rfc/rfc8037>
169    EdDSA: "EdDSA",
170
171    /// EdDSA using SHA-256 and Blake2b as pre-hash function.
172    EdBlake2b: "EdBlake2b", // TODO Blake2b is supposed to replace SHA-256
173
174    /// ECDSA using P-256 and SHA-256.
175    ///
176    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
177    ES256: "ES256",
178
179    /// ECDSA using P-384 and SHA-384.
180    ///
181    /// See: <https://www.rfc-editor.org/rfc/rfc7518.txt>
182    ES384: "ES384",
183
184    /// ECDSA using secp256k1 (K-256) and SHA-256.
185    ///
186    /// See: <https://datatracker.ietf.org/doc/html/rfc8812>
187    ES256K: "ES256K",
188
189    /// ECDSA using secp256k1 (K-256) and SHA-256 with a recovery bit.
190    ///
191    /// `ES256K-R` is similar to `ES256K` with the recovery bit appended, making
192    /// the signature 65 bytes instead of 64. The recovery bit is used to
193    /// extract the public key from the signature.
194    ///
195    /// See: <https://github.com/decentralized-identity/EcdsaSecp256k1RecoverySignature2020#es256k-r>
196    ES256KR: "ES256K-R",
197
198    /// ECDSA using secp256k1 (K-256) and Keccak-256.
199    ///
200    /// Like `ES256K` but using Keccak-256 instead of SHA-256.
201    ESKeccakK: "ESKeccakK",
202
203    /// ECDSA using secp256k1 (K-256) and Keccak-256 with a recovery bit.
204    ///
205    /// Like `ES256K-R` but using Keccak-256 instead of SHA-256.
206    ESKeccakKR: "ESKeccakKR",
207
208    /// ECDSA using P-256 and Blake2b.
209    ESBlake2b: "ESBlake2b",
210
211    /// ECDSA using secp256k1 (K-256) and Blake2b.
212    ESBlake2bK: "ESBlake2bK",
213
214    #[doc(hidden)]
215    AleoTestnet1Signature: "AleoTestnet1Signature"
216}
217
218impl Algorithm {
219    /// Checks if this algorithm is compatible with the `other` algorithm.
220    ///
221    /// An algorithm `A` is compatible with `B` if `A` can be used to verify a
222    /// signature created from `B`.
223    pub fn is_compatible_with(&self, other: Self) -> bool {
224        match self {
225            Self::ES256K | Self::ES256KR | Self::ESKeccakK | Self::ESKeccakKR => matches!(
226                other,
227                Self::ES256K | Self::ES256KR | Self::ESKeccakK | Self::ESKeccakKR
228            ),
229            a => *a == other,
230        }
231    }
232}
233
234impl SignatureAlgorithmType for Algorithm {
235    type Instance = Self;
236}
237
238impl SignatureAlgorithmInstance for Algorithm {
239    type Algorithm = Self;
240
241    fn algorithm(&self) -> Self {
242        *self
243    }
244}
245
246impl Default for Algorithm {
247    fn default() -> Self {
248        Self::None
249    }
250}
251
252impl AsRef<str> for Algorithm {
253    fn as_ref(&self) -> &str {
254        self.as_str()
255    }
256}
257
258impl fmt::Display for Algorithm {
259    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
260        self.as_str().fmt(f)
261    }
262}
263
264impl From<ssi_crypto::algorithm::AnyBlake2b> for Algorithm {
265    fn from(value: ssi_crypto::algorithm::AnyBlake2b) -> Self {
266        match value {
267            ssi_crypto::algorithm::AnyBlake2b::ESBlake2b => Self::ESBlake2b,
268            ssi_crypto::algorithm::AnyBlake2b::ESBlake2bK => Self::ESBlake2bK,
269            ssi_crypto::algorithm::AnyBlake2b::EdBlake2b => Self::EdBlake2b,
270        }
271    }
272}
273
274impl TryFrom<Algorithm> for ssi_crypto::algorithm::AnyBlake2b {
275    type Error = UnsupportedAlgorithm;
276
277    fn try_from(value: Algorithm) -> Result<Self, Self::Error> {
278        match value {
279            Algorithm::ESBlake2b => Ok(Self::ESBlake2b),
280            Algorithm::ESBlake2bK => Ok(Self::ESBlake2bK),
281            Algorithm::EdBlake2b => Ok(Self::EdBlake2b),
282            other => Err(UnsupportedAlgorithm(other.into())),
283        }
284    }
285}
286
287impl From<ES256OrES384> for Algorithm {
288    fn from(value: ES256OrES384) -> Self {
289        match value {
290            ES256OrES384::ES256 => Self::ES256,
291            ES256OrES384::ES384 => Self::ES384,
292        }
293    }
294}
295
296#[cfg(test)]
297mod tests {
298    use super::Algorithm;
299
300    #[test]
301    fn none_serializes() {
302        assert_eq!(
303            serde_json::to_string(&Algorithm::None).unwrap(),
304            r#""none""#
305        )
306    }
307
308    #[test]
309    fn none_deserializes() {
310        assert_eq!(
311            serde_json::from_str::<Algorithm>(r#""none""#).unwrap(),
312            Algorithm::None
313        );
314        assert_eq!(
315            serde_json::from_str::<Algorithm>(r#""None""#).unwrap(),
316            Algorithm::None
317        )
318    }
319}