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}