schnorr_fun 0.13.0

BIP340 Schnorr signatures based on secp256kfun
Documentation
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);
    }
}