use nostringer::{
blsag::{key_images_match, sign_blsag_hex, verify_blsag_hex},
generate_keypair_hex, generate_keypairs, get_public_keys,
sag::{sign, verify},
types::{Error, KeyImage},
};
#[test]
fn test_sign_verify_round_trip_xonly() {
let keypairs = generate_keypairs(3, "xonly");
let ring_pubkeys_hex = get_public_keys(&keypairs);
let signer_index = 1; let signer_kp = &keypairs[signer_index];
let message = b"Test message for xonly keys";
let signature =
sign(message, &signer_kp.private_key_hex, &ring_pubkeys_hex).expect("Signing failed");
let is_valid = verify(&signature, message, &ring_pubkeys_hex).expect("Verification failed");
assert!(is_valid, "Signature should be valid");
}
#[test]
fn test_sign_verify_round_trip_compressed() {
let keypairs = generate_keypairs(5, "compressed"); let ring_pubkeys_hex = get_public_keys(&keypairs);
let signer_index = 3; let signer_kp = &keypairs[signer_index];
let message = b"Another test with compressed keys!";
let signature =
sign(message, &signer_kp.private_key_hex, &ring_pubkeys_hex).expect("Signing failed");
let is_valid = verify(&signature, message, &ring_pubkeys_hex).expect("Verification failed");
assert!(is_valid, "Signature should be valid");
}
#[test]
fn test_sign_verify_round_trip_uncompressed() {
let keypairs = generate_keypairs(2, "uncompressed"); let ring_pubkeys_hex = get_public_keys(&keypairs);
let signer_index = 0; let signer_kp = &keypairs[signer_index];
let message = b"Testing uncompressed keys";
let signature =
sign(message, &signer_kp.private_key_hex, &ring_pubkeys_hex).expect("Signing failed");
let is_valid = verify(&signature, message, &ring_pubkeys_hex).expect("Verification failed");
assert!(is_valid, "Signature should be valid");
}
#[test]
fn test_sign_verify_mixed_key_formats() {
let kp1 = generate_keypair_hex("xonly");
let kp2 = generate_keypair_hex("compressed");
let kp3 = generate_keypair_hex("uncompressed");
let keypairs = vec![kp1, kp2, kp3];
let ring_pubkeys_hex = get_public_keys(&keypairs);
let signer_index = 1; let signer_kp = &keypairs[signer_index];
let message = b"Mixing key formats in the ring";
let signature =
sign(message, &signer_kp.private_key_hex, &ring_pubkeys_hex).expect("Signing failed");
let is_valid = verify(&signature, message, &ring_pubkeys_hex).expect("Verification failed");
assert!(is_valid, "Signature should be valid with mixed formats");
}
#[test]
fn test_verification_fail_tampered_message() {
let keypairs = generate_keypairs(3, "compressed");
let ring_pubkeys_hex = get_public_keys(&keypairs);
let signer_kp = &keypairs[0];
let message = b"Original content";
let signature =
sign(message, &signer_kp.private_key_hex, &ring_pubkeys_hex).expect("Signing failed");
let tampered_message = b"Tampered content";
let is_valid = verify(&signature, tampered_message, &ring_pubkeys_hex)
.expect("Verification process completed");
assert!(
!is_valid,
"Signature should be invalid for tampered message"
);
}
#[test]
fn test_verification_fail_wrong_ring() {
let keypairs1 = generate_keypairs(3, "xonly");
let ring1 = get_public_keys(&keypairs1);
let signer_kp = &keypairs1[0];
let message = b"Message for ring 1";
let signature = sign(message, &signer_kp.private_key_hex, &ring1).expect("Signing failed");
let keypairs2 = generate_keypairs(3, "xonly"); let ring2 = get_public_keys(&keypairs2);
let is_valid = verify(&signature, message, &ring2).expect("Verification process completed");
assert!(!is_valid, "Signature should be invalid for the wrong ring");
let keypairs3 = generate_keypairs(4, "xonly"); let ring3 = get_public_keys(&keypairs3);
let result = verify(&signature, message, &ring3);
assert!(matches!(result, Err(Error::InvalidSignatureFormat)));
}
#[test]
fn test_sign_fail_signer_not_in_ring() {
let keypairs = generate_keypairs(3, "compressed");
let ring = get_public_keys(&keypairs);
let outsider_kp = generate_keypair_hex("compressed"); let message = b"Attempt by outsider";
let result = sign(message, &outsider_kp.private_key_hex, &ring);
assert!(matches!(result, Err(Error::SignerNotInRing)));
}
#[test]
fn test_sign_fail_small_ring() {
let keypairs = generate_keypairs(1, "xonly"); let ring = get_public_keys(&keypairs);
let signer_kp = &keypairs[0];
let message = b"Ring too small";
let result = sign(message, &signer_kp.private_key_hex, &ring);
assert!(matches!(result, Err(Error::RingTooSmall(1))));
}
#[test]
fn test_nostr_event_simulation() {
let developers = generate_keypairs(3, "xonly");
let developer_ring = get_public_keys(&developers);
let author_index = 1;
let author = &developers[author_index];
let event_pubkey = "anonymous"; let created_at = 1711609836; let kind = 1001;
let tags_vec = vec![
vec!["g".to_string(), "reviews".to_string()],
vec!["ring".to_string()]
.into_iter()
.chain(developer_ring.iter().cloned())
.collect::<Vec<String>>(),
];
let content = "This app is great but has some privacy concerns.";
let tags_json_str = format!(
"[{}]",
tags_vec
.iter()
.map(|tag| format!(
"[{}]",
tag.iter()
.map(|s| format!("\"{}\"", s))
.collect::<Vec<_>>()
.join(",")
))
.collect::<Vec<_>>()
.join(",")
);
let message_str = format!(
"[0,\"{}\",{},{},{},\"{}\"]",
event_pubkey,
created_at,
kind,
tags_json_str, content
);
let message_bytes = message_str.as_bytes();
let signature = sign(message_bytes, &author.private_key_hex, &developer_ring)
.expect("Signing failed in Nostr sim");
let is_valid = verify(&signature, message_bytes, &developer_ring)
.expect("Verification failed in Nostr sim");
assert!(is_valid);
let tampered_content = "Modified content";
let tampered_message_str = format!(
"[0,\"{}\",{},{},{},\"{}\"]",
event_pubkey,
created_at,
kind,
tags_json_str, tampered_content
);
let tampered_message_bytes = tampered_message_str.as_bytes();
let is_tampered_valid = verify(&signature, tampered_message_bytes, &developer_ring)
.expect("Tampered verification process failed");
assert!(!is_tampered_valid, "Tampered message should not verify");
}
#[test]
fn test_blsag_sign_verify_round_trip() {
let ring_size = 3;
let keypairs = generate_keypairs(ring_size, "compressed"); let ring_pubkeys_hex = get_public_keys(&keypairs);
let signer_index = 1;
let signer_kp = &keypairs[signer_index];
let message = b"Test message for bLSAG round trip";
let sign_result = sign_blsag_hex(message, &signer_kp.private_key_hex, &ring_pubkeys_hex);
assert!(
sign_result.is_ok(),
"bLSAG signing failed: {:?}",
sign_result.err()
);
let (signature, key_image_hex) = sign_result.unwrap();
assert_eq!(signature.s.len(), ring_size);
let verification_result =
verify_blsag_hex(&signature, &key_image_hex, message, &ring_pubkeys_hex);
assert!(
verification_result.is_ok(),
"Verification result errored: {:?}",
verification_result.err()
);
assert!(verification_result.unwrap(), "Valid bLSAG signature failed");
}
#[test]
fn test_blsag_linkability() {
let keypairs = generate_keypairs(4, "compressed");
let ring = get_public_keys(&keypairs);
let signer_kp = &keypairs[1]; let message1 = b"First message";
let message2 = b"Second message";
let (sig1, ki1_hex) = sign_blsag_hex(message1, &signer_kp.private_key_hex, &ring).unwrap();
let (sig2, ki2_hex) = sign_blsag_hex(message2, &signer_kp.private_key_hex, &ring).unwrap();
assert!(verify_blsag_hex(&sig1, &ki1_hex, message1, &ring).unwrap());
assert!(verify_blsag_hex(&sig2, &ki2_hex, message2, &ring).unwrap());
assert_eq!(
ki1_hex, ki2_hex,
"Key images should match for the same signer"
);
let ki1 = KeyImage::from_hex(&ki1_hex).unwrap();
let ki2 = KeyImage::from_hex(&ki2_hex).unwrap();
assert!(key_images_match(&ki1, &ki2));
let different_signer_kp = &keypairs[2];
let (sig3, ki3_hex) =
sign_blsag_hex(message1, &different_signer_kp.private_key_hex, &ring).unwrap();
assert!(verify_blsag_hex(&sig3, &ki3_hex, message1, &ring).unwrap());
assert_ne!(
ki1_hex, ki3_hex,
"Key images should differ for different signers"
);
let ki3 = KeyImage::from_hex(&ki3_hex).unwrap();
assert!(!key_images_match(&ki1, &ki3));
}
#[test]
fn test_blsag_verification_fail_tampered_message() {
let keypairs = generate_keypairs(3, "xonly");
let ring = get_public_keys(&keypairs);
let signer_kp = &keypairs[0];
let message = b"Original BLSAG message";
let (sig, ki_hex) = sign_blsag_hex(message, &signer_kp.private_key_hex, &ring).unwrap();
let tampered_message = b"Tampered BLSAG message";
let is_valid = verify_blsag_hex(&sig, &ki_hex, tampered_message, &ring)
.expect("Verification process completed");
assert!(
!is_valid,
"BLSAG signature should be invalid for tampered message"
);
}
#[test]
fn test_blsag_verification_fail_wrong_key_image() {
let keypairs = generate_keypairs(3, "compressed");
let ring = get_public_keys(&keypairs);
let signer_kp = &keypairs[0];
let message = b"Message for key image test";
let (sig, _ki_hex) = sign_blsag_hex(message, &signer_kp.private_key_hex, &ring).unwrap();
let different_kp = &keypairs[1];
let (_, wrong_ki_hex) = sign_blsag_hex(message, &different_kp.private_key_hex, &ring).unwrap();
let is_valid = verify_blsag_hex(&sig, &wrong_ki_hex, message, &ring)
.expect("Verification process completed");
assert!(
!is_valid,
"BLSAG signature should be invalid with wrong key image"
);
}
#[test]
fn test_blsag_verification_fail_invalid_key_image_format() {
let keypairs = generate_keypairs(3, "compressed");
let ring = get_public_keys(&keypairs);
let signer_kp = &keypairs[0];
let message = b"Message for key image test";
let (sig, _ki_hex) = sign_blsag_hex(message, &signer_kp.private_key_hex, &ring).unwrap();
let invalid_ki_hex = "invalid-hex-string";
let result = verify_blsag_hex(&sig, invalid_ki_hex, message, &ring);
assert!(matches!(result, Err(Error::PublicKeyFormat(_))));
let short_ki_hex = "02aabbcc";
let result_short = verify_blsag_hex(&sig, short_ki_hex, message, &ring);
assert!(matches!(result_short, Err(Error::PublicKeyFormat(_))));
}