Skip to content

Commit a66b75a

Browse files
committed
recovery: rewrite API to not use context objects
This API is basically unused except for some niche or legacy applications, so I feel comfortable breaking it pretty dramatically. Move all the Secp256k1 functions onto RecoverableSignature and use self/Self as appropriate. Leave the stupid ecdsa_recoverable names even though they are even more redundant, because this module is basically in maintenance mode. We only do these changes since we'll be forced to once we drop the Secp256k1 object.
1 parent 87f3137 commit a66b75a

File tree

4 files changed

+74
-113
lines changed

4 files changed

+74
-113
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ unexpected_cfgs = { level = "deny", check-cfg = ['cfg(bench)', 'cfg(secp256k1_fu
5656

5757
[[example]]
5858
name = "sign_verify_recovery"
59-
required-features = ["recovery", "hashes", "std"]
59+
required-features = ["recovery", "hashes"]
6060

6161
[[example]]
6262
name = "sign_verify"

examples/sign_verify_recovery.rs

Lines changed: 8 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,36 +2,25 @@ extern crate hashes;
22
extern crate secp256k1;
33

44
use hashes::{sha256, Hash};
5-
use secp256k1::{ecdsa, Error, Message, PublicKey, Secp256k1, SecretKey, Signing, Verification};
6-
7-
fn recover<C: Verification>(
8-
secp: &Secp256k1<C>,
9-
msg: &[u8],
10-
sig: [u8; 64],
11-
recovery_id: u8,
12-
) -> Result<PublicKey, Error> {
5+
use secp256k1::{ecdsa, Error, Message, PublicKey, SecretKey};
6+
7+
fn recover(msg: &[u8], sig: [u8; 64], recovery_id: u8) -> Result<PublicKey, Error> {
138
let msg = sha256::Hash::hash(msg);
149
let msg = Message::from_digest_slice(msg.as_ref())?;
1510
let id = ecdsa::RecoveryId::try_from(i32::from(recovery_id))?;
1611
let sig = ecdsa::RecoverableSignature::from_compact(&sig, id)?;
1712

18-
secp.recover_ecdsa(msg, &sig)
13+
sig.recover_ecdsa(msg)
1914
}
2015

21-
fn sign_recovery<C: Signing>(
22-
secp: &Secp256k1<C>,
23-
msg: &[u8],
24-
seckey: [u8; 32],
25-
) -> Result<ecdsa::RecoverableSignature, Error> {
16+
fn sign_recovery(msg: &[u8], seckey: [u8; 32]) -> Result<ecdsa::RecoverableSignature, Error> {
2617
let msg = sha256::Hash::hash(msg);
2718
let msg = Message::from_digest_slice(msg.as_ref())?;
2819
let seckey = SecretKey::from_slice(&seckey)?;
29-
Ok(secp.sign_ecdsa_recoverable(msg, &seckey))
20+
Ok(ecdsa::RecoverableSignature::sign_ecdsa_recoverable(msg, &seckey))
3021
}
3122

3223
fn main() {
33-
let secp = Secp256k1::new();
34-
3524
let seckey = [
3625
59, 148, 11, 85, 134, 130, 61, 253, 2, 174, 59, 70, 27, 180, 51, 107, 94, 203, 174, 253,
3726
102, 39, 170, 146, 46, 252, 4, 143, 236, 12, 136, 28,
@@ -43,9 +32,9 @@ fn main() {
4332
.unwrap();
4433
let msg = b"This is some message";
4534

46-
let signature = sign_recovery(&secp, msg, seckey).unwrap();
35+
let signature = sign_recovery(msg, seckey).unwrap();
4736

4837
let (recovery_id, serialize_sig) = signature.serialize_compact();
4938

50-
assert_eq!(recover(&secp, msg, serialize_sig, recovery_id.to_u8()), Ok(pubkey));
39+
assert_eq!(recover(msg, serialize_sig, recovery_id.to_u8()), Ok(pubkey));
5140
}

no_std_test/src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use core::panic::PanicInfo;
5151

5252
use secp256k1::ecdh::{self, SharedSecret};
5353
use secp256k1::ffi::types::AlignedType;
54-
use secp256k1::rand::{self, RngCore};
54+
use secp256k1::rand::RngCore;
5555
use secp256k1::serde::Serialize;
5656
use secp256k1::*;
5757

@@ -93,9 +93,9 @@ fn start(_argc: isize, _argv: *const *const u8) -> isize {
9393
let sig = secp.sign_ecdsa(message, &secret_key);
9494
assert!(secp.verify_ecdsa(&sig, message, &public_key).is_ok());
9595

96-
let rec_sig = secp.sign_ecdsa_recoverable(message, &secret_key);
96+
let rec_sig = ecdso::RecoverableSignature::sign_ecdsa_recoverable(message, &secret_key);
9797
assert!(secp.verify_ecdsa(&rec_sig.to_standard(), message, &public_key).is_ok());
98-
assert_eq!(public_key, secp.recover_ecdsa(message, &rec_sig).unwrap());
98+
assert_eq!(public_key, rec_sig.recover_ecdsa(message).unwrap());
9999
let (rec_id, data) = rec_sig.serialize_compact();
100100
let new_rec_sig = ecdsa::RecoverableSignature::from_compact(&data, rec_id).unwrap();
101101
assert_eq!(rec_sig, new_rec_sig);

src/ecdsa/recovery.rs

Lines changed: 62 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use self::super_ffi::CPtr;
1010
use super::ffi as super_ffi;
1111
use crate::ecdsa::Signature;
1212
use crate::ffi::recovery as ffi;
13-
use crate::{key, Error, Message, Secp256k1, Signing, Verification};
13+
use crate::{key, Error, Message};
1414

1515
/// A tag used for recovering the public key from a compact signature.
1616
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
@@ -157,7 +157,7 @@ impl RecoverableSignature {
157157
#[inline]
158158
#[cfg(feature = "global-context")]
159159
pub fn recover(&self, msg: impl Into<Message>) -> Result<key::PublicKey, Error> {
160-
crate::SECP256K1.recover_ecdsa(msg, self)
160+
self.recover_ecdsa(msg)
161161
}
162162
}
163163

@@ -174,42 +174,49 @@ impl From<ffi::RecoverableSignature> for RecoverableSignature {
174174
fn from(sig: ffi::RecoverableSignature) -> RecoverableSignature { RecoverableSignature(sig) }
175175
}
176176

177-
impl<C: Signing> Secp256k1<C> {
177+
impl RecoverableSignature {
178178
fn sign_ecdsa_recoverable_with_noncedata_pointer(
179-
&self,
180179
msg: impl Into<Message>,
181180
sk: &key::SecretKey,
182181
noncedata_ptr: *const super_ffi::types::c_void,
183-
) -> RecoverableSignature {
182+
) -> Self {
184183
let msg = msg.into();
185184
let mut ret = ffi::RecoverableSignature::new();
186-
unsafe {
187-
// We can assume the return value because it's not possible to construct
188-
// an invalid signature from a valid `Message` and `SecretKey`
189-
assert_eq!(
190-
ffi::secp256k1_ecdsa_sign_recoverable(
191-
self.ctx.as_ptr(),
192-
&mut ret,
193-
msg.as_c_ptr(),
194-
sk.as_c_ptr(),
195-
super_ffi::secp256k1_nonce_function_rfc6979,
196-
noncedata_ptr
197-
),
198-
1
199-
);
185+
// xor the secret key and message together to get a rerandomization seed
186+
// for timing analysis defense-in-depth
187+
let mut rerandomize = sk.secret_bytes();
188+
for (rera, byte) in rerandomize.iter_mut().zip(msg[..].iter()) {
189+
*rera ^= *byte;
200190
}
191+
crate::with_raw_global_context(
192+
|ctx| unsafe {
193+
// We can assume the return value because it's not possible to construct
194+
// an invalid signature from a valid `Message` and `SecretKey`
195+
assert_eq!(
196+
ffi::secp256k1_ecdsa_sign_recoverable(
197+
ctx.as_ptr(),
198+
&mut ret,
199+
msg.as_c_ptr(),
200+
sk.as_c_ptr(),
201+
super_ffi::secp256k1_nonce_function_rfc6979,
202+
noncedata_ptr
203+
),
204+
1
205+
);
206+
},
207+
Some(&rerandomize),
208+
);
201209

202210
RecoverableSignature::from(ret)
203211
}
204212

205213
/// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce
206214
/// Requires a signing-capable context.
207215
pub fn sign_ecdsa_recoverable(
208-
&self,
209216
msg: impl Into<Message>,
210217
sk: &key::SecretKey,
211218
) -> RecoverableSignature {
212-
self.sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, ptr::null())
219+
Self::sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, ptr::null())
213220
}
214221

215222
/// Constructs a signature for `msg` using the secret key `sk` and RFC6979 nonce
@@ -218,38 +225,34 @@ impl<C: Signing> Secp256k1<C> {
218225
/// signatures are needed for the same Message and SecretKey while still using RFC6979.
219226
/// Requires a signing-capable context.
220227
pub fn sign_ecdsa_recoverable_with_noncedata(
221-
&self,
222228
msg: impl Into<Message>,
223229
sk: &key::SecretKey,
224230
noncedata: &[u8; 32],
225231
) -> RecoverableSignature {
226232
let noncedata_ptr = noncedata.as_ptr() as *const super_ffi::types::c_void;
227-
self.sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, noncedata_ptr)
233+
Self::sign_ecdsa_recoverable_with_noncedata_pointer(msg, sk, noncedata_ptr)
228234
}
229-
}
230235

231-
impl<C: Verification> Secp256k1<C> {
232236
/// Determines the public key for which `sig` is a valid signature for
233237
/// `msg`. Requires a verify-capable context.
234-
pub fn recover_ecdsa(
235-
&self,
236-
msg: impl Into<Message>,
237-
sig: &RecoverableSignature,
238-
) -> Result<key::PublicKey, Error> {
238+
pub fn recover_ecdsa(&self, msg: impl Into<Message>) -> Result<key::PublicKey, Error> {
239239
let msg = msg.into();
240-
unsafe {
241-
let mut pk = super_ffi::PublicKey::new();
242-
if ffi::secp256k1_ecdsa_recover(
243-
self.ctx.as_ptr(),
244-
&mut pk,
245-
sig.as_c_ptr(),
246-
msg.as_c_ptr(),
247-
) != 1
248-
{
249-
return Err(Error::InvalidSignature);
250-
}
251-
Ok(key::PublicKey::from(pk))
252-
}
240+
crate::with_raw_global_context(
241+
|ctx| unsafe {
242+
let mut pk = super_ffi::PublicKey::new();
243+
if ffi::secp256k1_ecdsa_recover(
244+
ctx.as_ptr(),
245+
&mut pk,
246+
self.as_c_ptr(),
247+
msg.as_c_ptr(),
248+
) != 1
249+
{
250+
return Err(Error::InvalidSignature);
251+
}
252+
Ok(key::PublicKey::from(pk))
253+
},
254+
None,
255+
)
253256
}
254257
}
255258

@@ -264,28 +267,13 @@ mod tests {
264267
use crate::{Error, Message, Secp256k1, SecretKey};
265268

266269
#[test]
267-
#[cfg(feature = "std")]
268270
fn capabilities() {
269-
let sign = Secp256k1::signing_only();
270-
let vrfy = Secp256k1::verification_only();
271-
let full = Secp256k1::new();
272-
273271
let msg = crate::test_random_32_bytes();
274272
let msg = Message::from_digest_slice(&msg).unwrap();
275273

276-
// Try key generation
277274
let (sk, pk) = crate::test_random_keypair();
278-
279-
// Try signing
280-
assert_eq!(sign.sign_ecdsa_recoverable(msg, &sk), full.sign_ecdsa_recoverable(msg, &sk));
281-
let sigr = full.sign_ecdsa_recoverable(msg, &sk);
282-
283-
// Try pk recovery
284-
assert!(vrfy.recover_ecdsa(msg, &sigr).is_ok());
285-
assert!(full.recover_ecdsa(msg, &sigr).is_ok());
286-
287-
assert_eq!(vrfy.recover_ecdsa(msg, &sigr), full.recover_ecdsa(msg, &sigr));
288-
assert_eq!(full.recover_ecdsa(msg, &sigr), Ok(pk));
275+
let sigr = RecoverableSignature::sign_ecdsa_recoverable(msg, &sk);
276+
assert_eq!(sigr.recover_ecdsa(msg), Ok(pk));
289277
}
290278

291279
#[test]
@@ -296,15 +284,12 @@ mod tests {
296284

297285
#[test]
298286
#[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs
299-
#[cfg(feature = "std")]
300287
#[rustfmt::skip]
301288
fn sign() {
302-
let s = Secp256k1::new();
303-
304289
let sk = SecretKey::from_slice(&ONE).unwrap();
305290
let msg = Message::from_digest_slice(&ONE).unwrap();
306291

307-
let sig = s.sign_ecdsa_recoverable(msg, &sk);
292+
let sig = RecoverableSignature::sign_ecdsa_recoverable(msg, &sk);
308293

309294
assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[
310295
0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
@@ -320,16 +305,13 @@ mod tests {
320305

321306
#[test]
322307
#[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs
323-
#[cfg(feature = "std")]
324308
#[rustfmt::skip]
325309
fn sign_with_noncedata() {
326-
let s = Secp256k1::new();
327-
328310
let sk = SecretKey::from_slice(&ONE).unwrap();
329311
let msg = Message::from_digest_slice(&ONE).unwrap();
330312
let noncedata = [42u8; 32];
331313

332-
let sig = s.sign_ecdsa_recoverable_with_noncedata(msg, &sk, &noncedata);
314+
let sig = RecoverableSignature::sign_ecdsa_recoverable_with_noncedata(msg, &sk, &noncedata);
333315

334316
assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[
335317
0xb5, 0x0b, 0xb6, 0x79, 0x5f, 0x31, 0x74, 0x8a,
@@ -351,57 +333,48 @@ mod tests {
351333
let msg = Message::from_digest(crate::test_random_32_bytes());
352334
let (sk, pk) = crate::test_random_keypair();
353335

354-
let sigr = s.sign_ecdsa_recoverable(msg, &sk);
336+
let sigr = RecoverableSignature::sign_ecdsa_recoverable(msg, &sk);
355337
let sig = sigr.to_standard();
356338

357339
let msg = Message::from_digest(crate::test_random_32_bytes());
358340
assert_eq!(s.verify_ecdsa(&sig, msg, &pk), Err(Error::IncorrectSignature));
359341

360-
let recovered_key = s.recover_ecdsa(msg, &sigr).unwrap();
342+
let recovered_key = sigr.recover_ecdsa(msg).unwrap();
361343
assert!(recovered_key != pk);
362344
}
363345

364346
#[test]
365-
#[cfg(feature = "std")]
366347
fn sign_with_recovery() {
367-
let s = Secp256k1::new();
368-
369348
let msg = Message::from_digest(crate::test_random_32_bytes());
370349
let (sk, pk) = crate::test_random_keypair();
371350

372-
let sig = s.sign_ecdsa_recoverable(msg, &sk);
351+
let sig = RecoverableSignature::sign_ecdsa_recoverable(msg, &sk);
373352

374-
assert_eq!(s.recover_ecdsa(msg, &sig), Ok(pk));
353+
assert_eq!(sig.recover_ecdsa(msg), Ok(pk));
375354
}
376355

377356
#[test]
378-
#[cfg(feature = "std")]
379357
fn sign_with_recovery_and_noncedata() {
380-
let s = Secp256k1::new();
381-
382358
let msg = Message::from_digest(crate::test_random_32_bytes());
383359
let noncedata = crate::test_random_32_bytes();
384360

385361
let (sk, pk) = crate::test_random_keypair();
386362

387-
let sig = s.sign_ecdsa_recoverable_with_noncedata(msg, &sk, &noncedata);
363+
let sig = RecoverableSignature::sign_ecdsa_recoverable_with_noncedata(msg, &sk, &noncedata);
388364

389-
assert_eq!(s.recover_ecdsa(msg, &sig), Ok(pk));
365+
assert_eq!(sig.recover_ecdsa(msg), Ok(pk));
390366
}
391367

392368
#[test]
393-
#[cfg(feature = "std")]
394369
fn bad_recovery() {
395-
let s = Secp256k1::new();
396-
397370
let msg = Message::from_digest(crate::test_random_32_bytes());
398371

399372
// Zero is not a valid sig
400373
let sig = RecoverableSignature::from_compact(&[0; 64], RecoveryId::Zero).unwrap();
401-
assert_eq!(s.recover_ecdsa(msg, &sig), Err(Error::InvalidSignature));
374+
assert_eq!(sig.recover_ecdsa(msg), Err(Error::InvalidSignature));
402375
// ...but 111..111 is
403376
let sig = RecoverableSignature::from_compact(&[1; 64], RecoveryId::Zero).unwrap();
404-
assert!(s.recover_ecdsa(msg, &sig).is_ok());
377+
assert!(sig.recover_ecdsa(msg).is_ok());
405378
}
406379

407380
#[test]
@@ -455,21 +428,20 @@ mod tests {
455428
}
456429

457430
#[cfg(bench)]
458-
#[cfg(feature = "std")] // Currently only a single bench that requires "rand" + "std".
459431
mod benches {
460432
use test::{black_box, Bencher};
461433

462-
use crate::{Message, Secp256k1, SecretKey};
434+
use super::RecoverableSignature;
435+
use crate::{Message, SecretKey};
463436

464437
#[bench]
465438
pub fn bench_recover(bh: &mut Bencher) {
466-
let s = Secp256k1::new();
467439
let msg = Message::from_digest(crate::test_random_32_bytes());
468440
let sk = SecretKey::test_random();
469-
let sig = s.sign_ecdsa_recoverable(msg, &sk);
441+
let sig = RecoverableSignature::sign_ecdsa_recoverable(msg, &sk);
470442

471443
bh.iter(|| {
472-
let res = s.recover_ecdsa(msg, &sig).unwrap();
444+
let res = sig.recover_ecdsa(msg).unwrap();
473445
black_box(res);
474446
});
475447
}

0 commit comments

Comments
 (0)