1use std::{collections::HashMap, fmt::Display, hash::Hash, str::FromStr};
2
3use anyhow::anyhow;
4use blake2::digest::{Digest, FixedOutput};
5use serde::{Deserialize, Serialize};
6
7use super::AggregateVerificationKey;
8use crate::{
9 AggregateSignatureError, Parameters, StmError, StmResult,
10 membership_commitment::MerkleBatchPath, proof_system::ConcatenationProof,
11};
12
13#[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
15pub enum AggregateSignatureType {
16 #[default]
18 Concatenation,
19 #[cfg(feature = "future_proof_system")]
21 Future,
22}
23
24impl AggregateSignatureType {
25 pub fn get_byte_encoding_prefix(&self) -> u8 {
29 match self {
30 AggregateSignatureType::Concatenation => 0,
31 #[cfg(feature = "future_proof_system")]
32 AggregateSignatureType::Future => 255,
33 }
34 }
35
36 pub fn from_byte_encoding_prefix(byte: u8) -> Option<Self> {
40 match byte {
41 0 => Some(AggregateSignatureType::Concatenation),
42 #[cfg(feature = "future_proof_system")]
43 255 => Some(AggregateSignatureType::Future),
44 _ => None,
45 }
46 }
47}
48
49impl<D: Clone + Digest + FixedOutput + Send + Sync> From<&AggregateSignature<D>>
50 for AggregateSignatureType
51{
52 fn from(aggr_sig: &AggregateSignature<D>) -> Self {
53 match aggr_sig {
54 AggregateSignature::Concatenation(_) => AggregateSignatureType::Concatenation,
55 #[cfg(feature = "future_proof_system")]
56 AggregateSignature::Future => AggregateSignatureType::Future,
57 }
58 }
59}
60
61impl FromStr for AggregateSignatureType {
62 type Err = StmError;
63
64 fn from_str(s: &str) -> Result<Self, Self::Err> {
65 match s {
66 "Concatenation" => Ok(AggregateSignatureType::Concatenation),
67 #[cfg(feature = "future_proof_system")]
68 "Future" => Ok(AggregateSignatureType::Future),
69 _ => Err(anyhow!("Unknown aggregate signature type: {}", s)),
70 }
71 }
72}
73
74impl Display for AggregateSignatureType {
75 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
76 match self {
77 AggregateSignatureType::Concatenation => write!(f, "Concatenation"),
78 #[cfg(feature = "future_proof_system")]
79 AggregateSignatureType::Future => write!(f, "Future"),
80 }
81 }
82}
83
84#[derive(Debug, Clone, Serialize, Deserialize)]
86#[serde(bound(
87 serialize = "MerkleBatchPath<D>: Serialize",
88 deserialize = "MerkleBatchPath<D>: Deserialize<'de>"
89))]
90pub enum AggregateSignature<D: Clone + Digest + FixedOutput + Send + Sync> {
91 #[cfg(feature = "future_proof_system")]
93 Future,
94
95 #[serde(untagged)]
100 Concatenation(ConcatenationProof<D>),
101}
102
103impl<D: Clone + Digest + FixedOutput + Send + Sync> AggregateSignature<D> {
104 pub fn verify(
106 &self,
107 msg: &[u8],
108 avk: &AggregateVerificationKey<D>,
109 parameters: &Parameters,
110 ) -> StmResult<()> {
111 match self {
112 AggregateSignature::Concatenation(concatenation_proof) => {
113 concatenation_proof.verify(msg, avk, parameters)
114 }
115 #[cfg(feature = "future_proof_system")]
116 AggregateSignature::Future => Err(anyhow!(
117 AggregateSignatureError::UnsupportedProofSystem(self.into())
118 )),
119 }
120 }
121
122 pub fn batch_verify(
124 stm_signatures: &[Self],
125 msgs: &[Vec<u8>],
126 avks: &[AggregateVerificationKey<D>],
127 parameters: &[Parameters],
128 ) -> StmResult<()> {
129 let stm_signatures: HashMap<AggregateSignatureType, Vec<Self>> =
130 stm_signatures.iter().fold(HashMap::new(), |mut acc, sig| {
131 acc.entry(sig.into()).or_default().push(sig.clone());
132 acc
133 });
134 stm_signatures.into_iter().try_for_each(
135 |(aggregate_signature_type, aggregate_signatures)| match aggregate_signature_type {
136 AggregateSignatureType::Concatenation => {
137 let aggregate_signatures_length = aggregate_signatures.len();
138 let concatenation_proofs = aggregate_signatures
139 .into_iter()
140 .filter_map(|s| s.to_concatenation_proof().cloned())
141 .collect::<Vec<_>>();
142 if concatenation_proofs.len() != aggregate_signatures_length {
143 return Err(anyhow!(AggregateSignatureError::BatchInvalid));
144 }
145
146 ConcatenationProof::batch_verify(&concatenation_proofs, msgs, avks, parameters)
147 }
148 #[cfg(feature = "future_proof_system")]
149 AggregateSignatureType::Future => Err(anyhow!(
150 AggregateSignatureError::UnsupportedProofSystem(aggregate_signature_type)
151 )),
152 },
153 )
154 }
155
156 pub fn to_bytes(&self) -> Vec<u8> {
158 let mut aggregate_signature_bytes = Vec::new();
159 let aggregate_signature_type: AggregateSignatureType = self.into();
160 aggregate_signature_bytes
161 .extend_from_slice(&[aggregate_signature_type.get_byte_encoding_prefix()]);
162
163 let mut proof_bytes = match self {
164 AggregateSignature::Concatenation(concatenation_proof) => {
165 concatenation_proof.to_bytes()
166 }
167 #[cfg(feature = "future_proof_system")]
168 AggregateSignature::Future => vec![],
169 };
170 aggregate_signature_bytes.append(&mut proof_bytes);
171
172 aggregate_signature_bytes
173 }
174
175 pub fn from_bytes(bytes: &[u8]) -> StmResult<Self> {
177 let proof_type_byte = bytes.first().ok_or(AggregateSignatureError::SerializationError)?;
178 let proof_bytes = &bytes[1..];
179 let proof_type = AggregateSignatureType::from_byte_encoding_prefix(*proof_type_byte)
180 .ok_or(AggregateSignatureError::SerializationError)?;
181 match proof_type {
182 AggregateSignatureType::Concatenation => Ok(AggregateSignature::Concatenation(
183 ConcatenationProof::from_bytes(proof_bytes)?,
184 )),
185 #[cfg(feature = "future_proof_system")]
186 AggregateSignatureType::Future => Ok(AggregateSignature::Future),
187 }
188 }
189
190 pub fn to_concatenation_proof(&self) -> Option<&ConcatenationProof<D>> {
192 match self {
193 AggregateSignature::Concatenation(proof) => Some(proof),
194 #[cfg(feature = "future_proof_system")]
195 AggregateSignature::Future => None,
196 }
197 }
198}
199
200#[cfg(test)]
201mod tests {
202 use super::*;
203
204 mod aggregate_signature_type_golden {
205 use super::*;
206
207 #[test]
208 fn golden_bytes_encoding_prefix() {
209 assert_eq!(
210 0u8,
211 AggregateSignatureType::Concatenation.get_byte_encoding_prefix()
212 );
213 assert_eq!(
214 AggregateSignatureType::from_byte_encoding_prefix(0u8),
215 Some(AggregateSignatureType::Concatenation)
216 );
217 }
218 }
219
220 mod aggregate_signature_golden_concatenation {
221 use blake2::{Blake2b, digest::consts::U32};
222 use rand_chacha::ChaCha20Rng;
223 use rand_core::SeedableRng;
224
225 use super::{AggregateSignature, AggregateSignatureType};
226 use crate::{
227 Clerk, ClosedKeyRegistration, KeyRegistration, Parameters, Signer,
228 signature_scheme::{BlsSigningKey, BlsVerificationKeyProofOfPossession},
229 };
230
231 type D = Blake2b<U32>;
232
233 const GOLDEN_JSON: &str = r#"
234 {
235 "signatures": [
236 [
237 {
238 "sigma": [
239 149, 157, 201, 187, 140, 54, 0, 128, 209, 88, 16, 203, 61, 78, 77, 98,
240 161, 133, 58, 152, 29, 74, 217, 113, 64, 100, 10, 161, 186, 167, 133,
241 114, 211, 153, 218, 56, 223, 84, 105, 242, 41, 54, 224, 170, 208, 185,
242 126, 83
243 ],
244 "indexes": [1, 4, 5, 8],
245 "signer_index": 0
246 },
247 [
248 [
249 143, 161, 255, 48, 78, 57, 204, 220, 25, 221, 164, 252, 248, 14, 56,
250 126, 186, 135, 228, 188, 145, 181, 52, 200, 97, 99, 213, 46, 0, 199,
251 193, 89, 187, 88, 29, 135, 173, 244, 86, 36, 83, 54, 67, 164, 6, 137,
252 94, 72, 6, 105, 128, 128, 93, 48, 176, 11, 4, 246, 138, 48, 180, 133,
253 90, 142, 192, 24, 193, 111, 142, 31, 76, 111, 110, 234, 153, 90, 208,
254 192, 31, 124, 95, 102, 49, 158, 99, 52, 220, 165, 94, 251, 68, 69,
255 121, 16, 224, 194
256 ],
257 1
258 ]
259 ],
260 [
261 {
262 "sigma": [
263 149, 169, 22, 201, 216, 97, 163, 188, 115, 210, 217, 236, 233, 161,
264 201, 13, 42, 132, 12, 63, 5, 31, 120, 22, 78, 177, 125, 134, 208, 205,
265 73, 58, 247, 141, 59, 62, 187, 81, 213, 30, 153, 218, 41, 42, 110,
266 156, 161, 205
267 ],
268 "indexes": [0, 3, 6],
269 "signer_index": 1
270 },
271 [
272 [
273 145, 56, 175, 32, 122, 187, 214, 226, 251, 148, 88, 9, 1, 103, 159,
274 146, 80, 166, 107, 243, 251, 236, 41, 28, 111, 128, 207, 164, 132,
275 147, 228, 83, 246, 228, 170, 68, 89, 78, 60, 28, 123, 130, 88, 234,
276 38, 97, 42, 65, 1, 100, 53, 18, 78, 131, 8, 61, 122, 131, 238, 84,
277 233, 223, 154, 118, 118, 73, 28, 27, 101, 78, 80, 233, 123, 206, 220,
278 174, 134, 205, 71, 110, 112, 180, 97, 98, 0, 113, 69, 145, 231, 168,
279 43, 173, 172, 56, 104, 208
280 ],
281 1
282 ]
283 ]
284 ],
285 "batch_proof": { "values": [], "indices": [0, 1], "hasher": null }
286 }
287 "#;
288
289 fn golden_value() -> AggregateSignature<D> {
290 let mut rng = ChaCha20Rng::from_seed([0u8; 32]);
291 let msg = [0u8; 16];
292 let params = Parameters {
293 m: 10,
294 k: 5,
295 phi_f: 0.8,
296 };
297 let sk_1 = BlsSigningKey::generate(&mut rng);
298 let sk_2 = BlsSigningKey::generate(&mut rng);
299 let pk_1 = BlsVerificationKeyProofOfPossession::from(&sk_1);
300 let pk_2 = BlsVerificationKeyProofOfPossession::from(&sk_2);
301 let mut key_reg = KeyRegistration::init();
302 key_reg.register(1, pk_1).unwrap();
303 key_reg.register(1, pk_2).unwrap();
304 let closed_key_reg: ClosedKeyRegistration<D> = key_reg.close();
305 let clerk = Clerk::new_clerk_from_closed_key_registration(¶ms, &closed_key_reg);
306 let signer_1 = Signer::set_signer(0, 1, params, sk_1, pk_1.vk, closed_key_reg.clone());
307 let signer_2 = Signer::set_signer(1, 1, params, sk_2, pk_2.vk, closed_key_reg);
308 let signature_1 = signer_1.sign(&msg).unwrap();
309 let signature_2 = signer_2.sign(&msg).unwrap();
310
311 clerk
312 .aggregate_signatures_with_type(
313 &[signature_1, signature_2],
314 &msg,
315 AggregateSignatureType::Concatenation,
316 )
317 .unwrap()
318 }
319
320 #[test]
321 fn golden_conversions() {
322 let value: AggregateSignature<D> = serde_json::from_str(GOLDEN_JSON)
323 .expect("This JSON deserialization should not fail");
324
325 let serialized =
326 serde_json::to_string(&value).expect("This JSON serialization should not fail");
327 let golden_serialized = serde_json::to_string(&golden_value())
328 .expect("This JSON serialization should not fail");
329 assert_eq!(golden_serialized, serialized);
330 }
331 }
332}