Skip to content

Commit 4ce914b

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 42ab25e commit 4ce914b

File tree

4 files changed

+75
-115
lines changed

4 files changed

+75
-115
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(msg.to_byte_array());
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(msg.to_byte_array());
2819
let seckey = SecretKey::from_byte_array(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 = ecdsa::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: 63 additions & 92 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(msg);
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,11 @@ 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_byte_array(ONE).unwrap();
305290
let msg = Message::from_digest(ONE);
306-
307-
let sig = s.sign_ecdsa_recoverable(msg, &sk);
291+
let sig = RecoverableSignature::sign_ecdsa_recoverable(msg, &sk);
308292

309293
assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[
310294
0x66, 0x73, 0xff, 0xad, 0x21, 0x47, 0x74, 0x1f,
@@ -320,16 +304,13 @@ mod tests {
320304

321305
#[test]
322306
#[cfg(not(secp256k1_fuzz))] // fixed sig vectors can't work with fuzz-sigs
323-
#[cfg(feature = "std")]
324307
#[rustfmt::skip]
325308
fn sign_with_noncedata() {
326-
let s = Secp256k1::new();
327-
328309
let sk = SecretKey::from_byte_array(ONE).unwrap();
329-
let msg = Message::from_digest(ONE);
330310
let noncedata = [42u8; 32];
311+
let msg = Message::from_digest(ONE);
331312

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

334315
assert_eq!(Ok(sig), RecoverableSignature::from_compact(&[
335316
0xb5, 0x0b, 0xb6, 0x79, 0x5f, 0x31, 0x74, 0x8a,
@@ -351,57 +332,48 @@ mod tests {
351332
let msg = Message::from_digest(crate::test_random_32_bytes());
352333
let (sk, pk) = crate::test_random_keypair();
353334

354-
let sigr = s.sign_ecdsa_recoverable(msg, &sk);
335+
let sigr = RecoverableSignature::sign_ecdsa_recoverable(msg, &sk);
355336
let sig = sigr.to_standard();
356337

357338
let msg = Message::from_digest(crate::test_random_32_bytes());
358339
assert_eq!(s.verify_ecdsa(&sig, msg, &pk), Err(Error::IncorrectSignature));
359340

360-
let recovered_key = s.recover_ecdsa(msg, &sigr).unwrap();
341+
let recovered_key = sigr.recover_ecdsa(msg).unwrap();
361342
assert!(recovered_key != pk);
362343
}
363344

364345
#[test]
365-
#[cfg(feature = "std")]
366346
fn sign_with_recovery() {
367-
let s = Secp256k1::new();
368-
369347
let msg = Message::from_digest(crate::test_random_32_bytes());
370348
let (sk, pk) = crate::test_random_keypair();
371349

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

374-
assert_eq!(s.recover_ecdsa(msg, &sig), Ok(pk));
352+
assert_eq!(sig.recover_ecdsa(msg), Ok(pk));
375353
}
376354

377355
#[test]
378-
#[cfg(feature = "std")]
379356
fn sign_with_recovery_and_noncedata() {
380-
let s = Secp256k1::new();
381-
382357
let msg = Message::from_digest(crate::test_random_32_bytes());
383358
let noncedata = crate::test_random_32_bytes();
384359

385360
let (sk, pk) = crate::test_random_keypair();
386361

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

389-
assert_eq!(s.recover_ecdsa(msg, &sig), Ok(pk));
364+
assert_eq!(sig.recover_ecdsa(msg), Ok(pk));
390365
}
391366

392367
#[test]
393-
#[cfg(feature = "std")]
394368
fn bad_recovery() {
395-
let s = Secp256k1::new();
396-
397369
let msg = Message::from_digest(crate::test_random_32_bytes());
398370

399371
// Zero is not a valid sig
400372
let sig = RecoverableSignature::from_compact(&[0; 64], RecoveryId::Zero).unwrap();
401-
assert_eq!(s.recover_ecdsa(msg, &sig), Err(Error::InvalidSignature));
373+
assert_eq!(sig.recover_ecdsa(msg), Err(Error::InvalidSignature));
402374
// ...but 111..111 is
403375
let sig = RecoverableSignature::from_compact(&[1; 64], RecoveryId::Zero).unwrap();
404-
assert!(s.recover_ecdsa(msg, &sig).is_ok());
376+
assert!(sig.recover_ecdsa(msg).is_ok());
405377
}
406378

407379
#[test]
@@ -455,21 +427,20 @@ mod tests {
455427
}
456428

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

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

464436
#[bench]
465437
pub fn bench_recover(bh: &mut Bencher) {
466-
let s = Secp256k1::new();
467438
let msg = Message::from_digest(crate::test_random_32_bytes());
468439
let sk = SecretKey::test_random();
469-
let sig = s.sign_ecdsa_recoverable(msg, &sk);
440+
let sig = RecoverableSignature::sign_ecdsa_recoverable(msg, &sk);
470441

471442
bh.iter(|| {
472-
let res = s.recover_ecdsa(msg, &sig).unwrap();
443+
let res = sig.recover_ecdsa(msg).unwrap();
473444
black_box(res);
474445
});
475446
}

0 commit comments

Comments
 (0)