1use crate::crypto;
4use crate::error::SignerError;
5use core::fmt;
6use k256::elliptic_curve::group::GroupEncoding;
7use k256::elliptic_curve::ops::Reduce;
8use k256::elliptic_curve::sec1::ToEncodedPoint;
9use k256::{AffinePoint, ProjectivePoint, Scalar};
10use zeroize::Zeroizing;
11
12fn tagged_hash_scalar(tag: &[u8], data: &[u8]) -> Scalar {
16 let hash = crypto::tagged_hash(tag, data);
17 let wide = k256::U256::from_be_slice(&hash);
18 <Scalar as Reduce<k256::U256>>::reduce(wide)
19}
20
21#[derive(Clone)]
25pub struct KeyAggContext {
26 pub aggregate_key: AffinePoint,
28 pub x_only_pubkey: [u8; 32],
30 pub(crate) coefficients: Vec<Scalar>,
32 pub(crate) pubkeys: Vec<[u8; 33]>,
34 pub(crate) parity: bool,
36}
37
38impl core::fmt::Debug for KeyAggContext {
39 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
40 f.debug_struct("KeyAggContext")
41 .field("x_only_pubkey", &hex::encode(self.x_only_pubkey))
42 .field("coefficients", &"[REDACTED]")
43 .field("num_keys", &self.pubkeys.len())
44 .finish()
45 }
46}
47
48#[derive(Clone, Debug)]
50pub struct PubNonce {
51 pub r1: AffinePoint,
53 pub r2: AffinePoint,
55}
56
57impl PubNonce {
58 pub fn to_bytes(&self) -> [u8; 66] {
60 let r1_enc = ProjectivePoint::from(self.r1)
61 .to_affine()
62 .to_encoded_point(true);
63 let r2_enc = ProjectivePoint::from(self.r2)
64 .to_affine()
65 .to_encoded_point(true);
66 let mut out = [0u8; 66];
67 out[..33].copy_from_slice(r1_enc.as_bytes());
68 out[33..].copy_from_slice(r2_enc.as_bytes());
69 out[33..].copy_from_slice(r2_enc.as_bytes());
70 out
71 }
72}
73
74pub struct SecNonce {
76 k1: Zeroizing<Scalar>,
78 k2: Zeroizing<Scalar>,
80 pub(crate) pubkey: [u8; 33],
82}
83
84impl Drop for SecNonce {
85 fn drop(&mut self) {
86 }
88}
89
90#[derive(Clone, Debug)]
92pub struct AggNonce {
93 pub r1: AffinePoint,
95 pub r2: AffinePoint,
97}
98
99#[derive(Clone)]
101pub struct PartialSignature {
102 pub s: Scalar,
104}
105
106impl fmt::Debug for PartialSignature {
107 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
108 f.debug_struct("PartialSignature")
109 .field("s", &"[REDACTED]")
110 .finish()
111 }
112}
113
114#[derive(Clone, Debug)]
116#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
117pub struct MuSig2Signature {
118 pub r: [u8; 32],
120 pub s: [u8; 32],
122}
123
124impl MuSig2Signature {
125 pub fn to_bytes(&self) -> [u8; 64] {
127 let mut out = [0u8; 64];
128 out[..32].copy_from_slice(&self.r);
129 out[32..].copy_from_slice(&self.s);
130 out
131 }
132}
133
134pub fn individual_pubkey(secret_key: &[u8; 32]) -> Result<[u8; 33], SignerError> {
138 let wide = k256::U256::from_be_slice(secret_key);
139 let scalar = <Scalar as Reduce<k256::U256>>::reduce(wide);
140 if scalar == Scalar::ZERO {
141 return Err(SignerError::InvalidPrivateKey("secret key is zero".into()));
142 }
143 let point = (ProjectivePoint::GENERATOR * scalar).to_affine();
144 let encoded = point.to_encoded_point(true);
145 let mut out = [0u8; 33];
146 out.copy_from_slice(encoded.as_bytes());
147 Ok(out)
148}
149
150pub fn key_sort(pubkeys: &[[u8; 33]]) -> Vec<[u8; 33]> {
152 let mut sorted = pubkeys.to_vec();
153 sorted.sort();
154 sorted
155}
156
157pub(crate) fn hash_keys(pubkeys: &[[u8; 33]]) -> [u8; 32] {
159 let mut data = Vec::with_capacity(pubkeys.len() * 33);
160 for pk in pubkeys {
161 data.extend_from_slice(pk);
162 }
163 crypto::tagged_hash(b"KeyAgg list", &data)
164}
165
166pub fn key_agg(pubkeys: &[[u8; 33]]) -> Result<KeyAggContext, SignerError> {
173 if pubkeys.is_empty() {
174 return Err(SignerError::InvalidPrivateKey("empty pubkey list".into()));
175 }
176
177 for pk in pubkeys {
179 parse_point(pk)?;
180 }
181
182 let pk_list_hash = hash_keys(pubkeys);
183
184 let second_key: Option<&[u8; 33]> = pubkeys.iter().find(|pk| *pk != &pubkeys[0]);
186
187 let mut coefficients = Vec::with_capacity(pubkeys.len());
190 for pk in pubkeys {
191 let a_i = if second_key == Some(pk) {
192 Scalar::ONE
194 } else {
195 let mut data = Vec::with_capacity(32 + 33);
196 data.extend_from_slice(&pk_list_hash);
197 data.extend_from_slice(pk);
198 tagged_hash_scalar(b"KeyAgg coefficient", &data)
199 };
200 coefficients.push(a_i);
201 }
202
203 let mut q = ProjectivePoint::IDENTITY;
205 for (i, pk) in pubkeys.iter().enumerate() {
206 let point = parse_point(pk)?;
207 q += point * coefficients[i];
208 }
209
210 let q_affine = q.to_affine();
211 let q_encoded = q_affine.to_encoded_point(true);
212 let q_bytes = q_encoded.as_bytes();
213
214 let mut x_only = [0u8; 32];
216 x_only.copy_from_slice(&q_bytes[1..33]);
217
218 let parity = q_bytes[0] == 0x03;
220
221 Ok(KeyAggContext {
222 aggregate_key: q_affine,
223 x_only_pubkey: x_only,
224 coefficients,
225 pubkeys: pubkeys.to_vec(),
226 parity,
227 })
228}
229
230pub fn nonce_gen(
238 _secret_key: &[u8; 32],
239 pubkey: &[u8; 33],
240 key_agg_ctx: &KeyAggContext,
241 msg: &[u8],
242 extra_in: &[u8],
243) -> Result<(SecNonce, PubNonce), SignerError> {
244 let mut rand_bytes = [0u8; 32];
246 crate::security::secure_random(&mut rand_bytes)?;
247
248 let k1 = {
250 let mut data = Vec::new();
251 data.extend_from_slice(&rand_bytes);
252 data.extend_from_slice(pubkey);
253 data.extend_from_slice(&key_agg_ctx.x_only_pubkey);
254 data.push(0x01); data.extend_from_slice(msg);
256 data.extend_from_slice(extra_in);
257 let hash = crypto::tagged_hash(b"MuSig/nonce", &data);
258 let wide = k256::U256::from_be_slice(&hash);
259 let s = <Scalar as Reduce<k256::U256>>::reduce(wide);
260 if s == Scalar::ZERO {
261 return Err(SignerError::EntropyError);
262 }
263 s
264 };
265
266 let k2 = {
268 let mut data = Vec::new();
269 data.extend_from_slice(&rand_bytes);
270 data.extend_from_slice(pubkey);
271 data.extend_from_slice(&key_agg_ctx.x_only_pubkey);
272 data.push(0x02); data.extend_from_slice(msg);
274 data.extend_from_slice(extra_in);
275 let hash = crypto::tagged_hash(b"MuSig/nonce", &data);
276 let wide = k256::U256::from_be_slice(&hash);
277 let s = <Scalar as Reduce<k256::U256>>::reduce(wide);
278 if s == Scalar::ZERO {
279 return Err(SignerError::EntropyError);
280 }
281 s
282 };
283
284 let r1 = (ProjectivePoint::GENERATOR * k1).to_affine();
285 let r2 = (ProjectivePoint::GENERATOR * k2).to_affine();
286
287 let sec_nonce = SecNonce {
288 k1: Zeroizing::new(k1),
289 k2: Zeroizing::new(k2),
290 pubkey: *pubkey,
291 };
292
293 let pub_nonce = PubNonce { r1, r2 };
294
295 Ok((sec_nonce, pub_nonce))
296}
297
298pub fn nonce_agg(pub_nonces: &[PubNonce]) -> Result<AggNonce, SignerError> {
300 if pub_nonces.is_empty() {
301 return Err(SignerError::InvalidPrivateKey("empty nonce list".into()));
302 }
303
304 let mut r1 = ProjectivePoint::IDENTITY;
305 let mut r2 = ProjectivePoint::IDENTITY;
306
307 for pn in pub_nonces {
308 r1 += ProjectivePoint::from(pn.r1);
309 r2 += ProjectivePoint::from(pn.r2);
310 }
311
312 Ok(AggNonce {
313 r1: r1.to_affine(),
314 r2: r2.to_affine(),
315 })
316}
317
318pub(crate) fn compute_nonce_coeff(
322 agg_nonce: &AggNonce,
323 x_only_pubkey: &[u8; 32],
324 msg: &[u8],
325) -> Scalar {
326 let r1_enc = ProjectivePoint::from(agg_nonce.r1)
327 .to_affine()
328 .to_encoded_point(true);
329 let r2_enc = ProjectivePoint::from(agg_nonce.r2)
330 .to_affine()
331 .to_encoded_point(true);
332
333 let mut data = Vec::new();
334 data.extend_from_slice(r1_enc.as_bytes());
335 data.extend_from_slice(r2_enc.as_bytes());
336 data.extend_from_slice(x_only_pubkey);
337 data.extend_from_slice(msg);
338
339 tagged_hash_scalar(b"MuSig/noncecoef", &data)
340}
341
342pub fn sign(
347 sec_nonce: SecNonce,
348 secret_key: &[u8; 32],
349 key_agg_ctx: &KeyAggContext,
350 agg_nonce: &AggNonce,
351 msg: &[u8],
352) -> Result<PartialSignature, SignerError> {
353 let sk_wide = k256::U256::from_be_slice(secret_key);
354 let sk_scalar = <Scalar as Reduce<k256::U256>>::reduce(sk_wide);
355
356 let b = compute_nonce_coeff(agg_nonce, &key_agg_ctx.x_only_pubkey, msg);
358
359 let r = ProjectivePoint::from(agg_nonce.r1) + ProjectivePoint::from(agg_nonce.r2) * b;
361 let r_affine = r.to_affine();
362 let r_encoded = r_affine.to_encoded_point(true);
363 let r_bytes = r_encoded.as_bytes();
364
365 let mut r_x = [0u8; 32];
367 r_x.copy_from_slice(&r_bytes[1..33]);
368
369 let nonce_negated = r_bytes[0] == 0x03;
371
372 let mut challenge_data = Vec::new();
374 challenge_data.extend_from_slice(&r_x);
375 challenge_data.extend_from_slice(&key_agg_ctx.x_only_pubkey);
376 challenge_data.extend_from_slice(msg);
377 let e = tagged_hash_scalar(b"BIP0340/challenge", &challenge_data);
378
379 let my_idx = key_agg_ctx
381 .pubkeys
382 .iter()
383 .position(|pk| pk == &sec_nonce.pubkey)
384 .ok_or_else(|| SignerError::SigningFailed("pubkey not in key_agg context".into()))?;
385 let a_i = key_agg_ctx.coefficients[my_idx];
386
387 let mut d = sk_scalar;
389 if key_agg_ctx.parity {
390 d = -d;
391 }
392
393 let mut k1 = *sec_nonce.k1;
395 let mut k2 = *sec_nonce.k2;
396 if nonce_negated {
397 k1 = -k1;
398 k2 = -k2;
399 }
400
401 let s = k1 + b * k2 + e * a_i * d;
403
404 Ok(PartialSignature { s })
405}
406
407pub fn partial_sig_agg(
411 partial_sigs: &[PartialSignature],
412 agg_nonce: &AggNonce,
413 key_agg_ctx: &KeyAggContext,
414 msg: &[u8],
415) -> Result<MuSig2Signature, SignerError> {
416 if partial_sigs.is_empty() {
417 return Err(SignerError::SigningFailed(
418 "empty partial signatures".into(),
419 ));
420 }
421
422 let b = compute_nonce_coeff(agg_nonce, &key_agg_ctx.x_only_pubkey, msg);
424 let r = ProjectivePoint::from(agg_nonce.r1) + ProjectivePoint::from(agg_nonce.r2) * b;
425 let r_affine = r.to_affine();
426 let r_encoded = r_affine.to_encoded_point(true);
427 let r_bytes = r_encoded.as_bytes();
428
429 let mut r_x = [0u8; 32];
431 r_x.copy_from_slice(&r_bytes[1..33]);
432
433 let mut s = Scalar::ZERO;
435 for psig in partial_sigs {
436 s += psig.s;
437 }
438
439 Ok(MuSig2Signature {
440 r: r_x,
441 s: s.to_bytes().into(),
442 })
443}
444
445pub fn verify(
449 sig: &MuSig2Signature,
450 x_only_pubkey: &[u8; 32],
451 msg: &[u8],
452) -> Result<bool, SignerError> {
453 let mut r_sec1 = [0u8; 33];
455 r_sec1[0] = 0x02; r_sec1[1..].copy_from_slice(&sig.r);
457 let r_point = parse_point(&r_sec1)?;
458
459 let s_wide = k256::U256::from_be_slice(&sig.s);
461 let s_scalar = <Scalar as Reduce<k256::U256>>::reduce(s_wide);
462
463 let mut pk_sec1 = [0u8; 33];
465 pk_sec1[0] = 0x02; pk_sec1[1..].copy_from_slice(x_only_pubkey);
467 let pk_point = parse_point(&pk_sec1)?;
468
469 let mut challenge_data = Vec::new();
471 challenge_data.extend_from_slice(&sig.r);
472 challenge_data.extend_from_slice(x_only_pubkey);
473 challenge_data.extend_from_slice(msg);
474 let e = tagged_hash_scalar(b"BIP0340/challenge", &challenge_data);
475
476 let lhs = ProjectivePoint::GENERATOR * s_scalar;
478 let rhs = r_point + pk_point * e;
479
480 Ok(lhs == rhs)
481}
482
483fn parse_point(bytes: &[u8; 33]) -> Result<ProjectivePoint, SignerError> {
487 if bytes[0] != 0x02 && bytes[0] != 0x03 {
489 return Err(SignerError::InvalidPrivateKey(
490 "invalid compressed point prefix".into(),
491 ));
492 }
493 let ct = AffinePoint::from_bytes(bytes.into());
494 if !bool::from(ct.is_some()) {
495 return Err(SignerError::InvalidPrivateKey(
496 "invalid compressed point".into(),
497 ));
498 }
499 #[allow(clippy::unwrap_used)]
501 Ok(ProjectivePoint::from(ct.unwrap()))
502}
503
504#[cfg(test)]
505#[allow(clippy::unwrap_used, clippy::expect_used)]
506mod tests {
507 use super::*;
508
509 #[test]
512 fn test_individual_pubkey_from_known_key() {
513 let sk = {
515 let mut k = [0u8; 32];
516 k[31] = 1;
517 k
518 };
519 let pk = individual_pubkey(&sk).unwrap();
520 assert_eq!(pk[0], 0x02);
522 assert_eq!(
523 hex::encode(&pk[1..]),
524 "79be667ef9dcbbac55a06295ce870b07029bfcdb2dce28d959f2815b16f81798"
525 );
526 }
527
528 #[test]
529 fn test_individual_pubkey_zero_key_rejected() {
530 let sk = [0u8; 32];
531 assert!(individual_pubkey(&sk).is_err());
532 }
533
534 #[test]
535 fn test_individual_pubkey_deterministic() {
536 let sk = [0x42u8; 32];
537 let pk1 = individual_pubkey(&sk).unwrap();
538 let pk2 = individual_pubkey(&sk).unwrap();
539 assert_eq!(pk1, pk2);
540 }
541
542 #[test]
545 fn test_key_agg_two_keys() {
546 let sk1 = [0x01u8; 32];
547 let sk2 = [0x02u8; 32];
548 let pk1 = individual_pubkey(&sk1).unwrap();
549 let pk2 = individual_pubkey(&sk2).unwrap();
550
551 let ctx = key_agg(&[pk1, pk2]).unwrap();
552 assert_eq!(ctx.x_only_pubkey.len(), 32);
553 assert_eq!(ctx.pubkeys.len(), 2);
554 assert_eq!(ctx.coefficients.len(), 2);
555 }
556
557 #[test]
558 fn test_key_agg_deterministic() {
559 let sk1 = [0x01u8; 32];
560 let sk2 = [0x02u8; 32];
561 let pk1 = individual_pubkey(&sk1).unwrap();
562 let pk2 = individual_pubkey(&sk2).unwrap();
563
564 let ctx1 = key_agg(&[pk1, pk2]).unwrap();
565 let ctx2 = key_agg(&[pk1, pk2]).unwrap();
566 assert_eq!(ctx1.x_only_pubkey, ctx2.x_only_pubkey);
567 }
568
569 #[test]
570 fn test_key_agg_empty_rejected() {
571 assert!(key_agg(&[]).is_err());
572 }
573
574 #[test]
575 fn test_key_agg_order_matters() {
576 let sk1 = [0x01u8; 32];
577 let sk2 = [0x02u8; 32];
578 let pk1 = individual_pubkey(&sk1).unwrap();
579 let pk2 = individual_pubkey(&sk2).unwrap();
580
581 let ctx_12 = key_agg(&[pk1, pk2]).unwrap();
582 let ctx_21 = key_agg(&[pk2, pk1]).unwrap();
583 assert_ne!(ctx_12.x_only_pubkey, ctx_21.x_only_pubkey);
586 }
587
588 #[test]
589 fn test_key_sort() {
590 let sk1 = [0x01u8; 32];
591 let sk2 = [0x02u8; 32];
592 let pk1 = individual_pubkey(&sk1).unwrap();
593 let pk2 = individual_pubkey(&sk2).unwrap();
594
595 let sorted = key_sort(&[pk2, pk1]);
596 let sorted2 = key_sort(&[pk1, pk2]);
597 assert_eq!(sorted, sorted2); }
599
600 #[test]
603 fn test_musig2_full_roundtrip() {
604 let sk1 = [0x11u8; 32];
605 let sk2 = [0x22u8; 32];
606 let pk1 = individual_pubkey(&sk1).unwrap();
607 let pk2 = individual_pubkey(&sk2).unwrap();
608
609 let key_agg_ctx = key_agg(&[pk1, pk2]).unwrap();
611
612 let msg = b"musig2 test message";
614 let (sec1, pub1) = nonce_gen(&sk1, &pk1, &key_agg_ctx, msg, &[]).unwrap();
615 let (sec2, pub2) = nonce_gen(&sk2, &pk2, &key_agg_ctx, msg, &[]).unwrap();
616
617 let agg_nonce = nonce_agg(&[pub1, pub2]).unwrap();
619
620 let psig1 = sign(sec1, &sk1, &key_agg_ctx, &agg_nonce, msg).unwrap();
622 let psig2 = sign(sec2, &sk2, &key_agg_ctx, &agg_nonce, msg).unwrap();
623
624 let sig = partial_sig_agg(&[psig1, psig2], &agg_nonce, &key_agg_ctx, msg).unwrap();
626 assert_eq!(sig.to_bytes().len(), 64);
627
628 let valid = verify(&sig, &key_agg_ctx.x_only_pubkey, msg).unwrap();
630 assert!(valid, "MuSig2 signature must verify");
631 }
632
633 #[test]
634 fn test_musig2_different_messages_different_sigs() {
635 let sk1 = [0x11u8; 32];
636 let sk2 = [0x22u8; 32];
637 let pk1 = individual_pubkey(&sk1).unwrap();
638 let pk2 = individual_pubkey(&sk2).unwrap();
639 let ctx = key_agg(&[pk1, pk2]).unwrap();
640
641 let msg1 = b"message one";
642 let msg2 = b"message two";
643
644 let (s1a, p1a) = nonce_gen(&sk1, &pk1, &ctx, msg1, &[]).unwrap();
645 let (s2a, p2a) = nonce_gen(&sk2, &pk2, &ctx, msg1, &[]).unwrap();
646 let an_a = nonce_agg(&[p1a, p2a]).unwrap();
647 let ps1a = sign(s1a, &sk1, &ctx, &an_a, msg1).unwrap();
648 let ps2a = sign(s2a, &sk2, &ctx, &an_a, msg1).unwrap();
649 let sig1 = partial_sig_agg(&[ps1a, ps2a], &an_a, &ctx, msg1).unwrap();
650
651 let (s1b, p1b) = nonce_gen(&sk1, &pk1, &ctx, msg2, &[]).unwrap();
652 let (s2b, p2b) = nonce_gen(&sk2, &pk2, &ctx, msg2, &[]).unwrap();
653 let an_b = nonce_agg(&[p1b, p2b]).unwrap();
654 let ps1b = sign(s1b, &sk1, &ctx, &an_b, msg2).unwrap();
655 let ps2b = sign(s2b, &sk2, &ctx, &an_b, msg2).unwrap();
656 let sig2 = partial_sig_agg(&[ps1b, ps2b], &an_b, &ctx, msg2).unwrap();
657
658 assert_ne!(sig1.to_bytes(), sig2.to_bytes());
660 }
661
662 #[test]
663 fn test_musig2_wrong_message_fails_verification() {
664 let sk1 = [0x11u8; 32];
665 let sk2 = [0x22u8; 32];
666 let pk1 = individual_pubkey(&sk1).unwrap();
667 let pk2 = individual_pubkey(&sk2).unwrap();
668 let ctx = key_agg(&[pk1, pk2]).unwrap();
669
670 let msg = b"correct message";
671 let (s1, p1) = nonce_gen(&sk1, &pk1, &ctx, msg, &[]).unwrap();
672 let (s2, p2) = nonce_gen(&sk2, &pk2, &ctx, msg, &[]).unwrap();
673 let an = nonce_agg(&[p1, p2]).unwrap();
674 let ps1 = sign(s1, &sk1, &ctx, &an, msg).unwrap();
675 let ps2 = sign(s2, &sk2, &ctx, &an, msg).unwrap();
676 let sig = partial_sig_agg(&[ps1, ps2], &an, &ctx, msg).unwrap();
677
678 let valid = verify(&sig, &ctx.x_only_pubkey, b"wrong message").unwrap();
680 assert!(!valid, "signature must not verify for wrong message");
681 }
682
683 #[test]
684 fn test_nonce_agg_empty_rejected() {
685 assert!(nonce_agg(&[]).is_err());
686 }
687
688 #[test]
689 fn test_partial_sig_agg_empty_rejected() {
690 let sk1 = [0x11u8; 32];
691 let sk2 = [0x22u8; 32];
692 let pk1 = individual_pubkey(&sk1).unwrap();
693 let pk2 = individual_pubkey(&sk2).unwrap();
694 let ctx = key_agg(&[pk1, pk2]).unwrap();
695 let (_, p1) = nonce_gen(&sk1, &pk1, &ctx, b"x", &[]).unwrap();
696 let (_, p2) = nonce_gen(&sk2, &pk2, &ctx, b"x", &[]).unwrap();
697 let an = nonce_agg(&[p1, p2]).unwrap();
698 assert!(partial_sig_agg(&[], &an, &ctx, b"x").is_err());
699 }
700}