1use ed25519_dalek::Signer as _;
4use p256::ecdsa::signature::Verifier as _;
5
6use crate::error::JoseError;
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
15#[non_exhaustive]
16pub enum Algorithm {
17 EdDsa,
19 Es256,
21}
22
23impl Algorithm {
24 #[must_use]
26 pub const fn as_str(self) -> &'static str {
27 match self {
28 Self::EdDsa => "EdDSA",
29 Self::Es256 => "ES256",
30 }
31 }
32
33 pub fn parse(alg: &str) -> Result<Self, JoseError> {
42 match alg {
43 "EdDSA" => Ok(Self::EdDsa),
44 "ES256" => Ok(Self::Es256),
45 other if crate::error::is_forbidden_alg(other) => Err(JoseError::ForbiddenAlg {
46 found: other.to_owned(),
47 }),
48 other => Err(JoseError::UnsupportedAlg {
49 found: other.to_owned(),
50 }),
51 }
52 }
53}
54
55pub const SIGNATURE_LEN: usize = 64;
62
63#[derive(Debug, Clone)]
65#[non_exhaustive]
66pub enum SigningKey {
67 Ed25519(Box<ed25519_dalek::SigningKey>),
69 P256(Box<p256::ecdsa::SigningKey>),
71}
72
73impl SigningKey {
74 #[must_use]
76 pub fn from_ed25519_seed(seed: &[u8; 32]) -> Self {
77 Self::Ed25519(Box::new(ed25519_dalek::SigningKey::from_bytes(seed)))
78 }
79
80 pub fn from_p256_scalar(scalar: &[u8; 32]) -> Result<Self, JoseError> {
87 p256::ecdsa::SigningKey::from_bytes(scalar.into())
88 .map(|key| Self::P256(Box::new(key)))
89 .map_err(|_| JoseError::InvalidKey)
90 }
91
92 #[must_use]
94 pub const fn algorithm(&self) -> Algorithm {
95 match self {
96 Self::Ed25519(_) => Algorithm::EdDsa,
97 Self::P256(_) => Algorithm::Es256,
98 }
99 }
100
101 #[must_use]
103 pub fn verifying_key(&self) -> VerifyingKey {
104 match self {
105 Self::Ed25519(key) => VerifyingKey::Ed25519(Box::new(key.verifying_key())),
106 Self::P256(key) => VerifyingKey::P256(Box::new(*key.verifying_key())),
107 }
108 }
109
110 #[must_use]
112 pub fn to_bytes(&self) -> [u8; 32] {
113 match self {
114 Self::Ed25519(key) => key.to_bytes(),
115 Self::P256(key) => key.to_bytes().into(),
116 }
117 }
118
119 #[must_use]
124 pub fn sign(&self, message: &[u8]) -> [u8; SIGNATURE_LEN] {
125 match self {
126 Self::Ed25519(key) => key.sign(message).to_bytes(),
127 Self::P256(key) => {
128 let signature: p256::ecdsa::Signature = key.sign(message);
129 signature.to_bytes().into()
130 }
131 }
132 }
133}
134
135#[derive(Debug, Clone, PartialEq, Eq)]
137#[non_exhaustive]
138pub enum VerifyingKey {
139 Ed25519(Box<ed25519_dalek::VerifyingKey>),
141 P256(Box<p256::ecdsa::VerifyingKey>),
143}
144
145impl VerifyingKey {
146 #[must_use]
148 pub const fn algorithm(&self) -> Algorithm {
149 match self {
150 Self::Ed25519(_) => Algorithm::EdDsa,
151 Self::P256(_) => Algorithm::Es256,
152 }
153 }
154
155 #[must_use]
163 pub fn to_raw_bytes(&self) -> Vec<u8> {
164 match self {
165 Self::Ed25519(key) => key.to_bytes().to_vec(),
166 Self::P256(key) => key.to_sec1_point(false).as_bytes().to_vec(),
167 }
168 }
169
170 pub fn from_raw_bytes(algorithm: Algorithm, bytes: &[u8]) -> Result<Self, JoseError> {
177 match algorithm {
178 Algorithm::EdDsa => {
179 let bytes: [u8; 32] = bytes.try_into().map_err(|_| JoseError::InvalidKey)?;
180 ed25519_dalek::VerifyingKey::from_bytes(&bytes)
181 .map(|key| Self::Ed25519(Box::new(key)))
182 .map_err(|_| JoseError::InvalidKey)
183 }
184 Algorithm::Es256 => p256::ecdsa::VerifyingKey::from_sec1_bytes(bytes)
185 .map(|key| Self::P256(Box::new(key)))
186 .map_err(|_| JoseError::InvalidKey),
187 }
188 }
189
190 pub fn verify(&self, message: &[u8], signature: &[u8]) -> Result<(), JoseError> {
196 let signature: [u8; SIGNATURE_LEN] = signature
197 .try_into()
198 .map_err(|_| JoseError::InvalidSignature)?;
199 match self {
200 Self::Ed25519(key) => key
201 .verify_strict(message, &ed25519_dalek::Signature::from_bytes(&signature))
205 .map_err(|_| JoseError::InvalidSignature),
206 Self::P256(key) => {
207 let signature = p256::ecdsa::Signature::from_bytes(&signature.into())
208 .map_err(|_| JoseError::InvalidSignature)?;
209 key.verify(message, &signature)
210 .map_err(|_| JoseError::InvalidSignature)
211 }
212 }
213 }
214}
215
216#[cfg(test)]
217mod tests {
218 use super::{Algorithm, SigningKey, VerifyingKey};
219 use crate::error::JoseError;
220
221 fn keys() -> [SigningKey; 2] {
222 [
223 SigningKey::from_ed25519_seed(&[7u8; 32]),
224 SigningKey::from_p256_scalar(&[7u8; 32]).unwrap(),
225 ]
226 }
227
228 #[test]
229 fn round_trips_for_both_algorithms() {
230 for key in keys() {
231 let signature = key.sign(b"payload");
232 assert!(key.verifying_key().verify(b"payload", &signature).is_ok());
233 assert!(key.verifying_key().verify(b"other", &signature).is_err());
234 }
235 }
236
237 #[test]
240 fn signing_is_deterministic_for_both_algorithms() {
241 for key in keys() {
242 assert_eq!(key.sign(b"payload"), key.sign(b"payload"));
243 }
244 }
245
246 #[test]
247 fn a_signature_does_not_verify_under_the_other_algorithm() {
248 let [ed, p256] = keys();
249 let signature = ed.sign(b"payload");
250 assert!(p256.verifying_key().verify(b"payload", &signature).is_err());
251 }
252
253 #[test]
254 fn rejects_a_wrong_length_signature() {
255 for key in keys() {
256 let err = key.verifying_key().verify(b"payload", &[0u8; 32]);
257 assert!(matches!(err, Err(JoseError::InvalidSignature)));
258 }
259 }
260
261 #[test]
262 fn parses_the_supported_algorithms() {
263 assert_eq!(Algorithm::parse("EdDSA").unwrap(), Algorithm::EdDsa);
264 assert_eq!(Algorithm::parse("ES256").unwrap(), Algorithm::Es256);
265 assert_eq!(Algorithm::EdDsa.as_str(), "EdDSA");
266 assert_eq!(Algorithm::Es256.as_str(), "ES256");
267 }
268
269 #[test]
272 fn separates_forbidden_from_merely_unsupported() {
273 for forbidden in ["none", "HS256", "ECDH-ES+A128KW"] {
274 assert!(
275 matches!(
276 Algorithm::parse(forbidden),
277 Err(JoseError::ForbiddenAlg { .. })
278 ),
279 "{forbidden} must be forbidden"
280 );
281 }
282 assert!(matches!(
283 Algorithm::parse("ES384"),
284 Err(JoseError::UnsupportedAlg { .. })
285 ));
286 }
287
288 #[test]
289 fn rejects_an_invalid_p256_scalar() {
290 assert!(matches!(
291 SigningKey::from_p256_scalar(&[0u8; 32]),
292 Err(JoseError::InvalidKey)
293 ));
294 }
295
296 #[test]
297 fn reports_its_algorithm() {
298 let [ed, p256] = keys();
299 assert_eq!(ed.algorithm(), Algorithm::EdDsa);
300 assert_eq!(p256.algorithm(), Algorithm::Es256);
301 assert_eq!(ed.verifying_key().algorithm(), Algorithm::EdDsa);
302 assert_eq!(p256.verifying_key().algorithm(), Algorithm::Es256);
303 }
304
305 #[test]
306 fn keys_round_trip_through_their_bytes() {
307 let [ed, p256] = keys();
308 assert_eq!(
309 SigningKey::from_ed25519_seed(&ed.to_bytes()).verifying_key(),
310 ed.verifying_key()
311 );
312 assert_eq!(
313 SigningKey::from_p256_scalar(&p256.to_bytes())
314 .unwrap()
315 .verifying_key(),
316 p256.verifying_key()
317 );
318 }
319
320 #[test]
321 fn raw_public_key_bytes_round_trip() {
322 for key in keys() {
323 let public = key.verifying_key();
324 let raw = public.to_raw_bytes();
325 assert_eq!(
326 VerifyingKey::from_raw_bytes(public.algorithm(), &raw).unwrap(),
327 public
328 );
329 }
330 let [ed, p256] = keys();
333 assert_eq!(ed.verifying_key().to_raw_bytes().len(), 32);
334 assert_eq!(p256.verifying_key().to_raw_bytes().len(), 65);
335 }
336
337 #[test]
338 fn rejects_raw_bytes_of_the_wrong_length() {
339 assert!(matches!(
340 VerifyingKey::from_raw_bytes(Algorithm::EdDsa, &[0u8; 65]),
341 Err(JoseError::InvalidKey)
342 ));
343 assert!(matches!(
344 VerifyingKey::from_raw_bytes(Algorithm::Es256, &[0u8; 32]),
345 Err(JoseError::InvalidKey)
346 ));
347 }
348
349 #[test]
350 fn verifying_keys_compare_by_value() {
351 let [ed, _] = keys();
352 let same: VerifyingKey = SigningKey::from_ed25519_seed(&[7u8; 32]).verifying_key();
353 assert_eq!(ed.verifying_key(), same);
354 }
355}