From 7fd50a6cba1e1b3491db8801a7fd54932ef3e7eb Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Mon, 12 Sep 2022 17:35:25 -0600 Subject: [PATCH] ecdsa: impl `Keypair` for `SigningKey` This trait describes the relationship between signing and verifying keys, allowing the latter to be accessed from the former in a trait-based manner. --- ecdsa/src/sign.rs | 100 +++++++++++++++++++++++++++++++------------- ecdsa/src/verify.rs | 2 +- 2 files changed, 71 insertions(+), 31 deletions(-) diff --git a/ecdsa/src/sign.rs b/ecdsa/src/sign.rs index ea89b818..900d98ee 100644 --- a/ecdsa/src/sign.rs +++ b/ecdsa/src/sign.rs @@ -22,20 +22,23 @@ use signature::{ DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer, }; -#[cfg(feature = "verify")] -use {crate::verify::VerifyingKey, elliptic_curve::PublicKey}; +#[cfg(feature = "pem")] +use { + crate::elliptic_curve::pkcs8::{EncodePrivateKey, SecretDocument}, + core::str::FromStr, +}; #[cfg(feature = "pkcs8")] use crate::elliptic_curve::{ pkcs8::{self, AssociatedOid, DecodePrivateKey}, sec1::{self, FromEncodedPoint, ToEncodedPoint}, - AffinePoint, }; -#[cfg(feature = "pem")] +#[cfg(feature = "verify")] use { - crate::elliptic_curve::pkcs8::{EncodePrivateKey, SecretDocument}, - core::str::FromStr, + crate::{hazmat::VerifyPrimitive, verify::VerifyingKey}, + elliptic_curve::{AffinePoint, PublicKey}, + signature::Keypair, }; /// ECDSA signing key. Generic over elliptic curves. @@ -50,7 +53,12 @@ where Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { - inner: NonZeroScalar, + /// ECDSA signing keys are non-zero elements of a given curve's scalar field. + secret_scalar: NonZeroScalar, + + /// Verifying key which corresponds to this signing key. + #[cfg(feature = "verify")] + verifying_key: VerifyingKey, } impl SigningKey @@ -61,23 +69,19 @@ where { /// Generate a cryptographically random [`SigningKey`]. pub fn random(rng: impl CryptoRng + RngCore) -> Self { - Self { - inner: NonZeroScalar::random(rng), - } + NonZeroScalar::::random(rng).into() } /// Initialize signing key from a raw scalar serialized as a byte slice. pub fn from_bytes(bytes: &[u8]) -> Result { - let inner = SecretKey::from_be_bytes(bytes) - .map(|sk| sk.to_nonzero_scalar()) - .map_err(|_| Error::new())?; - - Ok(Self { inner }) + SecretKey::::from_be_bytes(bytes) + .map(|sk| sk.to_nonzero_scalar().into()) + .map_err(|_| Error::new()) } /// Serialize this [`SigningKey`] as bytes pub fn to_bytes(&self) -> FieldBytes { - self.inner.to_repr() + self.secret_scalar.to_repr() } /// Borrow the secret [`NonZeroScalar`] value for this key. @@ -88,16 +92,28 @@ where /// /// Please treat it with the care it deserves! pub fn as_nonzero_scalar(&self) -> &NonZeroScalar { - &self.inner + &self.secret_scalar } /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`] + // TODO(tarcieri): make this return `&VerifyingKey` in the next breaking release #[cfg(feature = "verify")] #[cfg_attr(docsrs, doc(cfg(feature = "verify")))] pub fn verifying_key(&self) -> VerifyingKey { - VerifyingKey { - inner: PublicKey::from_secret_scalar(&self.inner), - } + self.verifying_key + } +} + +#[cfg(feature = "verify")] +#[cfg_attr(docsrs, doc(cfg(feature = "verify")))] +impl AsRef> for SigningKey +where + C: PrimeCurve + ProjectiveArithmetic, + Scalar: Invert>> + Reduce + SignPrimitive, + SignatureSize: ArrayLength, +{ + fn as_ref(&self) -> &VerifyingKey { + &self.verifying_key } } @@ -108,7 +124,7 @@ where SignatureSize: ArrayLength, { fn ct_eq(&self, other: &Self) -> Choice { - self.inner.ct_eq(&other.inner) + self.secret_scalar.ct_eq(&other.secret_scalar) } } @@ -130,7 +146,7 @@ where SignatureSize: ArrayLength, { fn drop(&mut self) { - self.inner.zeroize(); + self.secret_scalar.zeroize(); } } @@ -181,9 +197,7 @@ where SignatureSize: ArrayLength, { fn from(secret_key: &SecretKey) -> Self { - Self { - inner: secret_key.to_nonzero_scalar(), - } + secret_key.to_nonzero_scalar().into() } } @@ -201,10 +215,28 @@ where /// /// [RFC6979 ยง 3.2]: https://tools.ietf.org/html/rfc6979#section-3 fn try_sign_digest(&self, msg_digest: D) -> Result> { - Ok(self.inner.try_sign_digest_rfc6979::(msg_digest, &[])?.0) + Ok(self + .secret_scalar + .try_sign_digest_rfc6979::(msg_digest, &[])? + .0) } } +#[cfg(feature = "verify")] +#[cfg_attr(docsrs, doc(cfg(feature = "verify")))] +impl Keypair> for SigningKey +where + C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, + C::Digest: BlockSizeUser + FixedOutput> + FixedOutputReset, + C::UInt: for<'a> From<&'a Scalar>, + Scalar: Invert>> + Reduce + SignPrimitive, + AffinePoint: VerifyPrimitive, + Scalar: Reduce, + SignatureSize: ArrayLength, +{ + type VerifyingKey = VerifyingKey; +} + impl PrehashSigner> for SigningKey where C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, @@ -217,7 +249,7 @@ where let prehash = C::prehash_to_field_bytes(prehash)?; Ok(self - .inner + .secret_scalar .try_sign_prehashed_rfc6979::(prehash, &[])? .0) } @@ -253,7 +285,10 @@ where ) -> Result> { let mut ad = FieldBytes::::default(); rng.fill_bytes(&mut ad); - Ok(self.inner.try_sign_digest_rfc6979::(msg_digest, &ad)?.0) + Ok(self + .secret_scalar + .try_sign_digest_rfc6979::(msg_digest, &ad)? + .0) } } @@ -276,8 +311,13 @@ where SignatureSize: ArrayLength, { fn from(secret_scalar: NonZeroScalar) -> Self { + #[cfg(feature = "verify")] + let public_key = PublicKey::from_secret_scalar(&secret_scalar); + Self { - inner: secret_scalar, + secret_scalar, + #[cfg(feature = "verify")] + verifying_key: public_key.into(), } } } @@ -335,7 +375,7 @@ where SignatureSize: ArrayLength, { fn to_pkcs8_der(&self) -> pkcs8::Result { - SecretKey::from(self.inner).to_pkcs8_der() + SecretKey::from(self.secret_scalar).to_pkcs8_der() } } diff --git a/ecdsa/src/verify.rs b/ecdsa/src/verify.rs index 5e8d0ab9..5aa8bcb8 100644 --- a/ecdsa/src/verify.rs +++ b/ecdsa/src/verify.rs @@ -136,7 +136,7 @@ where impl Verifier> for VerifyingKey where C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, - C::Digest: Digest + FixedOutput>, + C::Digest: FixedOutput>, AffinePoint: VerifyPrimitive, Scalar: Reduce, SignatureSize: ArrayLength,