Skip to main content

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, PartialEq, Eq, Hash)]
347#[non_exhaustive]
348pub enum KeyRole {
349    /// The key signs as the issuer of a credential or status list: `assertionMethod`.
350    Issuer,
351    /// The key proves possession by the subject or holder (a presentation, a key binding JWT,
352    /// an ID token): `authentication`.
353    Subject,
354}
355
356impl KeyRole {
357    /// The verification relationship a DID document must list a key of this role under.
358    pub fn relationship(self) -> VerificationRelationship {
359        match self {
360            Self::Issuer => VerificationRelationship::AssertionMethod,
361            Self::Subject => VerificationRelationship::Authentication,
362        }
363    }
364}
365
366/// Finds the public key a [`KeyReference`] names.
367///
368/// [`Resolver`] implements it for DID URLs, [`JwkResolver`] for keys by value and, with the
369/// `jwks` feature, `JwksResolver` for the key sets of HTTPS issuers;
370/// [`CompositeKeyResolver`] combines them. An application can implement it over a cache, a
371/// fixed set of trusted keys, or a reference the crate does not resolve.
372#[async_trait::async_trait]
373pub trait KeyResolver: Send + Sync {
374    /// The public key `key` names, usable in `role`.
375    ///
376    /// # Errors
377    ///
378    /// [`KeyResolutionError::Unsupported`] for a kind of reference this resolver does not
379    /// handle, which lets a [`CompositeKeyResolver`] try the next one; any other
380    /// [`KeyResolutionError`] when the key cannot be found or used.
381    async fn resolve_key(
382        &self,
383        key: &KeyReference,
384        role: KeyRole,
385    ) -> Result<Jwk, KeyResolutionError>;
386}
387
388macro_rules! forward_key_resolver {
389    ($($ty:ty),* $(,)?) => {$(
390        #[async_trait::async_trait]
391        impl<T: KeyResolver + ?Sized> KeyResolver for $ty {
392            async fn resolve_key(
393                &self,
394                key: &KeyReference,
395                role: KeyRole,
396            ) -> Result<Jwk, KeyResolutionError> {
397                (**self).resolve_key(key, role).await
398            }
399        }
400    )*};
401}
402
403forward_key_resolver!(&T, Box<T>, Arc<T>);
404
405/// Resolves a [`KeyReference::DidUrl`] by dereferencing it to a verification method listed
406/// under the relationship of the role; every other reference is
407/// [`Unsupported`](KeyResolutionError::Unsupported).
408#[async_trait::async_trait]
409impl KeyResolver for Resolver {
410    async fn resolve_key(
411        &self,
412        key: &KeyReference,
413        role: KeyRole,
414    ) -> Result<Jwk, KeyResolutionError> {
415        match key {
416            KeyReference::DidUrl(url) => self
417                .resolve_public_key(url, Some(role.relationship()))
418                .await
419                .map_err(KeyResolutionError::from),
420            other => Err(KeyResolutionError::unsupported(other)),
421        }
422    }
423}
424
425/// Resolves a [`KeyReference::Jwk`] to its public part; every other reference is
426/// [`Unsupported`](KeyResolutionError::Unsupported).
427///
428/// Anybody can put a key in a `jwk` header, so the key says nothing about who signed: a
429/// validator accepts it only when the claimed party is that key's `did:jwk` or thumbprint
430/// ([`KeyReference::binds_to`]). Add it to a [`CompositeKeyResolver`] to accept such tokens.
431#[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
432pub struct JwkResolver;
433
434#[async_trait::async_trait]
435impl KeyResolver for JwkResolver {
436    async fn resolve_key(&self, key: &KeyReference, _: KeyRole) -> Result<Jwk, KeyResolutionError> {
437        match key {
438            KeyReference::Jwk(jwk) => Ok(jwk.public()),
439            other => Err(KeyResolutionError::unsupported(other)),
440        }
441    }
442}
443
444/// Resolvers tried in order: the first answer other than
445/// [`Unsupported`](KeyResolutionError::Unsupported) is the answer, an error included.
446///
447/// # Examples
448///
449/// ```
450/// # #[cfg(feature = "did-key")]
451/// # tokio::runtime::Builder::new_current_thread().build()?.block_on(async {
452/// use identity_kit::jose::Jwk;
453/// use identity_kit::methods::DidKey;
454/// use identity_kit::resolver::{CompositeKeyResolver, JwkResolver, KeyReference, KeyResolver as _, KeyRole, Resolver};
455///
456/// let resolver = CompositeKeyResolver::new()
457///     .with(Resolver::builder().with(DidKey::default()).build())
458///     .with(JwkResolver);
459///
460/// // RFC 8037 Appendix A.2.
461/// let key: Jwk = serde_json::from_str(
462///     r#"{"kty":"OKP","crv":"Ed25519","x":"11qYAYKxCrfVS_7TyWQHOg7hcvPapiMlrwIaaPcHURo"}"#,
463/// )?;
464/// let resolved = resolver.resolve_key(&KeyReference::Jwk(Box::new(key.clone())), KeyRole::Issuer).await?;
465/// assert_eq!(resolved, key);
466/// # Ok::<(), Box<dyn std::error::Error>>(())
467/// # })?;
468/// # Ok::<(), Box<dyn std::error::Error>>(())
469/// ```
470#[derive(Clone, Default)]
471pub struct CompositeKeyResolver {
472    resolvers: Vec<Arc<dyn KeyResolver>>,
473}
474
475impl CompositeKeyResolver {
476    /// A resolver with nothing to try: every reference is unsupported.
477    pub fn new() -> Self {
478        Self::default()
479    }
480
481    /// This resolver trying `resolver` after the ones added before.
482    #[must_use]
483    pub fn with(mut self, resolver: impl KeyResolver + 'static) -> Self {
484        self.resolvers.push(Arc::new(resolver));
485        self
486    }
487
488    /// This resolver trying the shared `resolver` after the ones added before.
489    #[must_use]
490    pub fn with_shared(mut self, resolver: Arc<dyn KeyResolver>) -> Self {
491        self.resolvers.push(resolver);
492        self
493    }
494
495    /// How many resolvers it tries.
496    pub fn len(&self) -> usize {
497        self.resolvers.len()
498    }
499
500    /// Whether it has no resolver to try.
501    pub fn is_empty(&self) -> bool {
502        self.resolvers.is_empty()
503    }
504}
505
506impl fmt::Debug for CompositeKeyResolver {
507    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
508        f.debug_struct("CompositeKeyResolver")
509            .field("resolvers", &self.resolvers.len())
510            .finish()
511    }
512}
513
514#[async_trait::async_trait]
515impl KeyResolver for CompositeKeyResolver {
516    async fn resolve_key(
517        &self,
518        key: &KeyReference,
519        role: KeyRole,
520    ) -> Result<Jwk, KeyResolutionError> {
521        for resolver in &self.resolvers {
522            match resolver.resolve_key(key, role).await {
523                Err(KeyResolutionError::Unsupported(_)) => {}
524                answer => return answer,
525            }
526        }
527        Err(KeyResolutionError::unsupported(key))
528    }
529}
530
531/// Why a [`KeyResolver`] found no key.
532#[derive(Debug)]
533#[non_exhaustive]
534pub enum KeyResolutionError {
535    /// The resolver does not handle this kind of reference.
536    Unsupported(Box<KeyReference>),
537    /// The resolver handles the reference and could not find or use the key.
538    Failed(Box<dyn Error + Send + Sync + 'static>),
539}
540
541impl KeyResolutionError {
542    /// The resolver does not handle `key`.
543    pub fn unsupported(key: &KeyReference) -> Self {
544        Self::Unsupported(Box::new(key.clone()))
545    }
546
547    /// A failure caused by `error`.
548    pub fn new<E: Error + Send + Sync + 'static>(error: E) -> Self {
549        Self::Failed(Box::new(error))
550    }
551
552    /// A failure described by `message` only.
553    pub fn msg<M: fmt::Display>(message: M) -> Self {
554        Self::Failed(message.to_string().into())
555    }
556
557    /// Whether the resolver does not handle the reference.
558    pub fn is_unsupported(&self) -> bool {
559        matches!(self, Self::Unsupported(_))
560    }
561
562    /// The error that caused a failure.
563    pub fn inner(&self) -> Option<&(dyn Error + Send + Sync + 'static)> {
564        match self {
565            Self::Failed(error) => Some(&**error),
566            Self::Unsupported(_) => None,
567        }
568    }
569}
570
571impl fmt::Display for KeyResolutionError {
572    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
573        match self {
574            Self::Unsupported(reference) => write!(f, "no resolver handles {reference}"),
575            Self::Failed(error) => fmt::Display::fmt(error, f),
576        }
577    }
578}
579
580impl Error for KeyResolutionError {
581    fn source(&self) -> Option<&(dyn Error + 'static)> {
582        match self {
583            Self::Failed(error) => error.source(),
584            Self::Unsupported(_) => None,
585        }
586    }
587}
588
589impl From<DereferenceError> for KeyResolutionError {
590    fn from(error: DereferenceError) -> Self {
591        Self::new(error)
592    }
593}