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
18const 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 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 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 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 #[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 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 pub fn to_jwk(&self) -> String {
412 self.key_pair.to_jwk(self.key_id.as_deref())
413 }
414
415 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 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 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 pub fn to_jwk(&self) -> String {
512 self.pk.to_jwk(self.key_id.as_deref())
513 }
514
515 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 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 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 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 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}