spark_cryptography/adaptor_signature.rs
1// use bitcoin::secp256k1::{Message, PublicKey, Secp256k1, SecretKey, schnorr::Signature};
2// use elliptic_curve::{PrimeField, generic_array::GenericArray, group::GroupEncoding};
3// use hmac::Mac;
4// use k256::ProjectivePoint;
5// use k256::schnorr::{
6// SigningKey, VerifyingKey,
7// signature::{Signer, Verifier},
8// };
9// use rand::thread_rng;
10// use sha2::{Digest, Sha256};
11
12// #[derive(Debug)]
13// pub struct AdaptorSignature {
14// pub signature: [u8; 64],
15// pub adaptor_private_key: [u8; 32],
16// }
17
18// const MOCK_SECRET_KEY: [u8; 32] = [
19// 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
20// 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01,
21// ];
22
23// const MOCK_ADAPTOR_KEY: [u8; 32] = [
24// 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
25// 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02, 0x02,
26// ];
27
28// pub fn generate_adaptor_from_signature(
29// signature: &[u8; 64],
30// ) -> Result<AdaptorSignature, Box<dyn std::error::Error>> {
31// let secp = Secp256k1::new();
32// let mut rng = thread_rng();
33
34// // Generate random adaptor private key
35// let adaptor_private_key = SecretKey::new(&mut rng);
36
37// // Parse the original signature
38// let sig = Signature::from_slice(signature)?;
39
40// // Extract r and s components
41// let r = sig[..32].to_vec();
42// let s = sig[32..].to_vec();
43
44// let s_ga = GenericArray::from_slice(&s);
45// let adaptor_private_key_ga = GenericArray::from_slice(adaptor_private_key.as_ref());
46
47// // Convert s to scalar
48// let s_scalar = k256::Scalar::from_repr(*s_ga).unwrap();
49// let t_scalar = k256::Scalar::from_repr(*adaptor_private_key_ga).unwrap();
50
51// // Calculate s - adaptor_private_key
52// let new_s = s_scalar - t_scalar;
53
54// // Create new signature
55// let mut new_signature = [0u8; 64];
56// new_signature[..32].copy_from_slice(&r);
57// new_signature[32..].copy_from_slice(&new_s.to_bytes());
58
59// Ok(AdaptorSignature {
60// signature: new_signature,
61// adaptor_private_key: adaptor_private_key.secret_bytes(),
62// })
63// }
64
65// pub fn validate_outbound_adaptor_signature(
66// pubkey: &[u8],
67// message: &[u8],
68// signature: &[u8],
69// adaptor_pubkey: &[u8],
70// ) -> Result<bool, Box<dyn std::error::Error>> {
71// schnorr_verify_with_adaptor(signature, message, pubkey, adaptor_pubkey, false)
72// }
73
74// pub fn apply_adaptor_to_signature(
75// pubkey: &[u8],
76// message: &[u8],
77// signature: &[u8],
78// adaptor_private_key: &[u8],
79// ) -> Result<[u8; 64], Box<dyn std::error::Error>> {
80// let secp = Secp256k1::new();
81
82// // Parse the signature
83// let sig: [u8; 64] = signature
84// .try_into()
85// .map_err(|_| "Invalid signature length")?;
86// let (r, s) = sig.split_at(32);
87
88// // let r_ga = GenericArray::from_slice(r);
89// let s_ga = GenericArray::from_slice(s);
90// let adaptor_private_key_ga = GenericArray::from_slice(adaptor_private_key);
91
92// // Convert to scalars
93// let s_scalar = k256::Scalar::from_repr(*s_ga).unwrap();
94// let t_scalar = k256::Scalar::from_repr(*adaptor_private_key_ga).unwrap();
95
96// // Try adding adaptor first
97// let new_s = s_scalar + t_scalar;
98// let mut new_sig = [0u8; 64];
99// new_sig[..32].copy_from_slice(r);
100// new_sig[32..].copy_from_slice(&new_s.to_bytes());
101
102// // Verify the signature
103// let msg = Message::from_slice(message)?;
104// let pk = PublicKey::from_slice(pubkey)?;
105
106// if secp
107// .verify_schnorr(
108// &bitcoin::secp256k1::schnorr::Signature::from_slice(&new_sig)?,
109// &msg,
110// &pk.x_only_public_key().0,
111// )
112// .is_ok()
113// {
114// return Ok(new_sig);
115// }
116
117// // If adding didn't work, try subtracting
118// let alt_s = s_scalar - t_scalar;
119// let mut alt_sig = [0u8; 64];
120// alt_sig[..32].copy_from_slice(r);
121// alt_sig[32..].copy_from_slice(&alt_s.to_bytes());
122
123// if secp
124// .verify_schnorr(
125// &bitcoin::secp256k1::schnorr::Signature::from_slice(&alt_sig)?,
126// &msg,
127// &pk.x_only_public_key().0,
128// )
129// .is_ok()
130// {
131// return Ok(alt_sig);
132// }
133
134// Err("Cannot apply adaptor to signature".into())
135// }
136
137// fn schnorr_verify_with_adaptor(
138// signature: &[u8],
139// message: &[u8],
140// pubkey: &[u8],
141// adaptor_pubkey: &[u8],
142// inbound: bool,
143// ) -> Result<bool, Box<dyn std::error::Error>> {
144// // Verify message length
145// if message.len() != 32 {
146// return Err(format!("wrong size for message (got {}, want 32)", message.len()).into());
147// }
148
149// // Parse public key
150// let pk = PublicKey::from_slice(pubkey)?;
151// let pk_bytes = pk.serialize();
152// let pk_ga = GenericArray::from_slice(pk_bytes.as_slice());
153// let pk_point = ProjectivePoint::from_bytes(pk_ga).unwrap();
154
155// // Parse signature
156// if signature.len() != 64 {
157// return Err(format!("wrong signature length: {}", signature.len()).into());
158// }
159
160// let (r_bytes, s_bytes) = signature.split_at(32);
161
162// // Compute tagged hash for challenge
163// let mut hasher = Sha256::new();
164// hasher.update(b"BIP0340/challenge");
165// hasher.update(r_bytes);
166// hasher.update(&pk.serialize()[1..]); // Skip first byte (parity)
167// hasher.update(message);
168// let e = hasher.finalize();
169
170// // Convert challenge to scalar
171// let e_ga = GenericArray::from_slice(e.as_slice());
172// let e_scalar = k256::Scalar::from_repr(*e_ga).unwrap();
173// let neg_e = -e_scalar;
174
175// // Calculate R = sG - eP
176// let s_ga = GenericArray::from_slice(s_bytes);
177// let s_scalar = k256::Scalar::from_repr(*s_ga).unwrap();
178// let base_point = ProjectivePoint::GENERATOR;
179// let r_point = (base_point * s_scalar) + (pk_point * neg_e);
180
181// // Add adaptor public key
182// let adaptor_ga = GenericArray::from_slice(adaptor_pubkey);
183// let adaptor_point = ProjectivePoint::from_bytes(adaptor_ga).unwrap();
184// let new_r = r_point + adaptor_point;
185
186// // TODO: Check for point at infinity
187// // if !inbound && new_r.is_identity().into() {
188// // return Err("calculated R point is the point at infinity".into());
189// // }
190
191// // TODO: Convert to affine and check y coordinate parity
192// // let new_r_affine = new_r.to_affine();
193// // if new_r_affine.y().is_odd().into() {
194// // return Err("calculated R y-value is odd".into());
195// // }
196
197// // TODO: Check if x coordinate matches r
198// let r_ga = GenericArray::from_slice(r_bytes);
199// let r_scalar = k256::Scalar::from_repr(*r_ga).unwrap();
200// // if new_r.x() != &r_scalar.into() {
201// // return Err("calculated R point was not given R".into());
202// // }
203
204// Ok(true)
205// }
206
207// #[cfg(test)]
208// mod tests {
209// use std::str::FromStr;
210
211// use super::*;
212// use k256::ecdsa::signature::SignerMut;
213// use schnorr_fun::{Schnorr, fun::nonce::Deterministic};
214// use secp256k1::Keypair;
215
216// #[test]
217// fn test_adaptor_signature() {
218// let secp = Secp256k1::new();
219
220// // Use mock secret key
221// println!(
222// "RUST: Using mock secret key: {:?}",
223// hex::encode(&MOCK_SECRET_KEY)
224// );
225// let secret_key = SecretKey::from_slice(&MOCK_SECRET_KEY).unwrap();
226// let public_key = PublicKey::from_secret_key(&secp, &secret_key);
227// println!(
228// "RUST: Generated public key: {:?}",
229// hex::encode(&public_key.serialize())
230// );
231// // generate Schnorr verifying key
232// let verifying_key = public_key.x_only_public_key().0;
233// println!(
234// "RUST: Schnorr verifying == xonly pubkey: {:?}",
235// hex::encode(&verifying_key.serialize())
236// );
237
238// // Create test message and hash
239// let message = b"test";
240// println!("RUST: Test message: {:?}", hex::encode(&message));
241// let mut hasher = Sha256::new();
242// hasher.update(message);
243// let msg_hash = hasher.finalize();
244// println!("RUST: Message hash: {:?}", hex::encode(&msg_hash));
245
246// // Create original signature
247// let msg = Message::from_slice(&msg_hash).unwrap();
248// let keypair = Keypair::from_secret_key(&secp, &secret_key);
249
250// let signature = Signature::from_str("b28a65d8651818ea04cfdf945455d64bbd7bac068532289a8cc0801a9ed283046159052e7ec6972cc113b2d6bc17883d5d1d4e12a4563f05b220e7b8533342e3")
251// .unwrap();
252// let is_valid = secp
253// .verify_schnorr(
254// &signature,
255// &msg,
256// &keypair.public_key().x_only_public_key().0,
257// )
258// .is_ok();
259
260// println!("is_valid: {}", is_valid);
261// }
262// }
263
264// const RFC6979_EXTRA_DATA: &[u8] = b"BIP-340";
265
266// fn rfc6979_generate_k(
267// priv_key: &[u8],
268// msg_hash: &[u8],
269// extra_data: &[u8],
270// iteration: u32,
271// ) -> [u8; 32] {
272// type HmacSha256 = hmac::Hmac<sha2::Sha256>;
273
274// let mut v = [0x01; 32];
275// let mut k = [0x00; 32];
276
277// // Step A
278// let mut hmac = HmacSha256::new_from_slice(&k).unwrap();
279// hmac.update(&v);
280// hmac.update(&[0x00]);
281// hmac.update(priv_key);
282// hmac.update(msg_hash);
283// hmac.update(extra_data);
284// hmac.update(&iteration.to_be_bytes());
285// k = hmac.finalize().into_bytes().try_into().unwrap();
286
287// // Step B
288// let mut hmac = HmacSha256::new_from_slice(&k).unwrap();
289// hmac.update(&v);
290// v = hmac.finalize().into_bytes().try_into().unwrap();
291
292// // Step C
293// let mut hmac = HmacSha256::new_from_slice(&k).unwrap();
294// hmac.update(&v);
295// hmac.update(&[0x01]);
296// hmac.update(priv_key);
297// hmac.update(msg_hash);
298// hmac.update(extra_data);
299// hmac.update(&iteration.to_be_bytes());
300// k = hmac.finalize().into_bytes().try_into().unwrap();
301
302// // Step D
303// let mut hmac = HmacSha256::new_from_slice(&k).unwrap();
304// hmac.update(&v);
305// v = hmac.finalize().into_bytes().try_into().unwrap();
306
307// // Step E
308// let mut hmac = HmacSha256::new_from_slice(&k).unwrap();
309// hmac.update(&v);
310// v = hmac.finalize().into_bytes().try_into().unwrap();
311
312// v
313// }
314
315// pub fn sign_schnorr_btcd_compatible(secret_key: &[u8; 32], msg: &[u8]) -> [u8; 64] {
316// // Hash message
317// let mut hasher = Sha256::new();
318// hasher.update(msg);
319// let msg_hash = hasher.finalize();
320
321// // Generate RFC6979 nonce matching btcd (with iteration 0)
322// let aux_rand = rfc6979_generate_k(secret_key, &msg_hash, RFC6979_EXTRA_DATA, 0);
323
324// // Create signing key
325// let signing_key = SigningKey::from_bytes(secret_key).unwrap();
326
327// // Sign using the generated aux_rand
328// let sig = signing_key
329// .sign_prehash_with_aux_rand(&msg_hash.into(), &aux_rand)
330// .unwrap();
331
332// sig.to_bytes()
333// }
334
335use bitcoin::secp256k1::{schnorr::Signature, Message, PublicKey, Secp256k1, SecretKey};
336use elliptic_curve::{generic_array::GenericArray, group::GroupEncoding, PrimeField};
337use k256::elliptic_curve::point::AffineCoordinates;
338use k256::ProjectivePoint;
339
340use rand::thread_rng;
341use sha2::{Digest, Sha256};
342
343#[derive(Debug)]
344pub struct AdaptorSignature {
345 pub signature: [u8; 64],
346 pub adaptor_private_key: [u8; 32],
347}
348
349pub fn generate_adaptor_from_signature(
350 signature: &[u8; 64],
351) -> Result<AdaptorSignature, Box<dyn std::error::Error>> {
352 let mut rng = thread_rng();
353
354 // Generate random adaptor private key
355 let adaptor_private_key = SecretKey::new(&mut rng);
356
357 // Parse the original signature
358 let sig = Signature::from_slice(signature)?;
359
360 // Extract r and s components
361 let r = sig[..32].to_vec();
362 let s = sig[32..].to_vec();
363
364 let s_ga = GenericArray::from_slice(&s);
365 let adaptor_private_key_ga = GenericArray::from_slice(adaptor_private_key.as_ref());
366
367 // Convert s to scalar
368 let s_scalar = k256::Scalar::from_repr(*s_ga).unwrap();
369 let t_scalar = k256::Scalar::from_repr(*adaptor_private_key_ga).unwrap();
370
371 // Calculate s - adaptor_private_key
372 let new_s = s_scalar - t_scalar;
373
374 // Create new signature
375 let mut new_signature = [0u8; 64];
376 new_signature[..32].copy_from_slice(&r);
377 new_signature[32..].copy_from_slice(&new_s.to_bytes());
378
379 Ok(AdaptorSignature {
380 signature: new_signature,
381 adaptor_private_key: adaptor_private_key.secret_bytes(),
382 })
383}
384
385pub fn validate_outbound_adaptor_signature(
386 pubkey: &[u8],
387 message: &[u8],
388 signature: &[u8],
389 adaptor_pubkey: &[u8],
390) -> Result<bool, Box<dyn std::error::Error>> {
391 schnorr_verify_with_adaptor(signature, message, pubkey, adaptor_pubkey, false)
392}
393
394pub fn apply_adaptor_to_signature(
395 pubkey: &[u8],
396 message: &[u8],
397 signature: &[u8],
398 adaptor_private_key: &[u8],
399) -> Result<[u8; 64], Box<dyn std::error::Error>> {
400 let secp = Secp256k1::new();
401
402 // Parse the signature
403 let sig: [u8; 64] = signature
404 .try_into()
405 .map_err(|_| "Invalid signature length")?;
406 let (r, s) = sig.split_at(32);
407
408 // let r_ga = GenericArray::from_slice(r);
409 let s_ga = GenericArray::from_slice(s);
410 let adaptor_private_key_ga = GenericArray::from_slice(adaptor_private_key);
411
412 // Convert to scalars
413 let s_scalar = k256::Scalar::from_repr(*s_ga).unwrap();
414 let t_scalar = k256::Scalar::from_repr(*adaptor_private_key_ga).unwrap();
415
416 // Try adding adaptor first
417 let new_s = s_scalar + t_scalar;
418 let mut new_sig = [0u8; 64];
419 new_sig[..32].copy_from_slice(r);
420 new_sig[32..].copy_from_slice(&new_s.to_bytes());
421
422 // Verify the signature
423 let msg = Message::from_digest_slice(message).map_err(|_| "Invalid message")?;
424 let pk = PublicKey::from_slice(pubkey).map_err(|_| "Invalid public key")?;
425
426 if secp
427 .verify_schnorr(
428 &bitcoin::secp256k1::schnorr::Signature::from_slice(&new_sig)?,
429 &msg,
430 &pk.x_only_public_key().0,
431 )
432 .is_ok()
433 {
434 return Ok(new_sig);
435 }
436
437 // If adding didn't work, try subtracting
438 let alt_s = s_scalar - t_scalar;
439 let mut alt_sig = [0u8; 64];
440 alt_sig[..32].copy_from_slice(r);
441 alt_sig[32..].copy_from_slice(&alt_s.to_bytes());
442
443 if secp
444 .verify_schnorr(
445 &bitcoin::secp256k1::schnorr::Signature::from_slice(&alt_sig)?,
446 &msg,
447 &pk.x_only_public_key().0,
448 )
449 .is_ok()
450 {
451 return Ok(alt_sig);
452 }
453
454 Err("Cannot apply adaptor to signature".into())
455}
456
457pub fn generate_signature_from_existing_adaptor(
458 signature: &[u8],
459 adaptor_private_key: &[u8],
460) -> Result<[u8; 64], Box<dyn std::error::Error>> {
461 // Parse signature
462 if signature.len() != 64 {
463 return Err(format!("wrong signature length: {}", signature.len()).into());
464 }
465
466 let (r, s) = signature.split_at(32);
467 let s_ga = GenericArray::from_slice(s);
468 let adaptor_private_key_ga = GenericArray::from_slice(adaptor_private_key);
469
470 // Convert to scalars
471 let s_scalar = k256::Scalar::from_repr(*s_ga).unwrap();
472 let mut t_scalar = k256::Scalar::from_repr(*adaptor_private_key_ga).unwrap();
473
474 // Negate t
475 t_scalar = -t_scalar;
476
477 // Add negated t to s
478 let new_s = s_scalar + t_scalar;
479
480 // Create new signature
481 let mut new_sig = [0u8; 64];
482 new_sig[..32].copy_from_slice(r);
483 new_sig[32..].copy_from_slice(&new_s.to_bytes());
484
485 Ok(new_sig)
486}
487
488fn schnorr_verify_with_adaptor(
489 signature: &[u8],
490 message: &[u8],
491 pubkey: &[u8],
492 adaptor_pubkey: &[u8],
493 inbound: bool,
494) -> Result<bool, Box<dyn std::error::Error>> {
495 // Verify message length
496 if message.len() != 32 {
497 return Err(format!("wrong size for message (got {}, want 32)", message.len()).into());
498 }
499
500 // Parse public key
501 let pk = PublicKey::from_slice(pubkey)?;
502 let pk_bytes = pk.serialize();
503 let pk_ga = GenericArray::from_slice(pk_bytes.as_slice());
504 let pk_point = ProjectivePoint::from_bytes(pk_ga).unwrap();
505
506 // Parse signature
507 if signature.len() != 64 {
508 return Err(format!("wrong signature length: {}", signature.len()).into());
509 }
510
511 let (r_bytes, s_bytes) = signature.split_at(32);
512
513 // Compute tagged hash for challenge
514 let mut hasher = Sha256::new();
515 hasher.update(b"BIP0340/chllenge");
516 hasher.update(r_bytes);
517 hasher.update(&pk.serialize()[1..]); // Skip first byte (parity)
518 hasher.update(message);
519 let e = hasher.finalize();
520
521 // Convert challenge to scalar
522 let e_ga = GenericArray::from_slice(e.as_slice());
523 let e_scalar = k256::Scalar::from_repr(*e_ga).unwrap();
524 let neg_e = -e_scalar;
525
526 // Calculate R = sG - eP
527 let s_ga = GenericArray::from_slice(s_bytes);
528 let s_scalar = k256::Scalar::from_repr(*s_ga).unwrap();
529 let base_point = ProjectivePoint::GENERATOR;
530 let r_point = (base_point * s_scalar) + (pk_point * neg_e);
531
532 // Add adaptor public key
533 let adaptor_ga = GenericArray::from_slice(adaptor_pubkey);
534 let adaptor_point = ProjectivePoint::from_bytes(adaptor_ga).unwrap();
535 let new_r = r_point + adaptor_point;
536
537 // TODO: Check for point at infinity
538 if !inbound && new_r == ProjectivePoint::IDENTITY {
539 return Err("calculated R point is the point at infinity".into());
540 }
541
542 // TODO: Convert to affine and check y coordinate parity
543 let new_r_affine = new_r.to_affine();
544 // if new_r_affine.y().is_odd().into() {
545 // return Err("calculated R y-value is odd".into());
546 // }
547
548 // TODO: Check if x coordinate matches r
549 let r_ga = GenericArray::from_slice(r_bytes);
550 let r_scalar = k256::Scalar::from_repr(*r_ga).unwrap();
551 let new_r_x = new_r_affine.x();
552 if new_r_x != r_scalar.to_bytes() {
553 return Err("calculated R point x-coordinate does not match r".into());
554 }
555
556 Ok(true)
557}
558
559#[cfg(test)]
560mod tests {
561 use secp256k1::Keypair;
562
563 use super::*;
564
565 #[test]
566 #[ignore]
567 fn test_adaptor_signature_flow() {
568 let secp = Secp256k1::new();
569 let mut rng = thread_rng();
570
571 // Generate test keys
572 let secret_key = SecretKey::new(&mut rng);
573 let public_key = PublicKey::from_secret_key(&secp, &secret_key);
574
575 // Create test message
576 let message = b"Hello, world!";
577 let msg_hash = Sha256::digest(message);
578
579 // Create original signature
580 let msg = Message::from_digest_slice(&msg_hash).unwrap();
581 let keypair = Keypair::from_secret_key(&secp, &secret_key);
582 let sig = secp.sign_schnorr(&msg, &keypair);
583
584 // Generate adaptor signature
585 let adaptor_result = generate_adaptor_from_signature(&sig.as_ref()).unwrap();
586
587 // Validate adaptor signature
588 let adaptor_pubkey = PublicKey::from_secret_key(
589 &secp,
590 &SecretKey::from_slice(&adaptor_result.adaptor_private_key).unwrap(),
591 );
592
593 let is_valid = validate_outbound_adaptor_signature(
594 &public_key.serialize(),
595 &msg_hash,
596 &adaptor_result.signature,
597 &adaptor_pubkey.serialize(),
598 )
599 .unwrap();
600
601 assert!(is_valid);
602
603 // Apply adaptor to signature
604 let final_sig = apply_adaptor_to_signature(
605 &public_key.serialize(),
606 &msg_hash,
607 &adaptor_result.signature,
608 &adaptor_result.adaptor_private_key,
609 )
610 .unwrap();
611
612 // Verify final signature
613 assert!(secp
614 .verify_schnorr(
615 &bitcoin::secp256k1::schnorr::Signature::from_slice(&final_sig).unwrap(),
616 &msg,
617 &public_key.x_only_public_key().0
618 )
619 .is_ok());
620 }
621}