ecdsa_flow/hazmat.rs
1//! Low-level ECDSA primitives.
2//!
3//! # ⚠️ Warning: Hazmat!
4//!
5//! YOU PROBABLY DON'T WANT TO USE THESE!
6//!
7//! These primitives are easy-to-misuse low-level interfaces intended to be
8//! implemented by elliptic curve crates and consumed only by this crate!
9//!
10//! If you are an end user / non-expert in cryptography, do not use these!
11//! Failure to use them correctly can lead to catastrophic failures including
12//! FULL PRIVATE KEY RECOVERY!
13
14#[cfg(feature = "arithmetic")]
15use {
16 crate::{Result, SignatureSize},
17 core::borrow::Borrow,
18 elliptic_curve_flow::{ops::Invert, ProjectiveArithmetic, Scalar},
19};
20
21#[cfg(feature = "digest")]
22use {
23 crate::signature_flow::{digest::Digest, PrehashSignature},
24 elliptic_curve_flow::FieldSize,
25};
26
27#[cfg(any(feature = "arithmetic", feature = "digest"))]
28use crate::{
29 elliptic_curve_flow::{generic_array::ArrayLength, PrimeCurve},
30 Signature,
31};
32
33/// Try to sign the given prehashed message using ECDSA.
34///
35/// This trait is intended to be implemented on a type with access
36/// to the secret scalar via `&self`, such as particular curve's `Scalar` type,
37/// or potentially a key handle to a hardware device.
38#[cfg(feature = "arithmetic")]
39#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
40pub trait SignPrimitive<C>
41where
42 C: PrimeCurve + ProjectiveArithmetic,
43 SignatureSize<C>: ArrayLength<u8>,
44{
45 /// Try to sign the prehashed message.
46 ///
47 /// Accepts the following arguments:
48 ///
49 /// - `ephemeral_scalar`: ECDSA `k` value. MUST BE UNIFORMLY RANDOM!!!
50 /// - `hashed_msg`: scalar computed from a hashed message digest to be signed.
51 /// MUST BE OUTPUT OF A CRYPTOGRAPHICALLY SECURE DIGEST ALGORITHM!!!
52 fn try_sign_prehashed<K: Borrow<Scalar<C>> + Invert<Output = Scalar<C>>>(
53 &self,
54 ephemeral_scalar: &K,
55 hashed_msg: &Scalar<C>,
56 ) -> Result<Signature<C>>;
57}
58
59/// [`SignPrimitive`] for signature implementations that can provide public key
60/// recovery implementation.
61#[cfg(feature = "arithmetic")]
62#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
63pub trait RecoverableSignPrimitive<C>
64where
65 C: PrimeCurve + ProjectiveArithmetic,
66 SignatureSize<C>: ArrayLength<u8>,
67{
68 /// Try to sign the prehashed message.
69 ///
70 /// Accepts the same arguments as [`SignPrimitive::try_sign_prehashed`]
71 /// but returns a boolean flag which indicates whether or not the
72 /// y-coordinate of the computed 𝐑 = 𝑘×𝑮 point is odd, which can be
73 /// incorporated into recoverable signatures.
74 fn try_sign_recoverable_prehashed<K: Borrow<Scalar<C>> + Invert<Output = Scalar<C>>>(
75 &self,
76 ephemeral_scalar: &K,
77 hashed_msg: &Scalar<C>,
78 ) -> Result<(Signature<C>, bool)>;
79}
80
81#[cfg(feature = "arithmetic")]
82impl<C, T> SignPrimitive<C> for T
83where
84 C: PrimeCurve + ProjectiveArithmetic,
85 T: RecoverableSignPrimitive<C>,
86 SignatureSize<C>: ArrayLength<u8>,
87{
88 fn try_sign_prehashed<K: Borrow<Scalar<C>> + Invert<Output = Scalar<C>>>(
89 &self,
90 ephemeral_scalar: &K,
91 hashed_msg: &Scalar<C>,
92 ) -> Result<Signature<C>> {
93 self.try_sign_recoverable_prehashed(ephemeral_scalar, hashed_msg)
94 .map(|res| res.0)
95 }
96}
97
98/// Verify the given prehashed message using ECDSA.
99///
100/// This trait is intended to be implemented on type which can access
101/// the affine point represeting the public key via `&self`, such as a
102/// particular curve's `AffinePoint` type.
103#[cfg(feature = "arithmetic")]
104#[cfg_attr(docsrs, doc(cfg(feature = "arithmetic")))]
105pub trait VerifyPrimitive<C>
106where
107 C: PrimeCurve + ProjectiveArithmetic,
108 SignatureSize<C>: ArrayLength<u8>,
109{
110 /// Verify the prehashed message against the provided signature
111 ///
112 /// Accepts the following arguments:
113 ///
114 /// - `hashed_msg`: prehashed message to be verified
115 /// - `signature`: signature to be verified against the key and message
116 fn verify_prehashed(&self, hashed_msg: &Scalar<C>, signature: &Signature<C>) -> Result<()>;
117}
118
119/// Bind a preferred [`Digest`] algorithm to an elliptic curve type.
120///
121/// Generally there is a preferred variety of the SHA-2 family used with ECDSA
122/// for a particular elliptic curve.
123///
124/// This trait can be used to specify it, and with it receive a blanket impl of
125/// [`PrehashSignature`], used by [`signature_derive`][1]) for the [`Signature`]
126/// type for a particular elliptic curve.
127///
128/// [1]: https://github.com/RustCrypto/traits/tree/master/signature/derive
129#[cfg(feature = "digest")]
130#[cfg_attr(docsrs, doc(cfg(feature = "digest")))]
131pub trait DigestPrimitive: PrimeCurve {
132 /// Preferred digest to use when computing ECDSA signatures for this
133 /// elliptic curve. This should be a member of the SHA-2 family.
134 type Digest: Digest;
135}
136
137/// Instantiate this type from the output of a digest.
138///
139/// This trait is intended for use in ECDSA and should perform a conversion
140/// which is compatible with the rules for calculating `h` from `H(M)` set out
141/// in RFC6979 section 2.4. This conversion cannot fail.
142///
143/// This trait may also be useful for other hash-to-scalar or hash-to-curve
144/// use cases.
145#[cfg(feature = "digest")]
146#[cfg_attr(docsrs, doc(cfg(feature = "digest")))]
147pub trait FromDigest<C: PrimeCurve> {
148 /// Instantiate this type from a [`Digest`] instance
149 fn from_digest<D>(digest: D) -> Self
150 where
151 D: Digest<OutputSize = FieldSize<C>>;
152}
153
154#[cfg(feature = "digest")]
155impl<C> PrehashSignature for Signature<C>
156where
157 C: DigestPrimitive,
158 <FieldSize<C> as core::ops::Add>::Output: ArrayLength<u8>,
159{
160 type Digest = C::Digest;
161}