summaryrefslogtreecommitdiffstats
path: root/vendor/ecdsa/src/hazmat.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/ecdsa/src/hazmat.rs')
-rw-r--r--vendor/ecdsa/src/hazmat.rs235
1 files changed, 132 insertions, 103 deletions
diff --git a/vendor/ecdsa/src/hazmat.rs b/vendor/ecdsa/src/hazmat.rs
index 3ca3fc4ce..6e59f2e2e 100644
--- a/vendor/ecdsa/src/hazmat.rs
+++ b/vendor/ecdsa/src/hazmat.rs
@@ -10,50 +10,53 @@
//! Failure to use them correctly can lead to catastrophic failures including
//! FULL PRIVATE KEY RECOVERY!
+use crate::{Error, Result};
+use core::cmp;
+use elliptic_curve::{generic_array::typenum::Unsigned, FieldBytes, PrimeCurve};
+
#[cfg(feature = "arithmetic")]
use {
crate::{RecoveryId, SignatureSize},
- core::borrow::Borrow,
elliptic_curve::{
- group::Curve as _,
- ops::{Invert, LinearCombination, Reduce},
+ ff::PrimeField,
+ group::{Curve as _, Group},
+ ops::{Invert, LinearCombination, MulByGenerator, Reduce},
+ point::AffineCoordinates,
+ scalar::IsHigh,
subtle::CtOption,
- AffineArithmetic, AffineXCoordinate, Field, Group, ProjectiveArithmetic, ProjectivePoint,
- Scalar, ScalarArithmetic,
+ CurveArithmetic, ProjectivePoint, Scalar,
},
};
#[cfg(feature = "digest")]
use {
- core::cmp,
- elliptic_curve::{bigint::Encoding, FieldSize},
- signature::{digest::Digest, PrehashSignature},
-};
-
-#[cfg(any(feature = "arithmetic", feature = "digest"))]
-use crate::{
- elliptic_curve::{generic_array::ArrayLength, FieldBytes, PrimeCurve},
- Error, Result, Signature,
+ elliptic_curve::FieldBytesSize,
+ signature::{
+ digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset},
+ PrehashSignature,
+ },
};
-#[cfg(all(feature = "arithmetic", feature = "digest"))]
-use signature::digest::FixedOutput;
+#[cfg(feature = "rfc6979")]
+use elliptic_curve::{FieldBytesEncoding, ScalarPrimitive};
-#[cfg(all(feature = "rfc6979"))]
-use {
- elliptic_curve::ScalarCore,
- signature::digest::{core_api::BlockSizeUser, FixedOutputReset},
-};
+#[cfg(any(feature = "arithmetic", feature = "digest"))]
+use crate::{elliptic_curve::generic_array::ArrayLength, Signature};
/// Try to sign the given prehashed message using ECDSA.
///
/// This trait is intended to be implemented on a type with access to the
/// secret scalar via `&self`, such as particular curve's `Scalar` type.
#[cfg(feature = "arithmetic")]
-#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
-pub trait SignPrimitive<C>: Field + Into<FieldBytes<C>> + Reduce<C::UInt> + Sized
+pub trait SignPrimitive<C>:
+ AsRef<Self>
+ + Into<FieldBytes<C>>
+ + IsHigh
+ + PrimeField<Repr = FieldBytes<C>>
+ + Reduce<C::Uint, Bytes = FieldBytes<C>>
+ + Sized
where
- C: PrimeCurve + ProjectiveArithmetic + ScalarArithmetic<Scalar = Self>,
+ C: PrimeCurve + CurveArithmetic + CurveArithmetic<Scalar = Self>,
SignatureSize<C>: ArrayLength<u8>,
{
/// Try to sign the prehashed message.
@@ -72,26 +75,27 @@ where
fn try_sign_prehashed<K>(
&self,
k: K,
- z: FieldBytes<C>,
+ z: &FieldBytes<C>,
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
- K: Borrow<Self> + Invert<Output = CtOption<Self>>,
+ K: AsRef<Self> + Invert<Output = CtOption<Self>>,
{
- if k.borrow().is_zero().into() {
+ if k.as_ref().is_zero().into() {
return Err(Error::new());
}
- let z = Self::from_be_bytes_reduced(z);
+ let z = <Self as Reduce<C::Uint>>::reduce_bytes(z);
// Compute scalar inversion of ๐‘˜
let k_inv = Option::<Scalar<C>>::from(k.invert()).ok_or_else(Error::new)?;
// Compute ๐‘น = ๐‘˜ร—๐‘ฎ
- let R = (C::ProjectivePoint::generator() * k.borrow()).to_affine();
+ let R = ProjectivePoint::<C>::mul_by_generator(k.as_ref()).to_affine();
// Lift x-coordinate of ๐‘น (element of base field) into a serialized big
// integer, then reduce it into an element of the scalar field
- let r = Self::from_be_bytes_reduced(R.x());
+ let r = Self::reduce_bytes(&R.x());
+ let x_is_reduced = r.to_repr() != R.x();
// Compute ๐’” as a signature over ๐’“ and ๐’›.
let s = k_inv * (z + (r * self));
@@ -100,8 +104,9 @@ where
return Err(Error::new());
}
- // TODO(tarcieri): support for computing recovery ID
- Ok((Signature::from_scalars(r, s)?, None))
+ let signature = Signature::from_scalars(r, s)?;
+ let recovery_id = RecoveryId::new(R.y_is_odd().into(), x_is_reduced);
+ Ok((signature, Some(recovery_id)))
}
/// Try to sign the given message digest deterministically using the method
@@ -113,40 +118,24 @@ where
///
/// [RFC6979]: https://datatracker.ietf.org/doc/html/rfc6979
#[cfg(all(feature = "rfc6979"))]
- #[cfg_attr(docsrs, doc(cfg(feature = "rfc6979")))]
fn try_sign_prehashed_rfc6979<D>(
&self,
- z: FieldBytes<C>,
+ z: &FieldBytes<C>,
ad: &[u8],
) -> Result<(Signature<C>, Option<RecoveryId>)>
where
- Self: From<ScalarCore<C>>,
- C::UInt: for<'a> From<&'a Self>,
- D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset,
+ Self: From<ScalarPrimitive<C>> + Invert<Output = CtOption<Self>>,
+ D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldBytesSize<C>> + FixedOutputReset,
{
- let x = C::UInt::from(self);
- let k = rfc6979::generate_k::<D, C::UInt>(&x, &C::ORDER, &z, ad);
- let k = Self::from(ScalarCore::<C>::new(*k).unwrap());
- self.try_sign_prehashed(k, z)
- }
+ let k = Scalar::<C>::from_repr(rfc6979::generate_k::<D, _>(
+ &self.to_repr(),
+ &C::ORDER.encode_field_bytes(),
+ z,
+ ad,
+ ))
+ .unwrap();
- /// Try to sign the given digest instance using the method described in
- /// [RFC6979].
- ///
- /// [RFC6979]: https://datatracker.ietf.org/doc/html/rfc6979
- #[cfg(all(feature = "rfc6979"))]
- #[cfg_attr(docsrs, doc(cfg(feature = "rfc6979")))]
- fn try_sign_digest_rfc6979<D>(
- &self,
- msg_digest: D,
- ad: &[u8],
- ) -> Result<(Signature<C>, Option<RecoveryId>)>
- where
- Self: From<ScalarCore<C>>,
- C::UInt: for<'a> From<&'a Self>,
- D: Digest + BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset,
- {
- self.try_sign_prehashed_rfc6979::<D>(msg_digest.finalize_fixed(), ad)
+ self.try_sign_prehashed::<Self>(k, z)
}
}
@@ -156,11 +145,9 @@ where
/// the affine point represeting the public key via `&self`, such as a
/// particular curve's `AffinePoint` type.
#[cfg(feature = "arithmetic")]
-#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
-pub trait VerifyPrimitive<C>: AffineXCoordinate<C> + Copy + Sized
+pub trait VerifyPrimitive<C>: AffineCoordinates<FieldRepr = FieldBytes<C>> + Copy + Sized
where
- C: PrimeCurve + AffineArithmetic<AffinePoint = Self> + ProjectiveArithmetic,
- Scalar<C>: Reduce<C::UInt>,
+ C: PrimeCurve + CurveArithmetic<AffinePoint = Self> + CurveArithmetic,
SignatureSize<C>: ArrayLength<u8>,
{
/// Verify the prehashed message against the provided signature
@@ -170,10 +157,10 @@ where
/// - `z`: message digest to be verified. MUST BE OUTPUT OF A
/// CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!!
/// - `sig`: signature to be verified against the key and message
- fn verify_prehashed(&self, z: FieldBytes<C>, sig: &Signature<C>) -> Result<()> {
- let z = Scalar::<C>::from_be_bytes_reduced(z);
+ fn verify_prehashed(&self, z: &FieldBytes<C>, sig: &Signature<C>) -> Result<()> {
+ let z = Scalar::<C>::reduce_bytes(z);
let (r, s) = sig.split_scalars();
- let s_inv = *s.invert();
+ let s_inv = *s.invert_vartime();
let u1 = z * s_inv;
let u2 = *r * s_inv;
let x = ProjectivePoint::<C>::lincomb(
@@ -185,7 +172,7 @@ where
.to_affine()
.x();
- if Scalar::<C>::from_be_bytes_reduced(x) == *r {
+ if *r == Scalar::<C>::reduce_bytes(&x) {
Ok(())
} else {
Err(Error::new())
@@ -194,12 +181,11 @@ where
/// Verify message digest against the provided signature.
#[cfg(feature = "digest")]
- #[cfg_attr(docsrs, doc(cfg(feature = "digest")))]
fn verify_digest<D>(&self, msg_digest: D, sig: &Signature<C>) -> Result<()>
where
- D: FixedOutput<OutputSize = FieldSize<C>>,
+ D: FixedOutput<OutputSize = FieldBytesSize<C>>,
{
- self.verify_prehashed(msg_digest.finalize_fixed(), sig)
+ self.verify_prehashed(&msg_digest.finalize_fixed(), sig)
}
}
@@ -214,48 +200,91 @@ where
///
/// [1]: https://github.com/RustCrypto/traits/tree/master/signature/derive
#[cfg(feature = "digest")]
-#[cfg_attr(docsrs, doc(cfg(feature = "digest")))]
pub trait DigestPrimitive: PrimeCurve {
/// Preferred digest to use when computing ECDSA signatures for this
/// elliptic curve. This is typically a member of the SHA-2 family.
- // TODO(tarcieri): add BlockSizeUser + FixedOutput(Reset) bounds in next breaking release
- // These bounds ensure the digest algorithm can be used for HMAC-DRBG for RFC6979
- type Digest: Digest;
-
- /// Compute field bytes for a prehash (message digest), either zero-padding
- /// or truncating if the prehash size does not match the field size.
- fn prehash_to_field_bytes(prehash: &[u8]) -> Result<FieldBytes<Self>> {
- // Minimum allowed prehash size is half the field size
- if prehash.len() < Self::UInt::BYTE_SIZE / 2 {
- return Err(Error::new());
- }
-
- let mut field_bytes = FieldBytes::<Self>::default();
-
- // This is a operation according to RFC6979 Section 2.3.2. and SEC1 Section 2.3.8.
- // https://datatracker.ietf.org/doc/html/rfc6979#section-2.3.2
- // https://www.secg.org/sec1-v2.pdf
- match prehash.len().cmp(&Self::UInt::BYTE_SIZE) {
- cmp::Ordering::Equal => field_bytes.copy_from_slice(prehash),
- cmp::Ordering::Less => {
- // If prehash is smaller than the field size, pad with zeroes on the left
- field_bytes[(Self::UInt::BYTE_SIZE - prehash.len())..].copy_from_slice(prehash);
- }
- cmp::Ordering::Greater => {
- // If prehash is larger than the field size, truncate
- field_bytes.copy_from_slice(&prehash[..Self::UInt::BYTE_SIZE]);
- }
- }
-
- Ok(field_bytes)
- }
+ type Digest: BlockSizeUser
+ + Digest
+ + FixedOutput<OutputSize = FieldBytesSize<Self>>
+ + FixedOutputReset;
}
#[cfg(feature = "digest")]
impl<C> PrehashSignature for Signature<C>
where
C: DigestPrimitive,
- <FieldSize<C> as core::ops::Add>::Output: ArrayLength<u8>,
+ <FieldBytesSize<C> as core::ops::Add>::Output: ArrayLength<u8>,
{
type Digest = C::Digest;
}
+
+/// Partial implementation of the `bits2int` function as defined in
+/// [RFC6979 ยง 2.3.2] as well as [SEC1] ยง 2.3.8.
+///
+/// This is used to convert a message digest whose size may be smaller or
+/// larger than the size of the curve's scalar field into a serialized
+/// (unreduced) field element.
+///
+/// [RFC6979 ยง 2.3.2]: https://datatracker.ietf.org/doc/html/rfc6979#section-2.3.2
+/// [SEC1]: https://www.secg.org/sec1-v2.pdf
+pub fn bits2field<C: PrimeCurve>(bits: &[u8]) -> Result<FieldBytes<C>> {
+ // Minimum allowed bits size is half the field size
+ if bits.len() < C::FieldBytesSize::USIZE / 2 {
+ return Err(Error::new());
+ }
+
+ let mut field_bytes = FieldBytes::<C>::default();
+
+ match bits.len().cmp(&C::FieldBytesSize::USIZE) {
+ cmp::Ordering::Equal => field_bytes.copy_from_slice(bits),
+ cmp::Ordering::Less => {
+ // If bits is smaller than the field size, pad with zeroes on the left
+ field_bytes[(C::FieldBytesSize::USIZE - bits.len())..].copy_from_slice(bits);
+ }
+ cmp::Ordering::Greater => {
+ // If bits is larger than the field size, truncate
+ field_bytes.copy_from_slice(&bits[..C::FieldBytesSize::USIZE]);
+ }
+ }
+
+ Ok(field_bytes)
+}
+
+#[cfg(test)]
+mod tests {
+ use super::bits2field;
+ use elliptic_curve::dev::MockCurve;
+ use hex_literal::hex;
+
+ #[test]
+ fn bits2field_too_small() {
+ assert!(bits2field::<MockCurve>(b"").is_err());
+ }
+
+ #[test]
+ fn bits2field_size_less() {
+ let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ let field_bytes = bits2field::<MockCurve>(&prehash).unwrap();
+ assert_eq!(
+ field_bytes.as_slice(),
+ &hex!("00000000000000000000000000000000AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
+ );
+ }
+
+ #[test]
+ fn bits2field_size_eq() {
+ let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
+ let field_bytes = bits2field::<MockCurve>(&prehash).unwrap();
+ assert_eq!(field_bytes.as_slice(), &prehash);
+ }
+
+ #[test]
+ fn bits2field_size_greater() {
+ let prehash = hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB");
+ let field_bytes = bits2field::<MockCurve>(&prehash).unwrap();
+ assert_eq!(
+ field_bytes.as_slice(),
+ &hex!("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")
+ );
+ }
+}