Skip to content

Commit fe9f829

Browse files
LLFournnickfarrow
authored andcommitted
Remove type parameters from Message and Signature, and remove unused Slice type
"Secret messages" was a nice idea - it made it possible to have constant time verification of signatures so you didn't leak via side channel which signature you were verifying. But since almost no one has this peculiar requirement it created more friction than it was worth. This simplifies the API by: - Removing the secrecy type parameter from both Message and Signature types, making them always public - Removing the unused Slice type from secp256kfun which was part of the secrecy marking system but wasn't being used
1 parent 1b1f081 commit fe9f829

File tree

17 files changed

+95
-233
lines changed

17 files changed

+95
-233
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
- `hash_to_curve_rfc9381_tai` - RFC 9381 VRF try-and-increment format
1313
- Add `Message::new` for BIP340-compliant domain separation using 33-byte padded prefix
1414
- Deprecate `Message::plain` which uses non-standard 64-byte prefix
15+
- Remove type parameters from `Message` and `Signature` types (always public now)
16+
- Remove unused `Slice` type from secp256kfun
1517

1618
## v0.11.0
1719

schnorr_fun/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ let schnorr = Schnorr::<Sha256, _>::new(nonce_gen.clone());
3939
// Generate your public/private key-pair
4040
let keypair = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
4141
// Sign a variable length message
42-
let message = Message::<Public>::plain("the-times-of-london", b"Chancellor on brink of second bailout for banks");
42+
let message = Message::new("the-times-of-london", b"Chancellor on brink of second bailout for banks");
4343
// Sign the message with our keypair
4444
let signature = schnorr.sign(&keypair, message);
4545
// Get the verifier's key

