Skip to content

Commit 8a43317

Browse files
committed
musig: add a bunch of unit tests
I asked Claude to create an initial set of unit tests, which it did. I then manually cleaned up a lot of its repeated logic (though not all of it, as you can tell) and added a whole bunch more failure cases. For example it did not bother trying to repeat or swap keys/nonces. When it generated the tests, the empty-pubkey-list bug (fixed in the previous commit) was still present. It failed to find it, I think because it was reading the code looking for panics to trigger, and there wasn't one. In future I will try giving it only the API, and try telling it to be more adversarial. We'll see.
1 parent 40a8b65 commit 8a43317

File tree

1 file changed

+335
-0
lines changed

1 file changed

+335
-0
lines changed

src/musig.rs

+335
Original file line numberDiff line numberDiff line change
@@ -1195,3 +1195,338 @@ impl Session {
11951195
/// Get a mut pointer to the inner Session
11961196
pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigSession { &mut self.0 }
11971197
}
1198+
1199+
#[cfg(test)]
1200+
mod tests {
1201+
use super::*;
1202+
#[cfg(feature = "std")]
1203+
#[cfg(feature = "rand")]
1204+
use crate::{Message, PublicKey, Secp256k1, SecretKey};
1205+
1206+
#[test]
1207+
#[cfg(feature = "std")]
1208+
#[cfg(feature = "rand")]
1209+
fn session_secret_rand() {
1210+
let mut rng = rand::rng();
1211+
let session_secrand = SessionSecretRand::from_rng(&mut rng);
1212+
let session_secrand1 = SessionSecretRand::from_rng(&mut rng);
1213+
assert_ne!(session_secrand.to_byte_array(), [0; 32]); // with overwhelming probability
1214+
assert_ne!(session_secrand, session_secrand1); // with overwhelming probability
1215+
}
1216+
1217+
#[test]
1218+
fn session_secret_no_rand() {
1219+
let custom_bytes = [42u8; 32];
1220+
let session_secrand = SessionSecretRand::assume_unique_per_nonce_gen(custom_bytes);
1221+
assert_eq!(session_secrand.to_byte_array(), custom_bytes);
1222+
assert_eq!(session_secrand.as_byte_array(), &custom_bytes);
1223+
}
1224+
1225+
#[test]
1226+
#[should_panic(expected = "session secrets may not be all zero")]
1227+
fn session_secret_rand_zero_panic() {
1228+
let zero_bytes = [0u8; 32];
1229+
let _session_secrand = SessionSecretRand::assume_unique_per_nonce_gen(zero_bytes);
1230+
}
1231+
1232+
#[test]
1233+
#[cfg(not(secp256k1_fuzz))]
1234+
#[cfg(feature = "std")]
1235+
#[cfg(feature = "rand")]
1236+
fn key_agg_cache() {
1237+
let secp = Secp256k1::new();
1238+
let mut rng = rand::rng();
1239+
1240+
let (_seckey1, pubkey1) = secp.generate_keypair(&mut rng);
1241+
let seckey2 = SecretKey::new(&mut rng);
1242+
let pubkey2 = PublicKey::from_secret_key(&secp, &seckey2);
1243+
1244+
let pubkeys = [&pubkey1, &pubkey2];
1245+
let key_agg_cache = KeyAggCache::new(&secp, &pubkeys);
1246+
let agg_pk = key_agg_cache.agg_pk();
1247+
1248+
// Test agg_pk_full
1249+
let agg_pk_full = key_agg_cache.agg_pk_full();
1250+
assert_eq!(agg_pk_full.x_only_public_key().0, agg_pk);
1251+
}
1252+
1253+
#[test]
1254+
#[cfg(not(secp256k1_fuzz))]
1255+
#[cfg(feature = "std")]
1256+
#[cfg(feature = "rand")]
1257+
fn key_agg_cache_tweaking() {
1258+
let secp = Secp256k1::new();
1259+
let mut rng = rand::rng();
1260+
1261+
let (_seckey1, pubkey1) = secp.generate_keypair(&mut rng);
1262+
let seckey2 = SecretKey::new(&mut rng);
1263+
let pubkey2 = PublicKey::from_secret_key(&secp, &seckey2);
1264+
1265+
let mut key_agg_cache = KeyAggCache::new(&secp, &[&pubkey1, &pubkey2]);
1266+
let key_agg_cache1 = KeyAggCache::new(&secp, &[&pubkey2, &pubkey1]);
1267+
let key_agg_cache2 = KeyAggCache::new(&secp, &[&pubkey1, &pubkey1]);
1268+
let key_agg_cache3 = KeyAggCache::new(&secp, &[&pubkey1, &pubkey1, &pubkey2]);
1269+
assert_ne!(key_agg_cache, key_agg_cache1); // swapped keys DOES mean not equal
1270+
assert_ne!(key_agg_cache, key_agg_cache2); // missing keys
1271+
assert_ne!(key_agg_cache, key_agg_cache3); // repeated key
1272+
let original_agg_pk = key_agg_cache.agg_pk();
1273+
assert_ne!(key_agg_cache.agg_pk(), key_agg_cache1.agg_pk()); // swapped keys DOES mean not equal
1274+
assert_ne!(key_agg_cache.agg_pk(), key_agg_cache2.agg_pk()); // missing keys
1275+
assert_ne!(key_agg_cache.agg_pk(), key_agg_cache3.agg_pk()); // repeated key
1276+
1277+
// Test EC tweaking
1278+
let plain_tweak: [u8; 32] = *b"this could be a BIP32 tweak....\0";
1279+
let plain_tweak = Scalar::from_be_bytes(plain_tweak).unwrap();
1280+
let tweaked_key = key_agg_cache.pubkey_ec_tweak_add(&secp, &plain_tweak).unwrap();
1281+
assert_ne!(key_agg_cache.agg_pk(), original_agg_pk);
1282+
assert_eq!(key_agg_cache.agg_pk(), tweaked_key.x_only_public_key().0);
1283+
1284+
// Test xonly tweaking
1285+
let xonly_tweak: [u8; 32] = *b"this could be a Taproot tweak..\0";
1286+
let xonly_tweak = Scalar::from_be_bytes(xonly_tweak).unwrap();
1287+
let tweaked_agg_pk = key_agg_cache.pubkey_xonly_tweak_add(&secp, &xonly_tweak).unwrap();
1288+
assert_eq!(key_agg_cache.agg_pk(), tweaked_agg_pk.x_only_public_key().0);
1289+
}
1290+
1291+
#[test]
1292+
#[cfg(feature = "std")]
1293+
#[cfg(feature = "rand")]
1294+
#[should_panic(expected = "Cannot aggregate an empty slice of pubkeys")]
1295+
fn key_agg_cache_empty_panic() {
1296+
let secp = Secp256k1::new();
1297+
let _ = KeyAggCache::new(&secp, &[]);
1298+
}
1299+
1300+
#[test]
1301+
#[cfg(feature = "std")]
1302+
#[cfg(feature = "rand")]
1303+
fn nonce_generation() {
1304+
let secp = Secp256k1::new();
1305+
let mut rng = rand::rng();
1306+
1307+
let (_seckey1, pubkey1) = secp.generate_keypair(&mut rng);
1308+
let seckey2 = SecretKey::new(&mut rng);
1309+
let pubkey2 = PublicKey::from_secret_key(&secp, &seckey2);
1310+
1311+
let key_agg_cache = KeyAggCache::new(&secp, &[&pubkey1, &pubkey2]);
1312+
1313+
let msg_bytes: [u8; 32] = *b"this_could_be_the_hash_of_a_msg!";
1314+
let msg = Message::from_digest_slice(&msg_bytes).unwrap();
1315+
1316+
// Test nonce generation with KeyAggCache
1317+
let session_secrand1 = SessionSecretRand::from_rng(&mut rng);
1318+
let (_sec_nonce1, pub_nonce1) =
1319+
key_agg_cache.nonce_gen(&secp, session_secrand1, pubkey1, msg, None);
1320+
1321+
// Test direct nonce generation
1322+
let session_secrand2 = SessionSecretRand::from_rng(&mut rng);
1323+
let extra_rand = Some([42u8; 32]);
1324+
let (_sec_nonce2, _pub_nonce2) = new_nonce_pair(
1325+
&secp,
1326+
session_secrand2,
1327+
Some(&key_agg_cache),
1328+
Some(seckey2),
1329+
pubkey2,
1330+
Some(msg),
1331+
extra_rand,
1332+
);
1333+
1334+
// Test PublicNonce serialization/deserialization
1335+
let serialized_nonce = pub_nonce1.serialize();
1336+
let deserialized_nonce = PublicNonce::from_byte_array(&serialized_nonce).unwrap();
1337+
assert_eq!(pub_nonce1.serialize(), deserialized_nonce.serialize());
1338+
}
1339+
1340+
#[test]
1341+
#[cfg(feature = "std")]
1342+
#[cfg(feature = "rand")]
1343+
fn aggregated_nonce() {
1344+
let secp = Secp256k1::new();
1345+
let mut rng = rand::rng();
1346+
1347+
let (_seckey1, pubkey1) = secp.generate_keypair(&mut rng);
1348+
let seckey2 = SecretKey::new(&mut rng);
1349+
let pubkey2 = PublicKey::from_secret_key(&secp, &seckey2);
1350+
1351+
let key_agg_cache = KeyAggCache::new(&secp, &[&pubkey1, &pubkey2]);
1352+
1353+
let msg_bytes: [u8; 32] = *b"this_could_be_the_hash_of_a_msg!";
1354+
let msg = Message::from_digest_slice(&msg_bytes).unwrap();
1355+
1356+
let session_secrand1 = SessionSecretRand::from_rng(&mut rng);
1357+
let (_, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_secrand1, pubkey1, msg, None);
1358+
1359+
let session_secrand2 = SessionSecretRand::from_rng(&mut rng);
1360+
let (_, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_secrand2, pubkey2, msg, None);
1361+
1362+
// Test AggregatedNonce creation
1363+
let agg_nonce = AggregatedNonce::new(&secp, &[&pub_nonce1, &pub_nonce2]);
1364+
let agg_nonce1 = AggregatedNonce::new(&secp, &[&pub_nonce2, &pub_nonce1]);
1365+
let agg_nonce2 = AggregatedNonce::new(&secp, &[&pub_nonce2, &pub_nonce2]);
1366+
let agg_nonce3 = AggregatedNonce::new(&secp, &[&pub_nonce2, &pub_nonce2]);
1367+
assert_eq!(agg_nonce, agg_nonce1); // swapped nonces
1368+
assert_ne!(agg_nonce, agg_nonce2); // repeated/different nonces
1369+
assert_ne!(agg_nonce, agg_nonce3); // repeated nonce but still both nonces present
1370+
1371+
// Test AggregatedNonce serialization/deserialization
1372+
let serialized_agg_nonce = agg_nonce.serialize();
1373+
let deserialized_agg_nonce =
1374+
AggregatedNonce::from_byte_array(&serialized_agg_nonce).unwrap();
1375+
assert_eq!(agg_nonce.serialize(), deserialized_agg_nonce.serialize());
1376+
}
1377+
1378+
#[test]
1379+
#[cfg(feature = "std")]
1380+
#[cfg(feature = "rand")]
1381+
#[should_panic(expected = "Cannot aggregate an empty slice of nonces")]
1382+
fn aggregated_nonce_empty_panic() {
1383+
let secp = Secp256k1::new();
1384+
let empty_nonces: Vec<&PublicNonce> = vec![];
1385+
let _agg_nonce = AggregatedNonce::new(&secp, &empty_nonces);
1386+
}
1387+
1388+
#[test]
1389+
#[cfg(not(secp256k1_fuzz))]
1390+
#[cfg(feature = "std")]
1391+
#[cfg(feature = "rand")]
1392+
fn session_and_partial_signing() {
1393+
let secp = Secp256k1::new();
1394+
let mut rng = rand::rng();
1395+
1396+
let (seckey1, pubkey1) = secp.generate_keypair(&mut rng);
1397+
let seckey2 = SecretKey::new(&mut rng);
1398+
let pubkey2 = PublicKey::from_secret_key(&secp, &seckey2);
1399+
1400+
let pubkeys = [&pubkey1, &pubkey2];
1401+
let key_agg_cache = KeyAggCache::new(&secp, &pubkeys);
1402+
1403+
let msg_bytes: [u8; 32] = *b"this_could_be_the_hash_of_a_msg!";
1404+
let msg = Message::from_digest_slice(&msg_bytes).unwrap();
1405+
1406+
let session_secrand1 = SessionSecretRand::from_rng(&mut rng);
1407+
let (sec_nonce1, pub_nonce1) =
1408+
key_agg_cache.nonce_gen(&secp, session_secrand1, pubkey1, msg, None);
1409+
1410+
let session_secrand2 = SessionSecretRand::from_rng(&mut rng);
1411+
let (sec_nonce2, pub_nonce2) =
1412+
key_agg_cache.nonce_gen(&secp, session_secrand2, pubkey2, msg, None);
1413+
1414+
let nonces = [&pub_nonce1, &pub_nonce2];
1415+
let agg_nonce = AggregatedNonce::new(&secp, &nonces);
1416+
1417+
// Test Session creation
1418+
let session = Session::new(&secp, &key_agg_cache, agg_nonce, msg);
1419+
1420+
// Test partial signing
1421+
let keypair1 = Keypair::from_secret_key(&secp, &seckey1);
1422+
let partial_sign1 = session.partial_sign(&secp, sec_nonce1, &keypair1, &key_agg_cache);
1423+
1424+
let keypair2 = Keypair::from_secret_key(&secp, &seckey2);
1425+
let partial_sign2 = session.partial_sign(&secp, sec_nonce2, &keypair2, &key_agg_cache);
1426+
1427+
// Test partial signature verification
1428+
assert!(session.partial_verify(&secp, &key_agg_cache, partial_sign1, pub_nonce1, pubkey1));
1429+
assert!(session.partial_verify(&secp, &key_agg_cache, partial_sign2, pub_nonce2, pubkey2));
1430+
// Test that they are invalid if you switch keys
1431+
assert!(!session.partial_verify(&secp, &key_agg_cache, partial_sign2, pub_nonce2, pubkey1));
1432+
assert!(!session.partial_verify(&secp, &key_agg_cache, partial_sign2, pub_nonce1, pubkey2));
1433+
assert!(!session.partial_verify(&secp, &key_agg_cache, partial_sign2, pub_nonce1, pubkey1));
1434+
1435+
// Test PartialSignature serialization/deserialization
1436+
let serialized_partial_sig = partial_sign1.serialize();
1437+
let deserialized_partial_sig =
1438+
PartialSignature::from_byte_array(&serialized_partial_sig).unwrap();
1439+
assert_eq!(partial_sign1.serialize(), deserialized_partial_sig.serialize());
1440+
}
1441+
1442+
#[test]
1443+
#[cfg(not(secp256k1_fuzz))]
1444+
#[cfg(feature = "std")]
1445+
#[cfg(feature = "rand")]
1446+
fn signature_aggregation_and_verification() {
1447+
let secp = Secp256k1::new();
1448+
let mut rng = rand::rng();
1449+
1450+
let (seckey1, pubkey1) = secp.generate_keypair(&mut rng);
1451+
let seckey2 = SecretKey::new(&mut rng);
1452+
let pubkey2 = PublicKey::from_secret_key(&secp, &seckey2);
1453+
1454+
let pubkeys = [&pubkey1, &pubkey2];
1455+
let key_agg_cache = KeyAggCache::new(&secp, &pubkeys);
1456+
1457+
let msg_bytes: [u8; 32] = *b"this_could_be_the_hash_of_a_msg!";
1458+
let msg = Message::from_digest_slice(&msg_bytes).unwrap();
1459+
1460+
let session_secrand1 = SessionSecretRand::from_rng(&mut rng);
1461+
let (sec_nonce1, pub_nonce1) =
1462+
key_agg_cache.nonce_gen(&secp, session_secrand1, pubkey1, msg, None);
1463+
1464+
let session_secrand2 = SessionSecretRand::from_rng(&mut rng);
1465+
let (sec_nonce2, pub_nonce2) =
1466+
key_agg_cache.nonce_gen(&secp, session_secrand2, pubkey2, msg, None);
1467+
1468+
let nonces = [&pub_nonce1, &pub_nonce2];
1469+
let agg_nonce = AggregatedNonce::new(&secp, &nonces);
1470+
let session = Session::new(&secp, &key_agg_cache, agg_nonce, msg);
1471+
1472+
let keypair1 = Keypair::from_secret_key(&secp, &seckey1);
1473+
let partial_sign1 = session.partial_sign(&secp, sec_nonce1, &keypair1, &key_agg_cache);
1474+
1475+
let keypair2 = Keypair::from_secret_key(&secp, &seckey2);
1476+
let partial_sign2 = session.partial_sign(&secp, sec_nonce2, &keypair2, &key_agg_cache);
1477+
1478+
// Test signature verification
1479+
let aggregated_signature = session.partial_sig_agg(&[&partial_sign1, &partial_sign2]);
1480+
let agg_pk = key_agg_cache.agg_pk();
1481+
aggregated_signature.verify(&secp, &agg_pk, &msg_bytes).unwrap();
1482+
1483+
// Test assume_valid
1484+
let schnorr_sig = aggregated_signature.assume_valid();
1485+
secp.verify_schnorr(&schnorr_sig, &msg_bytes, &agg_pk).unwrap();
1486+
1487+
// Test with wrong aggregate (repeated sigs)
1488+
let aggregated_signature = session.partial_sig_agg(&[&partial_sign1, &partial_sign1]);
1489+
aggregated_signature.verify(&secp, &agg_pk, &msg_bytes).unwrap_err();
1490+
let schnorr_sig = aggregated_signature.assume_valid();
1491+
secp.verify_schnorr(&schnorr_sig, &msg_bytes, &agg_pk).unwrap_err();
1492+
1493+
// Test with swapped sigs -- this will work. Unlike keys, sigs are not ordered.
1494+
let aggregated_signature = session.partial_sig_agg(&[&partial_sign2, &partial_sign1]);
1495+
aggregated_signature.verify(&secp, &agg_pk, &msg_bytes).unwrap();
1496+
let schnorr_sig = aggregated_signature.assume_valid();
1497+
secp.verify_schnorr(&schnorr_sig, &msg_bytes, &agg_pk).unwrap();
1498+
}
1499+
1500+
#[test]
1501+
#[cfg(feature = "std")]
1502+
#[cfg(feature = "rand")]
1503+
#[should_panic(expected = "Cannot aggregate an empty slice of partial signatures")]
1504+
fn partial_sig_agg_empty_panic() {
1505+
let secp = Secp256k1::new();
1506+
let mut rng = rand::rng();
1507+
1508+
let (_seckey1, pubkey1) = secp.generate_keypair(&mut rng);
1509+
let seckey2 = SecretKey::new(&mut rng);
1510+
let pubkey2 = PublicKey::from_secret_key(&secp, &seckey2);
1511+
1512+
let pubkeys = [pubkey1, pubkey2];
1513+
let mut pubkeys_ref: Vec<&PublicKey> = pubkeys.iter().collect();
1514+
let pubkeys_ref = pubkeys_ref.as_mut_slice();
1515+
1516+
let key_agg_cache = KeyAggCache::new(&secp, pubkeys_ref);
1517+
let msg_bytes: [u8; 32] = *b"this_could_be_the_hash_of_a_msg!";
1518+
let msg = Message::from_digest_slice(&msg_bytes).unwrap();
1519+
1520+
let session_secrand1 = SessionSecretRand::from_rng(&mut rng);
1521+
let (_, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_secrand1, pubkey1, msg, None);
1522+
let session_secrand2 = SessionSecretRand::from_rng(&mut rng);
1523+
let (_, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_secrand2, pubkey2, msg, None);
1524+
1525+
let nonces = [pub_nonce1, pub_nonce2];
1526+
let nonces_ref: Vec<&PublicNonce> = nonces.iter().collect();
1527+
let agg_nonce = AggregatedNonce::new(&secp, &nonces_ref);
1528+
let session = Session::new(&secp, &key_agg_cache, agg_nonce, msg);
1529+
1530+
let _agg_sig = session.partial_sig_agg(&[]);
1531+
}
1532+
}

0 commit comments

Comments
 (0)