Skip to main content

identity_kit/jose/
jwk.rs

1//! `Jwk`: a JSON Web Key (RFC 7517) with the parameters of RFC 7518 and RFC 8037.
2
3use 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/// The kind of key behind a [`Jwk`], as a key store or HSM names it.
15///
16/// Used as the hint for [`Jwk::from_spki_der`], where it selects the parser and is checked against
17/// the encoded key.
18#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19#[non_exhaustive]
20pub enum KeyType {
21    /// Ed25519, used with [`EdDSA`](super::JwsAlgorithm::EdDSA).
22    Ed25519,
23    /// NIST P-256 (secp256r1), used with [`ES256`](super::JwsAlgorithm::ES256).
24    P256,
25    /// secp256k1, used with [`ES256K`](super::JwsAlgorithm::ES256K).
26    Secp256k1,
27    /// RSA with a 2048-bit modulus, used with [`RS256`](super::JwsAlgorithm::RS256) and
28    /// [`PS256`](super::JwsAlgorithm::PS256).
29    Rsa2048,
30    /// RSA with a 4096-bit modulus, used with [`RS256`](super::JwsAlgorithm::RS256) and
31    /// [`PS256`](super::JwsAlgorithm::PS256).
32    Rsa4096,
33}
34
35/// A JSON Web Key (RFC 7517).
36///
37/// A `Jwk` carries the common members (`kty`, `use`, `key_ops`, `alg`, `kid`), the public
38/// parameters of RFC 7518 and RFC 8037 (`crv`, `x`, `y`, `n`, `e`), the private parameters (`d`,
39/// `p`, `q`, `dp`, `dq`, `qi`, `oth`, `k`) and any other member in an insertion-ordered map, so a
40/// key round-trips through JSON with its extension members intact.
41///
42/// Private parameters never appear in `Debug` or `Display` output and are zeroized when the key
43/// is dropped. [`Jwk::public`] strips them; serde serialization keeps them, because a key store
44/// that persists private JWKs needs it to.
45///
46/// Members are kept as the base64url strings they arrive as. Nothing is validated when a key is
47/// parsed; the accessors that decode the material ([`Jwk::ed25519_public_bytes`],
48/// [`Jwk::p256_public_bytes`], …) and the operations that need it ([`Jwk::thumbprint_sha256`],
49/// [`Jwk::to_decoding_key`], the verifiers) report what is wrong.
50///
51/// # Examples
52///
53/// ```
54/// use identity_kit::jose::Jwk;
55///
56/// // The Ed25519 key of RFC 8037 Appendix A.2.
57/// let jwk: Jwk = serde_json::from_str(
58///     r#"{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#,
59/// )?;
60/// assert_eq!(jwk.kty(), "OKP");
61/// assert_eq!(jwk.crv(), Some("Ed25519"));
62/// assert_eq!(jwk.ed25519_public_bytes()?.len(), 32);
63/// assert_eq!(jwk.thumbprint_sha256()?, "kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k");
64/// # Ok::<(), Box<dyn std::error::Error>>(())
65/// ```
66#[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    // Private parameters follow the public ones, as in the examples of RFC 7517 Appendix A.2.
80    #[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/// The private parameters of RFC 7518 §6.2.2, §6.3.2 and §6.4 and RFC 8037 §2.
95#[derive(Clone, Default, PartialEq, Eq, Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
96pub(crate) struct PrivateParams {
97    /// EC and OKP private key, RSA private exponent.
98    #[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    /// Symmetric key value.
113    #[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/// One entry of the RSA `oth` member (RFC 7518 §6.3.2.7).
124#[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
131/// Decodes the base64url member `name` of `value`.
132fn 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
137/// The shortest RSA modulus, in bits, a key may have to verify RS256 or PS256 signatures:
138/// RFC 7518 §3.3 and §3.5 require "a key of size 2048 bits or larger".
139const MIN_RSA_MODULUS_BITS: usize = 2048;
140
141/// Refuses an RSA modulus `n` (big-endian, leading zero octets allowed) shorter than
142/// [`MIN_RSA_MODULUS_BITS`].
143fn 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    /// A key with only `kty` set, the starting point of the `from_*` constructors.
161    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    /// An `OKP` key with `crv` `Ed25519` from the 32-byte public key of RFC 8032.
179    ///
180    /// # Errors
181    ///
182    /// [`JwkError::InvalidKey`] when `public` is not 32 bytes or not a valid point.
183    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    /// An `OKP` key with `crv` `X25519` from the 32-byte public key of RFC 7748, for key
199    /// agreement (RFC 8037 §3).
200    ///
201    /// # Errors
202    ///
203    /// [`JwkError::InvalidKeyLength`] when `public` is not 32 bytes.
204    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    /// An `EC` key with `crv` `P-256` from a SEC1-encoded point, compressed or uncompressed.
219    ///
220    /// # Errors
221    ///
222    /// [`JwkError::InvalidKey`] when `sec1` is not a valid P-256 point.
223    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    /// An `EC` key with `crv` `secp256k1` from a SEC1-encoded point, compressed or uncompressed.
242    ///
243    /// # Errors
244    ///
245    /// [`JwkError::InvalidKey`] when `sec1` is not a valid secp256k1 point.
246    #[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    /// An `RSA` key from the big-endian modulus `n` and public exponent `e`.
268    ///
269    /// Leading zero octets are dropped, as RFC 7518 §6.3.1 requires. The values are not checked
270    /// beyond being non-empty, like the other parsers of this crate leave the rules of an
271    /// algorithm to verification: [`DefaultVerifier`](super::DefaultVerifier) and
272    /// [`Jwk::to_decoding_key`] refuse a modulus shorter than 2048 bits (RFC 7518 §3.3, §3.5),
273    /// and the verifier an otherwise unusable modulus or exponent, when the key is used.
274    ///
275    /// # Errors
276    ///
277    /// [`JwkError::InvalidKey`] when `n` or `e` is empty.
278    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    /// A key from a DER-encoded X.509 `SubjectPublicKeyInfo` (RFC 5280 §4.1.2.7), the form in
292    /// which KMS and HSM backends return public keys.
293    ///
294    /// `hint` is what the caller expects and selects the parser; a key of another type is an
295    /// error, as is an RSA modulus of another size than the hint names.
296    ///
297    /// # Errors
298    ///
299    /// - [`JwkError::InvalidKey`] when `der` does not decode as a key of the hinted type.
300    /// - [`JwkError::KeyMismatch`] when it decodes but has another size than hinted.
301    /// - [`JwkError::FeatureDisabled`] when the hinted type is compiled out.
302    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    /// The key type (`kty`): `OKP`, `EC`, `RSA` or `oct`.
371    pub fn kty(&self) -> &str {
372        &self.kty
373    }
374
375    /// The curve (`crv`) of an `EC` or `OKP` key.
376    pub fn crv(&self) -> Option<&str> {
377        self.crv.as_deref()
378    }
379
380    /// The base64url `x` coordinate (`EC`) or public key (`OKP`).
381    pub fn x(&self) -> Option<&str> {
382        self.x.as_deref()
383    }
384
385    /// The base64url `y` coordinate of an `EC` key.
386    pub fn y(&self) -> Option<&str> {
387        self.y.as_deref()
388    }
389
390    /// The base64url RSA modulus `n`.
391    pub fn n(&self) -> Option<&str> {
392        self.n.as_deref()
393    }
394
395    /// The base64url RSA public exponent `e`.
396    pub fn e(&self) -> Option<&str> {
397        self.e.as_deref()
398    }
399
400    /// The public key use (`use`): `sig` or `enc`.
401    pub fn key_use(&self) -> Option<&str> {
402        self.key_use.as_deref()
403    }
404
405    /// The key operations (`key_ops`), such as `sign` and `verify`.
406    pub fn key_ops(&self) -> Option<&[String]> {
407        self.key_ops.as_deref()
408    }
409
410    /// The algorithm (`alg`) the key is intended for.
411    pub fn alg(&self) -> Option<&str> {
412        self.alg.as_deref()
413    }
414
415    /// The key id (`kid`).
416    pub fn kid(&self) -> Option<&str> {
417        self.kid.as_deref()
418    }
419
420    /// Members other than the ones this type models, in the order they were inserted.
421    pub fn additional(&self) -> &IndexMap<String, Value> {
422        &self.additional
423    }
424
425    /// Mutable access to the members other than the ones this type models.
426    pub fn additional_mut(&mut self) -> &mut IndexMap<String, Value> {
427        &mut self.additional
428    }
429
430    /// Whether the key carries private parameters (`d`, `p`, `q`, `dp`, `dq`, `qi`, `oth` or
431    /// `k`).
432    pub fn is_private(&self) -> bool {
433        !self.private.is_empty()
434    }
435
436    /// This key with its `kid` set.
437    #[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    /// This key with its `alg` set.
444    #[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    /// This key with its `use` set.
451    #[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    /// This key with its `key_ops` set.
458    #[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    /// A copy of this key without private parameters.
469    #[must_use]
470    pub fn public(&self) -> Self {
471        let mut public = self.clone();
472        public.private = PrivateParams::default();
473        public
474    }
475
476    /// The RFC 7638 JWK thumbprint with SHA-256, as base64url.
477    ///
478    /// The thumbprint hashes the required members of the key type (`crv`, `kty`, `x`, `y` for
479    /// `EC`; `crv`, `kty`, `x` for `OKP`; `e`, `kty`, `n` for `RSA`; `k`, `kty` for `oct`) in
480    /// lexicographic order with no whitespace, exactly as they appear in the key.
481    ///
482    /// # Errors
483    ///
484    /// [`JwkError::MissingMember`] when a required member is absent and
485    /// [`JwkError::UnsupportedKeyType`] for any other `kty`.
486    ///
487    /// # Examples
488    ///
489    /// ```
490    /// use identity_kit::jose::Jwk;
491    ///
492    /// // RFC 7638 §3.1.
493    /// let jwk: Jwk = serde_json::from_str(r#"{
494    ///   "kty": "RSA",
495    ///   "n": "0vx7agoebGcQSuuPiLJXZptN9nndrQmbXEps2aiAFbWhM78LhWx4cbbfAAtVT86zwu1RK7aPFFxuhDR1L6tSoc_BJECPebWKRXjBZCiFV4n3oknjhMstn64tZ_2W-5JsGY4Hc5n9yBXArwl93lqt7_RN5w6Cf0h4QyQ5v-65YGjQR0_FDW2QvzqY368QQMicAtaSqzs8KJZgnYb9c7d0zgdAZHzu6qMQvRL5hajrn1n91CbOpbISD08qNLyrdkt-bFTWhAI4vMQFh6WeZu0fM4lFd2NcRwr3XPksINHaQ-G_xBniIqbw0Ls1jF44-csFCur-kEgU8awapJzKnqDKgw",
496    ///   "e": "AQAB",
497    ///   "alg": "RS256",
498    ///   "kid": "2011-04-29"
499    /// }"#)?;
500    /// assert_eq!(jwk.thumbprint_sha256()?, "NzbLsXh8uDCcd-6MNwXF4W_7noWXFZAfHkxZsRGC9Xs");
501    /// # Ok::<(), Box<dyn std::error::Error>>(())
502    /// ```
503    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    /// The canonical JSON that [`Jwk::thumbprint_sha256`] hashes.
509    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            // Serializing a `str` cannot fail.
513            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    /// The 32-byte Ed25519 public key of an `OKP` key with `crv` `Ed25519`.
542    ///
543    /// # Errors
544    ///
545    /// [`JwkError::UnexpectedKeyType`] for any other key, [`JwkError::MissingMember`] or
546    /// [`JwkError::InvalidBase64Url`] for a broken `x`, and [`JwkError::InvalidKey`] when `x` is
547    /// not a valid point.
548    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    /// The 32-byte X25519 public key of an `OKP` key with `crv` `X25519`.
566    ///
567    /// # Errors
568    ///
569    /// [`JwkError::UnexpectedKeyType`] for any other key, [`JwkError::MissingMember`] or
570    /// [`JwkError::InvalidBase64Url`] for a broken `x`, and [`JwkError::InvalidKeyLength`] when
571    /// `x` is not 32 bytes.
572    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    /// The uncompressed SEC1 point (`0x04 || x || y`, 65 bytes) of an `EC` key with `crv`
585    /// `P-256`.
586    ///
587    /// # Errors
588    ///
589    /// [`JwkError::UnexpectedKeyType`] for any other key, [`JwkError::MissingMember`] or
590    /// [`JwkError::InvalidBase64Url`] for a broken `x` or `y`, and [`JwkError::InvalidKey`] when
591    /// the coordinates are not a point on the curve.
592    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    /// The uncompressed SEC1 point (`0x04 || x || y`, 65 bytes) of an `EC` key with `crv`
614    /// `secp256k1`.
615    ///
616    /// # Errors
617    ///
618    /// [`JwkError::UnexpectedKeyType`] for any other key, [`JwkError::MissingMember`] or
619    /// [`JwkError::InvalidBase64Url`] for a broken `x` or `y`, and [`JwkError::InvalidKey`] when
620    /// the coordinates are not a point on the curve.
621    #[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    /// `0x04 || x || y` from the `x` and `y` members, each of which must be `len` bytes.
646    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    /// The big-endian modulus `n` and public exponent `e` of an `RSA` key.
667    ///
668    /// # Errors
669    ///
670    /// [`JwkError::UnexpectedKeyType`] for any other key, [`JwkError::MissingMember`] or
671    /// [`JwkError::InvalidBase64Url`] for a broken `n` or `e`.
672    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        // `rsa` sizes an integer by its octets, so a leading zero octet (which RFC 7518 §6.3.1
688        // forbids but a JWK may carry) would make the modulus one octet longer than its value.
689        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    /// A [`jsonwebtoken::DecodingKey`] for this key, so a JWT can be verified with `jsonwebtoken`.
707    ///
708    /// Supports `OKP`/`Ed25519`, `EC`/`P-256`, `EC`/`P-384` and `RSA` keys, the types
709    /// `jsonwebtoken` can verify with. Private parameters are not needed and not used.
710    ///
711    /// # Errors
712    ///
713    /// [`JwkError::UnsupportedKeyType`] for any other key, [`JwkError::MissingMember`] when a
714    /// required member is absent, [`JwkError::InvalidBase64Url`] for an RSA modulus or exponent
715    /// that is not base64url, [`JwkError::InvalidKey`] for an RSA modulus shorter than 2048 bits
716    /// (RFC 7518 §3.3, §3.5) and [`JwkError::DecodingKey`] when `jsonwebtoken` rejects the
717    /// material.
718    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    /// Checks that this is a `kty` key on curve `crv`.
741    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    /// Public members only; private parameters are shown as `private: true`.
768    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    /// The public key as compact JSON, as [`Jwk::public`] serializes.
798    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}