1use core::fmt;
4
5use indexmap::IndexMap;
6use serde::{Deserialize, Serialize};
7use serde_json::Value;
8use sha2::{Digest as _, Sha256};
9use zeroize::{Zeroize, ZeroizeOnDrop};
10
11use super::JwkError;
12use super::base64url;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20pub enum KeyType {
21 Ed25519,
23 P256,
25 Secp256k1,
27 Rsa2048,
30 Rsa4096,
33}
34
35#[derive(Clone, PartialEq, Eq, Serialize, Deserialize)]
67pub struct Jwk {
68 kty: String,
69 #[serde(default, skip_serializing_if = "Option::is_none")]
70 crv: Option<String>,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
72 x: Option<String>,
73 #[serde(default, skip_serializing_if = "Option::is_none")]
74 y: Option<String>,
75 #[serde(default, skip_serializing_if = "Option::is_none")]
76 n: Option<String>,
77 #[serde(default, skip_serializing_if = "Option::is_none")]
78 e: Option<String>,
79 #[serde(flatten)]
81 pub(crate) private: PrivateParams,
82 #[serde(rename = "use", default, skip_serializing_if = "Option::is_none")]
83 key_use: Option<String>,
84 #[serde(default, skip_serializing_if = "Option::is_none")]
85 key_ops: Option<Vec<String>>,
86 #[serde(default, skip_serializing_if = "Option::is_none")]
87 alg: Option<String>,
88 #[serde(default, skip_serializing_if = "Option::is_none")]
89 kid: Option<String>,
90 #[serde(flatten)]
91 additional: IndexMap<String, Value>,
92}
93
94#[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
96pub(crate) struct PrivateParams {
97 #[serde(default, skip_serializing_if = "Option::is_none")]
99 pub(crate) d: Option<String>,
100 #[serde(default, skip_serializing_if = "Option::is_none")]
101 pub(crate) p: Option<String>,
102 #[serde(default, skip_serializing_if = "Option::is_none")]
103 pub(crate) q: Option<String>,
104 #[serde(default, skip_serializing_if = "Option::is_none")]
105 pub(crate) dp: Option<String>,
106 #[serde(default, skip_serializing_if = "Option::is_none")]
107 pub(crate) dq: Option<String>,
108 #[serde(default, skip_serializing_if = "Option::is_none")]
109 pub(crate) qi: Option<String>,
110 #[serde(default, skip_serializing_if = "Option::is_none")]
111 pub(crate) oth: Option<Vec<OtherPrime>>,
112 #[serde(default, skip_serializing_if = "Option::is_none")]
114 pub(crate) k: Option<String>,
115}
116
117impl PrivateParams {
118 fn is_empty(&self) -> bool {
119 *self == Self::default()
120 }
121}
122
123#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Zeroize)]
125pub(crate) struct OtherPrime {
126 pub(crate) r: String,
127 pub(crate) d: String,
128 pub(crate) t: String,
129}
130
131fn decode_member(name: &'static str, value: Option<&str>) -> Result<Vec<u8>, JwkError> {
133 let value = value.ok_or(JwkError::MissingMember(name))?;
134 base64url::decode(value).map_err(|_| JwkError::InvalidBase64Url(name))
135}
136
137const MIN_RSA_MODULUS_BITS: usize = 2048;
140
141fn check_rsa_modulus_size(n: &[u8]) -> Result<(), JwkError> {
144 let bits = match n.iter().position(|&b| b != 0) {
145 Some(first) => (n.len() - first) * 8 - n[first].leading_zeros() as usize,
146 None => 0,
147 };
148 if bits < MIN_RSA_MODULUS_BITS {
149 return Err(JwkError::InvalidKey {
150 key: "RSA",
151 reason: format!(
152 "the modulus is {bits} bits; RS256 and PS256 need at least {MIN_RSA_MODULUS_BITS}"
153 ),
154 });
155 }
156 Ok(())
157}
158
159impl Jwk {
160 fn with_kty(kty: &str) -> Self {
162 Self {
163 kty: kty.to_owned(),
164 crv: None,
165 x: None,
166 y: None,
167 n: None,
168 e: None,
169 key_use: None,
170 key_ops: None,
171 alg: None,
172 kid: None,
173 private: PrivateParams::default(),
174 additional: IndexMap::new(),
175 }
176 }
177
178 pub fn from_ed25519_public(public: &[u8]) -> Result<Self, JwkError> {
184 let bytes: &[u8; 32] = public.try_into().map_err(|_| JwkError::InvalidKey {
185 key: "Ed25519",
186 reason: format!("expected 32 bytes, found {}", public.len()),
187 })?;
188 ed25519_dalek::VerifyingKey::from_bytes(bytes).map_err(|e| JwkError::InvalidKey {
189 key: "Ed25519",
190 reason: e.to_string(),
191 })?;
192 let mut jwk = Self::with_kty("OKP");
193 jwk.crv = Some("Ed25519".to_owned());
194 jwk.x = Some(base64url::encode(bytes));
195 Ok(jwk)
196 }
197
198 pub fn from_x25519_public(public: &[u8]) -> Result<Self, JwkError> {
205 if public.len() != 32 {
206 return Err(JwkError::InvalidKeyLength {
207 key: "X25519",
208 expected: 32,
209 actual: public.len(),
210 });
211 }
212 let mut jwk = Self::with_kty("OKP");
213 jwk.crv = Some("X25519".to_owned());
214 jwk.x = Some(base64url::encode(public));
215 Ok(jwk)
216 }
217
218 pub fn from_p256_public(sec1: &[u8]) -> Result<Self, JwkError> {
224 let key = p256::PublicKey::from_sec1_bytes(sec1).map_err(|e| JwkError::InvalidKey {
225 key: "P-256",
226 reason: e.to_string(),
227 })?;
228 Ok(Self::from_p256_key(&key))
229 }
230
231 fn from_p256_key(key: &p256::PublicKey) -> Self {
232 use p256::elliptic_curve::sec1::ToSec1Point as _;
233 let point = key.to_sec1_point(false);
234 let mut jwk = Self::with_kty("EC");
235 jwk.crv = Some("P-256".to_owned());
236 jwk.x = point.x().map(|x| base64url::encode(x));
237 jwk.y = point.y().map(|y| base64url::encode(y));
238 jwk
239 }
240
241 #[cfg(feature = "secp256k1")]
247 #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
248 pub fn from_secp256k1_public(sec1: &[u8]) -> Result<Self, JwkError> {
249 let key = k256::PublicKey::from_sec1_bytes(sec1).map_err(|e| JwkError::InvalidKey {
250 key: "secp256k1",
251 reason: e.to_string(),
252 })?;
253 Ok(Self::from_secp256k1_key(&key))
254 }
255
256 #[cfg(feature = "secp256k1")]
257 fn from_secp256k1_key(key: &k256::PublicKey) -> Self {
258 use k256::elliptic_curve::sec1::ToSec1Point as _;
259 let point = key.to_sec1_point(false);
260 let mut jwk = Self::with_kty("EC");
261 jwk.crv = Some("secp256k1".to_owned());
262 jwk.x = point.x().map(|x| base64url::encode(x));
263 jwk.y = point.y().map(|y| base64url::encode(y));
264 jwk
265 }
266
267 pub fn from_rsa_public(n: &[u8], e: &[u8]) -> Result<Self, JwkError> {
279 if n.is_empty() || e.is_empty() {
280 return Err(JwkError::InvalidKey {
281 key: "RSA",
282 reason: "modulus and exponent must not be empty".to_owned(),
283 });
284 }
285 let mut jwk = Self::with_kty("RSA");
286 jwk.n = Some(base64url::encode_uint(n));
287 jwk.e = Some(base64url::encode_uint(e));
288 Ok(jwk)
289 }
290
291 pub fn from_spki_der(hint: KeyType, der: &[u8]) -> Result<Self, JwkError> {
303 match hint {
304 KeyType::Ed25519 => {
305 use ed25519_dalek::pkcs8::DecodePublicKey as _;
306 let key = ed25519_dalek::VerifyingKey::from_public_key_der(der).map_err(|e| {
307 JwkError::InvalidKey {
308 key: "Ed25519",
309 reason: e.to_string(),
310 }
311 })?;
312 Self::from_ed25519_public(&key.to_bytes())
313 }
314 KeyType::P256 => {
315 use p256::pkcs8::DecodePublicKey as _;
316 let key = p256::PublicKey::from_public_key_der(der).map_err(|e| {
317 JwkError::InvalidKey {
318 key: "P-256",
319 reason: e.to_string(),
320 }
321 })?;
322 Ok(Self::from_p256_key(&key))
323 }
324 #[cfg(feature = "secp256k1")]
325 KeyType::Secp256k1 => {
326 use k256::pkcs8::DecodePublicKey as _;
327 let key = k256::PublicKey::from_public_key_der(der).map_err(|e| {
328 JwkError::InvalidKey {
329 key: "secp256k1",
330 reason: e.to_string(),
331 }
332 })?;
333 Ok(Self::from_secp256k1_key(&key))
334 }
335 #[cfg(not(feature = "secp256k1"))]
336 KeyType::Secp256k1 => Err(JwkError::FeatureDisabled {
337 key: "secp256k1",
338 feature: "secp256k1",
339 }),
340 #[cfg(feature = "rsa")]
341 KeyType::Rsa2048 | KeyType::Rsa4096 => {
342 use rsa::pkcs8::DecodePublicKey as _;
343 use rsa::traits::PublicKeyParts as _;
344 let key = rsa::RsaPublicKey::from_public_key_der(der).map_err(|e| {
345 JwkError::InvalidKey {
346 key: "RSA",
347 reason: e.to_string(),
348 }
349 })?;
350 let expected_bits = match hint {
351 KeyType::Rsa4096 => 4096,
352 _ => 2048,
353 };
354 if key.n().bits_vartime() != expected_bits {
355 return Err(JwkError::KeyMismatch { expected: hint });
356 }
357 Self::from_rsa_public(
358 &key.n().to_be_bytes_trimmed_vartime(),
359 &key.e().to_be_bytes_trimmed_vartime(),
360 )
361 }
362 #[cfg(not(feature = "rsa"))]
363 KeyType::Rsa2048 | KeyType::Rsa4096 => Err(JwkError::FeatureDisabled {
364 key: "RSA",
365 feature: "rsa",
366 }),
367 }
368 }
369
370 pub fn kty(&self) -> &str {
372 &self.kty
373 }
374
375 pub fn crv(&self) -> Option<&str> {
377 self.crv.as_deref()
378 }
379
380 pub fn x(&self) -> Option<&str> {
382 self.x.as_deref()
383 }
384
385 pub fn y(&self) -> Option<&str> {
387 self.y.as_deref()
388 }
389
390 pub fn n(&self) -> Option<&str> {
392 self.n.as_deref()
393 }
394
395 pub fn e(&self) -> Option<&str> {
397 self.e.as_deref()
398 }
399
400 pub fn key_use(&self) -> Option<&str> {
402 self.key_use.as_deref()
403 }
404
405 pub fn key_ops(&self) -> Option<&[String]> {
407 self.key_ops.as_deref()
408 }
409
410 pub fn alg(&self) -> Option<&str> {
412 self.alg.as_deref()
413 }
414
415 pub fn kid(&self) -> Option<&str> {
417 self.kid.as_deref()
418 }
419
420 pub fn additional(&self) -> &IndexMap<String, Value> {
422 &self.additional
423 }
424
425 pub fn additional_mut(&mut self) -> &mut IndexMap<String, Value> {
427 &mut self.additional
428 }
429
430 pub fn is_private(&self) -> bool {
433 !self.private.is_empty()
434 }
435
436 #[must_use]
438 pub fn with_kid(mut self, kid: impl Into<String>) -> Self {
439 self.kid = Some(kid.into());
440 self
441 }
442
443 #[must_use]
445 pub fn with_alg(mut self, alg: impl Into<String>) -> Self {
446 self.alg = Some(alg.into());
447 self
448 }
449
450 #[must_use]
452 pub fn with_key_use(mut self, key_use: impl Into<String>) -> Self {
453 self.key_use = Some(key_use.into());
454 self
455 }
456
457 #[must_use]
459 pub fn with_key_ops<I, S>(mut self, key_ops: I) -> Self
460 where
461 I: IntoIterator<Item = S>,
462 S: Into<String>,
463 {
464 self.key_ops = Some(key_ops.into_iter().map(Into::into).collect());
465 self
466 }
467
468 #[must_use]
470 pub fn public(&self) -> Self {
471 let mut public = self.clone();
472 public.private = PrivateParams::default();
473 public
474 }
475
476 pub fn thumbprint_sha256(&self) -> Result<String, JwkError> {
504 let canonical = self.thumbprint_input()?;
505 Ok(base64url::encode(&Sha256::digest(canonical.as_bytes())))
506 }
507
508 pub(crate) fn thumbprint_input(&self) -> Result<String, JwkError> {
510 fn member(name: &'static str, value: Option<&str>) -> Result<String, JwkError> {
511 let value = value.ok_or(JwkError::MissingMember(name))?;
512 Ok(serde_json::to_string(value).unwrap_or_default())
514 }
515 let kty = serde_json::to_string(&self.kty).unwrap_or_default();
516 Ok(match self.kty.as_str() {
517 "EC" => format!(
518 r#"{{"crv":{},"kty":{kty},"x":{},"y":{}}}"#,
519 member("crv", self.crv())?,
520 member("x", self.x())?,
521 member("y", self.y())?
522 ),
523 "OKP" => format!(
524 r#"{{"crv":{},"kty":{kty},"x":{}}}"#,
525 member("crv", self.crv())?,
526 member("x", self.x())?
527 ),
528 "RSA" => format!(
529 r#"{{"e":{},"kty":{kty},"n":{}}}"#,
530 member("e", self.e())?,
531 member("n", self.n())?
532 ),
533 "oct" => format!(
534 r#"{{"k":{},"kty":{kty}}}"#,
535 member("k", self.private.k.as_deref())?
536 ),
537 _ => return Err(self.unsupported()),
538 })
539 }
540
541 pub fn ed25519_public_bytes(&self) -> Result<[u8; 32], JwkError> {
549 Ok(self.ed25519_verifying_key()?.to_bytes())
550 }
551
552 pub(crate) fn ed25519_verifying_key(&self) -> Result<ed25519_dalek::VerifyingKey, JwkError> {
553 self.expect("Ed25519", "OKP", Some("Ed25519"))?;
554 let x = decode_member("x", self.x())?;
555 let bytes: &[u8; 32] = x.as_slice().try_into().map_err(|_| JwkError::InvalidKey {
556 key: "Ed25519",
557 reason: format!("expected 32 bytes, found {}", x.len()),
558 })?;
559 ed25519_dalek::VerifyingKey::from_bytes(bytes).map_err(|e| JwkError::InvalidKey {
560 key: "Ed25519",
561 reason: e.to_string(),
562 })
563 }
564
565 pub fn x25519_public_bytes(&self) -> Result<[u8; 32], JwkError> {
573 self.expect("X25519", "OKP", Some("X25519"))?;
574 let x = decode_member("x", self.x())?;
575 x.as_slice()
576 .try_into()
577 .map_err(|_| JwkError::InvalidKeyLength {
578 key: "X25519",
579 expected: 32,
580 actual: x.len(),
581 })
582 }
583
584 pub fn p256_public_bytes(&self) -> Result<[u8; 65], JwkError> {
593 use p256::elliptic_curve::sec1::ToSec1Point as _;
594 let point = self.p256_public_key()?.to_sec1_point(false);
595 point
596 .as_bytes()
597 .try_into()
598 .map_err(|_| JwkError::InvalidKey {
599 key: "P-256",
600 reason: "point is not 65 bytes".to_owned(),
601 })
602 }
603
604 pub(crate) fn p256_public_key(&self) -> Result<p256::PublicKey, JwkError> {
605 self.expect("P-256", "EC", Some("P-256"))?;
606 let sec1 = self.ec_uncompressed_point(32, "P-256")?;
607 p256::PublicKey::from_sec1_bytes(&sec1).map_err(|e| JwkError::InvalidKey {
608 key: "P-256",
609 reason: e.to_string(),
610 })
611 }
612
613 #[cfg(feature = "secp256k1")]
622 #[cfg_attr(docsrs, doc(cfg(feature = "secp256k1")))]
623 pub fn secp256k1_public_bytes(&self) -> Result<[u8; 65], JwkError> {
624 use k256::elliptic_curve::sec1::ToSec1Point as _;
625 let point = self.secp256k1_public_key()?.to_sec1_point(false);
626 point
627 .as_bytes()
628 .try_into()
629 .map_err(|_| JwkError::InvalidKey {
630 key: "secp256k1",
631 reason: "point is not 65 bytes".to_owned(),
632 })
633 }
634
635 #[cfg(feature = "secp256k1")]
636 pub(crate) fn secp256k1_public_key(&self) -> Result<k256::PublicKey, JwkError> {
637 self.expect("secp256k1", "EC", Some("secp256k1"))?;
638 let sec1 = self.ec_uncompressed_point(32, "secp256k1")?;
639 k256::PublicKey::from_sec1_bytes(&sec1).map_err(|e| JwkError::InvalidKey {
640 key: "secp256k1",
641 reason: e.to_string(),
642 })
643 }
644
645 fn ec_uncompressed_point(&self, len: usize, key: &'static str) -> Result<Vec<u8>, JwkError> {
647 let x = decode_member("x", self.x())?;
648 let y = decode_member("y", self.y())?;
649 if x.len() != len || y.len() != len {
650 return Err(JwkError::InvalidKey {
651 key,
652 reason: format!(
653 "expected {len}-byte coordinates, found {} and {}",
654 x.len(),
655 y.len()
656 ),
657 });
658 }
659 let mut sec1 = Vec::with_capacity(1 + 2 * len);
660 sec1.push(0x04);
661 sec1.extend_from_slice(&x);
662 sec1.extend_from_slice(&y);
663 Ok(sec1)
664 }
665
666 pub fn rsa_public_components(&self) -> Result<(Vec<u8>, Vec<u8>), JwkError> {
673 self.expect("RSA", "RSA", None)?;
674 Ok((decode_member("n", self.n())?, decode_member("e", self.e())?))
675 }
676
677 #[cfg(feature = "rsa")]
678 pub(crate) fn rsa_public_key(&self) -> Result<rsa::RsaPublicKey, JwkError> {
679 let (n, e) = self.rsa_public_components()?;
680 if n.is_empty() || e.is_empty() {
681 return Err(JwkError::InvalidKey {
682 key: "RSA",
683 reason: "modulus and exponent must not be empty".to_owned(),
684 });
685 }
686 check_rsa_modulus_size(&n)?;
687 let trim = |value: &[u8]| {
690 let first = value
691 .iter()
692 .position(|&b| b != 0)
693 .unwrap_or(value.len() - 1);
694 value[first..].to_vec()
695 };
696 rsa::RsaPublicKey::new(
697 rsa::BoxedUint::from_be_slice_vartime(&trim(&n)),
698 rsa::BoxedUint::from_be_slice_vartime(&trim(&e)),
699 )
700 .map_err(|e| JwkError::InvalidKey {
701 key: "RSA",
702 reason: e.to_string(),
703 })
704 }
705
706 pub fn to_decoding_key(&self) -> Result<jsonwebtoken::DecodingKey, JwkError> {
719 use jsonwebtoken::DecodingKey;
720 let result = match (self.kty.as_str(), self.crv()) {
721 ("OKP", Some("Ed25519")) => {
722 DecodingKey::from_ed_components(self.x().ok_or(JwkError::MissingMember("x"))?)
723 }
724 ("EC", Some("P-256" | "P-384")) => DecodingKey::from_ec_components(
725 self.x().ok_or(JwkError::MissingMember("x"))?,
726 self.y().ok_or(JwkError::MissingMember("y"))?,
727 ),
728 ("RSA", _) => {
729 check_rsa_modulus_size(&self.rsa_public_components()?.0)?;
730 DecodingKey::from_rsa_components(
731 self.n().ok_or(JwkError::MissingMember("n"))?,
732 self.e().ok_or(JwkError::MissingMember("e"))?,
733 )
734 }
735 _ => return Err(self.unsupported()),
736 };
737 result.map_err(JwkError::DecodingKey)
738 }
739
740 pub(super) fn expect(
742 &self,
743 expected: &'static str,
744 kty: &str,
745 crv: Option<&str>,
746 ) -> Result<(), JwkError> {
747 if self.kty == kty && (crv.is_none() || self.crv() == crv) {
748 Ok(())
749 } else {
750 Err(JwkError::UnexpectedKeyType {
751 expected,
752 kty: self.kty.clone(),
753 crv: self.crv.clone(),
754 })
755 }
756 }
757
758 pub(super) fn unsupported(&self) -> JwkError {
759 JwkError::UnsupportedKeyType {
760 kty: self.kty.clone(),
761 crv: self.crv.clone(),
762 }
763 }
764}
765
766impl fmt::Debug for Jwk {
767 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
769 let mut s = f.debug_struct("Jwk");
770 s.field("kty", &self.kty);
771 for (name, value) in [
772 ("crv", &self.crv),
773 ("x", &self.x),
774 ("y", &self.y),
775 ("n", &self.n),
776 ("e", &self.e),
777 ("use", &self.key_use),
778 ("alg", &self.alg),
779 ("kid", &self.kid),
780 ] {
781 if let Some(value) = value {
782 s.field(name, value);
783 }
784 }
785 if let Some(key_ops) = &self.key_ops {
786 s.field("key_ops", key_ops);
787 }
788 if !self.additional.is_empty() {
789 s.field("additional", &self.additional);
790 }
791 s.field("private", &self.is_private());
792 s.finish()
793 }
794}
795
796impl fmt::Display for Jwk {
797 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
799 let json = serde_json::to_string(&self.public()).map_err(|_| fmt::Error)?;
800 f.write_str(&json)
801 }
802}