bc_components/ec_key/
schnorr_public_key.rs

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