identity_kit/resolver/key.rs
1//! `KeyReference`, `KeyRole` and `KeyResolver`: where a token says its key is, and the trait
2//! that finds it.
3
4use core::fmt;
5use std::error::Error;
6use std::sync::Arc;
7
8use base64::Engine as _;
9use base64::engine::general_purpose::STANDARD;
10use url::Url;
11
12use super::{DereferenceError, Resolver};
13use crate::did::{Did, DidError, DidUrl, VerificationRelationship};
14use crate::jose::{CompactJws, Jwk, base64url};
15
16/// Where a token says the key that signed it is (ADR 0006).
17///
18/// A JWT names its key in one of several ways: a DID URL in `kid`, a `#fragment` in `kid`
19/// relative to a DID `iss`, a `kid` into the key set an HTTPS `iss` publishes, the key itself in
20/// a `jwk` header, a certificate chain in `x5c`, or a key set URL in `jku`.
21/// [`from_jws`](Self::from_jws) reads which from a JWS header; `sd_jwt_vc` builds one from a
22/// `cnf` claim. A [`KeyResolver`] turns it into a key.
23///
24/// Resolving a reference says nothing about trust: a key by value resolves to itself whoever
25/// made the token. Once the signature verifies, [`binds_to`](Self::binds_to) checks that the key
26/// belongs to the party the token claims.
27///
28/// # Examples
29///
30/// ```
31/// use identity_kit::jose::{CompactJws, JwtClaims};
32/// use identity_kit::resolver::{KeyReference, KeyReferenceError};
33///
34/// // {"alg":"EdDSA","kid":"#key-1"}.{"iss":"did:example:123"}
35/// let jws = CompactJws::decode(
36/// "eyJhbGciOiJFZERTQSIsImtpZCI6IiNrZXktMSJ9.eyJpc3MiOiJkaWQ6ZXhhbXBsZToxMjMifQ.c2lnbmF0dXJl",
37/// )?;
38/// let claims: JwtClaims = jws.claims()?;
39/// let reference = KeyReference::from_jws(&jws, claims.iss.as_deref())?;
40/// assert_eq!(reference.to_string(), "did:example:123#key-1");
41///
42/// // A relative `kid` needs a DID to resolve against.
43/// assert!(matches!(
44/// KeyReference::from_jws(&jws, None),
45/// Err(KeyReferenceError::RelativeKidWithoutIssuer(_))
46/// ));
47/// # Ok::<(), Box<dyn std::error::Error>>(())
48/// ```
49#[derive(Debug, Clone, PartialEq, Eq)]
50#[non_exhaustive]
51pub enum KeyReference {
52 /// A verification method: a `kid` that is an absolute DID URL, or a `#fragment` joined with
53 /// a DID `iss`.
54 DidUrl(DidUrl),
55 /// The public key itself, from a `jwk` header or `cnf.jwk`.
56 Jwk(Box<Jwk>),
57 /// An `x5c` certificate chain, DER, leaf first. No resolver in the crate handles it yet.
58 X509Chain(Vec<Vec<u8>>),
59 /// The key `kid` names in the key set of the HTTPS issuer `issuer` (SD-JWT VC §3.5).
60 Jwks {
61 /// The issuer, the `iss` of the token.
62 issuer: Url,
63 /// The `kid` of the key in the issuer's key set.
64 kid: String,
65 },
66 /// The key set at `url`, from `jku`, and the `kid` that selects a key in it. No resolver in
67 /// the crate handles it yet.
68 Jku {
69 /// The key set URL.
70 url: Url,
71 /// The `kid`, if the token has one.
72 kid: Option<String>,
73 },
74}
75
76impl KeyReference {
77 /// Where the key of `jws` is, given the `iss` of its claims (when `iss` is a string).
78 ///
79 /// The header parameters are tried in this order, and the first that applies decides:
80 ///
81 /// 1. A `kid` that parses as a DID URL is [`DidUrl`](Self::DidUrl). A path or DID
82 /// parameters such as `versionId` are refused: they could select a document version
83 /// that still lists a retired key (ADR 0004).
84 /// 2. A `kid` starting with `#` is joined with `iss` into a [`DidUrl`](Self::DidUrl) when
85 /// `iss` is a DID, and refused otherwise.
86 /// 3. Another `kid`, with an `https` `iss`, is [`Jwks`](Self::Jwks).
87 /// 4. A `jwk` header is [`Jwk`](Self::Jwk); a private key there is refused.
88 /// 5. An `x5c` header is [`X509Chain`](Self::X509Chain), each certificate base64-decoded
89 /// but not parsed.
90 /// 6. A `jku` header is [`Jku`](Self::Jku), with the `kid` when there is one.
91 ///
92 /// A `kid` that none of the first three cases takes is an error when no `jwk`, `x5c` or
93 /// `jku` follows.
94 ///
95 /// # Errors
96 ///
97 /// A [`KeyReferenceError`]: [`Missing`](KeyReferenceError::Missing) when the header names no
98 /// key, otherwise the parameter that is wrong.
99 pub fn from_jws(jws: &CompactJws, iss: Option<&str>) -> Result<Self, KeyReferenceError> {
100 let header = jws.header();
101 let mut kid_error = None;
102 if let Some(kid) = header.kid.as_deref() {
103 match Self::from_kid(kid, iss)? {
104 Ok(reference) => return Ok(reference),
105 Err(source) => {
106 kid_error = Some(KeyReferenceError::InvalidKid {
107 kid: kid.to_owned(),
108 source,
109 });
110 }
111 }
112 }
113 if let Some(jwk) = &header.jwk {
114 if jwk.is_private() {
115 return Err(KeyReferenceError::InvalidJwk(
116 "the `jwk` header holds a private key".to_owned(),
117 ));
118 }
119 return Ok(Self::Jwk(Box::new(jwk.clone())));
120 }
121 if let Some(x5c) = header.additional.get("x5c") {
122 return x5c_chain(x5c).map(Self::X509Chain);
123 }
124 if let Some(jku) = header.additional.get("jku") {
125 let url = jku
126 .as_str()
127 .ok_or_else(|| KeyReferenceError::InvalidUrl {
128 parameter: "jku",
129 value: jku.to_string(),
130 reason: "not a string".to_owned(),
131 })
132 .and_then(|jku| parse_url("jku", jku))?;
133 return Ok(Self::Jku {
134 url,
135 kid: header.kid.clone(),
136 });
137 }
138 Err(kid_error.unwrap_or(KeyReferenceError::Missing))
139 }
140
141 /// Rules 1 to 3 of [`from_jws`](Self::from_jws): the reference `kid` makes with `iss`, or
142 /// why `kid` is not a DID URL when it makes none.
143 fn from_kid(kid: &str, iss: Option<&str>) -> Result<Result<Self, DidError>, KeyReferenceError> {
144 let not_did_url = match DidUrl::parse(kid) {
145 Ok(url) => return did_url(url).map(Ok),
146 Err(error) => error,
147 };
148 if kid.starts_with('#') {
149 let did = iss
150 .and_then(|iss| Did::parse(iss).ok())
151 .ok_or_else(|| KeyReferenceError::RelativeKidWithoutIssuer(kid.to_owned()))?;
152 let url = did
153 .join(kid)
154 .map_err(|source| KeyReferenceError::InvalidKid {
155 kid: kid.to_owned(),
156 source,
157 })?;
158 return did_url(url).map(Ok);
159 }
160 if let Some(issuer) = iss
161 .and_then(|iss| Url::parse(iss).ok())
162 .filter(|issuer| issuer.scheme() == "https")
163 {
164 return Ok(Ok(Self::Jwks {
165 issuer,
166 kid: kid.to_owned(),
167 }));
168 }
169 Ok(Err(not_did_url))
170 }
171
172 /// Whether `key`, which this reference resolved to and which verified the signature,
173 /// belongs to `party`, the issuer or holder the token claims:
174 ///
175 /// | Reference | Binds when |
176 /// |---|---|
177 /// | [`DidUrl`](Self::DidUrl) | `party` is the DID of the URL |
178 /// | [`Jwk`](Self::Jwk) | `party` is the `did:jwk` of `key`, or its RFC 7638 SHA-256 thumbprint, or absent (a SIOPv2 self-issued `sub`) |
179 /// | [`Jwks`](Self::Jwks) | `party` is the issuer URL (compared as parsed URLs) |
180 /// | [`X509Chain`](Self::X509Chain), [`Jku`](Self::Jku) | never, until the crate resolves them |
181 ///
182 /// Only a key by value can bind to no party; a validator that requires an issuer or holder
183 /// passes it.
184 ///
185 /// # Examples
186 ///
187 /// ```
188 /// use identity_kit::did::DidUrl;
189 /// use identity_kit::jose::Jwk;
190 /// use identity_kit::resolver::KeyReference;
191 ///
192 /// // RFC 8037 Appendix A.2.
193 /// let key: Jwk = serde_json::from_str(
194 /// r#"{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#,
195 /// )?;
196 /// let by_value = KeyReference::Jwk(Box::new(key.clone()));
197 /// // RFC 8037 Appendix A.3.
198 /// assert!(by_value.binds_to(Some("kPrK_qmxVWaYVA9wwBF6Iuo3vVzz7TxHCTwXBygrS4k"), &key));
199 /// assert!(by_value.binds_to(None, &key));
200 /// assert!(!by_value.binds_to(Some("did:example:123"), &key));
201 ///
202 /// let by_url = KeyReference::DidUrl(DidUrl::parse("did:example:123#key-1")?);
203 /// assert!(by_url.binds_to(Some("did:example:123"), &key));
204 /// assert!(!by_url.binds_to(Some("did:example:456"), &key));
205 /// assert!(!by_url.binds_to(None, &key));
206 /// # Ok::<(), Box<dyn std::error::Error>>(())
207 /// ```
208 pub fn binds_to(&self, party: Option<&str>, key: &Jwk) -> bool {
209 match (self, party) {
210 (Self::DidUrl(url), Some(party)) => {
211 Did::parse(party).is_ok_and(|did| did == *url.did())
212 }
213 (Self::Jwk(_), None) => true,
214 (Self::Jwk(_), Some(party)) => is_key_identifier(party, key),
215 (Self::Jwks { issuer, .. }, Some(party)) => {
216 Url::parse(party).is_ok_and(|party| party == *issuer)
217 }
218 _ => false,
219 }
220 }
221}
222
223impl fmt::Display for KeyReference {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 match self {
226 Self::DidUrl(url) => fmt::Display::fmt(url, f),
227 Self::Jwk(jwk) => match jwk.thumbprint_sha256() {
228 Ok(thumbprint) => write!(f, "JWK {thumbprint}"),
229 Err(_) => f.write_str("JWK"),
230 },
231 Self::X509Chain(chain) => write!(f, "`x5c` chain of {} certificates", chain.len()),
232 Self::Jwks { issuer, kid } => write!(f, "`{kid}` of issuer `{issuer}`"),
233 Self::Jku {
234 url,
235 kid: Some(kid),
236 } => write!(f, "`{kid}` of `{url}`"),
237 Self::Jku { url, kid: None } => write!(f, "the key of `{url}`"),
238 }
239 }
240}
241
242/// `url`, refused when it carries a path or DID parameters.
243fn did_url(url: DidUrl) -> Result<KeyReference, KeyReferenceError> {
244 if url.path().is_some() || url.query().is_some() {
245 return Err(KeyReferenceError::KidWithParameters(url));
246 }
247 Ok(KeyReference::DidUrl(url))
248}
249
250/// The certificates of an `x5c` header: an array of standard base64 DER (RFC 7515 §4.1.6).
251fn x5c_chain(x5c: &serde_json::Value) -> Result<Vec<Vec<u8>>, KeyReferenceError> {
252 let invalid = |reason: &str| KeyReferenceError::InvalidX5c(reason.to_owned());
253 let certificates = x5c.as_array().ok_or_else(|| invalid("not an array"))?;
254 if certificates.is_empty() {
255 return Err(invalid("the chain is empty"));
256 }
257 certificates
258 .iter()
259 .map(|certificate| {
260 let certificate = certificate
261 .as_str()
262 .ok_or_else(|| invalid("a certificate is not a string"))?;
263 STANDARD
264 .decode(certificate)
265 .map_err(|_| invalid("a certificate is not base64"))
266 })
267 .collect()
268}
269
270/// `value` of `parameter` as an absolute URL.
271fn parse_url(parameter: &'static str, value: &str) -> Result<Url, KeyReferenceError> {
272 Url::parse(value).map_err(|e| KeyReferenceError::InvalidUrl {
273 parameter,
274 value: value.to_owned(),
275 reason: e.to_string(),
276 })
277}
278
279/// Whether `party` is the `did:jwk` or the RFC 7638 SHA-256 thumbprint of `key`.
280fn is_key_identifier(party: &str, key: &Jwk) -> bool {
281 let Ok(thumbprint) = key.thumbprint_sha256() else {
282 return false;
283 };
284 if party == thumbprint {
285 return true;
286 }
287 // A `did:jwk` names the key by value, in whatever member order its creator serialized it;
288 // the thumbprint compares the required members only.
289 Did::parse(party)
290 .ok()
291 .filter(|did| did.method() == "jwk")
292 .and_then(|did| base64url::decode(did.method_specific_id()).ok())
293 .and_then(|json| serde_json::from_slice::<Jwk>(&json).ok())
294 .and_then(|jwk| jwk.thumbprint_sha256().ok())
295 .is_some_and(|named| named == thumbprint)
296}
297
298/// Why a token names no key this crate can look for.
299#[derive(Debug, thiserror::Error)]
300#[non_exhaustive]
301pub enum KeyReferenceError {
302 /// The token names no key: no `kid`, `jwk`, `x5c` or `jku` (or, in `cnf`, no `jwk`, `kid`
303 /// or `jku`).
304 #[error("the token names no key: no `kid`, `jwk`, `x5c` or `jku`")]
305 Missing,
306 /// The `kid` is a `#fragment` and `iss` is not a DID to resolve it against.
307 #[error("`kid` {0:?} is relative and `iss` is not a DID")]
308 RelativeKidWithoutIssuer(String),
309 /// The `kid` is neither a DID URL nor anything else the token resolves it with.
310 #[error("`kid` {kid:?} is not a DID URL: {source}")]
311 InvalidKid {
312 /// The `kid`.
313 kid: String,
314 /// Why it is not a DID URL.
315 #[source]
316 source: DidError,
317 },
318 /// The `kid` carries a path or DID parameters. `versionId` and `versionTime` could select a
319 /// document version that still lists a retired key, so only `did:…#fragment` keys are
320 /// accepted.
321 #[error("`kid` `{0}` carries a path or DID parameters")]
322 KidWithParameters(DidUrl),
323 /// The key by value is not usable: it holds private material, say.
324 #[error("invalid `jwk`: {0}")]
325 InvalidJwk(String),
326 /// The `x5c` header is not an array of base64 certificates.
327 #[error("invalid `x5c`: {0}")]
328 InvalidX5c(String),
329 /// A URL parameter (`jku`) is not an absolute URL.
330 #[error("`{parameter}` {value:?} is not a URL: {reason}")]
331 InvalidUrl {
332 /// The parameter.
333 parameter: &'static str,
334 /// Its value.
335 value: String,
336 /// Why it did not parse.
337 reason: String,
338 },
339 /// The confirmation names its key only by a method the crate does not resolve, such as
340 /// `x5t#S256` or `jwe`.
341 #[error("`cnf` names its key only by `{0}`, which is not supported")]
342 UnsupportedConfirmation(String),
343}
344
345/// The role a key plays in a token, which decides what a DID document must say about it.
346#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Hash)]
347#[non_exhaustive]
348pub enum KeyRole {
349 /// The key signs as the issuer of a credential or status list: `assertionMethod`. The
350 /// default.
351 #[default]
352 Issuer,
353 /// The key proves possession by the subject or holder (a presentation, a key binding JWT,
354 /// an ID token): `authentication`.
355 Subject,
356}
357
358impl KeyRole {
359 /// The verification relationship a DID document must list a key of this role under.
360 pub fn relationship(self) -> VerificationRelationship {
361 match self {
362 Self::Issuer => VerificationRelationship::AssertionMethod,
363 Self::Subject => VerificationRelationship::Authentication,
364 }
365 }
366}
367
368/// Finds the public key a [`KeyReference`] names.
369///
370/// [`Resolver`] implements it for DID URLs, [`JwkResolver`] for keys by value and, with the
371/// `jwks` feature, `JwksResolver` for the key sets of HTTPS issuers;
372/// [`CompositeKeyResolver`] combines them. An application can implement it over a cache, a
373/// fixed set of trusted keys, or a reference the crate does not resolve.
374#[async_trait::async_trait]
375pub trait KeyResolver: Send + Sync {
376 /// The public key `key` names, usable in `role`.
377 ///
378 /// # Errors
379 ///
380 /// [`KeyResolutionError::Unsupported`] for a kind of reference this resolver does not
381 /// handle, which lets a [`CompositeKeyResolver`] try the next one; any other
382 /// [`KeyResolutionError`] when the key cannot be found or used.
383 async fn resolve_key(
384 &self,
385 key: &KeyReference,
386 role: KeyRole,
387 ) -> Result<Jwk, KeyResolutionError>;
388}
389
390macro_rules! forward_key_resolver {
391 ($($ty:ty),* $(,)?) => {$(
392 #[async_trait::async_trait]
393 impl<T: KeyResolver + ?Sized> KeyResolver for $ty {
394 async fn resolve_key(
395 &self,
396 key: &KeyReference,
397 role: KeyRole,
398 ) -> Result<Jwk, KeyResolutionError> {
399 (**self).resolve_key(key, role).await
400 }
401 }
402 )*};
403}
404
405forward_key_resolver!(&T, Box<T>, Arc<T>);
406
407/// Resolves a [`KeyReference::DidUrl`] by dereferencing it to a verification method listed
408/// under the relationship of the role; every other reference is
409/// [`Unsupported`](KeyResolutionError::Unsupported).
410#[async_trait::async_trait]
411impl KeyResolver for Resolver {
412 async fn resolve_key(
413 &self,
414 key: &KeyReference,
415 role: KeyRole,
416 ) -> Result<Jwk, KeyResolutionError> {
417 match key {
418 KeyReference::DidUrl(url) => self
419 .resolve_public_key(url, Some(role.relationship()))
420 .await
421 .map_err(KeyResolutionError::from),
422 other => Err(KeyResolutionError::unsupported(other)),
423 }
424 }
425}
426
427/// Resolves a [`KeyReference::Jwk`] to its public part; every other reference is
428/// [`Unsupported`](KeyResolutionError::Unsupported).
429///
430/// Anybody can put a key in a `jwk` header, so the key says nothing about who signed: a
431/// validator accepts it only when the claimed party is that key's `did:jwk` or thumbprint
432/// ([`KeyReference::binds_to`]). Add it to a [`CompositeKeyResolver`] to accept such tokens.
433#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
434pub struct JwkResolver;
435
436#[async_trait::async_trait]
437impl KeyResolver for JwkResolver {
438 async fn resolve_key(&self, key: &KeyReference, _: KeyRole) -> Result<Jwk, KeyResolutionError> {
439 match key {
440 KeyReference::Jwk(jwk) => Ok(jwk.public()),
441 other => Err(KeyResolutionError::unsupported(other)),
442 }
443 }
444}
445
446/// Resolvers tried in order: the first answer other than
447/// [`Unsupported`](KeyResolutionError::Unsupported) is the answer, an error included.
448///
449/// # Examples
450///
451/// ```
452/// # #[cfg(feature = "did-key")]
453/// # tokio::runtime::Builder::new_current_thread().build()?.block_on(async {
454/// use identity_kit::jose::Jwk;
455/// use identity_kit::methods::DidKey;
456/// use identity_kit::resolver::{CompositeKeyResolver, JwkResolver, KeyReference, KeyResolver as _, KeyRole, Resolver};
457///
458/// let resolver = CompositeKeyResolver::new()
459/// .with(Resolver::builder().with(DidKey::default()).build())
460/// .with(JwkResolver);
461///
462/// // RFC 8037 Appendix A.2.
463/// let key: Jwk = serde_json::from_str(
464/// r#"{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#,
465/// )?;
466/// let resolved = resolver.resolve_key(&KeyReference::Jwk(Box::new(key.clone())), KeyRole::Issuer).await?;
467/// assert_eq!(resolved, key);
468/// # Ok::<(), Box<dyn std::error::Error>>(())
469/// # })?;
470/// # Ok::<(), Box<dyn std::error::Error>>(())
471/// ```
472#[derive(Clone, Default)]
473pub struct CompositeKeyResolver {
474 resolvers: Vec<Arc<dyn KeyResolver>>,
475}
476
477impl CompositeKeyResolver {
478 /// A resolver with nothing to try: every reference is unsupported.
479 pub fn new() -> Self {
480 Self::default()
481 }
482
483 /// This resolver trying `resolver` after the ones added before.
484 #[must_use]
485 pub fn with(mut self, resolver: impl KeyResolver + 'static) -> Self {
486 self.resolvers.push(Arc::new(resolver));
487 self
488 }
489
490 /// This resolver trying the shared `resolver` after the ones added before.
491 #[must_use]
492 pub fn with_shared(mut self, resolver: Arc<dyn KeyResolver>) -> Self {
493 self.resolvers.push(resolver);
494 self
495 }
496
497 /// How many resolvers it tries.
498 pub fn len(&self) -> usize {
499 self.resolvers.len()
500 }
501
502 /// Whether it has no resolver to try.
503 pub fn is_empty(&self) -> bool {
504 self.resolvers.is_empty()
505 }
506}
507
508impl fmt::Debug for CompositeKeyResolver {
509 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510 f.debug_struct("CompositeKeyResolver")
511 .field("resolvers", &self.resolvers.len())
512 .finish()
513 }
514}
515
516#[async_trait::async_trait]
517impl KeyResolver for CompositeKeyResolver {
518 async fn resolve_key(
519 &self,
520 key: &KeyReference,
521 role: KeyRole,
522 ) -> Result<Jwk, KeyResolutionError> {
523 for resolver in &self.resolvers {
524 match resolver.resolve_key(key, role).await {
525 Err(KeyResolutionError::Unsupported(_)) => {}
526 answer => return answer,
527 }
528 }
529 Err(KeyResolutionError::unsupported(key))
530 }
531}
532
533/// Why a [`KeyResolver`] found no key.
534#[derive(Debug)]
535#[non_exhaustive]
536pub enum KeyResolutionError {
537 /// The resolver does not handle this kind of reference.
538 Unsupported(Box<KeyReference>),
539 /// The resolver handles the reference and could not find or use the key.
540 Failed(Box<dyn Error + Send + Sync + 'static>),
541}
542
543impl KeyResolutionError {
544 /// The resolver does not handle `key`.
545 pub fn unsupported(key: &KeyReference) -> Self {
546 Self::Unsupported(Box::new(key.clone()))
547 }
548
549 /// A failure caused by `error`.
550 pub fn new<E: Error + Send + Sync + 'static>(error: E) -> Self {
551 Self::Failed(Box::new(error))
552 }
553
554 /// A failure described by `message` only.
555 pub fn msg<M: fmt::Display>(message: M) -> Self {
556 Self::Failed(message.to_string().into())
557 }
558
559 /// Whether the resolver does not handle the reference.
560 pub fn is_unsupported(&self) -> bool {
561 matches!(self, Self::Unsupported(_))
562 }
563
564 /// The error that caused a failure.
565 pub fn inner(&self) -> Option<&(dyn Error + Send + Sync + 'static)> {
566 match self {
567 Self::Failed(error) => Some(&**error),
568 Self::Unsupported(_) => None,
569 }
570 }
571}
572
573impl fmt::Display for KeyResolutionError {
574 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575 match self {
576 Self::Unsupported(reference) => write!(f, "no resolver handles {reference}"),
577 Self::Failed(error) => fmt::Display::fmt(error, f),
578 }
579 }
580}
581
582impl Error for KeyResolutionError {
583 fn source(&self) -> Option<&(dyn Error + 'static)> {
584 match self {
585 Self::Failed(error) => error.source(),
586 Self::Unsupported(_) => None,
587 }
588 }
589}
590
591impl From<DereferenceError> for KeyResolutionError {
592 fn from(error: DereferenceError) -> Self {
593 Self::new(error)
594 }
595}