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}