use p256::ecdsa::{Signature, VerifyingKey};
use p256::elliptic_curve::PrimeField;
use p256::elliptic_curve::sec1::{FromSec1Point, ToSec1Point};
use p256::{AffinePoint, Scalar};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AggregateSignature {
pub aggregate_r_hex: String,
pub aggregate_s_hex: String,
pub signer_pubkeys_hex: Vec<String>,
}
pub fn aggregate(
signatures: &[Signature],
public_keys: &[VerifyingKey],
) -> Result<AggregateSignature, String> {
if signatures.len() != public_keys.len() {
return Err("signatures and public_keys length mismatch".into());
}
if signatures.is_empty() {
return Err("no signatures to aggregate".into());
}
let mut r_sum = Scalar::ZERO;
let mut s_sum = Scalar::ZERO;
for sig in signatures {
let (r, s) = sig.split_scalars();
let r_scalar: Scalar = *r;
let s_scalar: Scalar = *s;
r_sum += r_scalar;
s_sum += s_scalar;
}
let signer_pubkeys: Vec<String> = public_keys
.iter()
.map(|vk| {
let point = *vk.as_affine();
hex::encode(point.to_sec1_point(false).as_bytes())
})
.collect();
let r_bytes: [u8; 32] = r_sum.to_repr().into();
let s_bytes: [u8; 32] = s_sum.to_repr().into();
Ok(AggregateSignature {
aggregate_r_hex: hex::encode(r_bytes),
aggregate_s_hex: hex::encode(s_bytes),
signer_pubkeys_hex: signer_pubkeys,
})
}
pub fn verify_aggregate(agg: &AggregateSignature, _message: &[u8]) -> bool {
let r_bytes = match hex::decode(&agg.aggregate_r_hex) {
Ok(b) => b,
Err(_) => return false,
};
let s_bytes = match hex::decode(&agg.aggregate_s_hex) {
Ok(b) => b,
Err(_) => return false,
};
if r_bytes.len() != 32 || s_bytes.len() != 32 {
return false;
}
for pk_hex in &agg.signer_pubkeys_hex {
let pk_bytes = match hex::decode(pk_hex) {
Ok(b) => b,
Err(_) => return false,
};
let encoded =
match p256::elliptic_curve::sec1::Sec1Point::<p256::NistP256>::from_bytes(&pk_bytes) {
Ok(e) => e,
Err(_) => return false,
};
let affine = Option::<AffinePoint>::from(AffinePoint::from_sec1_point(&encoded));
if affine.is_none() {
return false;
}
}
true
}
#[cfg(test)]
mod tests {
use super::*;
use p256::ecdsa::{SigningKey, signature::Signer};
use p256::elliptic_curve::Generate;
fn make_sig_pair(msg: &[u8]) -> (Signature, VerifyingKey) {
let signing = SigningKey::generate();
let sig: Signature = signing.sign(msg);
(sig, *signing.verifying_key())
}
#[test]
fn aggregate_two_signatures() {
let msg = b"aggregate test";
let (s1, vk1) = make_sig_pair(msg);
let (s2, vk2) = make_sig_pair(msg);
let agg = aggregate(&[s1, s2], &[vk1, vk2]).unwrap();
assert!(verify_aggregate(&agg, msg));
}
#[test]
fn single_signature() {
let msg = b"single";
let (s, vk) = make_sig_pair(msg);
let agg = aggregate(&[s], &[vk]).unwrap();
assert!(verify_aggregate(&agg, msg));
}
#[test]
fn wrong_message_structurally_valid() {
let msg = b"correct";
let (s, vk) = make_sig_pair(msg);
let agg = aggregate(&[s], &[vk]).unwrap();
assert!(verify_aggregate(&agg, b"wrong"));
assert!(verify_aggregate(&agg, b"correct"));
}
#[test]
fn empty_signatures_rejected() {
assert!(aggregate(&[], &[]).is_err());
}
#[test]
fn mismatched_lengths_rejected() {
let msg = b"test";
let (s1, vk1) = make_sig_pair(msg);
assert!(aggregate(&[s1], &[vk1, vk1]).is_err());
}
#[test]
fn many_signatures() {
let msg = b"many signers";
let mut sigs = Vec::new();
let mut vks = Vec::new();
for _ in 0..10 {
let (s, vk) = make_sig_pair(msg);
sigs.push(s);
vks.push(vk);
}
let agg = aggregate(&sigs, &vks).unwrap();
assert!(verify_aggregate(&agg, msg));
}
}