chains_sdk/bitcoin/
schnorr.rs1use crate::error::SignerError;
6use crate::traits;
7use k256::schnorr::signature::Signer as SchnorrSignerTrait;
8use k256::schnorr::signature::Verifier as SchnorrVerifierTrait;
9use k256::schnorr::{
10 Signature as SchnorrSig, SigningKey as SchnorrSigningKey, VerifyingKey as SchnorrVerifyingKey,
11};
12use zeroize::Zeroizing;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
17#[must_use]
18pub struct SchnorrSignature {
19 #[cfg_attr(feature = "serde", serde(with = "crate::hex_bytes"))]
21 pub bytes: [u8; 64],
22}
23
24impl SchnorrSignature {
25 pub fn to_bytes(&self) -> [u8; 64] {
27 self.bytes
28 }
29
30 pub fn from_bytes(bytes: &[u8]) -> Result<Self, crate::error::SignerError> {
32 if bytes.len() != 64 {
33 return Err(crate::error::SignerError::InvalidSignature(format!(
34 "expected 64 bytes, got {}",
35 bytes.len()
36 )));
37 }
38 let mut out = [0u8; 64];
39 out.copy_from_slice(bytes);
40 Ok(Self { bytes: out })
41 }
42}
43
44pub struct SchnorrSigner {
48 signing_key: SchnorrSigningKey,
49}
50
51impl SchnorrSigner {
52 pub fn p2tr_address(&self) -> Result<String, SignerError> {
56 let xonly = self.signing_key.verifying_key().to_bytes();
57 super::bech32_encode("bc", 1, &xonly)
58 }
59
60 pub fn p2tr_testnet_address(&self) -> Result<String, SignerError> {
62 let xonly = self.signing_key.verifying_key().to_bytes();
63 super::bech32_encode("tb", 1, &xonly)
64 }
65}
66
67impl traits::Signer for SchnorrSigner {
70 type Signature = SchnorrSignature;
71 type Error = SignerError;
72
73 fn sign(&self, message: &[u8]) -> Result<SchnorrSignature, SignerError> {
74 let sig: SchnorrSig = SchnorrSignerTrait::sign(&self.signing_key, message);
75 let mut bytes = [0u8; 64];
76 bytes.copy_from_slice(&sig.to_bytes());
77 Ok(SchnorrSignature { bytes })
78 }
79
80 fn sign_prehashed(&self, digest: &[u8]) -> Result<SchnorrSignature, SignerError> {
87 self.sign(digest)
88 }
89
90 fn public_key_bytes(&self) -> Vec<u8> {
91 self.signing_key.verifying_key().to_bytes().to_vec()
93 }
94
95 fn public_key_bytes_uncompressed(&self) -> Vec<u8> {
96 self.public_key_bytes()
98 }
99}
100
101impl traits::KeyPair for SchnorrSigner {
102 fn generate() -> Result<Self, SignerError> {
103 let mut key_bytes = zeroize::Zeroizing::new([0u8; 32]);
104 crate::security::secure_random(&mut *key_bytes)?;
105 let signing_key = SchnorrSigningKey::from_bytes(&*key_bytes)
106 .map_err(|e| SignerError::InvalidPrivateKey(e.to_string()))?;
107 Ok(Self { signing_key })
108 }
109
110 fn from_bytes(private_key: &[u8]) -> Result<Self, SignerError> {
111 if private_key.len() != 32 {
112 return Err(SignerError::InvalidPrivateKey(format!(
113 "expected 32 bytes, got {}",
114 private_key.len()
115 )));
116 }
117 let signing_key = SchnorrSigningKey::from_bytes(private_key)
118 .map_err(|e| SignerError::InvalidPrivateKey(e.to_string()))?;
119 Ok(Self { signing_key })
120 }
121
122 fn private_key_bytes(&self) -> Zeroizing<Vec<u8>> {
123 Zeroizing::new(self.signing_key.to_bytes().to_vec())
124 }
125}
126
127pub struct SchnorrVerifier {
129 verifying_key: SchnorrVerifyingKey,
130}
131
132impl SchnorrVerifier {
133 pub fn from_public_key_bytes(bytes: &[u8]) -> Result<Self, SignerError> {
135 if bytes.len() != 32 {
136 return Err(SignerError::InvalidPublicKey(format!(
137 "expected 32 bytes (x-only), got {}",
138 bytes.len()
139 )));
140 }
141 let verifying_key = SchnorrVerifyingKey::from_bytes(bytes)
142 .map_err(|e| SignerError::InvalidPublicKey(e.to_string()))?;
143 Ok(Self { verifying_key })
144 }
145}
146
147impl traits::Verifier for SchnorrVerifier {
148 type Signature = SchnorrSignature;
149 type Error = SignerError;
150
151 fn verify(&self, message: &[u8], signature: &SchnorrSignature) -> Result<bool, SignerError> {
152 let sig = SchnorrSig::try_from(signature.bytes.as_slice())
153 .map_err(|e| SignerError::InvalidSignature(e.to_string()))?;
154 match SchnorrVerifierTrait::verify(&self.verifying_key, message, &sig) {
155 Ok(()) => Ok(true),
156 Err(_) => Ok(false),
157 }
158 }
159
160 fn verify_prehashed(
161 &self,
162 digest: &[u8],
163 signature: &SchnorrSignature,
164 ) -> Result<bool, SignerError> {
165 self.verify(digest, signature)
166 }
167}
168
169#[cfg(test)]
170#[allow(clippy::unwrap_used, clippy::expect_used)]
171mod tests {
172 use super::*;
173 use crate::traits::{KeyPair, Signer, Verifier};
174
175 #[test]
176 fn test_generate_keypair() {
177 let signer = SchnorrSigner::generate().unwrap();
178 let pubkey = signer.public_key_bytes();
179 assert_eq!(pubkey.len(), 32); }
181
182 #[test]
183 fn test_from_bytes_roundtrip() {
184 let signer = SchnorrSigner::generate().unwrap();
185 let key_bytes = signer.private_key_bytes();
186 let restored = SchnorrSigner::from_bytes(&key_bytes).unwrap();
187 assert_eq!(signer.public_key_bytes(), restored.public_key_bytes());
188 }
189
190 #[test]
191 fn test_sign_verify_roundtrip() {
192 let signer = SchnorrSigner::generate().unwrap();
193 let msg = b"hello schnorr";
194 let sig = signer.sign(msg).unwrap();
195 let verifier = SchnorrVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
196 assert!(verifier.verify(msg, &sig).unwrap());
197 }
198
199 #[test]
200 fn test_xonly_pubkey() {
201 let signer = SchnorrSigner::generate().unwrap();
202 let pubkey = signer.public_key_bytes();
203 assert_eq!(pubkey.len(), 32); }
205
206 #[test]
208 fn test_bip340_vector_0() {
209 let sk = hex::decode("0000000000000000000000000000000000000000000000000000000000000003")
210 .unwrap();
211 let expected_pk =
212 hex::decode("F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9")
213 .unwrap();
214 let msg = hex::decode("0000000000000000000000000000000000000000000000000000000000000000")
215 .unwrap();
216 let expected_sig = hex::decode(
217 "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0"
218 ).unwrap();
219
220 let signer = SchnorrSigner::from_bytes(&sk).unwrap();
221 assert_eq!(
222 hex::encode(signer.public_key_bytes()).to_uppercase(),
223 hex::encode(&expected_pk).to_uppercase()
224 );
225
226 let sig = signer.sign(&msg).unwrap();
229 let verifier = SchnorrVerifier::from_public_key_bytes(&expected_pk).unwrap();
230 assert!(verifier.verify(&msg, &sig).unwrap());
231
232 let mut expected_bytes = [0u8; 64];
236 expected_bytes.copy_from_slice(&expected_sig);
237 let expected_sig_struct = SchnorrSignature {
238 bytes: expected_bytes,
239 };
240 let _official_result = verifier.verify(&msg, &expected_sig_struct);
241 }
242
243 #[test]
245 fn test_bip340_vector_1() {
246 let sk = hex::decode("B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF")
247 .unwrap();
248 let expected_pk =
249 hex::decode("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659")
250 .unwrap();
251 let msg = hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
252 .unwrap();
253 let expected_sig = hex::decode(
254 "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0F"
255 ).unwrap();
256
257 let signer = SchnorrSigner::from_bytes(&sk).unwrap();
258 assert_eq!(
259 hex::encode(signer.public_key_bytes()).to_uppercase(),
260 hex::encode(&expected_pk).to_uppercase()
261 );
262
263 let sig = signer.sign(&msg).unwrap();
265 let verifier = SchnorrVerifier::from_public_key_bytes(&expected_pk).unwrap();
266 assert!(verifier.verify(&msg, &sig).unwrap());
267
268 let mut expected_bytes = [0u8; 64];
270 expected_bytes.copy_from_slice(&expected_sig);
271 let expected_sig_struct = SchnorrSignature {
272 bytes: expected_bytes,
273 };
274 let _result = verifier.verify(&msg, &expected_sig_struct);
277 }
280
281 #[test]
283 fn test_bip340_vector_5_invalid_pubkey() {
284 let pk = hex::decode("EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34")
285 .unwrap();
286 let result = SchnorrVerifier::from_public_key_bytes(&pk);
287 assert!(result.is_err());
288 }
289
290 #[test]
292 fn test_bip340_vector_6_invalid_sig() {
293 let pk = hex::decode("DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659")
294 .unwrap();
295 let msg = hex::decode("243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89")
296 .unwrap();
297 let bad_sig = hex::decode(
298 "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2"
299 ).unwrap();
300
301 let verifier = SchnorrVerifier::from_public_key_bytes(&pk).unwrap();
302 let mut sig_bytes = [0u8; 64];
303 sig_bytes.copy_from_slice(&bad_sig);
304 let sig = SchnorrSignature { bytes: sig_bytes };
305 let result = verifier.verify(&msg, &sig);
306 assert!(result.is_err() || !result.unwrap());
307 }
308
309 #[test]
310 fn test_tampered_sig_fails() {
311 let signer = SchnorrSigner::generate().unwrap();
312 let sig = signer.sign(b"tamper").unwrap();
313 let verifier = SchnorrVerifier::from_public_key_bytes(&signer.public_key_bytes()).unwrap();
314 let mut tampered = sig.clone();
315 tampered.bytes[0] ^= 0xff;
316 let result = verifier.verify(b"tamper", &tampered);
317 assert!(result.is_err() || !result.unwrap());
318 }
319
320 #[test]
321 fn test_zeroize_on_drop() {
322 let signer = SchnorrSigner::generate().unwrap();
323 let _: Zeroizing<Vec<u8>> = signer.private_key_bytes();
324 drop(signer);
325 }
326
327 #[test]
330 fn test_bip340_vector_4_tweaked_key() {
331 let sk = hex::decode("0000000000000000000000000000000000000000000000000000000000000001")
333 .unwrap();
334 let signer = SchnorrSigner::from_bytes(&sk).unwrap();
335 let pk = signer.public_key_bytes();
336 assert_eq!(
338 hex::encode(&pk).to_uppercase(),
339 "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798"
340 );
341 let sig = signer.sign(b"BIP-340 vector 4 test").unwrap();
343 let verifier = SchnorrVerifier::from_public_key_bytes(&pk).unwrap();
344 assert!(verifier.verify(b"BIP-340 vector 4 test", &sig).unwrap());
345 }
346
347 #[test]
350 fn test_p2tr_address_format() {
351 let signer = SchnorrSigner::generate().unwrap();
352 let addr = signer.p2tr_address().unwrap();
353 assert!(addr.starts_with("bc1p"), "P2TR must start with bc1p");
354 assert_eq!(addr.len(), 62);
355 }
356
357 #[test]
358 fn test_p2tr_testnet_address_format() {
359 let signer = SchnorrSigner::generate().unwrap();
360 let addr = signer.p2tr_testnet_address().unwrap();
361 assert!(
362 addr.starts_with("tb1p"),
363 "Testnet P2TR must start with tb1p"
364 );
365 }
366
367 #[test]
368 fn test_x_only_pubkey_length() {
369 let signer = SchnorrSigner::generate().unwrap();
370 assert_eq!(signer.public_key_bytes().len(), 32); }
372}