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}