//! ECDSA signing key. // TODO(tarcieri): support for hardware crypto accelerators use crate::{ hazmat::{DigestPrimitive, SignPrimitive}, Error, Result, Signature, SignatureSize, }; use core::fmt::{self, Debug}; use elliptic_curve::{ generic_array::ArrayLength, group::ff::PrimeField, ops::{Invert, Reduce}, subtle::{Choice, ConstantTimeEq, CtOption}, zeroize::{Zeroize, ZeroizeOnDrop}, FieldBytes, FieldSize, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, Scalar, SecretKey, }; use signature::{ digest::{core_api::BlockSizeUser, Digest, FixedOutput, FixedOutputReset}, hazmat::PrehashSigner, rand_core::{CryptoRng, RngCore}, DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer, }; #[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 = "verify")] use {crate::verify::VerifyingKey, elliptic_curve::PublicKey, signature::Keypair}; /// ECDSA signing key. Generic over elliptic curves. /// /// Requires an [`elliptic_curve::ProjectiveArithmetic`] impl on the curve, and a /// [`SignPrimitive`] impl on its associated `Scalar` type. #[derive(Clone)] #[cfg_attr(docsrs, doc(cfg(feature = "sign")))] pub struct SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + 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 = "verify")] verifying_key: VerifyingKey, } impl SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { /// Generate a cryptographically random [`SigningKey`]. pub fn random(rng: impl CryptoRng + RngCore) -> Self { NonZeroScalar::::random(rng).into() } /// Initialize signing key from a raw scalar serialized as a byte slice. pub fn from_bytes(bytes: &[u8]) -> Result { 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.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`] // 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 { 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 } } impl ConstantTimeEq for SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + 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 + ProjectiveArithmetic, Scalar: Invert>> + Reduce + 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 + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn drop(&mut self) { self.secret_scalar.zeroize(); } } impl ZeroizeOnDrop for SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { } /// Constant-time comparison impl Eq for SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { } /// Constant-time comparison impl PartialEq for SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn eq(&self, other: &SigningKey) -> bool { self.ct_eq(other).into() } } impl From> for SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: SecretKey) -> Self { Self::from(&secret_key) } } impl From<&SecretKey> for SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: &SecretKey) -> Self { secret_key.to_nonzero_scalar().into() } } impl From> for SecretKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn from(key: SigningKey) -> Self { key.secret_scalar.into() } } impl From<&SigningKey> for SecretKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_key: &SigningKey) -> Self { secret_key.secret_scalar.into() } } impl DigestSigner> for SigningKey where C: PrimeCurve + ProjectiveArithmetic, C::UInt: for<'a> From<&'a Scalar>, D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { /// 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 fn try_sign_digest(&self, msg_digest: D) -> Result> { 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, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { type VerifyingKey = VerifyingKey; } impl PrehashSigner> for SigningKey where C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, C::Digest: BlockSizeUser + FixedOutput> + FixedOutputReset, C::UInt: for<'a> From<&'a Scalar>, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn sign_prehash(&self, prehash: &[u8]) -> Result> { let prehash = C::prehash_to_field_bytes(prehash)?; Ok(self .secret_scalar .try_sign_prehashed_rfc6979::(prehash, &[])? .0) } } impl Signer> for SigningKey where Self: DigestSigner>, C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, Scalar: Invert>> + Reduce + 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 + ProjectiveArithmetic, C::UInt: for<'a> From<&'a Scalar>, D: Digest + BlockSizeUser + FixedOutput> + FixedOutputReset, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { /// Sign message prehash using an ephemeral scalar (`k`) derived according /// to a variant of RFC 6979 (Section 3.6) which supplies additional /// entropy from an RNG. fn try_sign_digest_with_rng( &self, mut rng: impl CryptoRng + RngCore, msg_digest: D, ) -> Result> { let mut ad = FieldBytes::::default(); rng.fill_bytes(&mut ad); Ok(self .secret_scalar .try_sign_digest_rfc6979::(msg_digest, &ad)? .0) } } impl RandomizedSigner> for SigningKey where Self: RandomizedDigestSigner>, C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn try_sign_with_rng(&self, rng: impl CryptoRng + RngCore, msg: &[u8]) -> Result> { self.try_sign_digest_with_rng(rng, C::Digest::new_with_prefix(msg)) } } impl From> for SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn from(secret_scalar: NonZeroScalar) -> Self { #[cfg(feature = "verify")] let public_key = PublicKey::from_secret_scalar(&secret_scalar); Self { secret_scalar, #[cfg(feature = "verify")] verifying_key: public_key.into(), } } } impl TryFrom<&[u8]> for SigningKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { type Error = Error; fn try_from(bytes: &[u8]) -> Result { Self::from_bytes(bytes) } } #[cfg(feature = "verify")] impl From<&SigningKey> for VerifyingKey where C: PrimeCurve + ProjectiveArithmetic, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn from(signing_key: &SigningKey) -> VerifyingKey { signing_key.verifying_key() } } #[cfg(feature = "pkcs8")] #[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] impl TryFrom> for SigningKey where C: PrimeCurve + AssociatedOid + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldSize: sec1::ModulusSize, Scalar: Invert>> + Reduce + 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")] #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl EncodePrivateKey for SigningKey where C: AssociatedOid + PrimeCurve + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldSize: sec1::ModulusSize, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { fn to_pkcs8_der(&self) -> pkcs8::Result { SecretKey::from(self.secret_scalar).to_pkcs8_der() } } #[cfg(feature = "pkcs8")] #[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))] impl DecodePrivateKey for SigningKey where C: PrimeCurve + AssociatedOid + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldSize: sec1::ModulusSize, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { } #[cfg(feature = "pem")] #[cfg_attr(docsrs, doc(cfg(feature = "pem")))] impl FromStr for SigningKey where C: PrimeCurve + AssociatedOid + ProjectiveArithmetic, AffinePoint: FromEncodedPoint + ToEncodedPoint, FieldSize: sec1::ModulusSize, Scalar: Invert>> + Reduce + SignPrimitive, SignatureSize: ArrayLength, { type Err = Error; fn from_str(s: &str) -> Result { Self::from_pkcs8_pem(s).map_err(|_| Error::new()) } }