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}