bc_components/ec_key/
schnorr_public_key.rs

1use bc_crypto::SCHNORR_SIGNATURE_SIZE;
2
3use crate::{Digest, ECKeyBase, Error, Reference, ReferenceProvider, 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
109impl ReferenceProvider for SchnorrPublicKey {
110    fn reference(&self) -> Reference {
111        Reference::from_digest(Digest::from_image(self.data()))
112    }
113}
114
115/// Formats the key as a hexadecimal string.
116impl std::fmt::Display for SchnorrPublicKey {
117    /// Displays the key as a hexadecimal string.
118    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
119        write!(f, "SchnorrPublicKey({})", self.ref_hex_short())
120    }
121}
122
123/// Formats the key for debugging, showing type name and hexadecimal value.
124impl std::fmt::Debug for SchnorrPublicKey {
125    /// Displays the key with type information and hexadecimal value.
126    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
127        write!(f, "SchnorrPublicKey({})", self.hex())
128    }
129}
130
131/// Implements the `ECKeyBase` trait methods for `SchnorrPublicKey`.
132impl ECKeyBase for SchnorrPublicKey {
133    /// The size of a Schnorr public key (32 bytes).
134    const KEY_SIZE: usize = bc_crypto::SCHNORR_PUBLIC_KEY_SIZE;
135
136    /// Creates a key from a byte slice, with validation.
137    fn from_data_ref(data: impl AsRef<[u8]>) -> Result<Self>
138    where
139        Self: Sized,
140    {
141        let data = data.as_ref();
142        if data.len() != SCHNORR_PUBLIC_KEY_SIZE {
143            return Err(Error::invalid_size(
144                "Schnorr public key",
145                SCHNORR_PUBLIC_KEY_SIZE,
146                data.len(),
147            ));
148        }
149        let mut key = [0u8; SCHNORR_PUBLIC_KEY_SIZE];
150        key.copy_from_slice(data);
151        Ok(Self(key))
152    }
153
154    /// Returns the key as a byte slice.
155    fn data(&self) -> &[u8] { &self.0 }
156}