Skip to main content

jwt_simple/algorithms/
eddsa.rs

1use ct_codecs::{Base64UrlSafeNoPadding, Encoder};
2use hmac_sha1_compact::Hash as SHA1;
3use hmac_sha256::Hash as SHA256;
4use serde::{de::DeserializeOwned, Serialize};
5
6use crate::algorithms::jwk::{
7    decode_fixed, decode_secret, is_encoding_of, okp_export, okp_thumbprint, Jwk, JwkDescriptor,
8    KeyRole, KeyType,
9};
10use crate::claims::*;
11use crate::common::*;
12#[cfg(feature = "cwt")]
13use crate::cwt_token::*;
14use crate::error::*;
15use crate::jwt_header::*;
16use crate::token::*;
17
18// Keep signing and exporting as "EdDSA" for existing verifiers.
19const ED25519_ALGS: &[&str] = &["EdDSA", "Ed25519"];
20const ED25519_ALG_ALIASES: &[&str] = &["Ed25519"];
21
22const ED25519_PUBLIC_JWK: JwkDescriptor = JwkDescriptor::new(
23    KeyType::Okp,
24    Some("Ed25519"),
25    "EdDSA",
26    KeyRole::SignaturePublic,
27)
28.with_aliases(ED25519_ALG_ALIASES);
29
30const ED25519_PRIVATE_JWK: JwkDescriptor = ED25519_PUBLIC_JWK.with_role(KeyRole::SignaturePrivate);
31
32#[doc(hidden)]
33#[derive(Debug, Clone)]
34pub struct Edwards25519PublicKey(ed25519_compact::PublicKey);
35
36impl AsRef<ed25519_compact::PublicKey> for Edwards25519PublicKey {
37    fn as_ref(&self) -> &ed25519_compact::PublicKey {
38        &self.0
39    }
40}
41
42impl Edwards25519PublicKey {
43    // Parsing a public key alone doesn't reject weak keys.
44    fn checked(
45        ed25519_pk: Result<ed25519_compact::PublicKey, ed25519_compact::Error>,
46    ) -> Result<Self, Error> {
47        let ed25519_pk = ed25519_pk.map_err(|_| JWTError::InvalidPublicKey)?;
48        ed25519_pk
49            .validate()
50            .map_err(|_| JWTError::InvalidPublicKey)?;
51        Ok(Edwards25519PublicKey(ed25519_pk))
52    }
53
54    pub fn from_bytes(raw: &[u8]) -> Result<Self, Error> {
55        Self::checked(ed25519_compact::PublicKey::from_slice(raw))
56    }
57
58    pub fn from_der(der: &[u8]) -> Result<Self, Error> {
59        Self::checked(ed25519_compact::PublicKey::from_der(der))
60    }
61
62    pub fn from_pem(pem: &str) -> Result<Self, Error> {
63        Self::checked(ed25519_compact::PublicKey::from_pem(pem))
64    }
65
66    pub fn to_bytes(&self) -> Vec<u8> {
67        self.0.as_ref().to_vec()
68    }
69
70    pub fn to_der(&self) -> Vec<u8> {
71        self.0.to_der()
72    }
73
74    pub fn to_pem(&self) -> String {
75        self.0.to_pem()
76    }
77
78    fn thumbprint(&self, hash: impl Fn(&[u8]) -> Vec<u8>) -> String {
79        Base64UrlSafeNoPadding::encode_to_string(hash(&self.to_der())).unwrap()
80    }
81
82    pub(crate) fn from_jwk(jwk: &str) -> Result<(Self, Option<String>), Error> {
83        let jwk = Jwk::parse(jwk, &ED25519_PUBLIC_JWK)?;
84        let mut x = [0u8; ed25519_compact::PublicKey::BYTES];
85        ensure!(decode_fixed(jwk.x(), &mut x), JWTError::InvalidPublicKey);
86        Ok((Self::from_bytes(&x)?, jwk.kid().map(Into::into)))
87    }
88
89    pub(crate) fn to_jwk(&self, key_id: Option<&str>) -> String {
90        okp_export(&ED25519_PUBLIC_JWK, &self.0[..], None, key_id)
91    }
92
93    pub(crate) fn jwk_thumbprint(&self) -> String {
94        okp_thumbprint("Ed25519", &self.0[..])
95    }
96}
97
98#[doc(hidden)]
99#[derive(Clone)]
100pub struct Edwards25519KeyPair {
101    ed25519_kp: ed25519_compact::KeyPair,
102    metadata: Option<KeyMetadata>,
103}
104
105impl std::fmt::Debug for Edwards25519KeyPair {
106    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
107        f.debug_struct("PKey")
108            .field("algorithm", &"Ed25519")
109            .finish()
110    }
111}
112
113impl AsRef<ed25519_compact::KeyPair> for Edwards25519KeyPair {
114    fn as_ref(&self) -> &ed25519_compact::KeyPair {
115        &self.ed25519_kp
116    }
117}
118
119impl Edwards25519KeyPair {
120    /// The bytes must contain a seed followed by its matching public key.
121    pub fn from_bytes(raw: &[u8]) -> Result<Self, Error> {
122        let ed25519_kp = ed25519_compact::KeyPair::from_slice(raw)?;
123        ed25519_kp
124            .validate()
125            .map_err(|_| JWTError::InvalidKeyPair)?;
126        Ok(Edwards25519KeyPair {
127            ed25519_kp,
128            metadata: None,
129        })
130    }
131
132    pub fn from_der(der: &[u8]) -> Result<Self, Error> {
133        let ed25519_kp = ed25519_compact::KeyPair::from_der(der)?;
134        Ok(Edwards25519KeyPair {
135            ed25519_kp,
136            metadata: None,
137        })
138    }
139
140    pub fn from_pem(pem: &str) -> Result<Self, Error> {
141        let ed25519_kp = ed25519_compact::KeyPair::from_pem(pem)?;
142        Ok(Edwards25519KeyPair {
143            ed25519_kp,
144            metadata: None,
145        })
146    }
147
148    pub fn to_bytes(&self) -> Vec<u8> {
149        self.ed25519_kp.to_vec()
150    }
151
152    pub fn to_der(&self) -> Vec<u8> {
153        self.ed25519_kp.sk.to_der()
154    }
155
156    pub fn to_pem(&self) -> String {
157        self.ed25519_kp.to_pem()
158    }
159
160    pub fn public_key(&self) -> Edwards25519PublicKey {
161        let ed25519_pk = self.ed25519_kp.pk;
162        Edwards25519PublicKey(ed25519_pk)
163    }
164
165    pub fn generate() -> Self {
166        let ed25519_kp = ed25519_compact::KeyPair::from_seed(ed25519_compact::Seed::generate());
167        Edwards25519KeyPair {
168            ed25519_kp,
169            metadata: None,
170        }
171    }
172
173    pub(crate) fn from_jwk(jwk: &str) -> Result<(Self, Option<String>), Error> {
174        let jwk = Jwk::parse(jwk, &ED25519_PRIVATE_JWK)?;
175        let d =
176            decode_secret(jwk.d(), ed25519_compact::Seed::BYTES).ok_or(JWTError::InvalidKeyPair)?;
177        // Clear the seed copy because `Seed` isn't wiped on drop.
178        let mut seed =
179            ed25519_compact::Seed::from_slice(&d).map_err(|_| JWTError::InvalidKeyPair)?;
180        let ed25519_kp = ed25519_compact::KeyPair::try_from_seed(seed);
181        seed.wipe_mut();
182        let ed25519_kp = ed25519_kp.map_err(|_| JWTError::InvalidKeyPair)?;
183        ensure!(
184            is_encoding_of(jwk.x(), &ed25519_kp.pk[..]),
185            JWTError::InvalidKeyPair
186        );
187        let key_pair = Edwards25519KeyPair {
188            ed25519_kp,
189            metadata: None,
190        };
191        Ok((key_pair, jwk.kid().map(Into::into)))
192    }
193
194    pub(crate) fn to_jwk(&self, key_id: Option<&str>) -> String {
195        let seed = &self.ed25519_kp.sk[..ed25519_compact::Seed::BYTES];
196        okp_export(
197            &ED25519_PRIVATE_JWK,
198            &self.ed25519_kp.pk[..],
199            Some(seed),
200            key_id,
201        )
202    }
203}
204
205pub trait EdDSAKeyPairLike {
206    fn jwt_alg_name() -> &'static str;
207    fn key_pair(&self) -> &Edwards25519KeyPair;
208    fn key_id(&self) -> &Option<String>;
209    fn metadata(&self) -> &Option<KeyMetadata>;
210    fn attach_metadata(&mut self, metadata: KeyMetadata) -> Result<(), Error>;
211
212    fn sign<CustomClaims: Serialize>(
213        &self,
214        claims: JWTClaims<CustomClaims>,
215    ) -> Result<String, Error> {
216        self.sign_with_options(claims, &Default::default())
217    }
218
219    fn sign_with_options<CustomClaims: Serialize>(
220        &self,
221        claims: JWTClaims<CustomClaims>,
222        opts: &HeaderOptions,
223    ) -> Result<String, Error> {
224        let jwt_header = JWTHeader::new(Self::jwt_alg_name().to_string(), self.key_id().clone())
225            .with_key_metadata(self.metadata())
226            .with_options(opts);
227        Token::build(&jwt_header, claims, |authenticated| {
228            let noise = ed25519_compact::Noise::generate();
229            let signature = self.key_pair().as_ref().sk.sign(authenticated, Some(noise));
230            Ok(signature.to_vec())
231        })
232    }
233}
234
235fn verify_signature(
236    public_key: &Edwards25519PublicKey,
237    authenticated: &str,
238    signature: &[u8],
239) -> Result<(), Error> {
240    let ed25519_signature = ed25519_compact::Signature::from_slice(signature)?;
241    public_key
242        .as_ref()
243        .verify(authenticated, &ed25519_signature)
244        .map_err(|_| JWTError::InvalidSignature)?;
245    Ok(())
246}
247
248pub trait EdDSAPublicKeyLike {
249    fn jwt_alg_name() -> &'static str;
250    fn public_key(&self) -> &Edwards25519PublicKey;
251    fn key_id(&self) -> &Option<String>;
252    fn set_key_id(&mut self, key_id: String);
253
254    fn verify_token<CustomClaims: DeserializeOwned>(
255        &self,
256        token: &str,
257        options: Option<VerificationOptions>,
258    ) -> Result<JWTClaims<CustomClaims>, Error> {
259        Token::verify(
260            Self::jwt_alg_name(),
261            token,
262            options,
263            |authenticated, signature| {
264                verify_signature(self.public_key(), authenticated, signature)
265            },
266            |_salt: Option<&[u8]>| Ok(()),
267        )
268    }
269
270    #[cfg(feature = "cwt")]
271    fn verify_cwt_token<CustomClaims: DeserializeOwned>(
272        &self,
273        token: &[u8],
274        options: Option<VerificationOptions>,
275    ) -> Result<JWTClaims<NoCustomClaims>, Error> {
276        CWTToken::verify(
277            Self::jwt_alg_name(),
278            token,
279            options,
280            |authenticated, signature| {
281                verify_signature(self.public_key(), authenticated, signature)
282            },
283        )
284    }
285
286    /// Decode CWT token metadata that can be useful prior to signature/tag verification
287    #[cfg(feature = "cwt")]
288    fn decode_cwt_metadata(&self, token: impl AsRef<[u8]>) -> Result<TokenMetadata, Error> {
289        CWTToken::decode_metadata(token)
290    }
291
292    fn create_key_id(&mut self) -> &str {
293        self.set_key_id(
294            Base64UrlSafeNoPadding::encode_to_string(hmac_sha256::Hash::hash(
295                &self.public_key().to_bytes(),
296            ))
297            .unwrap(),
298        );
299        self.key_id().as_ref().map(|x| x.as_str()).unwrap()
300    }
301}
302
303#[derive(Clone)]
304pub struct Ed25519KeyPair {
305    key_pair: Edwards25519KeyPair,
306    key_id: Option<String>,
307}
308
309impl std::fmt::Debug for Ed25519KeyPair {
310    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
311        f.debug_struct("PKey")
312            .field("algorithm", &"Ed25519")
313            .finish()
314    }
315}
316
317#[derive(Debug, Clone)]
318pub struct Ed25519PublicKey {
319    pk: Edwards25519PublicKey,
320    key_id: Option<String>,
321}
322
323impl EdDSAKeyPairLike for Ed25519KeyPair {
324    fn jwt_alg_name() -> &'static str {
325        "EdDSA"
326    }
327
328    fn key_pair(&self) -> &Edwards25519KeyPair {
329        &self.key_pair
330    }
331
332    fn key_id(&self) -> &Option<String> {
333        &self.key_id
334    }
335
336    fn metadata(&self) -> &Option<KeyMetadata> {
337        &self.key_pair.metadata
338    }
339
340    fn attach_metadata(&mut self, metadata: KeyMetadata) -> Result<(), Error> {
341        self.key_pair.metadata = Some(metadata);
342        Ok(())
343    }
344}
345
346impl Ed25519KeyPair {
347    pub fn from_bytes(raw: &[u8]) -> Result<Self, Error> {
348        Ok(Ed25519KeyPair {
349            key_pair: Edwards25519KeyPair::from_bytes(raw)?,
350            key_id: None,
351        })
352    }
353
354    pub fn from_der(der: &[u8]) -> Result<Self, Error> {
355        Ok(Ed25519KeyPair {
356            key_pair: Edwards25519KeyPair::from_der(der)?,
357            key_id: None,
358        })
359    }
360
361    pub fn from_pem(pem: &str) -> Result<Self, Error> {
362        Ok(Ed25519KeyPair {
363            key_pair: Edwards25519KeyPair::from_pem(pem)?,
364            key_id: None,
365        })
366    }
367
368    pub fn to_bytes(&self) -> Vec<u8> {
369        self.key_pair.to_bytes()
370    }
371
372    pub fn to_der(&self) -> Vec<u8> {
373        self.key_pair.to_der()
374    }
375
376    pub fn to_pem(&self) -> String {
377        self.key_pair.to_pem()
378    }
379
380    pub fn public_key(&self) -> Ed25519PublicKey {
381        Ed25519PublicKey {
382            pk: self.key_pair.public_key(),
383            key_id: self.key_id.clone(),
384        }
385    }
386
387    pub fn generate() -> Self {
388        Ed25519KeyPair {
389            key_pair: Edwards25519KeyPair::generate(),
390            key_id: None,
391        }
392    }
393
394    pub fn with_key_id(mut self, key_id: &str) -> Self {
395        self.key_id = Some(key_id.to_string());
396        self
397    }
398
399    /// Import a key pair from a private JWK (`kty: OKP`, `crv: Ed25519`).
400    ///
401    /// `alg` can be either `EdDSA` or `Ed25519`.
402    /// See [strict JWK validation](crate#strict-jwk-validation) for the other rules.
403    pub fn from_jwk(jwk: &str) -> Result<Self, Error> {
404        let (key_pair, key_id) = Edwards25519KeyPair::from_jwk(jwk)?;
405        Ok(Ed25519KeyPair { key_pair, key_id })
406    }
407
408    /// Export the key pair as a JWK, private key included.
409    ///
410    /// Use `public_key().to_jwk()` to share the public key.
411    pub fn to_jwk(&self) -> String {
412        self.key_pair.to_jwk(self.key_id.as_deref())
413    }
414
415    /// The JWK thumbprint of the public key.
416    pub fn jwk_thumbprint(&self) -> String {
417        self.key_pair.public_key().jwk_thumbprint()
418    }
419}
420
421impl EdDSAPublicKeyLike for Ed25519PublicKey {
422    fn jwt_alg_name() -> &'static str {
423        "EdDSA"
424    }
425
426    /// Accepts both `EdDSA` and `Ed25519` as the token algorithm.
427    fn verify_token<CustomClaims: DeserializeOwned>(
428        &self,
429        token: &str,
430        options: Option<VerificationOptions>,
431    ) -> Result<JWTClaims<CustomClaims>, Error> {
432        Token::verify_with_algorithms(
433            ED25519_ALGS,
434            token,
435            options,
436            |authenticated, signature| verify_signature(&self.pk, authenticated, signature),
437            |_salt: Option<&[u8]>| Ok(()),
438        )
439    }
440
441    fn public_key(&self) -> &Edwards25519PublicKey {
442        &self.pk
443    }
444
445    fn key_id(&self) -> &Option<String> {
446        &self.key_id
447    }
448
449    fn set_key_id(&mut self, key_id: String) {
450        self.key_id = Some(key_id);
451    }
452}
453
454impl Ed25519PublicKey {
455    pub fn from_bytes(raw: &[u8]) -> Result<Self, Error> {
456        Ok(Ed25519PublicKey {
457            pk: Edwards25519PublicKey::from_bytes(raw)?,
458            key_id: None,
459        })
460    }
461
462    pub fn from_der(der: &[u8]) -> Result<Self, Error> {
463        Ok(Ed25519PublicKey {
464            pk: Edwards25519PublicKey::from_der(der)?,
465            key_id: None,
466        })
467    }
468
469    pub fn from_pem(pem: &str) -> Result<Self, Error> {
470        Ok(Ed25519PublicKey {
471            pk: Edwards25519PublicKey::from_pem(pem)?,
472            key_id: None,
473        })
474    }
475
476    pub fn to_bytes(&self) -> Vec<u8> {
477        self.pk.to_bytes()
478    }
479
480    pub fn to_der(&self) -> Vec<u8> {
481        self.pk.to_der()
482    }
483
484    pub fn to_pem(&self) -> String {
485        self.pk.to_pem()
486    }
487
488    pub fn with_key_id(mut self, key_id: &str) -> Self {
489        self.key_id = Some(key_id.to_string());
490        self
491    }
492
493    pub fn sha1_thumbprint(&self) -> String {
494        self.pk.thumbprint(|data| SHA1::hash(data).to_vec())
495    }
496
497    pub fn sha256_thumbprint(&self) -> String {
498        self.pk.thumbprint(|data| SHA256::hash(data).to_vec())
499    }
500
501    /// Import a public key from a JWK (`kty: OKP`, `crv: Ed25519`).
502    ///
503    /// `alg` can be either `EdDSA` or `Ed25519`.
504    /// See [strict JWK validation](crate#strict-jwk-validation) for the other rules.
505    pub fn from_jwk(jwk: &str) -> Result<Self, Error> {
506        let (pk, key_id) = Edwards25519PublicKey::from_jwk(jwk)?;
507        Ok(Ed25519PublicKey { pk, key_id })
508    }
509
510    /// Export the public key as a JWK, with `alg: EdDSA` and `use: sig`.
511    pub fn to_jwk(&self) -> String {
512        self.pk.to_jwk(self.key_id.as_deref())
513    }
514
515    /// The JWK thumbprint of the key.
516    pub fn jwk_thumbprint(&self) -> String {
517        self.pk.jwk_thumbprint()
518    }
519}
520
521#[cfg(test)]
522mod tests {
523    use ct_codecs::{Decoder, Hex};
524
525    use super::*;
526    use crate::algorithms::test_util::{b64, error_of, pem};
527
528    const SPKI_PREFIX: [u8; 12] = [48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0];
529    const PKCS8_PREFIX: [u8; 16] = [48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32];
530
531    // All eight small-order points, then the invalid encodings y = p and y = p + 1.
532    //
533    // ed25519-compact before 2.6.0 missed half of the order-8 points.
534    const WEAK_PUBLIC_KEYS: [&str; 10] = [
535        "0100000000000000000000000000000000000000000000000000000000000000",
536        "ecffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
537        "0000000000000000000000000000000000000000000000000000000000000000",
538        "0000000000000000000000000000000000000000000000000000000000000080",
539        "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc05",
540        "26e8958fc2b227b045c3f489f2ef98f0d5dfac05d3c63339b13802886d53fc85",
541        "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac037a",
542        "c7176a703d4dd84fba3c0b760d10670f2a2053fa2c39ccc64ec7fd7792ac03fa",
543        "edffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
544        "eeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff7f",
545    ];
546
547    #[test]
548    fn weak_public_keys_are_rejected_by_every_constructor() {
549        for pk in WEAK_PUBLIC_KEYS {
550            let raw = Hex::decode_to_vec(pk, None).unwrap();
551            let der = [&SPKI_PREFIX[..], &raw].concat();
552            let jwk = format!(r#"{{"kty":"OKP","crv":"Ed25519","x":"{}"}}"#, b64(&raw));
553            for (format, res) in [
554                ("raw", Ed25519PublicKey::from_bytes(&raw)),
555                ("DER", Ed25519PublicKey::from_der(&der)),
556                ("PEM", Ed25519PublicKey::from_pem(&pem("PUBLIC KEY", &der))),
557                ("JWK", Ed25519PublicKey::from_jwk(&jwk)),
558            ] {
559                assert!(
560                    matches!(error_of(res), JWTError::InvalidPublicKey),
561                    "{} as {}",
562                    pk,
563                    format
564                );
565            }
566        }
567
568        let public_key = Ed25519KeyPair::generate().public_key();
569        assert_eq!(public_key.to_der()[..12], SPKI_PREFIX);
570        Ed25519PublicKey::from_bytes(&public_key.to_bytes()).unwrap();
571        Ed25519PublicKey::from_der(&public_key.to_der()).unwrap();
572        Ed25519PublicKey::from_pem(&public_key.to_pem()).unwrap();
573    }
574
575    #[test]
576    fn invalid_der_and_pem_keys_are_rejected() {
577        let key_pair = Ed25519KeyPair::generate();
578        let sk_der = key_pair.to_der();
579        let pk_der = key_pair.public_key().to_der();
580        let long_sk_der = [&sk_der[..], &[0]].concat();
581        assert_eq!(sk_der[..16], PKCS8_PREFIX);
582        // ed25519-compact before 2.6.0 panicked on an all-zero seed.
583        let zero_seed_der = [&PKCS8_PREFIX[..], &[0u8; 32]].concat();
584
585        for (case, der) in [
586            ("empty", &sk_der[..0]),
587            ("header only", &sk_der[..16]),
588            ("truncated seed", &sk_der[..31]),
589            ("one byte short", &sk_der[..47]),
590            ("trailing byte", &long_sk_der[..]),
591            ("zero seed", &zero_seed_der[..]),
592        ] {
593            assert!(Ed25519KeyPair::from_der(der).is_err(), "{}", case);
594            assert!(
595                Ed25519KeyPair::from_pem(&pem("PRIVATE KEY", der)).is_err(),
596                "{}",
597                case
598            );
599        }
600        for (case, der) in [
601            ("empty", &pk_der[..0]),
602            ("header only", &pk_der[..12]),
603            ("one byte short", &pk_der[..43]),
604        ] {
605            assert!(Ed25519PublicKey::from_der(der).is_err(), "{}", case);
606            assert!(
607                Ed25519PublicKey::from_pem(&pem("PUBLIC KEY", der)).is_err(),
608                "{}",
609                case
610            );
611        }
612
613        let restored = Ed25519KeyPair::from_pem(&pem("PRIVATE KEY", &sk_der)).unwrap();
614        assert_eq!(restored.to_bytes(), key_pair.to_bytes());
615    }
616
617    #[test]
618    fn key_pairs_must_be_consistent() {
619        let key_pair = Ed25519KeyPair::generate();
620        let raw = key_pair.to_bytes();
621        let (seed, public_key) = raw.split_at(32);
622        let other_raw = Ed25519KeyPair::generate().to_bytes();
623        let (other_seed, other_public_key) = other_raw.split_at(32);
624        for (case, seed, public_key) in [
625            ("other public key", seed, other_public_key),
626            ("other seed", other_seed, public_key),
627            ("zero seed", &[0u8; 32][..], public_key),
628        ] {
629            let raw = [seed, public_key].concat();
630            let jwk = format!(
631                r#"{{"kty":"OKP","crv":"Ed25519","x":"{}","d":"{}"}}"#,
632                b64(public_key),
633                b64(seed)
634            );
635            for (format, res) in [
636                ("raw", Ed25519KeyPair::from_bytes(&raw)),
637                ("JWK", Ed25519KeyPair::from_jwk(&jwk)),
638            ] {
639                assert!(
640                    matches!(error_of(res), JWTError::InvalidKeyPair),
641                    "{} as {}",
642                    case,
643                    format
644                );
645            }
646        }
647
648        let restored = Ed25519KeyPair::from_bytes(&raw).unwrap();
649        let token = restored
650            .sign(Claims::create(coarsetime::Duration::from_hours(1)))
651            .unwrap();
652        key_pair
653            .public_key()
654            .verify_token::<NoCustomClaims>(&token, None)
655            .unwrap();
656    }
657}
658
659#[cfg(test)]
660mod jwk_tests {
661    use super::*;
662    use crate::algorithms::jwk_test_vectors;
663    use crate::algorithms::test_util::{b64, error_of, json, member_bytes, unb64};
664
665    const RFC8037_PRIVATE: &str = r#"{"kty":"OKP","crv":"Ed25519",
666        "d":"nWGxne_9WmC6hEr0kuwsxERJxWl7MmkZcDusAxyuf2A",
667        "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#;
668    const RFC8037_PUBLIC: &str = r#"{"kty":"OKP","crv":"Ed25519",
669        "x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#;
670
671    #[test]
672    fn rfc8037_vectors() {
673        let key_pair = Ed25519KeyPair::from_jwk(RFC8037_PRIVATE).unwrap();
674        let public_key = Ed25519PublicKey::from_jwk(RFC8037_PUBLIC).unwrap();
675        assert_eq!(key_pair.public_key().to_bytes(), public_key.to_bytes());
676
677        let seed = member_bytes(RFC8037_PRIVATE, "d");
678        let raw = [&seed[..], &public_key.to_bytes()].concat();
679        assert_eq!(Ed25519KeyPair::from_bytes(&raw).unwrap().to_bytes(), raw);
680
681        let thumbprint = "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k";
682        assert_eq!(public_key.jwk_thumbprint(), thumbprint);
683        assert_eq!(key_pair.jwk_thumbprint(), thumbprint);
684
685        let signing_input = "eyJhbGciOiJFZERTQSJ9.RXhhbXBsZSBvZiBFZDI1NTE5IHNpZ25pbmc";
686        let signature = "hgyY0il_MGCjP0JzlnLWG1PPOt7-09PGcvMg3AIbQR6dWbhijcNR4ki4iylGjg5BhVsPt9g7sVvpAr_MuM0KAg";
687        let computed = key_pair.key_pair().as_ref().sk.sign(signing_input, None);
688        assert_eq!(b64(&computed[..]), signature);
689        verify_signature(public_key.public_key(), signing_input, &unb64(signature)).unwrap();
690    }
691
692    #[test]
693    fn round_trips_and_key_ids() {
694        let key_pair = Ed25519KeyPair::generate().with_key_id("key \"1\"");
695        let private_jwk = key_pair.to_jwk();
696        let public_jwk = key_pair.public_key().to_jwk();
697
698        let exported = json(&public_jwk);
699        let members: Vec<&String> = exported.as_object().unwrap().keys().collect();
700        assert_eq!(members, ["alg", "crv", "kid", "kty", "use", "x"]);
701        assert_eq!(exported["alg"], "EdDSA");
702        assert_eq!(exported["use"], "sig");
703        assert_eq!(exported["kid"], "key \"1\"");
704        let exported = json(&private_jwk);
705        assert_eq!(exported.as_object().unwrap().len(), 7);
706        assert!(exported["d"].is_string());
707
708        let restored = Ed25519KeyPair::from_jwk(&private_jwk).unwrap();
709        assert_eq!(restored.to_bytes(), key_pair.to_bytes());
710        assert_eq!(restored.key_id.as_deref(), Some("key \"1\""));
711        let public_key = Ed25519PublicKey::from_jwk(&public_jwk).unwrap();
712        assert_eq!(public_key.key_id.as_deref(), Some("key \"1\""));
713        assert_eq!(public_key.jwk_thumbprint(), restored.jwk_thumbprint());
714        assert_eq!(public_key.to_jwk(), public_jwk);
715        assert_eq!(restored.to_jwk(), private_jwk);
716
717        let unnamed = Ed25519PublicKey::from_bytes(&public_key.to_bytes()).unwrap();
718        assert_eq!(unnamed.jwk_thumbprint(), public_key.jwk_thumbprint());
719        assert!(!unnamed.to_jwk().contains("kid"));
720
721        let token = restored
722            .sign(Claims::create(coarsetime::Duration::from_hours(1)))
723            .unwrap();
724        let options = VerificationOptions {
725            required_key_id: Some("key \"1\"".into()),
726            ..Default::default()
727        };
728        public_key
729            .verify_token::<NoCustomClaims>(&token, Some(options.clone()))
730            .unwrap();
731        restored
732            .public_key()
733            .verify_token::<NoCustomClaims>(&token, Some(options))
734            .unwrap();
735
736        let mut public_key = public_key;
737        let key_id = public_key.create_key_id().to_string();
738        let raw_hash = hmac_sha256::Hash::hash(&public_key.to_bytes());
739        assert_eq!(key_id, b64(&raw_hash));
740    }
741
742    #[test]
743    fn webcrypto_exports() {
744        for export in jwk_test_vectors::ALL {
745            let export = json(export);
746            let entry = &export["keys"]["Ed25519"];
747            let key_pair = Ed25519KeyPair::from_jwk(&entry["private"].to_string()).unwrap();
748            let public_key = Ed25519PublicKey::from_jwk(&entry["public"].to_string()).unwrap();
749            assert_eq!(key_pair.jwk_thumbprint(), public_key.jwk_thumbprint());
750            let jwt = entry["jwt"].as_str().unwrap();
751            let claims = public_key
752                .verify_token::<NoCustomClaims>(jwt, None)
753                .unwrap();
754            assert_eq!(claims.subject.as_deref(), Some("Ed25519"));
755
756            for other in ["ES256", "X25519", "RS256", "ML-DSA-44"] {
757                let other_key = &export["keys"][other]["public"];
758                if !other_key.is_null() {
759                    assert!(
760                        Ed25519PublicKey::from_jwk(&other_key.to_string()).is_err(),
761                        "{}",
762                        other
763                    );
764                }
765            }
766        }
767    }
768
769    fn resign_with_alg(key_pair: &Ed25519KeyPair, token: &str, alg: &str) -> String {
770        let mut parts = token.split('.');
771        let mut header: serde_json::Value =
772            serde_json::from_slice(&unb64(parts.next().unwrap())).unwrap();
773        header["alg"] = alg.into();
774        let claims = parts.next().unwrap();
775        let signed = format!("{}.{}", b64(header.to_string().as_bytes()), claims);
776        let signature = key_pair.key_pair().as_ref().sk.sign(&signed, None);
777        format!("{}.{}", signed, b64(&signature[..]))
778    }
779
780    struct CustomVerifier {
781        pk: Edwards25519PublicKey,
782        key_id: Option<String>,
783    }
784
785    impl EdDSAPublicKeyLike for CustomVerifier {
786        fn jwt_alg_name() -> &'static str {
787            "EdDSA"
788        }
789
790        fn public_key(&self) -> &Edwards25519PublicKey {
791            &self.pk
792        }
793
794        fn key_id(&self) -> &Option<String> {
795            &self.key_id
796        }
797
798        fn set_key_id(&mut self, key_id: String) {
799            self.key_id = Some(key_id);
800        }
801    }
802
803    #[test]
804    fn both_algorithm_names_verify() {
805        let key_pair = Ed25519KeyPair::generate();
806        let public_key = key_pair.public_key();
807        let token = key_pair
808            .sign(Claims::create(coarsetime::Duration::from_hours(1)))
809            .unwrap();
810        assert_eq!(Token::decode_metadata(&token).unwrap().algorithm(), "EdDSA");
811
812        let ed25519_token = resign_with_alg(&key_pair, &token, "Ed25519");
813        for token in [&token, &ed25519_token] {
814            public_key
815                .verify_token::<NoCustomClaims>(token, None)
816                .unwrap();
817        }
818        for alg in ["ED25519", "Ed448", "ES256", "none"] {
819            let token = resign_with_alg(&key_pair, &token, alg);
820            assert!(
821                matches!(
822                    error_of(public_key.verify_token::<NoCustomClaims>(&token, None)),
823                    JWTError::AlgorithmMismatch
824                ),
825                "{}",
826                alg
827            );
828        }
829
830        // Other implementations of the trait only accept `EdDSA`.
831        let custom = CustomVerifier {
832            pk: public_key.pk.clone(),
833            key_id: None,
834        };
835        custom.verify_token::<NoCustomClaims>(&token, None).unwrap();
836        assert!(matches!(
837            error_of(custom.verify_token::<NoCustomClaims>(&ed25519_token, None)),
838            JWTError::AlgorithmMismatch
839        ));
840
841        // Changing the algorithm name must invalidate the signature.
842        let mut parts: Vec<&str> = ed25519_token.split('.').collect();
843        let original_header = token.split('.').next().unwrap();
844        parts[0] = original_header;
845        assert!(public_key
846            .verify_token::<NoCustomClaims>(&parts.join("."), None)
847            .is_err());
848    }
849}