bc_components/ec_key/
schnorr_public_key.rs

1use anyhow::{Result, bail};
2use bc_crypto::SCHNORR_SIGNATURE_SIZE;
3
4use crate::ECKeyBase;
5
6/// The size of a Schnorr public key in bytes (32 bytes).
7pub const SCHNORR_PUBLIC_KEY_SIZE: usize = bc_crypto::SCHNORR_PUBLIC_KEY_SIZE;
8
9/// A Schnorr (x-only) elliptic curve public key.
10///
11/// A `SchnorrPublicKey` is a 32-byte "x-only" public key used with the BIP-340
12/// Schnorr signature scheme. Unlike compressed ECDSA public keys (33 bytes)
13/// that include a prefix byte indicating the parity of the y-coordinate,
14/// Schnorr public keys only contain the x-coordinate of the elliptic curve
15/// point.
16///
17/// Schnorr signatures offer several advantages over traditional ECDSA
18/// signatures:
19///
20/// - **Linearity**: Enables key and signature aggregation (eg., for
21///   multisignature schemes)
22/// - **Non-malleability**: Prevents third parties from modifying signatures
23/// - **Smaller size**: Signatures are 64 bytes vs 70-72 bytes for ECDSA
24/// - **Better privacy**: Makes different multisig policies indistinguishable
25/// - **Provable security**: Requires fewer cryptographic assumptions than ECDSA
26///
27/// Schnorr signatures were introduced to Bitcoin via the Taproot upgrade
28/// (BIP-340) and are becoming more widely used in cryptocurrency applications.
29///
30/// # Examples
31///
32/// Verifying a Schnorr signature:
33///
34/// ```
35/// use bc_components::ECPrivateKey;
36///
37/// // Generate a private key
38/// let private_key = ECPrivateKey::new();
39///
40/// // Get the Schnorr public key
41/// let schnorr_public_key = private_key.schnorr_public_key();
42///
43/// // Sign a message
44/// let message = b"Hello, world!";
45/// let signature = private_key.schnorr_sign(message);
46///
47/// // Verify the signature
48/// assert!(schnorr_public_key.schnorr_verify(&signature, message));
49/// ```
50#[derive(Clone, PartialEq, Eq, Hash)]
51pub struct SchnorrPublicKey([u8; SCHNORR_PUBLIC_KEY_SIZE]);
52
53impl SchnorrPublicKey {
54    /// Restores a Schnorr public key from an array of bytes.
55    ///
56    /// This method performs no validation on the input data.
57    pub const fn from_data(data: [u8; SCHNORR_PUBLIC_KEY_SIZE]) -> Self {
58        Self(data)
59    }
60
61    /// Returns the Schnorr public key as an array of bytes.
62    pub fn data(&self) -> &[u8; SCHNORR_PUBLIC_KEY_SIZE] { &self.0 }
63
64    /// Get the Schnorr public key as a byte slice.
65    pub fn as_bytes(&self) -> &[u8] { self.as_ref() }
66}
67
68impl SchnorrPublicKey {
69    /// Verifies a Schnorr signature for a message using this public key.
70    ///
71    /// Returns `true` if the signature is valid for the given message and this
72    /// public key, and `false` otherwise.
73    ///
74    /// This implementation follows the BIP-340 Schnorr signature verification
75    /// algorithm.
76    ///
77    /// # Parameters
78    /// - `signature`: A 64-byte Schnorr signature
79    /// - `message`: The message that was signed
80    pub fn schnorr_verify(
81        &self,
82        signature: &[u8; SCHNORR_SIGNATURE_SIZE],
83        message: impl AsRef<[u8]>,
84    ) -> bool {
85        bc_crypto::schnorr_verify(self.into(), signature, message)
86    }
87}
88
89/// Converts a reference to a `SchnorrPublicKey` to a reference to a fixed-size
90/// byte array.
91impl<'a> From<&'a SchnorrPublicKey> for &'a [u8; SchnorrPublicKey::KEY_SIZE] {
92    /// Returns a reference to the underlying byte array.
93    fn from(value: &'a SchnorrPublicKey) -> Self { &value.0 }
94}
95
96/// Converts a fixed-size byte array to a `SchnorrPublicKey`.
97impl From<[u8; SCHNORR_PUBLIC_KEY_SIZE]> for SchnorrPublicKey {
98    /// Converts a 32-byte array into a Schnorr public key.
99    fn from(value: [u8; SCHNORR_PUBLIC_KEY_SIZE]) -> Self {
100        Self::from_data(value)
101    }
102}
103
104/// Provides a reference to the key data as a byte slice.
105impl AsRef<[u8]> for SchnorrPublicKey {
106    /// Returns a reference to the key as a byte slice.
107    fn as_ref(&self) -> &[u8] { &self.0 }
108}
109
110/// Formats the key as a hexadecimal string.
111impl std::fmt::Display for SchnorrPublicKey {
112    /// Displays the key as a hexadecimal string.
113    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
114        write!(f, "{}", self.hex())
115    }
116}
117
118/// Formats the key for debugging, showing type name and hexadecimal value.
119impl std::fmt::Debug for SchnorrPublicKey {
120    /// Displays the key with type information and hexadecimal value.
121    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
122        write!(f, "SchnorrPublicKey({})", self.hex())
123    }
124}
125
126/// Implements the `ECKeyBase` trait methods for `SchnorrPublicKey`.
127impl ECKeyBase for SchnorrPublicKey {
128    /// The size of a Schnorr public key (32 bytes).
129    const KEY_SIZE: usize = bc_crypto::SCHNORR_PUBLIC_KEY_SIZE;
130
131    /// Creates a key from a byte slice, with validation.
132    fn from_data_ref(data: impl AsRef<[u8]>) -> Result<Self>
133    where
134        Self: Sized,
135    {
136        let data = data.as_ref();
137        if data.len() != SCHNORR_PUBLIC_KEY_SIZE {
138            bail!("invalid Schnorr public key size");
139        }
140        let mut key = [0u8; SCHNORR_PUBLIC_KEY_SIZE];
141        key.copy_from_slice(data);
142        Ok(Self(key))
143    }
144
145    /// Returns the key as a byte slice.
146    fn data(&self) -> &[u8] { &self.0 }
147}