Skip to content

Commit beb7271

Browse files
authored
ecdsa: add trial recovery functionality (#580)
This functionality can be used to compute a `RecoveryId` from a verifying key, message, and signature, without having this information computed during signing time. It uses a brute force method and is thus much slower than computing it at signing time.
1 parent 1578fe3 commit beb7271

3 files changed

Lines changed: 80 additions & 5 deletions

File tree

ecdsa/src/recovery.rs

Lines changed: 78 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,78 @@ impl RecoveryId {
7171
}
7272
}
7373

74+
#[cfg(feature = "verify")]
75+
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
76+
impl RecoveryId {
77+
/// Given a public key, message, and signature, use trial recovery
78+
/// to determine if a suitable recovery ID exists, or return an error
79+
/// otherwise.
80+
pub fn trial_recovery_from_msg<C>(
81+
verifying_key: &VerifyingKey<C>,
82+
msg: &[u8],
83+
signature: &Signature<C>,
84+
) -> Result<Self>
85+
where
86+
C: DigestPrimitive + PrimeCurve + ProjectiveArithmetic,
87+
AffinePoint<C>:
88+
DecompressPoint<C> + FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
89+
FieldSize<C>: sec1::ModulusSize,
90+
Scalar<C>: Reduce<C::UInt>,
91+
SignatureSize<C>: ArrayLength<u8>,
92+
{
93+
Self::trial_recovery_from_digest(verifying_key, C::Digest::new_with_prefix(msg), signature)
94+
}
95+
96+
/// Given a public key, message digest, and signature, use trial recovery
97+
/// to determine if a suitable recovery ID exists, or return an error
98+
/// otherwise.
99+
pub fn trial_recovery_from_digest<C, D>(
100+
verifying_key: &VerifyingKey<C>,
101+
digest: D,
102+
signature: &Signature<C>,
103+
) -> Result<Self>
104+
where
105+
C: PrimeCurve + ProjectiveArithmetic,
106+
D: Digest,
107+
AffinePoint<C>:
108+
DecompressPoint<C> + FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
109+
FieldSize<C>: sec1::ModulusSize,
110+
Scalar<C>: Reduce<C::UInt>,
111+
SignatureSize<C>: ArrayLength<u8>,
112+
{
113+
Self::trial_recovery_from_prehash(verifying_key, &digest.finalize(), signature)
114+
}
115+
116+
/// Given a public key, message digest, and signature, use trial recovery
117+
/// to determine if a suitable recovery ID exists, or return an error
118+
/// otherwise.
119+
pub fn trial_recovery_from_prehash<C>(
120+
verifying_key: &VerifyingKey<C>,
121+
prehash: &[u8],
122+
signature: &Signature<C>,
123+
) -> Result<Self>
124+
where
125+
C: PrimeCurve + ProjectiveArithmetic,
126+
AffinePoint<C>:
127+
DecompressPoint<C> + FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
128+
FieldSize<C>: sec1::ModulusSize,
129+
Scalar<C>: Reduce<C::UInt>,
130+
SignatureSize<C>: ArrayLength<u8>,
131+
{
132+
for id in 0..=Self::MAX {
133+
let recovery_id = RecoveryId(id);
134+
135+
if let Ok(vk) = VerifyingKey::recover_from_prehash(prehash, signature, recovery_id) {
136+
if verifying_key == &vk {
137+
return Ok(recovery_id);
138+
}
139+
}
140+
}
141+
142+
Err(Error::new())
143+
}
144+
}
145+
74146
impl TryFrom<u8> for RecoveryId {
75147
type Error = Error;
76148

@@ -89,7 +161,7 @@ impl From<RecoveryId> for u8 {
89161
#[cfg_attr(docsrs, doc(cfg(feature = "verify")))]
90162
impl<C> VerifyingKey<C>
91163
where
92-
C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
164+
C: PrimeCurve + ProjectiveArithmetic,
93165
AffinePoint<C>:
94166
DecompressPoint<C> + FromEncodedPoint<C> + ToEncodedPoint<C> + VerifyPrimitive<C>,
95167
FieldSize<C>: sec1::ModulusSize,
@@ -100,11 +172,14 @@ where
100172
/// [`RecoveryId`].
101173
///
102174
/// The message is first hashed using this curve's [`DigestPrimitive`].
103-
pub fn recover_from_message(
175+
pub fn recover_from_msg(
104176
msg: &[u8],
105177
signature: &Signature<C>,
106178
recovery_id: RecoveryId,
107-
) -> Result<Self> {
179+
) -> Result<Self>
180+
where
181+
C: DigestPrimitive,
182+
{
108183
Self::recover_from_digest(C::Digest::new_with_prefix(msg), signature, recovery_id)
109184
}
110185

ecdsa/src/sign.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ where
200200
impl<C> PrehashSigner<der::Signature<C>> for SigningKey<C>
201201
where
202202
C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
203-
C::Digest: BlockSizeUser + FixedOutput<OutputSize = FieldSize<C>> + FixedOutputReset,
203+
C::Digest: FixedOutput<OutputSize = FieldSize<C>>,
204204
C::UInt: for<'a> From<&'a Scalar<C>>,
205205
Scalar<C>: Invert<Output = CtOption<Scalar<C>>> + Reduce<C::UInt> + SignPrimitive<C>,
206206
SignatureSize<C>: ArrayLength<u8>,

ecdsa/src/verify.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ where
116116

117117
impl<C> PrehashVerifier<Signature<C>> for VerifyingKey<C>
118118
where
119-
C: PrimeCurve + ProjectiveArithmetic + DigestPrimitive,
119+
C: PrimeCurve + ProjectiveArithmetic,
120120
AffinePoint<C>: VerifyPrimitive<C>,
121121
Scalar<C>: Reduce<C::UInt>,
122122
SignatureSize<C>: ArrayLength<u8>,

0 commit comments

Comments
 (0)