From 837b550238aa671a591ccf282dddeab29cadb206 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 04:49:42 +0200 Subject: Merging upstream version 1.71.1+dfsg1. Signed-off-by: Daniel Baumann --- vendor/ecdsa/src/recovery.rs | 247 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 247 insertions(+) (limited to 'vendor/ecdsa/src/recovery.rs') diff --git a/vendor/ecdsa/src/recovery.rs b/vendor/ecdsa/src/recovery.rs index c923bbad7..dd1b6aed5 100644 --- a/vendor/ecdsa/src/recovery.rs +++ b/vendor/ecdsa/src/recovery.rs @@ -2,6 +2,38 @@ use crate::{Error, Result}; +#[cfg(feature = "signing")] +use { + crate::{hazmat::SignPrimitive, SigningKey}, + elliptic_curve::subtle::CtOption, + signature::{hazmat::PrehashSigner, DigestSigner, Signer}, +}; + +#[cfg(feature = "verifying")] +use { + crate::{hazmat::VerifyPrimitive, VerifyingKey}, + elliptic_curve::{ + bigint::CheckedAdd, + ops::{LinearCombination, Reduce}, + point::DecompressPoint, + sec1::{self, FromEncodedPoint, ToEncodedPoint}, + AffinePoint, FieldBytesEncoding, FieldBytesSize, Group, PrimeField, ProjectivePoint, + }, + signature::hazmat::PrehashVerifier, +}; + +#[cfg(any(feature = "signing", feature = "verifying"))] +use { + crate::{ + hazmat::{bits2field, DigestPrimitive}, + Signature, SignatureSize, + }, + elliptic_curve::{ + generic_array::ArrayLength, ops::Invert, CurveArithmetic, PrimeCurve, Scalar, + }, + signature::digest::Digest, +}; + /// Recovery IDs, a.k.a. "recid". /// /// This is an integer value `0`, `1`, `2`, or `3` included along with a @@ -55,6 +87,74 @@ impl RecoveryId { } } +#[cfg(feature = "verifying")] +impl RecoveryId { + /// Given a public key, message, and signature, use trial recovery + /// to determine if a suitable recovery ID exists, or return an error + /// otherwise. + pub fn trial_recovery_from_msg( + verifying_key: &VerifyingKey, + msg: &[u8], + signature: &Signature, + ) -> Result + where + C: DigestPrimitive + PrimeCurve + CurveArithmetic, + AffinePoint: + DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: sec1::ModulusSize, + SignatureSize: ArrayLength, + { + Self::trial_recovery_from_digest(verifying_key, C::Digest::new_with_prefix(msg), signature) + } + + /// Given a public key, message digest, and signature, use trial recovery + /// to determine if a suitable recovery ID exists, or return an error + /// otherwise. + pub fn trial_recovery_from_digest( + verifying_key: &VerifyingKey, + digest: D, + signature: &Signature, + ) -> Result + where + C: PrimeCurve + CurveArithmetic, + D: Digest, + AffinePoint: + DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: sec1::ModulusSize, + SignatureSize: ArrayLength, + { + Self::trial_recovery_from_prehash(verifying_key, &digest.finalize(), signature) + } + + /// Given a public key, message digest, and signature, use trial recovery + /// to determine if a suitable recovery ID exists, or return an error + /// otherwise. + pub fn trial_recovery_from_prehash( + verifying_key: &VerifyingKey, + prehash: &[u8], + signature: &Signature, + ) -> Result + where + C: PrimeCurve + CurveArithmetic, + AffinePoint: + DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: sec1::ModulusSize, + SignatureSize: ArrayLength, + { + for id in 0..=Self::MAX { + let recovery_id = RecoveryId(id); + + if let Ok(vk) = VerifyingKey::recover_from_prehash(prehash, signature, recovery_id) { + if verifying_key == &vk { + return Ok(recovery_id); + } + } + } + + Err(Error::new()) + } +} + impl TryFrom for RecoveryId { type Error = Error; @@ -69,6 +169,153 @@ impl From for u8 { } } +#[cfg(feature = "signing")] +impl SigningKey +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, +{ + /// Sign the given message prehash, returning a signature and recovery ID. + pub fn sign_prehash_recoverable(&self, prehash: &[u8]) -> Result<(Signature, RecoveryId)> { + let z = bits2field::(prehash)?; + let (sig, recid) = self + .as_nonzero_scalar() + .try_sign_prehashed_rfc6979::(&z, &[])?; + + Ok((sig, recid.ok_or_else(Error::new)?)) + } + + /// Sign the given message digest, returning a signature and recovery ID. + pub fn sign_digest_recoverable(&self, msg_digest: D) -> Result<(Signature, RecoveryId)> + where + D: Digest, + { + self.sign_prehash_recoverable(&msg_digest.finalize()) + } + + /// Sign the given message, hashing it with the curve's default digest + /// function, and returning a signature and recovery ID. + pub fn sign_recoverable(&self, msg: &[u8]) -> Result<(Signature, RecoveryId)> { + self.sign_digest_recoverable(C::Digest::new_with_prefix(msg)) + } +} + +#[cfg(feature = "signing")] +impl DigestSigner, RecoveryId)> for SigningKey +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive, + D: Digest, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, +{ + fn try_sign_digest(&self, msg_digest: D) -> Result<(Signature, RecoveryId)> { + self.sign_digest_recoverable(msg_digest) + } +} + +#[cfg(feature = "signing")] +impl PrehashSigner<(Signature, RecoveryId)> for SigningKey +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, +{ + fn sign_prehash(&self, prehash: &[u8]) -> Result<(Signature, RecoveryId)> { + self.sign_prehash_recoverable(prehash) + } +} + +#[cfg(feature = "signing")] +impl Signer<(Signature, RecoveryId)> for SigningKey +where + C: PrimeCurve + CurveArithmetic + DigestPrimitive, + Scalar: Invert>> + SignPrimitive, + SignatureSize: ArrayLength, +{ + fn try_sign(&self, msg: &[u8]) -> Result<(Signature, RecoveryId)> { + self.sign_recoverable(msg) + } +} + +#[cfg(feature = "verifying")] +impl VerifyingKey +where + C: PrimeCurve + CurveArithmetic, + AffinePoint: + DecompressPoint + FromEncodedPoint + ToEncodedPoint + VerifyPrimitive, + FieldBytesSize: sec1::ModulusSize, + SignatureSize: ArrayLength, +{ + /// Recover a [`VerifyingKey`] from the given message, signature, and + /// [`RecoveryId`]. + /// + /// The message is first hashed using this curve's [`DigestPrimitive`]. + pub fn recover_from_msg( + msg: &[u8], + signature: &Signature, + recovery_id: RecoveryId, + ) -> Result + where + C: DigestPrimitive, + { + Self::recover_from_digest(C::Digest::new_with_prefix(msg), signature, recovery_id) + } + + /// Recover a [`VerifyingKey`] from the given message [`Digest`], + /// signature, and [`RecoveryId`]. + pub fn recover_from_digest( + msg_digest: D, + signature: &Signature, + recovery_id: RecoveryId, + ) -> Result + where + D: Digest, + { + Self::recover_from_prehash(&msg_digest.finalize(), signature, recovery_id) + } + + /// Recover a [`VerifyingKey`] from the given `prehash` of a message, the + /// signature over that prehashed message, and a [`RecoveryId`]. + #[allow(non_snake_case)] + pub fn recover_from_prehash( + prehash: &[u8], + signature: &Signature, + recovery_id: RecoveryId, + ) -> Result { + let (r, s) = signature.split_scalars(); + let z = as Reduce>::reduce_bytes(&bits2field::(prehash)?); + + let mut r_bytes = r.to_repr(); + if recovery_id.is_x_reduced() { + match Option::::from( + C::Uint::decode_field_bytes(&r_bytes).checked_add(&C::ORDER), + ) { + Some(restored) => r_bytes = restored.encode_field_bytes(), + // No reduction should happen here if r was reduced + None => return Err(Error::new()), + }; + } + let R = AffinePoint::::decompress(&r_bytes, u8::from(recovery_id.is_y_odd()).into()); + + if R.is_none().into() { + return Err(Error::new()); + } + + let R = ProjectivePoint::::from(R.unwrap()); + let r_inv = *r.invert(); + let u1 = -(r_inv * z); + let u2 = r_inv * *s; + let pk = ProjectivePoint::::lincomb(&ProjectivePoint::::generator(), &u1, &R, &u2); + let vk = Self::from_affine(pk.into())?; + + // Ensure signature verifies with the recovered key + vk.verify_prehash(prehash, signature)?; + + Ok(vk) + } +} + #[cfg(test)] mod tests { use super::RecoveryId; -- cgit v1.2.3