uselesskey_ecdsa/spec.rs
1/// ECDSA algorithm specification.
2///
3/// # Examples
4///
5/// ```
6/// use uselesskey_ecdsa::EcdsaSpec;
7///
8/// let es256 = EcdsaSpec::es256();
9/// assert_eq!(es256.alg_name(), "ES256");
10/// assert_eq!(es256.curve_name(), "P-256");
11///
12/// let es384 = EcdsaSpec::es384();
13/// assert_eq!(es384.alg_name(), "ES384");
14/// assert_eq!(es384.curve_name(), "P-384");
15/// ```
16#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
17pub enum EcdsaSpec {
18 /// P-256 / secp256r1 / prime256v1 (for ES256 JWT signing).
19 Es256,
20 /// P-384 / secp384r1 (for ES384 JWT signing).
21 Es384,
22}
23
24impl EcdsaSpec {
25 /// Spec suitable for ES256 JWT signing.
26 ///
27 /// # Examples
28 ///
29 /// ```
30 /// use uselesskey_ecdsa::EcdsaSpec;
31 ///
32 /// let spec = EcdsaSpec::es256();
33 /// assert_eq!(spec.alg_name(), "ES256");
34 /// assert_eq!(spec.curve_name(), "P-256");
35 /// ```
36 pub fn es256() -> Self {
37 Self::Es256
38 }
39
40 /// Spec suitable for ES384 JWT signing.
41 ///
42 /// # Examples
43 ///
44 /// ```
45 /// use uselesskey_ecdsa::EcdsaSpec;
46 ///
47 /// let spec = EcdsaSpec::es384();
48 /// assert_eq!(spec.alg_name(), "ES384");
49 /// assert_eq!(spec.curve_name(), "P-384");
50 /// ```
51 pub fn es384() -> Self {
52 Self::Es384
53 }
54
55 /// Returns the JWT algorithm name.
56 ///
57 /// # Examples
58 ///
59 /// ```
60 /// use uselesskey_ecdsa::EcdsaSpec;
61 ///
62 /// assert_eq!(EcdsaSpec::es256().alg_name(), "ES256");
63 /// assert_eq!(EcdsaSpec::es384().alg_name(), "ES384");
64 /// ```
65 pub fn alg_name(&self) -> &'static str {
66 match self {
67 Self::Es256 => "ES256",
68 Self::Es384 => "ES384",
69 }
70 }
71
72 /// Returns the curve name.
73 ///
74 /// # Examples
75 ///
76 /// ```
77 /// use uselesskey_ecdsa::EcdsaSpec;
78 ///
79 /// assert_eq!(EcdsaSpec::es256().curve_name(), "P-256");
80 /// assert_eq!(EcdsaSpec::es384().curve_name(), "P-384");
81 /// ```
82 pub fn curve_name(&self) -> &'static str {
83 match self {
84 Self::Es256 => "P-256",
85 Self::Es384 => "P-384",
86 }
87 }
88
89 /// Returns the expected coordinate length in bytes for uncompressed points.
90 ///
91 /// # Examples
92 ///
93 /// ```
94 /// use uselesskey_ecdsa::EcdsaSpec;
95 ///
96 /// assert_eq!(EcdsaSpec::es256().coordinate_len_bytes(), 32);
97 /// assert_eq!(EcdsaSpec::es384().coordinate_len_bytes(), 48);
98 /// ```
99 pub fn coordinate_len_bytes(&self) -> usize {
100 match self {
101 Self::Es256 => 32,
102 Self::Es384 => 48,
103 }
104 }
105
106 /// Stable encoding for cache keys / deterministic derivation.
107 ///
108 /// If you change this, bump the derivation version in `uselesskey-core`.
109 ///
110 /// # Examples
111 ///
112 /// ```
113 /// use uselesskey_ecdsa::EcdsaSpec;
114 ///
115 /// let bytes = EcdsaSpec::es256().stable_bytes();
116 /// assert_eq!(bytes.len(), 4);
117 /// ```
118 pub fn stable_bytes(&self) -> [u8; 4] {
119 match self {
120 Self::Es256 => [0, 0, 0, 1],
121 Self::Es384 => [0, 0, 0, 2],
122 }
123 }
124}
125
126#[cfg(test)]
127mod tests {
128 use super::*;
129
130 #[test]
131 fn alg_and_curve_names_match_specs() {
132 let es256 = EcdsaSpec::es256();
133 assert_eq!(es256.alg_name(), "ES256");
134 assert_eq!(es256.curve_name(), "P-256");
135 assert_eq!(es256.coordinate_len_bytes(), 32);
136
137 let es384 = EcdsaSpec::es384();
138 assert_eq!(es384.alg_name(), "ES384");
139 assert_eq!(es384.curve_name(), "P-384");
140 assert_eq!(es384.coordinate_len_bytes(), 48);
141 }
142
143 #[test]
144 fn stable_bytes_are_unique() {
145 let es256 = EcdsaSpec::es256().stable_bytes();
146 let es384 = EcdsaSpec::es384().stable_bytes();
147 assert_ne!(es256, es384);
148 }
149}