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}