ecdsa_flow/
sign.rs

1//! ECDSA signing key.
2
3// TODO(tarcieri): support for hardware crypto accelerators
4
5use crate::{
6    hazmat::{DigestPrimitive, FromDigest, SignPrimitive},
7    rfc6979, Error, Result, Signature, SignatureSize,
8};
9use core::{
10    convert::TryFrom,
11    fmt::{self, Debug},
12};
13use elliptic_curve_flow::{
14    generic_array::ArrayLength,
15    group::ff::PrimeField,
16    ops::Invert,
17    subtle::{Choice, ConstantTimeEq},
18    zeroize::Zeroize,
19    FieldBytes, FieldSize, NonZeroScalar, PrimeCurve, ProjectiveArithmetic, Scalar, SecretKey,
20};
21use signature_flow::{
22    digest::{BlockInput, Digest, FixedOutput, Reset, Update},
23    rand_core::{CryptoRng, RngCore},
24    DigestSigner, RandomizedDigestSigner, RandomizedSigner, Signer,
25};
26
27#[cfg(feature = "verify")]
28use {crate::verify::VerifyingKey, elliptic_curve_flow::PublicKey};
29
30#[cfg(feature = "pkcs8")]
31use crate::elliptic_curve_flow::{
32    consts::U1,
33    ops::Add,
34    pkcs8::{self, FromPrivateKey},
35    sec1::{FromEncodedPoint, ToEncodedPoint, UncompressedPointSize, UntaggedPointSize},
36    AffinePoint, AlgorithmParameters,
37};
38
39#[cfg(feature = "pem")]
40use core::str::FromStr;
41
42/// ECDSA signing key. Generic over elliptic curves.
43///
44/// Requires an [`elliptic_curve_flow::ProjectiveArithmetic`] impl on the curve, and a
45/// [`SignPrimitive`] impl on its associated `Scalar` type.
46#[derive(Clone)]
47#[cfg_attr(docsrs, doc(cfg(feature = "sign")))]
48pub struct SigningKey<C>
49where
50    C: PrimeCurve + ProjectiveArithmetic,
51    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
52    SignatureSize<C>: ArrayLength<u8>,
53{
54    inner: NonZeroScalar<C>,
55}
56
57impl<C> SigningKey<C>
58where
59    C: PrimeCurve + ProjectiveArithmetic,
60    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
61    SignatureSize<C>: ArrayLength<u8>,
62{
63    /// Generate a cryptographically random [`SigningKey`].
64    pub fn random(rng: impl CryptoRng + RngCore) -> Self {
65        Self {
66            inner: NonZeroScalar::random(rng),
67        }
68    }
69
70    /// Initialize signing key from a raw scalar serialized as a byte slice.
71    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
72        let inner = SecretKey::from_be_bytes(bytes)
73            .map(|sk| sk.to_nonzero_scalar())
74            .map_err(|_| Error::new())?;
75
76        Ok(Self { inner })
77    }
78
79    /// Get the [`VerifyingKey`] which corresponds to this [`SigningKey`]
80    #[cfg(feature = "verify")]
81    #[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
82    pub fn verifying_key(&self) -> VerifyingKey<C> {
83        VerifyingKey {
84            inner: PublicKey::from_secret_scalar(&self.inner),
85        }
86    }
87
88    /// Serialize this [`SigningKey`] as bytes
89    pub fn to_bytes(&self) -> FieldBytes<C> {
90        self.inner.to_repr()
91    }
92}
93
94impl<C> ConstantTimeEq for SigningKey<C>
95where
96    C: PrimeCurve + ProjectiveArithmetic,
97    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
98    SignatureSize<C>: ArrayLength<u8>,
99{
100    fn ct_eq(&self, other: &Self) -> Choice {
101        self.inner.ct_eq(&other.inner)
102    }
103}
104
105impl<C> Debug for SigningKey<C>
106where
107    C: PrimeCurve + ProjectiveArithmetic,
108    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
109    SignatureSize<C>: ArrayLength<u8>,
110{
111    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
112        // TODO(tarcieri): use `finish_non_exhaustive` when stable
113        f.debug_tuple("SigningKey").field(&"...").finish()
114    }
115}
116
117impl<C> Drop for SigningKey<C>
118where
119    C: PrimeCurve + ProjectiveArithmetic,
120    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
121    SignatureSize<C>: ArrayLength<u8>,
122{
123    fn drop(&mut self) {
124        self.inner.zeroize();
125    }
126}
127
128impl<C> Eq for SigningKey<C>
129where
130    C: PrimeCurve + ProjectiveArithmetic,
131    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
132    SignatureSize<C>: ArrayLength<u8>,
133{
134}
135
136impl<C> PartialEq for SigningKey<C>
137where
138    C: PrimeCurve + ProjectiveArithmetic,
139    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
140    SignatureSize<C>: ArrayLength<u8>,
141{
142    fn eq(&self, other: &SigningKey<C>) -> bool {
143        self.ct_eq(other).into()
144    }
145}
146
147impl<C> From<SecretKey<C>> for SigningKey<C>
148where
149    C: PrimeCurve + ProjectiveArithmetic,
150    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
151    SignatureSize<C>: ArrayLength<u8>,
152{
153    fn from(secret_key: SecretKey<C>) -> Self {
154        Self::from(&secret_key)
155    }
156}
157
158impl<C> From<&SecretKey<C>> for SigningKey<C>
159where
160    C: PrimeCurve + ProjectiveArithmetic,
161    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
162    SignatureSize<C>: ArrayLength<u8>,
163{
164    fn from(secret_key: &SecretKey<C>) -> Self {
165        Self {
166            inner: secret_key.to_nonzero_scalar(),
167        }
168    }
169}
170
171impl<C, D> DigestSigner<D, Signature<C>> for SigningKey<C>
172where
173    C: PrimeCurve + ProjectiveArithmetic,
174    D: FixedOutput<OutputSize = FieldSize<C>> + BlockInput + Clone + Default + Reset + Update,
175    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
176    SignatureSize<C>: ArrayLength<u8>,
177{
178    /// Sign message prehash using a deterministic ephemeral scalar (`k`)
179    /// computed using the algorithm described in RFC 6979 (Section 3.2):
180    /// <https://tools.ietf.org/html/rfc6979#section-3>
181    fn try_sign_digest(&self, digest: D) -> Result<Signature<C>> {
182        let k = rfc6979::generate_k(&self.inner, digest.clone(), &[]);
183        let msg_scalar = Scalar::<C>::from_digest(digest);
184        self.inner.try_sign_prehashed(&**k, &msg_scalar)
185    }
186}
187
188impl<C> Signer<Signature<C>> for SigningKey<C>
189where
190    Self: DigestSigner<C::Digest, Signature<C>>,
191    C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
192    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
193    SignatureSize<C>: ArrayLength<u8>,
194{
195    fn try_sign(&self, msg: &[u8]) -> Result<Signature<C>> {
196        self.try_sign_digest(C::Digest::new().chain(msg))
197    }
198}
199
200impl<C, D> RandomizedDigestSigner<D, Signature<C>> for SigningKey<C>
201where
202    C: PrimeCurve + ProjectiveArithmetic,
203    D: FixedOutput<OutputSize = FieldSize<C>> + BlockInput + Clone + Default + Reset + Update,
204    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
205    SignatureSize<C>: ArrayLength<u8>,
206{
207    /// Sign message prehash using an ephemeral scalar (`k`) derived according
208    /// to a variant of RFC 6979 (Section 3.6) which supplies additional
209    /// entropy from an RNG.
210    fn try_sign_digest_with_rng(
211        &self,
212        mut rng: impl CryptoRng + RngCore,
213        digest: D,
214    ) -> Result<Signature<C>> {
215        let mut added_entropy = FieldBytes::<C>::default();
216        rng.fill_bytes(&mut added_entropy);
217
218        let k = rfc6979::generate_k(&self.inner, digest.clone(), &added_entropy);
219        let msg_scalar = Scalar::<C>::from_digest(digest);
220        self.inner.try_sign_prehashed(&**k, &msg_scalar)
221    }
222}
223
224impl<C> RandomizedSigner<Signature<C>> for SigningKey<C>
225where
226    Self: RandomizedDigestSigner<C::Digest, Signature<C>>,
227    C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
228    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
229    SignatureSize<C>: ArrayLength<u8>,
230{
231    fn try_sign_with_rng(&self, rng: impl CryptoRng + RngCore, msg: &[u8]) -> Result<Signature<C>> {
232        self.try_sign_digest_with_rng(rng, C::Digest::new().chain(msg))
233    }
234}
235
236impl<C> From<NonZeroScalar<C>> for SigningKey<C>
237where
238    C: PrimeCurve + ProjectiveArithmetic,
239    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
240    SignatureSize<C>: ArrayLength<u8>,
241{
242    fn from(secret_scalar: NonZeroScalar<C>) -> Self {
243        Self {
244            inner: secret_scalar,
245        }
246    }
247}
248
249impl<C> TryFrom<&[u8]> for SigningKey<C>
250where
251    C: PrimeCurve + ProjectiveArithmetic,
252    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
253    SignatureSize<C>: ArrayLength<u8>,
254{
255    type Error = Error;
256
257    fn try_from(bytes: &[u8]) -> Result<Self> {
258        Self::from_bytes(bytes)
259    }
260}
261
262#[cfg(feature = "verify")]
263impl<C> From<&SigningKey<C>> for VerifyingKey<C>
264where
265    C: PrimeCurve + ProjectiveArithmetic,
266
267    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
268    SignatureSize<C>: ArrayLength<u8>,
269{
270    fn from(signing_key: &SigningKey<C>) -> VerifyingKey<C> {
271        signing_key.verifying_key()
272    }
273}
274
275#[cfg(feature = "pkcs8")]
276#[cfg_attr(docsrs, doc(cfg(feature = "pkcs8")))]
277impl<C> FromPrivateKey for SigningKey<C>
278where
279    C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic,
280    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
281    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
282    SignatureSize<C>: ArrayLength<u8>,
283    UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
284    UncompressedPointSize<C>: ArrayLength<u8>,
285{
286    fn from_pkcs8_private_key_info(
287        private_key_info: pkcs8::PrivateKeyInfo<'_>,
288    ) -> pkcs8::Result<Self> {
289        SecretKey::from_pkcs8_private_key_info(private_key_info).map(Into::into)
290    }
291}
292
293#[cfg(feature = "pem")]
294#[cfg_attr(docsrs, doc(cfg(feature = "pem")))]
295impl<C> FromStr for SigningKey<C>
296where
297    C: PrimeCurve + AlgorithmParameters + ProjectiveArithmetic,
298    AffinePoint<C>: FromEncodedPoint<C> + ToEncodedPoint<C>,
299    Scalar<C>: FromDigest<C> + Invert<Output = Scalar<C>> + SignPrimitive<C>,
300    SignatureSize<C>: ArrayLength<u8>,
301    UntaggedPointSize<C>: Add<U1> + ArrayLength<u8>,
302    UncompressedPointSize<C>: ArrayLength<u8>,
303{
304    type Err = Error;
305
306    fn from_str(s: &str) -> Result<Self> {
307        Self::from_pkcs8_pem(s).map_err(|_| Error::new())
308    }
309}