identity_jose/jwk/
key.rs

1// Copyright 2020-2023 IOTA Stiftung
2// SPDX-License-Identifier: Apache-2.0
3
4use crypto::hashes::sha::SHA256;
5use crypto::hashes::sha::SHA256_LEN;
6use identity_core::common::Url;
7use zeroize::Zeroize;
8
9use crate::error::Error;
10use crate::error::Result;
11use crate::jwk::EcCurve;
12use crate::jwk::EcxCurve;
13use crate::jwk::EdCurve;
14use crate::jwk::JwkOperation;
15use crate::jwk::JwkParams;
16use crate::jwk::JwkParamsEc;
17use crate::jwk::JwkParamsOct;
18use crate::jwk::JwkParamsOkp;
19use crate::jwk::JwkParamsRsa;
20use crate::jwk::JwkType;
21use crate::jwk::JwkUse;
22use crate::jwu::encode_b64;
23
24/// A SHA256 JSON Web Key Thumbprint.
25pub type JwkThumbprintSha256 = [u8; SHA256_LEN];
26
27/// JSON Web Key.
28///
29/// [More Info](https://tools.ietf.org/html/rfc7517#section-4)
30#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
31pub struct Jwk {
32  /// Key Type.
33  ///
34  /// Identifies the cryptographic algorithm family used with the key.
35  ///
36  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.1)
37  pub(super) kty: JwkType,
38  /// Public Key Use.
39  ///
40  /// Identifies the intended use of the public key.
41  ///
42  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.2)
43  #[serde(rename = "use", skip_serializing_if = "Option::is_none")]
44  pub(super) use_: Option<JwkUse>,
45  /// Key Operations.
46  ///
47  /// Identifies the operation(s) for which the key is intended to be used.
48  ///
49  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.3)
50  #[serde(skip_serializing_if = "Option::is_none")]
51  pub(super) key_ops: Option<Vec<JwkOperation>>,
52  /// Algorithm.
53  ///
54  /// Identifies the algorithm intended for use with the key.
55  ///
56  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.4)
57  #[serde(skip_serializing_if = "Option::is_none")]
58  pub(super) alg: Option<String>,
59  /// Key ID.
60  ///
61  /// Used to match a specific key among a set of keys within a JWK Set.
62  ///
63  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.5)
64  #[serde(skip_serializing_if = "Option::is_none")]
65  pub(super) kid: Option<String>,
66  /// X.509 URL.
67  ///
68  /// A URI that refers to a resource for an X.509 public key certificate or
69  /// certificate chain.
70  ///
71  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.6)
72  #[serde(skip_serializing_if = "Option::is_none")]
73  pub(super) x5u: Option<Url>,
74  /// X.509 Certificate Chain.
75  ///
76  /// Contains a chain of one or more PKIX certificates.
77  ///
78  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.7)
79  #[serde(skip_serializing_if = "Option::is_none")]
80  pub(super) x5c: Option<Vec<String>>,
81  /// X.509 Certificate SHA-1 Thumbprint.
82  ///
83  /// A base64url-encoded SHA-1 thumbprint of the DER encoding of an X.509
84  /// certificate.
85  ///
86  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.8)
87  #[serde(skip_serializing_if = "Option::is_none")]
88  pub(super) x5t: Option<String>,
89  /// X.509 Certificate SHA-256 Thumbprint.
90  ///
91  /// A base64url-encoded SHA-256 thumbprint of the DER encoding of an X.509
92  /// certificate.
93  ///
94  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4.9)
95  #[serde(rename = "x5t#S256", skip_serializing_if = "Option::is_none")]
96  pub(super) x5t_s256: Option<String>,
97  /// Type-Specific Key Properties.
98  ///
99  /// [More Info](https://tools.ietf.org/html/rfc7517#section-4)
100  #[serde(flatten)]
101  pub(super) params: JwkParams,
102}
103
104impl Jwk {
105  /// Creates a new `Jwk` with the given `kty` parameter.
106  pub const fn new(kty: JwkType) -> Self {
107    Self {
108      kty,
109      use_: None,
110      key_ops: None,
111      alg: None,
112      kid: None,
113      x5u: None,
114      x5c: None,
115      x5t: None,
116      x5t_s256: None,
117      params: JwkParams::new(kty),
118    }
119  }
120
121  /// Creates a new `Jwk` from the given params.
122  pub fn from_params(params: impl Into<JwkParams>) -> Self {
123    let params: JwkParams = params.into();
124
125    Self {
126      kty: params.kty(),
127      use_: None,
128      key_ops: None,
129      alg: None,
130      kid: None,
131      x5u: None,
132      x5c: None,
133      x5t: None,
134      x5t_s256: None,
135      params,
136    }
137  }
138
139  /// Returns the value for the key type parameter (kty).
140  pub fn kty(&self) -> JwkType {
141    self.kty
142  }
143
144  /// Sets a value for the key type parameter (kty).
145  ///
146  /// Removes any previously set `params`.
147  pub fn set_kty(&mut self, value: impl Into<JwkType>) {
148    self.kty = value.into();
149    self.params = JwkParams::new(self.kty);
150  }
151
152  /// Returns the value for the use property (use).
153  pub fn use_(&self) -> Option<JwkUse> {
154    self.use_
155  }
156
157  /// Sets a value for the key use parameter (use).
158  pub fn set_use(&mut self, value: impl Into<JwkUse>) {
159    self.use_ = Some(value.into());
160  }
161
162  /// Returns the value for the key operations parameter (key_ops).
163  pub fn key_ops(&self) -> Option<&[JwkOperation]> {
164    self.key_ops.as_deref()
165  }
166
167  /// Sets values for the key operations parameter (key_ops).
168  pub fn set_key_ops(&mut self, value: impl IntoIterator<Item = impl Into<JwkOperation>>) {
169    self.key_ops = Some(value.into_iter().map(Into::into).collect());
170  }
171
172  /// Returns the value for the algorithm property (alg).
173  pub fn alg(&self) -> Option<&str> {
174    self.alg.as_deref()
175  }
176
177  /// Sets a value for the algorithm property (alg).
178  pub fn set_alg(&mut self, value: impl Into<String>) {
179    self.alg = Some(value.into());
180  }
181
182  /// Returns the value of the key ID property (kid).
183  pub fn kid(&self) -> Option<&str> {
184    self.kid.as_deref()
185  }
186
187  /// Sets a value for the key ID property (kid).
188  pub fn set_kid(&mut self, value: impl Into<String>) {
189    self.kid = Some(value.into());
190  }
191
192  /// Returns the value of the X.509 URL property (x5u).
193  pub fn x5u(&self) -> Option<&Url> {
194    self.x5u.as_ref()
195  }
196
197  /// Sets a value for the X.509 URL property (x5u).
198  pub fn set_x5u(&mut self, value: impl Into<Url>) {
199    self.x5u = Some(value.into());
200  }
201
202  /// Returns the value of the X.509 certificate chain property (x5c).
203  pub fn x5c(&self) -> Option<&[String]> {
204    self.x5c.as_deref()
205  }
206
207  /// Sets values for the X.509 certificate chain property (x5c).
208  pub fn set_x5c(&mut self, value: impl IntoIterator<Item = impl Into<String>>) {
209    self.x5c = Some(value.into_iter().map(Into::into).collect());
210  }
211
212  /// Returns the value of the X.509 certificate SHA-1 thumbprint property
213  /// (x5t).
214  pub fn x5t(&self) -> Option<&str> {
215    self.x5t.as_deref()
216  }
217
218  /// Sets a value for the X.509 certificate SHA-1 thumbprint property (x5t).
219  pub fn set_x5t(&mut self, value: impl Into<String>) {
220    self.x5t = Some(value.into());
221  }
222
223  /// Returns the value of the X.509 certificate SHA-256 thumbprint property
224  /// (x5t#S256).
225  pub fn x5t_s256(&self) -> Option<&str> {
226    self.x5t_s256.as_deref()
227  }
228
229  /// Sets a value for the X.509 certificate SHA-256 thumbprint property
230  /// (x5t#S256).
231  pub fn set_x5t_s256(&mut self, value: impl Into<String>) {
232    self.x5t_s256 = Some(value.into());
233  }
234
235  /// Returns a reference to the custom JWK properties.
236  pub fn params(&self) -> &JwkParams {
237    &self.params
238  }
239
240  /// Returns a mutable reference to the custom JWK properties.
241  pub fn params_mut(&mut self) -> &mut JwkParams {
242    &mut self.params
243  }
244
245  /// Sets the value of the custom JWK properties.
246  ///
247  /// The passed `params` must be appropriate for the key type (`kty`), an error is returned otherwise.
248  ///
249  /// If you want to set `params` unchecked, use [`set_params_unchecked`](Self::set_params_unchecked).
250  pub fn set_params(&mut self, params: impl Into<JwkParams>) -> Result<()> {
251    match (self.kty, params.into()) {
252      (JwkType::Ec, value @ JwkParams::Ec(_)) => {
253        self.set_params_unchecked(value);
254      }
255      (JwkType::Rsa, value @ JwkParams::Rsa(_)) => {
256        self.set_params_unchecked(value);
257      }
258      (JwkType::Oct, value @ JwkParams::Oct(_)) => {
259        self.set_params_unchecked(value);
260      }
261      (JwkType::Okp, value @ JwkParams::Okp(_)) => {
262        self.set_params_unchecked(value);
263      }
264      (_, _) => {
265        return Err(Error::InvalidParam("`params` type does not match `kty`"));
266      }
267    }
268    Ok(())
269  }
270
271  /// Sets the value of the custom JWK properties.
272  ///
273  /// Does not check whether the passed params are appropriate for the set key type (`kty`).
274  pub fn set_params_unchecked(&mut self, value: impl Into<JwkParams>) {
275    self.params = value.into();
276  }
277
278  /// Returns the [`JwkParamsEc`] in this JWK if it is of type `Ec`.
279  pub fn try_ec_params(&self) -> Result<&JwkParamsEc> {
280    match self.params() {
281      JwkParams::Ec(params) => Ok(params),
282      _ => Err(Error::KeyError("Ec")),
283    }
284  }
285
286  /// Returns a mutable reference to the [`JwkParamsEc`] in this JWK if it is of type `Ec`.
287  pub fn try_ec_params_mut(&mut self) -> Result<&mut JwkParamsEc> {
288    match self.params_mut() {
289      JwkParams::Ec(params) => Ok(params),
290      _ => Err(Error::KeyError("Ec")),
291    }
292  }
293
294  /// Returns the [`JwkParamsRsa`] in this JWK if it is of type `Rsa`.
295  pub fn try_rsa_params(&self) -> Result<&JwkParamsRsa> {
296    match self.params() {
297      JwkParams::Rsa(params) => Ok(params),
298      _ => Err(Error::KeyError("Rsa")),
299    }
300  }
301
302  /// Returns a mutable reference to the [`JwkParamsRsa`] in this JWK if it is of type `Rsa`.
303  pub fn try_rsa_params_mut(&mut self) -> Result<&mut JwkParamsRsa> {
304    match self.params_mut() {
305      JwkParams::Rsa(params) => Ok(params),
306      _ => Err(Error::KeyError("Rsa")),
307    }
308  }
309
310  /// Returns the [`JwkParamsOct`] in this JWK if it is of type `Oct`.
311  pub fn try_oct_params(&self) -> Result<&JwkParamsOct> {
312    match self.params() {
313      JwkParams::Oct(params) => Ok(params),
314      _ => Err(Error::KeyError("Oct")),
315    }
316  }
317
318  /// Returns a mutable reference to the [`JwkParamsOct`] in this JWK if it is of type `Oct`.
319  pub fn try_oct_params_mut(&mut self) -> Result<&mut JwkParamsOct> {
320    match self.params_mut() {
321      JwkParams::Oct(params) => Ok(params),
322      _ => Err(Error::KeyError("Oct")),
323    }
324  }
325
326  /// Returns the [`JwkParamsOkp`] in this JWK if it is of type `Okp`.
327  pub fn try_okp_params(&self) -> Result<&JwkParamsOkp> {
328    match self.params() {
329      JwkParams::Okp(params) => Ok(params),
330      _ => Err(Error::KeyError("Okp")),
331    }
332  }
333
334  /// Returns a mutable reference to the [`JwkParamsOkp`] in this JWK if it is of type `Okp`.
335  pub fn try_okp_params_mut(&mut self) -> Result<&mut JwkParamsOkp> {
336    match self.params_mut() {
337      JwkParams::Okp(params) => Ok(params),
338      _ => Err(Error::KeyError("Okp")),
339    }
340  }
341
342  // ===========================================================================
343  // Thumbprint
344  // ===========================================================================
345
346  /// Creates a Thumbprint of the JSON Web Key according to [RFC7638](https://tools.ietf.org/html/rfc7638).
347  ///
348  /// `SHA2-256` is used as the hash function *H*.
349  ///
350  /// The thumbprint is returned as a base64url-encoded string.
351  pub fn thumbprint_sha256_b64(&self) -> String {
352    encode_b64(self.thumbprint_sha256())
353  }
354
355  /// Creates a Thumbprint of the JSON Web Key according to [RFC7638](https://tools.ietf.org/html/rfc7638).
356  ///
357  /// `SHA2-256` is used as the hash function *H*.
358  ///
359  /// The thumbprint is returned as an unencoded array of bytes.
360  pub fn thumbprint_sha256(&self) -> JwkThumbprintSha256 {
361    let json: String = self.thumbprint_hash_input();
362
363    let mut out: JwkThumbprintSha256 = Default::default();
364
365    SHA256(json.as_bytes(), &mut out);
366
367    out
368  }
369
370  /// Creates the JSON string of the JSON Web Key according to [RFC7638](https://tools.ietf.org/html/rfc7638),
371  /// which is used as the input for the JWK thumbprint hashing procedure.
372  /// This can be used as input for a custom hash function.
373  pub fn thumbprint_hash_input(&self) -> String {
374    let kty: &str = self.kty.name();
375
376    match self.params() {
377      JwkParams::Ec(JwkParamsEc { crv, x, y, .. }) => {
378        format!(r#"{{"crv":"{crv}","kty":"{kty}","x":"{x}","y":"{y}"}}"#)
379      }
380      JwkParams::Rsa(JwkParamsRsa { e, n, .. }) => {
381        format!(r#"{{"e":"{e}","kty":"{kty}","n":"{n}"}}"#)
382      }
383      JwkParams::Oct(JwkParamsOct { k }) => {
384        format!(r#"{{"k":"{k}","kty":"{kty}"}}"#)
385      }
386      // Implementation according to https://www.rfc-editor.org/rfc/rfc8037#section-2.
387      JwkParams::Okp(JwkParamsOkp { crv, x, .. }) => {
388        format!(r#"{{"crv":"{crv}","kty":"{kty}","x":"{x}"}}"#)
389      }
390    }
391  }
392
393  // ===========================================================================
394  // Validations
395  // ===========================================================================
396
397  /// Checks if the `alg` claim of the JWK is equal to `expected`.
398  pub fn check_alg(&self, expected: impl AsRef<str>) -> Result<()> {
399    match self.alg() {
400      Some(value) if value == expected.as_ref() => Ok(()),
401      Some(_) => Err(Error::InvalidClaim("alg")),
402      None => Ok(()),
403    }
404  }
405
406  /// Returns the [`EcCurve`] of this JWK if it is of type `Ec`.
407  pub fn try_ec_curve(&self) -> Result<EcCurve> {
408    match self.params() {
409      JwkParams::Ec(inner) => inner.try_ec_curve(),
410      _ => Err(Error::KeyError("Ec Curve")),
411    }
412  }
413
414  /// Returns the [`EdCurve`] of this JWK if it is of type `Okp`.
415  pub fn try_ed_curve(&self) -> Result<EdCurve> {
416    match self.params() {
417      JwkParams::Okp(inner) => inner.try_ed_curve(),
418      _ => Err(Error::KeyError("Ed Curve")),
419    }
420  }
421
422  /// Returns the [`EcxCurve`] of this JWK if it is of type `Okp`.
423  pub fn try_ecx_curve(&self) -> Result<EcxCurve> {
424    match self.params() {
425      JwkParams::Okp(inner) => inner.try_ecx_curve(),
426      _ => Err(Error::KeyError("Ecx Curve")),
427    }
428  }
429
430  /// Returns `true` if _all_ private key components of the key are unset, `false` otherwise.
431  pub fn is_public(&self) -> bool {
432    self.params.is_public()
433  }
434
435  /// Returns `true` if _all_ private key components of the key are set, `false` otherwise.
436  pub fn is_private(&self) -> bool {
437    match self.params() {
438      JwkParams::Ec(params) => params.is_private(),
439      JwkParams::Rsa(params) => params.is_private(),
440      JwkParams::Oct(_) => true,
441      JwkParams::Okp(params) => params.is_private(),
442    }
443  }
444
445  /// Returns a clone of the Jwk with _all_ private key components unset.
446  ///
447  /// The `None` variant is returned when `kty = oct` as this key type is not considered public by this library.
448  pub fn to_public(&self) -> Option<Jwk> {
449    let mut public: Jwk = Jwk::from_params(self.params().to_public()?);
450
451    if let Some(value) = self.use_() {
452      public.set_use(value);
453    }
454
455    if let Some(value) = self.key_ops() {
456      public.set_key_ops(value.iter().map(|op| op.invert()));
457    }
458
459    if let Some(value) = self.alg() {
460      public.set_alg(value);
461    }
462
463    if let Some(value) = self.kid() {
464      public.set_kid(value);
465    }
466
467    Some(public)
468  }
469}
470
471impl Zeroize for Jwk {
472  fn zeroize(&mut self) {
473    self.params.zeroize();
474  }
475}
476
477impl Drop for Jwk {
478  fn drop(&mut self) {
479    self.zeroize();
480  }
481}