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