ed25519_dalek/
verifying.rs

1// -*- mode: rust; -*-
2//
3// This file is part of ed25519-dalek.
4// Copyright (c) 2017-2019 isis lovecruft
5// See LICENSE for licensing information.
6//
7// Authors:
8// - isis agora lovecruft <isis@patternsinthevoid.net>
9
10//! ed25519 public keys.
11
12use core::convert::TryFrom;
13use core::fmt::Debug;
14use core::hash::{Hash, Hasher};
15
16use curve25519_dalek::{
17    digest::{generic_array::typenum::U64, Digest},
18    edwards::{CompressedEdwardsY, EdwardsPoint},
19    montgomery::MontgomeryPoint,
20    scalar::Scalar,
21};
22
23use ed25519::signature::Verifier;
24
25use sha2::Sha512;
26
27#[cfg(feature = "pkcs8")]
28use ed25519::pkcs8;
29
30#[cfg(feature = "serde")]
31use serde::{Deserialize, Deserializer, Serialize, Serializer};
32
33#[cfg(feature = "digest")]
34use crate::context::Context;
35#[cfg(feature = "digest")]
36use signature::DigestVerifier;
37
38use crate::{
39    constants::PUBLIC_KEY_LENGTH,
40    errors::{InternalError, SignatureError},
41    hazmat::ExpandedSecretKey,
42    signature::InternalSignature,
43    signing::SigningKey,
44};
45
46/// An ed25519 public key.
47///
48/// # Note
49///
50/// The `Eq` and `Hash` impls here use the compressed Edwards y encoding, _not_ the algebraic
51/// representation. This means if this `VerifyingKey` is non-canonically encoded, it will be
52/// considered unequal to the other equivalent encoding, despite the two representing the same
53/// point. More encoding details can be found
54/// [here](https://hdevalence.ca/blog/2020-10-04-its-25519am).
55/// If you want to make sure that signatures produced with respect to those sorts of public keys
56/// are rejected, use [`VerifyingKey::verify_strict`].
57// Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
58#[derive(Copy, Clone, Default, Eq)]
59pub struct VerifyingKey {
60    /// Serialized compressed Edwards-y point.
61    pub(crate) compressed: CompressedEdwardsY,
62
63    /// Decompressed Edwards point used for curve arithmetic operations.
64    pub(crate) point: EdwardsPoint,
65}
66
67impl Debug for VerifyingKey {
68    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
69        write!(f, "VerifyingKey({:?}), {:?})", self.compressed, self.point)
70    }
71}
72
73impl AsRef<[u8]> for VerifyingKey {
74    fn as_ref(&self) -> &[u8] {
75        self.as_bytes()
76    }
77}
78
79impl Hash for VerifyingKey {
80    fn hash<H: Hasher>(&self, state: &mut H) {
81        self.as_bytes().hash(state);
82    }
83}
84
85impl PartialEq<VerifyingKey> for VerifyingKey {
86    fn eq(&self, other: &VerifyingKey) -> bool {
87        self.as_bytes() == other.as_bytes()
88    }
89}
90
91impl From<&ExpandedSecretKey> for VerifyingKey {
92    /// Derive this public key from its corresponding `ExpandedSecretKey`.
93    fn from(expanded_secret_key: &ExpandedSecretKey) -> VerifyingKey {
94        VerifyingKey::from(EdwardsPoint::mul_base(&expanded_secret_key.scalar))
95    }
96}
97
98impl From<&SigningKey> for VerifyingKey {
99    fn from(signing_key: &SigningKey) -> VerifyingKey {
100        signing_key.verifying_key()
101    }
102}
103
104impl From<EdwardsPoint> for VerifyingKey {
105    fn from(point: EdwardsPoint) -> VerifyingKey {
106        VerifyingKey {
107            point,
108            compressed: point.compress(),
109        }
110    }
111}
112
113impl VerifyingKey {
114    /// Convert this public key to a byte array.
115    #[inline]
116    pub fn to_bytes(&self) -> [u8; PUBLIC_KEY_LENGTH] {
117        self.compressed.to_bytes()
118    }
119
120    /// View this public key as a byte array.
121    #[inline]
122    pub fn as_bytes(&self) -> &[u8; PUBLIC_KEY_LENGTH] {
123        &(self.compressed).0
124    }
125
126    /// Construct a `VerifyingKey` from a slice of bytes.
127    ///
128    /// # Warning
129    ///
130    /// The caller is responsible for ensuring that the bytes passed into this
131    /// method actually represent a `curve25519_dalek::curve::CompressedEdwardsY`
132    /// and that said compressed point is actually a point on the curve.
133    ///
134    /// # Example
135    ///
136    /// ```
137    /// use ed25519_dalek::VerifyingKey;
138    /// use ed25519_dalek::PUBLIC_KEY_LENGTH;
139    /// use ed25519_dalek::SignatureError;
140    ///
141    /// # fn doctest() -> Result<VerifyingKey, SignatureError> {
142    /// let public_key_bytes: [u8; PUBLIC_KEY_LENGTH] = [
143    ///    215,  90, 152,   1, 130, 177,  10, 183, 213,  75, 254, 211, 201, 100,   7,  58,
144    ///     14, 225, 114, 243, 218, 166,  35,  37, 175,   2,  26, 104, 247,   7,   81, 26];
145    ///
146    /// let public_key = VerifyingKey::from_bytes(&public_key_bytes)?;
147    /// #
148    /// # Ok(public_key)
149    /// # }
150    /// #
151    /// # fn main() {
152    /// #     doctest();
153    /// # }
154    /// ```
155    ///
156    /// # Returns
157    ///
158    /// A `Result` whose okay value is an EdDSA `VerifyingKey` or whose error value
159    /// is a `SignatureError` describing the error that occurred.
160    #[inline]
161    pub fn from_bytes(bytes: &[u8; PUBLIC_KEY_LENGTH]) -> Result<VerifyingKey, SignatureError> {
162        let compressed = CompressedEdwardsY(*bytes);
163        let point = compressed
164            .decompress()
165            .ok_or(InternalError::PointDecompression)?;
166
167        // Invariant: VerifyingKey.1 is always the decompression of VerifyingKey.0
168        Ok(VerifyingKey { compressed, point })
169    }
170
171    /// Create a verifying context that can be used for Ed25519ph with
172    /// [`DigestVerifier`].
173    #[cfg(feature = "digest")]
174    pub fn with_context<'k, 'v>(
175        &'k self,
176        context_value: &'v [u8],
177    ) -> Result<Context<'k, 'v, Self>, SignatureError> {
178        Context::new(self, context_value)
179    }
180
181    /// Returns whether this is a _weak_ public key, i.e., if this public key has low order.
182    ///
183    /// A weak public key can be used to generate a signature that's valid for almost every
184    /// message. [`Self::verify_strict`] denies weak keys, but if you want to check for this
185    /// property before verification, then use this method.
186    pub fn is_weak(&self) -> bool {
187        self.point.is_small_order()
188    }
189
190    // A helper function that computes `H(R || A || M)` where `H` is the 512-bit hash function
191    // given by `CtxDigest` (this is SHA-512 in spec-compliant Ed25519). If `context.is_some()`,
192    // this does the prehashed variant of the computation using its contents.
193    #[allow(non_snake_case)]
194    fn compute_challenge<CtxDigest>(
195        context: Option<&[u8]>,
196        R: &CompressedEdwardsY,
197        A: &CompressedEdwardsY,
198        M: &[u8],
199    ) -> Scalar
200    where
201        CtxDigest: Digest<OutputSize = U64>,
202    {
203        let mut h = CtxDigest::new();
204        if let Some(c) = context {
205            h.update(b"SigEd25519 no Ed25519 collisions");
206            h.update([1]); // Ed25519ph
207            h.update([c.len() as u8]);
208            h.update(c);
209        }
210        h.update(R.as_bytes());
211        h.update(A.as_bytes());
212        h.update(M);
213
214        Scalar::from_hash(h)
215    }
216
217    // Helper function for verification. Computes the _expected_ R component of the signature. The
218    // caller compares this to the real R component.  If `context.is_some()`, this does the
219    // prehashed variant of the computation using its contents.
220    // Note that this returns the compressed form of R and the caller does a byte comparison. This
221    // means that all our verification functions do not accept non-canonically encoded R values.
222    // See the validation criteria blog post for more details:
223    //     https://hdevalence.ca/blog/2020-10-04-its-25519am
224    #[allow(non_snake_case)]
225    fn recompute_R<CtxDigest>(
226        &self,
227        context: Option<&[u8]>,
228        signature: &InternalSignature,
229        M: &[u8],
230    ) -> CompressedEdwardsY
231    where
232        CtxDigest: Digest<OutputSize = U64>,
233    {
234        let k = Self::compute_challenge::<CtxDigest>(context, &signature.R, &self.compressed, M);
235        let minus_A: EdwardsPoint = -self.point;
236        // Recall the (non-batched) verification equation: -[k]A + [s]B = R
237        EdwardsPoint::vartime_double_scalar_mul_basepoint(&k, &(minus_A), &signature.s).compress()
238    }
239
240    /// The ordinary non-batched Ed25519 verification check, rejecting non-canonical R values. (see
241    /// [`Self::recompute_R`]). `CtxDigest` is the digest used to calculate the pseudorandomness
242    /// needed for signing. According to the spec, `CtxDigest = Sha512`.
243    ///
244    /// This definition is loose in its parameters so that end-users of the `hazmat` module can
245    /// change how the `ExpandedSecretKey` is calculated and which hash function to use.
246    #[allow(non_snake_case)]
247    pub(crate) fn raw_verify<CtxDigest>(
248        &self,
249        message: &[u8],
250        signature: &ed25519::Signature,
251    ) -> Result<(), SignatureError>
252    where
253        CtxDigest: Digest<OutputSize = U64>,
254    {
255        let signature = InternalSignature::try_from(signature)?;
256
257        let expected_R = self.recompute_R::<CtxDigest>(None, &signature, message);
258        if expected_R == signature.R {
259            Ok(())
260        } else {
261            Err(InternalError::Verify.into())
262        }
263    }
264
265    /// The prehashed non-batched Ed25519 verification check, rejecting non-canonical R values.
266    /// (see [`Self::recompute_R`]). `CtxDigest` is the digest used to calculate the
267    /// pseudorandomness needed for signing. `MsgDigest` is the digest used to hash the signed
268    /// message. According to the spec, `MsgDigest = CtxDigest = Sha512`.
269    ///
270    /// This definition is loose in its parameters so that end-users of the `hazmat` module can
271    /// change how the `ExpandedSecretKey` is calculated and which hash function to use.
272    #[cfg(feature = "digest")]
273    #[allow(non_snake_case)]
274    pub(crate) fn raw_verify_prehashed<CtxDigest, MsgDigest>(
275        &self,
276        prehashed_message: MsgDigest,
277        context: Option<&[u8]>,
278        signature: &ed25519::Signature,
279    ) -> Result<(), SignatureError>
280    where
281        CtxDigest: Digest<OutputSize = U64>,
282        MsgDigest: Digest<OutputSize = U64>,
283    {
284        let signature = InternalSignature::try_from(signature)?;
285
286        let ctx: &[u8] = context.unwrap_or(b"");
287        debug_assert!(
288            ctx.len() <= 255,
289            "The context must not be longer than 255 octets."
290        );
291
292        let message = prehashed_message.finalize();
293        let expected_R = self.recompute_R::<CtxDigest>(Some(ctx), &signature, &message);
294
295        if expected_R == signature.R {
296            Ok(())
297        } else {
298            Err(InternalError::Verify.into())
299        }
300    }
301
302    /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm.
303    ///
304    /// # Inputs
305    ///
306    /// * `prehashed_message` is an instantiated hash digest with 512-bits of
307    ///   output which has had the message to be signed previously fed into its
308    ///   state.
309    /// * `context` is an optional context string, up to 255 bytes inclusive,
310    ///   which may be used to provide additional domain separation.  If not
311    ///   set, this will default to an empty string.
312    /// * `signature` is a purported Ed25519ph signature on the `prehashed_message`.
313    ///
314    /// # Returns
315    ///
316    /// Returns `true` if the `signature` was a valid signature created by this
317    /// [`SigningKey`] on the `prehashed_message`.
318    ///
319    /// # Note
320    ///
321    /// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
322    /// function technically works, and is probably safe to use, with any secure hash function with
323    /// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
324    /// [`crate::Sha512`] for user convenience.
325    #[cfg(feature = "digest")]
326    #[allow(non_snake_case)]
327    pub fn verify_prehashed<MsgDigest>(
328        &self,
329        prehashed_message: MsgDigest,
330        context: Option<&[u8]>,
331        signature: &ed25519::Signature,
332    ) -> Result<(), SignatureError>
333    where
334        MsgDigest: Digest<OutputSize = U64>,
335    {
336        self.raw_verify_prehashed::<Sha512, MsgDigest>(prehashed_message, context, signature)
337    }
338
339    /// Strictly verify a signature on a message with this keypair's public key.
340    ///
341    /// # On The (Multiple) Sources of Malleability in Ed25519 Signatures
342    ///
343    /// This version of verification is technically non-RFC8032 compliant.  The
344    /// following explains why.
345    ///
346    /// 1. Scalar Malleability
347    ///
348    /// The authors of the RFC explicitly stated that verification of an ed25519
349    /// signature must fail if the scalar `s` is not properly reduced mod $\ell$:
350    ///
351    /// > To verify a signature on a message M using public key A, with F
352    /// > being 0 for Ed25519ctx, 1 for Ed25519ph, and if Ed25519ctx or
353    /// > Ed25519ph is being used, C being the context, first split the
354    /// > signature into two 32-octet halves.  Decode the first half as a
355    /// > point R, and the second half as an integer S, in the range
356    /// > 0 <= s < L.  Decode the public key A as point A'.  If any of the
357    /// > decodings fail (including S being out of range), the signature is
358    /// > invalid.)
359    ///
360    /// All `verify_*()` functions within ed25519-dalek perform this check.
361    ///
362    /// 2. Point malleability
363    ///
364    /// The authors of the RFC added in a malleability check to step #3 in
365    /// ยง5.1.7, for small torsion components in the `R` value of the signature,
366    /// *which is not strictly required*, as they state:
367    ///
368    /// > Check the group equation \[8\]\[S\]B = \[8\]R + \[8\]\[k\]A'.  It's
369    /// > sufficient, but not required, to instead check \[S\]B = R + \[k\]A'.
370    ///
371    /// # History of Malleability Checks
372    ///
373    /// As originally defined (cf. the "Malleability" section in the README of
374    /// this repo), ed25519 signatures didn't consider *any* form of
375    /// malleability to be an issue.  Later the scalar malleability was
376    /// considered important.  Still later, particularly with interests in
377    /// cryptocurrency design and in unique identities (e.g. for Signal users,
378    /// Tor onion services, etc.), the group element malleability became a
379    /// concern.
380    ///
381    /// However, libraries had already been created to conform to the original
382    /// definition.  One well-used library in particular even implemented the
383    /// group element malleability check, *but only for batch verification*!
384    /// Which meant that even using the same library, a single signature could
385    /// verify fine individually, but suddenly, when verifying it with a bunch
386    /// of other signatures, the whole batch would fail!
387    ///
388    /// # "Strict" Verification
389    ///
390    /// This method performs *both* of the above signature malleability checks.
391    ///
392    /// It must be done as a separate method because one doesn't simply get to
393    /// change the definition of a cryptographic primitive ten years
394    /// after-the-fact with zero consideration for backwards compatibility in
395    /// hardware and protocols which have it already have the older definition
396    /// baked in.
397    ///
398    /// # Return
399    ///
400    /// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
401    #[allow(non_snake_case)]
402    pub fn verify_strict(
403        &self,
404        message: &[u8],
405        signature: &ed25519::Signature,
406    ) -> Result<(), SignatureError> {
407        let signature = InternalSignature::try_from(signature)?;
408
409        let signature_R = signature
410            .R
411            .decompress()
412            .ok_or_else(|| SignatureError::from(InternalError::Verify))?;
413
414        // Logical OR is fine here as we're not trying to be constant time.
415        if signature_R.is_small_order() || self.point.is_small_order() {
416            return Err(InternalError::Verify.into());
417        }
418
419        let expected_R = self.recompute_R::<Sha512>(None, &signature, message);
420        if expected_R == signature.R {
421            Ok(())
422        } else {
423            Err(InternalError::Verify.into())
424        }
425    }
426
427    /// Verify a `signature` on a `prehashed_message` using the Ed25519ph algorithm,
428    /// using strict signture checking as defined by [`Self::verify_strict`].
429    ///
430    /// # Inputs
431    ///
432    /// * `prehashed_message` is an instantiated hash digest with 512-bits of
433    ///   output which has had the message to be signed previously fed into its
434    ///   state.
435    /// * `context` is an optional context string, up to 255 bytes inclusive,
436    ///   which may be used to provide additional domain separation.  If not
437    ///   set, this will default to an empty string.
438    /// * `signature` is a purported Ed25519ph signature on the `prehashed_message`.
439    ///
440    /// # Returns
441    ///
442    /// Returns `true` if the `signature` was a valid signature created by this
443    /// [`SigningKey`] on the `prehashed_message`.
444    ///
445    /// # Note
446    ///
447    /// The RFC only permits SHA-512 to be used for prehashing, i.e., `MsgDigest = Sha512`. This
448    /// function technically works, and is probably safe to use, with any secure hash function with
449    /// 512-bit digests, but anything outside of SHA-512 is NOT specification-compliant. We expose
450    /// [`crate::Sha512`] for user convenience.
451    #[cfg(feature = "digest")]
452    #[allow(non_snake_case)]
453    pub fn verify_prehashed_strict<MsgDigest>(
454        &self,
455        prehashed_message: MsgDigest,
456        context: Option<&[u8]>,
457        signature: &ed25519::Signature,
458    ) -> Result<(), SignatureError>
459    where
460        MsgDigest: Digest<OutputSize = U64>,
461    {
462        let signature = InternalSignature::try_from(signature)?;
463
464        let ctx: &[u8] = context.unwrap_or(b"");
465        debug_assert!(
466            ctx.len() <= 255,
467            "The context must not be longer than 255 octets."
468        );
469
470        let signature_R = signature
471            .R
472            .decompress()
473            .ok_or_else(|| SignatureError::from(InternalError::Verify))?;
474
475        // Logical OR is fine here as we're not trying to be constant time.
476        if signature_R.is_small_order() || self.point.is_small_order() {
477            return Err(InternalError::Verify.into());
478        }
479
480        let message = prehashed_message.finalize();
481        let expected_R = self.recompute_R::<Sha512>(Some(ctx), &signature, &message);
482
483        if expected_R == signature.R {
484            Ok(())
485        } else {
486            Err(InternalError::Verify.into())
487        }
488    }
489
490    /// Convert this verifying key into Montgomery form.
491    ///
492    /// This can be used for performing X25519 Diffie-Hellman using Ed25519 keys. The output of
493    /// this function is a valid X25519 public key whose secret key is `sk.to_scalar_bytes()`,
494    /// where `sk` is a valid signing key for this `VerifyingKey`.
495    ///
496    /// # Note
497    ///
498    /// We do NOT recommend this usage of a signing/verifying key. Signing keys are usually
499    /// long-term keys, while keys used for key exchange should rather be ephemeral. If you can
500    /// help it, use a separate key for encryption.
501    ///
502    /// For more information on the security of systems which use the same keys for both signing
503    /// and Diffie-Hellman, see the paper
504    /// [On using the same key pair for Ed25519 and an X25519 based KEM](https://eprint.iacr.org/2021/509).
505    pub fn to_montgomery(&self) -> MontgomeryPoint {
506        self.point.to_montgomery()
507    }
508}
509
510impl Verifier<ed25519::Signature> for VerifyingKey {
511    /// Verify a signature on a message with this keypair's public key.
512    ///
513    /// # Return
514    ///
515    /// Returns `Ok(())` if the signature is valid, and `Err` otherwise.
516    fn verify(&self, message: &[u8], signature: &ed25519::Signature) -> Result<(), SignatureError> {
517        self.raw_verify::<Sha512>(message, signature)
518    }
519}
520
521/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`None`].
522#[cfg(feature = "digest")]
523impl<MsgDigest> DigestVerifier<MsgDigest, ed25519::Signature> for VerifyingKey
524where
525    MsgDigest: Digest<OutputSize = U64>,
526{
527    fn verify_digest(
528        &self,
529        msg_digest: MsgDigest,
530        signature: &ed25519::Signature,
531    ) -> Result<(), SignatureError> {
532        self.verify_prehashed(msg_digest, None, signature)
533    }
534}
535
536/// Equivalent to [`VerifyingKey::verify_prehashed`] with `context` set to [`Some`]
537/// containing `self.value()`.
538#[cfg(feature = "digest")]
539impl<MsgDigest> DigestVerifier<MsgDigest, ed25519::Signature> for Context<'_, '_, VerifyingKey>
540where
541    MsgDigest: Digest<OutputSize = U64>,
542{
543    fn verify_digest(
544        &self,
545        msg_digest: MsgDigest,
546        signature: &ed25519::Signature,
547    ) -> Result<(), SignatureError> {
548        self.key()
549            .verify_prehashed(msg_digest, Some(self.value()), signature)
550    }
551}
552
553impl TryFrom<&[u8]> for VerifyingKey {
554    type Error = SignatureError;
555
556    #[inline]
557    fn try_from(bytes: &[u8]) -> Result<Self, Self::Error> {
558        let bytes = bytes.try_into().map_err(|_| InternalError::BytesLength {
559            name: "VerifyingKey",
560            length: PUBLIC_KEY_LENGTH,
561        })?;
562        Self::from_bytes(bytes)
563    }
564}
565
566#[cfg(all(feature = "alloc", feature = "pkcs8"))]
567impl pkcs8::EncodePublicKey for VerifyingKey {
568    fn to_public_key_der(&self) -> pkcs8::spki::Result<pkcs8::Document> {
569        pkcs8::PublicKeyBytes::from(self).to_public_key_der()
570    }
571}
572
573#[cfg(feature = "pkcs8")]
574impl TryFrom<pkcs8::PublicKeyBytes> for VerifyingKey {
575    type Error = pkcs8::spki::Error;
576
577    fn try_from(pkcs8_key: pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
578        VerifyingKey::try_from(&pkcs8_key)
579    }
580}
581
582#[cfg(feature = "pkcs8")]
583impl TryFrom<&pkcs8::PublicKeyBytes> for VerifyingKey {
584    type Error = pkcs8::spki::Error;
585
586    fn try_from(pkcs8_key: &pkcs8::PublicKeyBytes) -> pkcs8::spki::Result<Self> {
587        VerifyingKey::from_bytes(pkcs8_key.as_ref()).map_err(|_| pkcs8::spki::Error::KeyMalformed)
588    }
589}
590
591#[cfg(feature = "pkcs8")]
592impl From<VerifyingKey> for pkcs8::PublicKeyBytes {
593    fn from(verifying_key: VerifyingKey) -> pkcs8::PublicKeyBytes {
594        pkcs8::PublicKeyBytes::from(&verifying_key)
595    }
596}
597
598#[cfg(feature = "pkcs8")]
599impl From<&VerifyingKey> for pkcs8::PublicKeyBytes {
600    fn from(verifying_key: &VerifyingKey) -> pkcs8::PublicKeyBytes {
601        pkcs8::PublicKeyBytes(verifying_key.to_bytes())
602    }
603}
604
605#[cfg(feature = "pkcs8")]
606impl TryFrom<pkcs8::spki::SubjectPublicKeyInfoRef<'_>> for VerifyingKey {
607    type Error = pkcs8::spki::Error;
608
609    fn try_from(public_key: pkcs8::spki::SubjectPublicKeyInfoRef<'_>) -> pkcs8::spki::Result<Self> {
610        pkcs8::PublicKeyBytes::try_from(public_key)?.try_into()
611    }
612}
613
614#[cfg(feature = "serde")]
615impl Serialize for VerifyingKey {
616    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
617    where
618        S: Serializer,
619    {
620        serializer.serialize_bytes(&self.as_bytes()[..])
621    }
622}
623
624#[cfg(feature = "serde")]
625impl<'d> Deserialize<'d> for VerifyingKey {
626    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
627    where
628        D: Deserializer<'d>,
629    {
630        struct VerifyingKeyVisitor;
631
632        impl<'de> serde::de::Visitor<'de> for VerifyingKeyVisitor {
633            type Value = VerifyingKey;
634
635            fn expecting(&self, formatter: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result {
636                write!(formatter, concat!("An ed25519 verifying (public) key"))
637            }
638
639            fn visit_bytes<E: serde::de::Error>(self, bytes: &[u8]) -> Result<Self::Value, E> {
640                VerifyingKey::try_from(bytes).map_err(E::custom)
641            }
642
643            fn visit_seq<A>(self, mut seq: A) -> Result<Self::Value, A::Error>
644            where
645                A: serde::de::SeqAccess<'de>,
646            {
647                let mut bytes = [0u8; 32];
648
649                #[allow(clippy::needless_range_loop)]
650                for i in 0..32 {
651                    bytes[i] = seq
652                        .next_element()?
653                        .ok_or_else(|| serde::de::Error::invalid_length(i, &"expected 32 bytes"))?;
654                }
655
656                let remaining = (0..)
657                    .map(|_| seq.next_element::<u8>())
658                    .take_while(|el| matches!(el, Ok(Some(_))))
659                    .count();
660
661                if remaining > 0 {
662                    return Err(serde::de::Error::invalid_length(
663                        32 + remaining,
664                        &"expected 32 bytes",
665                    ));
666                }
667
668                VerifyingKey::try_from(&bytes[..]).map_err(serde::de::Error::custom)
669            }
670        }
671
672        deserializer.deserialize_bytes(VerifyingKeyVisitor)
673    }
674}