schnorr_fun/benches/bench_schnorr.rs

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ fn sign_schnorr(c: &mut Criterion) {
2020
{
2121
let keypair = schnorr.new_keypair(*SK);
2222
group.bench_function("fun::schnorr_sign", |b| {
23-
b.iter(|| schnorr.sign(&keypair, Message::<Public>::raw(MESSAGE)))
23+
b.iter(|| schnorr.sign(&keypair, Message::raw(MESSAGE)))
2424
});
2525
}
2626

@@ -40,19 +40,14 @@ fn verify_schnorr(c: &mut Criterion) {
4040
let mut group = c.benchmark_group("schnorr_verify");
4141
let keypair = schnorr.new_keypair(*SK);
4242
{
43-
let message = Message::<Public>::raw(MESSAGE);
43+
let message = Message::raw(MESSAGE);
4444
let sig = schnorr.sign(&keypair, message);
4545
let verification_key = &keypair.public_key();
4646
group.bench_function("fun::schnorr_verify", |b| {
4747
b.iter(|| schnorr.verify(verification_key, message, &sig))
4848
});
4949

50-
{
51-
let sig = sig.set_secrecy::<Secret>();
52-
group.bench_function("fun::schnorr_verify_ct", |b| {
53-
b.iter(|| schnorr.verify(verification_key, message, &sig))
54-
});
55-
}
50+
// Constant-time verification is no longer supported after removing type parameters
5651
}
5752

5853
{

schnorr_fun/src/adaptor/encrypted_signature.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,11 +47,8 @@ mod test {
4747
let schnorr = crate::new_with_deterministic_nonces::<sha2::Sha256>();
4848
let kp = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
4949
let encryption_key = Point::random(&mut rand::thread_rng());
50-
let encrypted_signature = schnorr.encrypted_sign(
51-
&kp,
52-
&encryption_key,
53-
Message::<Public>::plain("test", b"foo"),
54-
);
50+
let encrypted_signature =
51+
schnorr.encrypted_sign(&kp, &encryption_key, Message::new("test", b"foo"));
5552
let serialized = bincode::encode_to_vec(
5653
bincode::serde::Compat(&encrypted_signature),
5754
bincode::config::standard(),

schnorr_fun/src/adaptor/mod.rs

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
//! let verification_key = signing_keypair.public_key();
2424
//! let decryption_key = Scalar::random(&mut rand::thread_rng());
2525
//! let encryption_key = schnorr.encryption_key_for(&decryption_key);
26-
//! let message = Message::<Public>::plain("text-bitcoin", b"send 1 BTC to Bob");
26+
//! let message = Message::new("text-bitcoin", b"send 1 BTC to Bob");
2727
//!
2828
//! // Alice knows: signing_keypair, encryption_key
2929
//! // Bob knows: decryption_key, verification_key
@@ -68,7 +68,7 @@ pub trait EncryptedSign {
6868
&self,
6969
signing_keypair: &KeyPair<EvenY>,
7070
encryption_key: &Point<Normal, impl Secrecy>,
71-
message: Message<'_, impl Secrecy>,
71+
message: Message<'_>,
7272
) -> EncryptedSignature;
7373
}
7474

@@ -81,7 +81,7 @@ where
8181
&self,
8282
signing_key: &KeyPair<EvenY>,
8383
encryption_key: &Point<Normal, impl Secrecy>,
84-
message: Message<'_, impl Secrecy>,
84+
message: Message<'_>,
8585
) -> EncryptedSignature {
8686
let (x, X) = signing_key.as_tuple();
8787
let Y = encryption_key;
@@ -139,7 +139,7 @@ pub trait Adaptor {
139139
&self,
140140
verification_key: &Point<EvenY, impl Secrecy>,
141141
encryption_key: &Point<impl PointType, impl Secrecy>,
142-
message: Message<'_, impl Secrecy>,
142+
message: Message<'_>,
143143
encrypted_signature: &EncryptedSignature<impl Secrecy>,
144144
) -> bool;
145145

@@ -179,7 +179,7 @@ pub trait Adaptor {
179179
&self,
180180
encryption_key: &Point<impl Normalized, impl Secrecy>,
181181
encrypted_signature: &EncryptedSignature<impl Secrecy>,
182-
signature: &Signature<impl Secrecy>,
182+
signature: &Signature,
183183
) -> Option<Scalar>;
184184
}
185185

@@ -195,7 +195,7 @@ where
195195
&self,
196196
verification_key: &Point<EvenY, impl Secrecy>,
197197
encryption_key: &Point<impl PointType, impl Secrecy>,
198-
message: Message<'_, impl Secrecy>,
198+
message: Message<'_>,
199199
encrypted_signature: &EncryptedSignature<impl Secrecy>,
200200
) -> bool {
201201
let EncryptedSignature {
@@ -236,7 +236,7 @@ where
236236
&self,
237237
encryption_key: &Point<impl PointType, impl Secrecy>,
238238
encrypted_signature: &EncryptedSignature<impl Secrecy>,
239-
signature: &Signature<impl Secrecy>,
239+
signature: &Signature,
240240
) -> Option<Scalar> {
241241
if signature.R != encrypted_signature.R {
242242
return None;
@@ -298,7 +298,7 @@ mod test {
298298
let signing_keypair = schnorr.new_keypair(secret_key);
299299
let verification_key = signing_keypair.public_key();
300300
let encryption_key = schnorr.encryption_key_for(&decryption_key);
301-
let message = Message::<Public>::new("test", b"give 100 coins to Bob".as_ref());
301+
let message = Message::new("test", b"give 100 coins to Bob".as_ref());
302302

303303
let encrypted_signature =
304304
schnorr.encrypted_sign(&signing_keypair, &encryption_key, message);

schnorr_fun/src/frost/chilldkg.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ pub mod simplepedpop {
101101
let secret_poly = poly::scalar::generate(threshold as usize, rng);
102102
let pop_keypair = KeyPair::new_xonly(secret_poly[0]);
103103
// XXX The thing that's singed differs from the spec
104-
let pop = schnorr.sign(&pop_keypair, Message::<Public>::empty());
104+
let pop = schnorr.sign(&pop_keypair, Message::empty());
105105
let com = poly::scalar::to_point_poly(&secret_poly);
106106

107107
let shares = share_receivers
@@ -201,7 +201,7 @@ pub mod simplepedpop {
201201
}
202202

203203
let (first_coeff_even_y, _) = input.com[0].into_point_with_even_y();
204-
if !schnorr.verify(&first_coeff_even_y, Message::<Public>::empty(), &input.pop) {
204+
if !schnorr.verify(&first_coeff_even_y, Message::empty(), &input.pop) {
205205
return Err("☠ pop didn't verify");
206206
}
207207
*entry = Some(input);
@@ -324,7 +324,7 @@ pub mod simplepedpop {
324324
{
325325
for (key_contrib, pop) in &agg_input.key_contrib {
326326
let (first_coeff_even_y, _) = key_contrib.into_point_with_even_y();
327-
if !schnorr.verify(&first_coeff_even_y, Message::<Public>::empty(), pop) {
327+
if !schnorr.verify(&first_coeff_even_y, Message::empty(), pop) {
328328
return Err(ReceiveShareError::InvalidPop);
329329
}
330330
}
@@ -606,7 +606,7 @@ pub mod encpedpop {
606606
{
607607
schnorr.sign(
608608
keypair,
609-
Message::<Public>::new("BIP DKG/cert", self.cert_bytes().as_ref()),
609+
Message::new("BIP DKG/cert", self.cert_bytes().as_ref()),
610610
)
611611
}
612612

@@ -620,7 +620,7 @@ pub mod encpedpop {
620620
) -> bool {
621621
schnorr.verify(
622622
&cert_key,
623-
Message::<Public>::new("BIP DKG/cert", self.cert_bytes().as_ref()),
623+
Message::new("BIP DKG/cert", self.cert_bytes().as_ref()),
624624
&signature,
625625
)
626626
}

schnorr_fun/src/frost/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
//! let xonly_shared_key = shared_key.into_xonly(); // this is the key signatures will be valid under
3131
//! let xonly_my_secret_share = my_secret_share.into_xonly();
3232
//! # let xonly_secret_share3 = secret_share3.into_xonly();
33-
//! let message = Message::plain("my-app", b"chancellor on brink of second bailout for banks");
33+
//! let message = Message::new("my-app", b"chancellor on brink of second bailout for banks");
3434
//! // Generate nonces for this signing session (and send them to coordinator somehow)
3535
//! // ⚠ session_id MUST be different for every signing attempt to avoid nonce reuse (if using deterministic nonces).
3636
//! let session_id = b"signing-ominous-message-about-banks-attempt-1".as_slice();
@@ -446,7 +446,7 @@ mod test {
446446
let session = frost.coordinator_sign_session(
447447
&frost_poly.into_xonly(),
448448
BTreeMap::from_iter([(s!(1).public(), nonce), (s!(2).public(), malicious_nonce)]),
449-
Message::<Public>::new("test", b"hello"),
449+
Message::new("test", b"hello"),
450450
);
451451

452452
assert_eq!(session.final_nonce(), *G);

schnorr_fun/src/frost/shared_key.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -231,7 +231,7 @@ impl<T: PointType, Z: ZeroChoice> SharedKey<T, Z> {
231231
}
232232

233233
impl SharedKey<Normal> {
234-
/// Convert the key into a BIP340 "x-only" SharedKey.
234+
/// Convert the key into a [BIP340] "x-only" SharedKey.
235235
///
236236
/// This is the [BIP340] compatible version of the key which you can put in a segwitv1 output.
237237
///

schnorr_fun/src/message.rs

Lines changed: 18 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,13 @@
11
use secp256kfun::{
2-
Slice,
32
digest::{self},
43
hash::HashInto,
5-
marker::*,
64
};
75

86
/// A message to be signed.
9-
///
10-
/// The `S` parameter is a [`Secrecy`] which is used when signing a verifying to check whether the
11-
/// challenge scalar produced with the message should be secret.
127
#[derive(Debug, Clone, Copy, PartialEq)]
13-
pub struct Message<'a, S = Public> {
8+
pub struct Message<'a> {
149
/// The message bytes
15-
pub bytes: Slice<'a, S>,
10+
pub bytes: &'a [u8],
1611
/// The optional application tag to separate the signature from other applications.
1712
#[deprecated(
1813
since = "0.11.0",
@@ -26,12 +21,12 @@ pub struct Message<'a, S = Public> {
2621
}
2722

2823
#[allow(deprecated)]
29-
impl<'a, S: Secrecy> Message<'a, S> {
24+
impl<'a> Message<'a> {
3025
/// Create a raw message with no domain separation. The message bytes will be passed straight into the
3126
/// challenge hash. Usually, you only use this when signing a pre-hashed message.
3227
pub fn raw(bytes: &'a [u8]) -> Self {
3328
Message {
34-
bytes: Slice::from(bytes),
29+
bytes,
3530
app_tag: None,
3631
bip340_domain_sep: None,
3732
}
@@ -51,8 +46,8 @@ impl<'a, S: Secrecy> Message<'a, S> {
5146
///
5247
/// # Example
5348
/// ```
54-
/// use schnorr_fun::{Message, fun::marker::Public};
55-
/// let message = Message::<Public>::new("my-app/sign", b"hello world");
49+
/// use schnorr_fun::Message;
50+
/// let message = Message::new("my-app/sign", b"hello world");
5651
/// ```
5752
pub fn new(domain_sep: &'static str, bytes: &'a [u8]) -> Self {
5853
assert!(!domain_sep.is_empty(), "domain separator must not be empty");
@@ -61,7 +56,7 @@ impl<'a, S: Secrecy> Message<'a, S> {
6156
"domain separator must be 33 bytes or less"
6257
);
6358
Message {
64-
bytes: Slice::from(bytes),
59+
bytes,
6560
app_tag: None,
6661
bip340_domain_sep: Some(domain_sep),
6762
}
@@ -88,7 +83,7 @@ impl<'a, S: Secrecy> Message<'a, S> {
8883
assert!(app_tag.len() <= 64, "tag must be 64 bytes or less");
8984
assert!(!app_tag.is_empty(), "tag must not be empty");
9085
Message {
91-
bytes: Slice::from(bytes),
86+
bytes,
9287
app_tag: Some(app_tag),
9388
bip340_domain_sep: None,
9489
}
@@ -102,15 +97,15 @@ impl<'a, S: Secrecy> Message<'a, S> {
10297
/// Length of the message as it is hashed
10398
pub fn len(&self) -> usize {
10499
match (self.app_tag, self.bip340_domain_sep) {
105-
(Some(_), _) => 64 + self.bytes.as_inner().len(),
106-
(_, Some(_)) => 33 + self.bytes.as_inner().len(), // BIP340 style uses 33-byte prefix
107-
(None, None) => self.bytes.as_inner().len(),
100+
(Some(_), _) => 64 + self.bytes.len(),
101+
(_, Some(_)) => 33 + self.bytes.len(), // BIP340 style uses 33-byte prefix
102+
(None, None) => self.bytes.len(),
108103
}
109104
}
110105
}
111106

112107
#[allow(deprecated)]
113-
impl<S> HashInto for Message<'_, S> {
108+
impl HashInto for Message<'_> {
114109
fn hash_into(self, hash: &mut impl digest::Update) {
115110
if let Some(prefix) = self.app_tag {
116111
let mut padded_prefix = [0u8; 64];
@@ -122,7 +117,7 @@ impl<S> HashInto for Message<'_, S> {
122117
padded_prefix[..domain_sep.len()].copy_from_slice(domain_sep.as_bytes());
123118
hash.update(&padded_prefix);
124119
}
125-
hash.update(self.bytes.as_inner());
120+
hash.update(self.bytes);
126121
}
127122
}
128123

@@ -134,13 +129,13 @@ mod test {
134129
#[test]
135130
fn bip340_domain_separation() {
136131
// Test that BIP340 domain separation uses 33-byte prefix
137-
let msg = Message::<Public>::new("test", b"hello");
132+
let msg = Message::new("test", b"hello");
138133

139134
// Expected: "test" padded to 33 bytes + "hello"
140135
let mut expected_hash = Sha256::default();
141136
let mut padded_prefix = [0u8; 33];
142137
padded_prefix[..4].copy_from_slice(b"test");
143-
expected_hash.update(&padded_prefix);
138+
expected_hash.update(padded_prefix);
144139
expected_hash.update(b"hello");
145140

146141
let mut actual_hash = Sha256::default();
@@ -154,20 +149,20 @@ mod test {
154149

155150
#[test]
156151
fn message_new_fixed_key_signature() {
157-
use crate::{fun::s, new_with_deterministic_nonces};
152+
use crate::{Signature, fun::prelude::*, new_with_deterministic_nonces};
158153
use core::str::FromStr;
159154

160155
// Fixed test to ensure Message::new domain separation doesn't accidentally change
161156
let schnorr = new_with_deterministic_nonces::<Sha256>();
162157
let secret_key = s!(42);
163158
let keypair = schnorr.new_keypair(secret_key);
164159

165-
let message = Message::<Public>::new("test-app", b"test message");
160+
let message = Message::new("test-app", b"test message");
166161
let signature = schnorr.sign(&keypair, message);
167162

168163
// This signature was generated with the current implementation and should never change
169164
// to ensure backwards compatibility
170-
let expected_sig = crate::Signature::<Public>::from_str(
165+
let expected_sig = Signature::from_str(
171166
"5c49762df465f21993af631caedb3e478793142e15f200e70511e5af71387e52a3b9b6af189fa4b28a767254f2a8977f2e9db1866ad4dfbb083bb4fbd8dfe82e"
172167
).unwrap();
173168

0 commit comments

Comments
 (0)