bc_components/ec_key/
schnorr_public_key.rs

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