//! ECDSA signing: producing signatures using a [`SigningKey`]. use crate::{ ecdsa_oid_for_digest, hazmat::{bits2field, DigestPrimitive, SignPrimitive}, Error, Result, Signature, SignatureSize, SignatureWithOid, }; use core::fmt::{self, Debug}; use digest::{const_oid::AssociatedOid, Digest, FixedOutput}; use elliptic_curve::{ generic_array::ArrayLength, group::ff::PrimeField, ops::Invert, subtle::{Choice, ConstantTimeEq, CtOption}, zeroize::{Zeroize, ZeroizeOnDrop}, CurveArithmetic, FieldBytes, FieldBytesSize, NonZeroScalar, PrimeCurve, Scalar, SecretKey, }; use signature::{ hazmat::{PrehashSigner, RandomizedPrehashSigner}, rand_core::CryptoRngCore, DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer, }; #[cfg(feature = "der")] use {crate::der, core::ops::Add}; #[cfg(feature = "pem")] use { crate::elliptic_curve::pkcs8::{DecodePrivateKey, EncodePrivateKey, SecretDocument}, core::str::FromStr, }; #[cfg(feature = "pkcs8")] use crate::elliptic_curve::{ pkcs8::{ self, der::AnyRef, spki::{AlgorithmIdentifier, AssociatedAlgorithmIdentifier, SignatureAlgorithmIdentifier}, ObjectIdentifier, }, sec1::{self, FromEncodedPoint, ToEncodedPoint}, AffinePoint, }; #[cfg(feature = "verifying")] use {crate::VerifyingKey, elliptic_curve::PublicKey, signature::KeypairRef}; /// ECDSA secret key used for signing. Generic over prime order elliptic curves /// (e.g. NIST P-curves) /// /// Requires an [`elliptic_curve::CurveArithmetic`] impl on the curve, and a /// [`SignPrimitive`] impl on its associated `Scalar` type. /// /// ## Usage /// /// The [`signature`] crate defines the following traits which are the /// primary API for signing: /// /// - [`Signer`]: sign a message using this key /// - [`DigestSigner`]: sign the output of a [`Digest`] using this key /// - [`PrehashSigner`]: sign the low-level raw output bytes of a message digest /// /// See the [`p256` crate](https://docs.rs/p256/latest/p256/ecdsa/index.html) /// for examples of using this type with a concrete elliptic curve. #[derive(Clone)] pub struct SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { /// 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 = "verifying")] verifying_key: VerifyingKey, } impl SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { /// Generate a cryptographically random [`SigningKey`]. pub fn random(rng: &mut impl CryptoRngCore) -> Self { NonZeroScalar::::random(rng).into() } /// Initialize signing key from a raw scalar serialized as a byte array. pub fn from_bytes(bytes: &FieldBytes) -> Result { SecretKey::::from_bytes(bytes) .map(Into::into) .map_err(|_| Error::new()) } /// Initialize signing key from a raw scalar serialized as a byte slice. pub fn from_slice(bytes: &[u8]) -> Result { SecretKey::::from_slice(bytes) .map(Into::into) .map_err(|_| Error::new()) } /// Serialize this [`SigningKey`] as bytes pub fn to_bytes(&self) -> FieldBytes { self.secret_scalar.to_repr() } /// Borrow the secret [`NonZeroScalar`] value for this key. /// /// # ⚠️ Warning /// /// This value is key material. /// /// Please treat it with the care it deserves! pub fn as_nonzero_scalar(&self) -> &NonZeroScalar { &self.secret_scalar } /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`]. #[cfg(feature = "verifying")] pub fn verifying_key(&self) -> &VerifyingKey { &self.verifying_key } } // // `*Signer` trait impls // /// Sign message digest using a deterministic ephemeral scalar (`k`) /// computed using the algorithm described in [RFC6979 § 3.2]. /// /// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3 impl DigestSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput>, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_digest(&self, msg_digest: D) -> Result> { self.sign_prehash(&msg_digest.finalize_fixed()) } } /// Sign message prehash using a deterministic ephemeral scalar (`k`) /// computed using the algorithm described in [RFC6979 § 3.2]. /// /// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3 impl PrehashSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn sign_prehash(&self, prehash: &[u8]) -> Result> { let z = bits2field::(prehash)?; Ok(self .secret_scalar .try_sign_prehashed_rfc6979::(&z, &[])? .0) } } /// Sign message using a deterministic ephemeral scalar (`k`) /// computed using the algorithm described in [RFC6979 § 3.2]. /// /// [RFC6979 § 3.2]: https://tools.ietf.org/html/rfc6979#section-3 impl Signer> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign(&self, msg: &[u8]) -> Result> { self.try_sign_digest(C::Digest::new_with_prefix(msg)) } } impl RandomizedDigestSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput>, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_digest_with_rng( &self, rng: &mut impl CryptoRngCore, msg_digest: D, ) -> Result> { self.sign_prehash_with_rng(rng, &msg_digest.finalize_fixed()) } } impl RandomizedPrehashSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn sign_prehash_with_rng( &self, rng: &mut impl CryptoRngCore, prehash: &[u8], ) -> Result> { let z = bits2field::(prehash)?; let mut ad = FieldBytes::::default(); rng.fill_bytes(&mut ad); Ok(self .secret_scalar .try_sign_prehashed_rfc6979::(&z, &ad)? .0) } } impl RandomizedSigner> for SigningKey where Self: RandomizedDigestSigner>, C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_with_rng(&self, rng: &mut impl CryptoRngCore, msg: &[u8]) -> Result> { self.try_sign_digest_with_rng(rng, C::Digest::new_with_prefix(msg)) } } impl DigestSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: AssociatedOid + Digest + FixedOutput>, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_digest(&self, msg_digest: D) -> Result> { let signature: Signature = self.try_sign_digest(msg_digest)?; let oid = ecdsa_oid_for_digest(D::OID).ok_or_else(Error::new)?; SignatureWithOid::new(signature, oid) } } impl Signer> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, C::Digest: AssociatedOid, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign(&self, msg: &[u8]) -> Result> { self.try_sign_digest(C::Digest::new_with_prefix(msg)) } } #[cfg(feature = "der")] impl PrehashSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn sign_prehash(&self, prehash: &[u8]) -> Result> { PrehashSigner::>::sign_prehash(self, prehash).map(Into::into) } } #[cfg(feature = "der")] impl Signer> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn try_sign(&self, msg: &[u8]) -> Result> { Signer::>::try_sign(self, msg).map(Into::into) } } #[cfg(feature = "der")] impl RandomizedDigestSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, D: Digest + FixedOutput>, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn try_sign_digest_with_rng( &self, rng: &mut impl CryptoRngCore, msg_digest: D, ) -> Result> { RandomizedDigestSigner::>::try_sign_digest_with_rng(self, rng, msg_digest) .map(Into::into) } } #[cfg(feature = "der")] impl RandomizedPrehashSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn sign_prehash_with_rng( &self, rng: &mut impl CryptoRngCore, prehash: &[u8], ) -> Result> { RandomizedPrehashSigner::>::sign_prehash_with_rng(self, rng, prehash) .map(Into::into) } } #[cfg(feature = "der")] impl RandomizedSigner> for SigningKey where C: PrimeCurve + CurveArithmetic + DigestPrimitive, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, der::MaxSize: ArrayLength, as Add>::Output: Add + ArrayLength, { fn try_sign_with_rng( &self, rng: &mut impl CryptoRngCore, msg: &[u8], ) -> Result> { RandomizedSigner::>::try_sign_with_rng(self, rng, msg).map(Into::into) } } // // Other trait impls // #[cfg(feature = "verifying")] impl AsRef> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn as_ref(&self) -> &VerifyingKey { &self.verifying_key } } impl ConstantTimeEq for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn ct_eq(&self, other: &Self) -> Choice { self.secret_scalar.ct_eq(&other.secret_scalar) } } impl Debug for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct("SigningKey").finish_non_exhaustive() } } impl Drop for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn drop(&mut self) { self.secret_scalar.zeroize(); } } /// Constant-time comparison impl Eq for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { } impl PartialEq for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn eq(&self, other: &SigningKey) -> bool { self.ct_eq(other).into() } } impl From> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_scalar: NonZeroScalar) -> Self { #[cfg(feature = "verifying")] let public_key = PublicKey::from_secret_scalar(&secret_scalar); Self { secret_scalar, #[cfg(feature = "verifying")] verifying_key: public_key.into(), } } } impl From> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: SecretKey) -> Self { Self::from(&secret_key) } } impl From<&SecretKey> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: &SecretKey) -> Self { secret_key.to_nonzero_scalar().into() } } impl From> for SecretKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(key: SigningKey) -> Self { key.secret_scalar.into() } } impl From<&SigningKey> for SecretKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: &SigningKey) -> Self { secret_key.secret_scalar.into() } } impl TryFrom<&[u8]> for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type Error = Error; fn try_from(bytes: &[u8]) -> Result { Self::from_slice(bytes) } } impl ZeroizeOnDrop for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { } #[cfg(feature = "verifying")] impl From> for VerifyingKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(signing_key: SigningKey) -> VerifyingKey { signing_key.verifying_key } } #[cfg(feature = "verifying")] impl From<&SigningKey> for VerifyingKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn from(signing_key: &SigningKey) -> VerifyingKey { signing_key.verifying_key } } #[cfg(feature = "verifying")] impl KeypairRef for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type VerifyingKey = VerifyingKey; } #[cfg(feature = "pkcs8")] impl AssociatedAlgorithmIdentifier for SigningKey where C: AssociatedOid + CurveArithmetic + PrimeCurve, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type Params = ObjectIdentifier; const ALGORITHM_IDENTIFIER: AlgorithmIdentifier = SecretKey::::ALGORITHM_IDENTIFIER; } #[cfg(feature = "pkcs8")] impl SignatureAlgorithmIdentifier for SigningKey where C: PrimeCurve + CurveArithmetic, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, Signature: AssociatedAlgorithmIdentifier>, { type Params = AnyRef<'static>; const SIGNATURE_ALGORITHM_IDENTIFIER: AlgorithmIdentifier = Signature::::ALGORITHM_IDENTIFIER; } #[cfg(feature = "pkcs8")] impl TryFrom> for SigningKey where C: PrimeCurve + AssociatedOid + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type Error = pkcs8::Error; fn try_from(private_key_info: pkcs8::PrivateKeyInfo<'_>) -> pkcs8::Result { SecretKey::try_from(private_key_info).map(Into::into) } } #[cfg(feature = "pem")] impl EncodePrivateKey for SigningKey where C: AssociatedOid + PrimeCurve + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { fn to_pkcs8_der(&self) -> pkcs8::Result { SecretKey::from(self.secret_scalar).to_pkcs8_der() } } #[cfg(feature = "pem")] impl FromStr for SigningKey where C: PrimeCurve + AssociatedOid + CurveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldBytesSize: sec1::ModulusSize, Scalar: Invert>> + SignPrimitive, SignatureSize: ArrayLength, { type Err = Error; fn from_str(s: &str) -> Result { Self::from_pkcs8_pem(s).map_err(|_| Error::new()) } }