1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use crate::fun::{Point, Scalar, marker::*, rand_core::RngCore};
/// A Schnorr signature.
#[derive(Clone, Eq, PartialEq, Copy)]
pub struct Signature {
/// The signature's public nonce
///
/// [`Point`]: secp256kfun::Point
pub R: Point<EvenY>,
/// The challenge _response_ part of the signature.
pub s: Scalar<Public, Zero>,
}
impl Signature {
/// Serializes the signature as 64 bytes -- First the 32-byte nonce
/// x-coordinate and then the 32-byte challenge response scalar.
/// # Examples
/// ```
/// # let signature = schnorr_fun::Signature::random(&mut rand::thread_rng());
/// let bytes = signature.to_bytes();
/// assert_eq!(signature.R.to_xonly_bytes(), bytes[..32]);
/// assert_eq!(signature.s.to_bytes(), bytes[32..]);
/// ```
pub fn to_bytes(&self) -> [u8; 64] {
let mut bytes = [0u8; 64];
bytes[0..32].copy_from_slice(&self.R.to_xonly_bytes());
bytes[32..64].copy_from_slice(&self.s.to_bytes());
bytes
}
/// Gets a reference to the signature components as a tuple.
///
/// # Examples
/// ```
/// # let signature = schnorr_fun::Signature::random(&mut rand::thread_rng());
/// let (R, s) = signature.as_tuple();
/// ```
pub fn as_tuple(&self) -> (Point<EvenY>, &Scalar<Public, Zero>) {
(self.R, &self.s)
}
/// Generates a uniformly distributed signature. It will be valid for an
/// infinite number of messages on every key but computationally you will
/// never be able to find one! Useful for testing.
///
/// # Examples
///
/// ```
/// use schnorr_fun::Signature;
/// let random_signature = Signature::random(&mut rand::thread_rng());
pub fn random<R: RngCore>(rng: &mut R) -> Self {
Signature {
R: Point::random(rng).into_point_with_even_y().0,
s: Scalar::random(rng).public().mark_zero(),
}
}
/// Deserializes a signature from the byte representation produced by [`to_bytes`].
///
/// This returns `None` if the first 32 bytes were not a valid x-only key or the last 32 bytes were not a valid scalar.
///
/// # Examples
/// ```
/// # use schnorr_fun::Signature;
/// # let bytes = [0u8;64];
/// match Signature::from_bytes(bytes) {
/// Some(signature) => println!("the bytes were a valid encoding of a signature!"),
/// None => eprintln!("the bytes did *not* encode a valid signature"),
/// }
/// ```
///
/// [`to_bytes`]: crate::Signature::to_bytes
pub fn from_bytes(bytes: [u8; 64]) -> Option<Self> {
let mut R = [0u8; 32];
R.copy_from_slice(&bytes[0..32]);
let mut s = [0u8; 32];
s.copy_from_slice(&bytes[32..64]);
let R = Point::from_xonly_bytes(R)?;
Some(Signature {
R,
s: Scalar::from_bytes(s)?,
})
}
}
secp256kfun::impl_fromstr_deserialize! {
name => "secp256k1 Schnorr signature",
fn from_bytes(bytes: [u8;64]) -> Option<Signature> {
Signature::from_bytes(bytes)
}
}
secp256kfun::impl_display_debug_serialize! {
fn to_bytes(signature: &Signature) -> [u8;64] {
signature.to_bytes()
}
}
#[cfg(test)]
mod test {
#[cfg(feature = "serde")]
#[test]
fn signature_serialization_roundtrip() {
use super::*;
use crate::{Message, fun::Scalar};
let schnorr = crate::new_with_deterministic_nonces::<sha2::Sha256>();
let kp = schnorr.new_keypair(Scalar::random(&mut rand::thread_rng()));
#[allow(deprecated)]
let signature = schnorr.sign(&kp, Message::new("test", b"foo"));
let serialized = bincode::encode_to_vec(
bincode::serde::Compat(&signature),
bincode::config::standard(),
)
.unwrap();
assert_eq!(serialized.len(), 64);
let deserialized = bincode::decode_from_slice::<bincode::serde::Compat<Signature>, _>(
&serialized,
bincode::config::standard(),
)
.unwrap()
.0;
assert_eq!(signature, deserialized.0);
}
}