amadeus_utils/
bls12_381.rs

1use bls12_381::*;
2use group::Curve;
3
4// we use blst for signing/verification (hash_to_curve with DST) and serialization
5use blst::BLST_ERROR;
6use blst::min_pk::{PublicKey as BlsPublicKey, SecretKey as BlsSecretKey, Signature as BlsSignature};
7
8use crate::types::{PublicKey, Signature};
9
10#[derive(Debug, thiserror::Error)]
11pub enum Error {
12    #[error("invalid secret key")]
13    InvalidSecretKey,
14    #[error("invalid point")]
15    InvalidPoint,
16    #[error("invalid signature")]
17    InvalidSignature,
18    #[error("verification failed")]
19    VerificationFailed,
20    #[error("zero-sized input")]
21    ZeroSizedInput,
22}
23
24/// For 64-byte keys, uses Scalar::from_bytes_wide exactly like Elixir BlsEx
25fn parse_secret_key(sk_bytes: &[u8]) -> Result<BlsSecretKey, Error> {
26    // Follow Elixir bls_ex approach exactly:
27    // 1. Use Scalar::from_bytes_wide for 64-byte keys
28    // 2. Convert scalar to bytes and reverse them
29    // 3. Create BLST SecretKey from reversed bytes
30    if let Ok(bytes_64) = <&[u8; 64]>::try_from(sk_bytes) {
31        let sk_scalar = Scalar::from_bytes_wide(bytes_64);
32        let mut sk_be = sk_scalar.to_bytes();
33        sk_be.reverse(); // Critical: reverse bytes like Elixir does
34
35        return BlsSecretKey::from_bytes(&sk_be).map_err(|_| Error::InvalidSecretKey);
36    }
37    Err(Error::InvalidSecretKey)
38}
39
40fn g1_projective_is_valid(projective: &G1Projective) -> bool {
41    let is_identity: bool = projective.is_identity().into();
42    let is_on_curve = projective.is_on_curve().into();
43    let is_torsion_free = projective.to_affine().is_torsion_free().into();
44    !is_identity && is_on_curve && is_torsion_free
45}
46
47fn g2_affine_is_valid(affine: &G2Affine) -> bool {
48    let is_identity: bool = affine.is_identity().into();
49    let is_on_curve = affine.is_on_curve().into();
50    let is_torsion_free = affine.is_torsion_free().into();
51    !is_identity && is_on_curve && is_torsion_free
52}
53
54fn parse_public_key(bytes: &[u8]) -> Result<G1Projective, Error> {
55    if bytes.len() != 48 {
56        return Err(Error::InvalidPoint);
57    }
58    let mut res = [0u8; 48];
59    res.copy_from_slice(bytes);
60
61    match Option::<G1Affine>::from(G1Affine::from_compressed(&res)) {
62        Some(affine) => {
63            let projective = G1Projective::from(affine);
64            if g1_projective_is_valid(&projective) { Ok(projective) } else { Err(Error::InvalidPoint) }
65        }
66        None => Err(Error::InvalidPoint),
67    }
68}
69
70fn parse_signature(bytes: &[u8]) -> Result<G2Projective, Error> {
71    if bytes.len() != 96 {
72        return Err(Error::InvalidPoint);
73    }
74    let mut res = [0u8; 96];
75    res.copy_from_slice(bytes);
76
77    match Option::from(G2Affine::from_compressed(&res)) {
78        Some(affine) => {
79            if g2_affine_is_valid(&affine) {
80                Ok(G2Projective::from(affine))
81            } else {
82                Err(Error::InvalidPoint)
83            }
84        }
85        None => Err(Error::InvalidPoint),
86    }
87}
88
89fn sign_from_secret_key(sk: BlsSecretKey, msg: &[u8], dst: &[u8]) -> Result<BlsSignature, Error> {
90    Ok(sk.sign(msg, dst, &[]))
91}
92
93// public API
94
95/// Uses Scalar::from_bytes_wide for 64-byte keys to match Elixir exactly
96pub fn get_public_key(sk_bytes: &[u8]) -> Result<PublicKey, Error> {
97    // For 64-byte keys: use bls12_381 directly for full Elixir compatibility
98    let bytes_64: [u8; 64] = sk_bytes.try_into().map_err(|_| Error::InvalidSecretKey)?;
99    let sk_scalar = Scalar::from_bytes_wide(&bytes_64);
100
101    // Compute public key: G1 generator * scalar (exactly like Elixir)
102    let pk_g1 = G1Projective::generator() * sk_scalar;
103    Ok(PublicKey::from(pk_g1.to_affine().to_compressed()))
104}
105
106pub fn generate_sk() -> [u8; 64] {
107    // Generate a valid 64-byte secret key that works with our scalar approach
108    loop {
109        let sk_64: [u8; 64] = rand::random();
110
111        // Try to create a scalar from this - if it works, use it
112        let scalar = Scalar::from_bytes_wide(&sk_64);
113        let scalar_bytes = scalar.to_bytes();
114
115        // Check if this creates a valid BLST key
116        if BlsSecretKey::from_bytes(&scalar_bytes).is_ok() {
117            return sk_64;
118        }
119
120        // If it doesn't work, try again (very rare)
121    }
122}
123
124/// Sign a message with secret key, returns signature bytes (96 bytes in min_pk)
125/// For 64-byte keys, uses the same scalar derivation as public key generation
126pub fn sign(sk_bytes: &[u8], message: &[u8], dst: &[u8]) -> Result<Signature, Error> {
127    // Use exactly the same approach as parse_secret_key to ensure consistency
128    let sk = parse_secret_key(sk_bytes)?;
129    let signature = sign_from_secret_key(sk, message, dst)?;
130    Ok(Signature::from(signature.to_bytes()))
131}
132
133/// Verify a signature using a compressed G1 public key (48 bytes) and signature (96 bytes)
134/// Errors out if the signature is invalid
135pub fn verify(pk_bytes: impl AsRef<[u8]>, sig_bytes: impl AsRef<[u8]>, msg: &[u8], dst: &[u8]) -> Result<(), Error> {
136    let pk = BlsPublicKey::deserialize(pk_bytes.as_ref()).map_err(|_| Error::InvalidPoint)?;
137    let sig = BlsSignature::deserialize(sig_bytes.as_ref()).map_err(|_| Error::InvalidSignature)?;
138
139    let err = sig.verify(
140        true, // hash_to_curve
141        msg,
142        dst, // domain separation tag
143        &[], // no augmentation
144        &pk,
145        true, // validate pk ∈ G1
146    );
147
148    if err == BLST_ERROR::BLST_SUCCESS { Ok(()) } else { Err(Error::VerificationFailed) }
149}
150
151/// Aggregate multiple compressed G1 public keys into one compressed G1 public key (48 bytes)
152pub fn aggregate_public_keys<T>(public_keys: T) -> Result<PublicKey, Error>
153where
154    T: IntoIterator,
155    T::Item: AsRef<[u8]>,
156{
157    let mut iter = public_keys.into_iter();
158    let first = match iter.next() {
159        Some(v) => v,
160        None => return Err(Error::ZeroSizedInput),
161    };
162    let mut acc = parse_public_key(first.as_ref())?;
163    for pk in iter {
164        let p = parse_public_key(pk.as_ref())?;
165        acc += p;
166    }
167    Ok(PublicKey::from(acc.to_affine().to_compressed()))
168}
169
170/// Aggregate multiple signatures (compressed G2, 96 bytes) into one compressed G2 (96 bytes)
171pub fn aggregate_signatures<T>(signatures: T) -> Result<Signature, Error>
172where
173    T: IntoIterator,
174    T::Item: AsRef<[u8]>,
175{
176    let mut iter = signatures.into_iter();
177    let first = match iter.next() {
178        Some(v) => v,
179        None => return Err(Error::ZeroSizedInput),
180    };
181    let mut acc = parse_signature(first.as_ref())?;
182    for s in iter {
183        let p = parse_signature(s.as_ref())?;
184        acc += p;
185    }
186    Ok(Signature::from(acc.to_affine().to_compressed()))
187}
188
189/// Compute Diffie-Hellman shared secret: pk_g1 * sk -> compressed G1 (48 bytes).
190/// Uses the same approach as public key generation for consistency
191pub fn get_shared_secret(public_key: impl AsRef<[u8]>, sk_bytes: &[u8]) -> Result<PublicKey, Error> {
192    let pk_g1 = parse_public_key(public_key.as_ref())?;
193
194    // Use exactly the same scalar derivation as get_public_key() for consistency
195    let sk_scalar = if sk_bytes.len() == 64 {
196        // For 64-byte keys: use bls12_381 directly (exactly like get_public_key)
197        let bytes_64: [u8; 64] = sk_bytes.try_into().map_err(|_| Error::InvalidSecretKey)?;
198        Scalar::from_bytes_wide(&bytes_64)
199    } else {
200        return Err(Error::InvalidSecretKey);
201    };
202
203    // Compute shared secret: pk_g1 * sk_scalar
204    Ok(PublicKey::from((pk_g1 * sk_scalar).to_affine().to_compressed()))
205}
206
207/// Validate a compressed G1 public key.
208pub fn validate_public_key(public_key: impl AsRef<[u8]>) -> Result<(), Error> {
209    parse_public_key(public_key.as_ref()).map(|_| ())
210}
211
212#[cfg(test)]
213mod tests {
214    use super::*;
215
216    #[allow(dead_code)]
217    fn seed32(b: u8) -> [u8; 32] {
218        [b; 32]
219    }
220
221    #[test]
222    fn pk_sign_verify_and_validate() {
223        let seed = generate_sk();
224        let pk = get_public_key(&seed).expect("pk");
225        validate_public_key(&pk).expect("valid pk");
226
227        let msg = b"context7:message";
228        let dst = b"CONTEXT7-BLS-DST";
229        let sig = sign(&seed, msg, dst).expect("sign");
230        verify(&pk, &sig, msg, dst).expect("verify");
231    }
232
233    #[test]
234    fn shared_secret_symmetry() {
235        let a = generate_sk();
236        let b = generate_sk();
237        let pk_a = get_public_key(&a).unwrap();
238        let pk_b = get_public_key(&b).unwrap();
239        let ab = get_shared_secret(&pk_b, &a).unwrap();
240        let ba = get_shared_secret(&pk_a, &b).unwrap();
241        assert_eq!(ab, ba);
242    }
243
244    #[test]
245    fn shared_secret_symmetry_64byte() {
246        // Test with 64-byte keys like EncryptedMessage tests
247        let sk_a = generate_sk();
248        let sk_b = generate_sk();
249
250        let pk_a = get_public_key(&sk_a).unwrap();
251        let pk_b = get_public_key(&sk_b).unwrap();
252
253        // Check which path each key takes
254        let sk_a_scalar = Scalar::from_bytes_wide(&sk_a);
255        let sk_b_scalar = Scalar::from_bytes_wide(&sk_b);
256        let sk_a_blst_valid = BlsSecretKey::from_bytes(&sk_a_scalar.to_bytes()).is_ok();
257        let sk_b_blst_valid = BlsSecretKey::from_bytes(&sk_b_scalar.to_bytes()).is_ok();
258
259        println!("sk_a BLST valid: {}", sk_a_blst_valid);
260        println!("sk_b BLST valid: {}", sk_b_blst_valid);
261
262        let ab = get_shared_secret(&pk_b, &sk_a).unwrap();
263        let ba = get_shared_secret(&pk_a, &sk_b).unwrap();
264
265        println!("AB: {:?}", &ab[..8]);
266        println!("BA: {:?}", &ba[..8]);
267
268        assert_eq!(ab, ba, "64-byte shared secrets should be symmetric");
269    }
270
271    #[test]
272    fn aggregation_behaviour() {
273        let s1 = generate_sk();
274        let s2 = generate_sk();
275        let pk1 = get_public_key(&s1).unwrap();
276        let pk2 = get_public_key(&s2).unwrap();
277
278        // test single public key aggregation
279        let agg1 = aggregate_public_keys([pk1]).unwrap();
280        assert_eq!(agg1.len(), 48);
281        assert_eq!(agg1, pk1); // single key aggregation should equal original key
282
283        // test multiple public key aggregation
284        let agg_pk = aggregate_public_keys([pk1, pk2]).unwrap();
285        assert_eq!(agg_pk.len(), 48);
286        assert_ne!(agg_pk, pk1); // aggregated key should differ from individual keys
287        assert_ne!(agg_pk, pk2);
288
289        // zero-sized input should fail
290        assert!(matches!(aggregate_public_keys::<[&[u8]; 0]>([]), Err(Error::ZeroSizedInput)));
291
292        // test signature aggregation
293        let dst = b"DST";
294        let msg = b"m";
295        let sig1 = sign(&s1, msg, dst).unwrap();
296        let sig2 = sign(&s2, msg, dst).unwrap();
297
298        // test single signature aggregation
299        let agg_sig1 = aggregate_signatures([sig1.as_slice()]).unwrap();
300        assert_eq!(agg_sig1.len(), 96);
301        assert_eq!(agg_sig1, sig1); // single signature aggregation should equal original
302
303        // test multiple signature aggregation
304        let agg_sig = aggregate_signatures([sig1.as_slice(), sig2.as_slice()]).unwrap();
305        assert_eq!(agg_sig.len(), 96);
306        assert_ne!(agg_sig, sig1); // aggregated signature should differ from individual signatures
307        assert_ne!(agg_sig, sig2);
308
309        // zero-sized signature input should fail
310        assert!(matches!(aggregate_signatures::<[&[u8]; 0]>([]), Err(Error::ZeroSizedInput)));
311
312        // test that aggregated signature verifies against aggregated public key
313        verify(&agg_pk, &agg_sig, msg, dst).expect("aggregated signature should verify against aggregated public key");
314
315        // test that individual signatures don't verify against aggregated public key
316        assert!(verify(&agg_pk, &sig1, msg, dst).is_err());
317        assert!(verify(&agg_pk, &sig2, msg, dst).is_err());
318
319        // test that aggregated signature doesn't verify against individual public keys
320        assert!(verify(&pk1, &agg_sig, msg, dst).is_err());
321        assert!(verify(&pk2, &agg_sig, msg, dst).is_err());
322    }
323
324    #[test]
325    fn elixir_key_compatibility_test() {
326        const SK_B58: &str = "QPHHRpzuJ8nBKnrY9hcT8DuaWX8ev42QWHMPtpWtg11Rkbq37cpE5BGD8RTBe6NrfQqboKusvz119rUMDjoMXQ2";
327        const PK_B58: &str = "7HBdTuiVETYS9bWgZt2ZQ2edrmUYVW9gMPJuVRA2PEFXUvTt62ZxP1juPbHpUS8M1k";
328        const OTHER_PK_B58: &str = "7KUntjPCFEmTtG9NBLNjqaaXourYDjBASwLtXFcPr1DmDNPCLVKRznppysMMyAcVa7";
329        const EXPECTED_SIG_B58: &str = "nDmcy3orsbusmMA9ugTXYyCXuCpdeuar5TonQqZquGbfLGGpCkawaStX9vCxm4nnjF9CXtwVUxjbvyU5KRP6nd24niXu7oLRhvGkkiSqgnxenAgjnUJwvahfDz94t7LyBmY";
330        const EXPECTED_SHARED_SECRET_B58: &str = "69m86NjrftmWr8in6dbDBYiYrsiJjeKAvkM8WzLWk4Feub5p3YC2oDa8FbSyhS3f9d";
331
332        // Skip test if values not filled in
333        if SK_B58.contains("PASTE_") {
334            println!("Skipping test - replace values from iex commands first");
335            return;
336        }
337
338        println!("\n=== ELIXIR KEY COMPATIBILITY TEST ===");
339
340        // Decode Base58 values
341        let sk_bytes = bs58::decode(SK_B58).into_vec().expect("decode sk");
342        let expected_pk = bs58::decode(PK_B58).into_vec().expect("decode pk");
343        let other_pk = bs58::decode(OTHER_PK_B58).into_vec().expect("decode other pk");
344        let expected_sig = bs58::decode(EXPECTED_SIG_B58).into_vec().expect("decode sig");
345        let expected_shared_secret = bs58::decode(EXPECTED_SHARED_SECRET_B58).into_vec().expect("decode shared secret");
346
347        println!("SK length: {}", sk_bytes.len());
348        println!("Expected PK length: {}", expected_pk.len());
349        println!("SK first 8 bytes: {:?}", &sk_bytes[..8]);
350        println!("SK last 8 bytes: {:?}", &sk_bytes[sk_bytes.len() - 8..]);
351
352        // Test 1: Key parsing
353        println!("\n--- Test 1: Secret Key Parsing ---");
354        let sk_64: [u8; 64] = sk_bytes.try_into().expect("sk should be 64 bytes");
355        match get_public_key(&sk_64) {
356            Ok(rust_pk) => {
357                println!("✓ Rust successfully parsed Elixir secret key");
358                println!("Rust PK: {:?}", rust_pk.to_vec());
359                println!("Elixir PK: {:?}", expected_pk);
360
361                if rust_pk.to_vec() == expected_pk {
362                    println!("✓ Public keys match perfectly!");
363                } else {
364                    panic!("✗ Public key mismatch - different BLS implementations");
365                }
366            }
367            Err(e) => {
368                panic!("✗ Rust failed to parse Elixir secret key: {:?}", e);
369            }
370        }
371
372        // Test 2: Signature compatibility
373        println!("\n--- Test 2: Signature Compatibility ---");
374        let test_data = b"Hello Amadeus Blockchain!";
375        let dst = b"AMADEUS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_TEST_";
376
377        match sign(&sk_64, test_data, dst) {
378            Ok(rust_sig) => {
379                println!("✓ Rust signature generation successful");
380                println!("Rust signature: {:?}", rust_sig.to_vec());
381                println!("Elixir signature: {:?}", expected_sig);
382
383                if rust_sig.to_vec() == expected_sig {
384                    println!("✓ Signatures match perfectly!");
385                } else {
386                    println!("~ Signatures differ (expected with different hash-to-curve)");
387                }
388
389                // Verify Rust signature with Rust
390                if verify(&expected_pk, &rust_sig, test_data, dst).is_ok() {
391                    println!("✓ Rust signature verifies with Elixir public key");
392                } else {
393                    panic!("✗ Rust signature doesn't verify with Elixir public key");
394                }
395
396                // Try to verify Elixir signature with Rust
397                if expected_sig.len() == 96 {
398                    let elixir_sig: [u8; 96] = expected_sig.try_into().unwrap();
399                    if verify(&expected_pk, &elixir_sig, test_data, dst).is_ok() {
400                        println!("✓ Elixir signature verifies in Rust");
401                    } else {
402                        panic!("✗ Elixir signature doesn't verify in Rust");
403                    }
404                }
405            }
406            Err(e) => {
407                panic!("✗ Rust signature generation failed: {:?}", e);
408            }
409        }
410
411        // Test 3: Shared secret compatibility
412        println!("\n--- Test 3: Shared Secret Compatibility ---");
413        match get_shared_secret(&other_pk, &sk_64) {
414            Ok(rust_shared_secret) => {
415                println!("✓ Rust shared secret generation successful");
416                println!("Rust shared secret: {:?}", rust_shared_secret.to_vec());
417                println!("Elixir shared secret: {:?}", expected_shared_secret);
418
419                if rust_shared_secret.to_vec() == expected_shared_secret {
420                    println!("✓ Shared secrets match perfectly!");
421                } else {
422                    panic!("✗ Shared secret mismatch");
423                }
424            }
425            Err(e) => {
426                println!("✗ Rust shared secret generation failed: {:?}", e);
427            }
428        }
429
430        println!("\n=== COMPATIBILITY TEST COMPLETE ===");
431    }
432
433    #[test]
434    fn elixir_signature_compatibility_test() {
435        const SK_B58: &str = "559mzNeU7itDyHs2yUzZurTvoaLHi3nJeGjCQSi44PwcJzdqBVymRdh9G25Hg6u9pi59avrqcPpeq6DBQQVEqPxV";
436        const PK_B58: &str = "7gX58gLTX7WUGUq3PQTNYcbwfH18b3SeRTgfJ6mM5badEvbhjRXxNEBYSyfH6RjnoP";
437        const TEST_DATA_1_B58: &str = "89YouX2vBz5FKYQZueX6744sBPHjZ8AgFAmN1ySS61KebyrhdkcUk5jY2vqsqgZ8XatbkL";
438        const ELIXIR_SIG_1_B58: &str = "riqRrRupu5KuaimWbSjS8NKfV8eMYTVKt5xhTkKo9FVDzP7kKhQLmT2VJu15r9GDbkZTk1N78uMGYa6yG7NzboEHet7Xv9wtf7cn86is5GH2PzvH95Kt8RbtqC9iRr13fAZ";
439        const TEST_DATA_2_B58: &str =
440            "2moGA7MbJet3qHLaSA9kN9eFy5fTr7TdHqJGoaq5hEbt5kGEVnFxCKkC8kNE2nfrnhWtVTtaVoUWeP1GEmKs";
441        const ELIXIR_SIG_2_B58: &str = "oGRbCRrCwMVKXqHebyDsF2JTMcWghbkrHszG6oU4t4FGGp351p5L5ud7XFhrhDixVS38NWgUdmr4qprsoCe1SPq8q8FKkfGLFPjPzb6BH8Lhk3zKjWoDjmCJqUp66rwEp4c";
442
443        // Skip test if values not filled in
444        if SK_B58.contains("PLACEHOLDER_") {
445            println!("Skipping test - replace values from iex commands first");
446            return;
447        }
448
449        println!("\n=== ELIXIR SIGNATURE COMPATIBILITY TEST ===");
450
451        // Decode Base58 values
452        let sk_bytes = bs58::decode(SK_B58).into_vec().expect("decode sk");
453        let expected_pk = bs58::decode(PK_B58).into_vec().expect("decode pk");
454        let test_data_1 = bs58::decode(TEST_DATA_1_B58).into_vec().expect("decode test data 1");
455        let elixir_sig_1 = bs58::decode(ELIXIR_SIG_1_B58).into_vec().expect("decode elixir sig 1");
456        let test_data_2 = bs58::decode(TEST_DATA_2_B58).into_vec().expect("decode test data 2");
457        let elixir_sig_2 = bs58::decode(ELIXIR_SIG_2_B58).into_vec().expect("decode elixir sig 2");
458
459        let dst = b"AMADEUS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_ANRCHALLENGE_";
460
461        println!("SK length: {}", sk_bytes.len());
462        println!("Expected PK length: {}", expected_pk.len());
463
464        // Test 1: Public Key Compatibility
465        println!("\n--- Test 1: Public Key Compatibility ---");
466        let rust_pk = get_public_key(&sk_bytes).expect("get public key");
467        println!("Rust PK: {:?}", rust_pk.to_vec());
468        println!("Elixir PK: {:?}", expected_pk);
469
470        if rust_pk.to_vec() == expected_pk {
471            println!("✓ Public keys match perfectly!");
472        } else {
473            panic!("✗ Public key mismatch");
474        }
475
476        // Test 2: Signature Verification - Case 1
477        println!("\n--- Test 2: Signature Verification - Case 1 ---");
478        println!("Test data 1: {:?}", test_data_1);
479        println!("Elixir signature 1: {:?}", elixir_sig_1);
480
481        // Generate Rust signature for comparison
482        let rust_sig_1 = sign(&sk_bytes, &test_data_1, dst).expect("rust sign 1");
483        println!("Rust signature 1: {:?}", rust_sig_1.to_vec());
484
485        // Verify Rust signature
486        match verify(&expected_pk, &rust_sig_1, &test_data_1, dst) {
487            Ok(_) => println!("✓ Rust signature 1 verifies"),
488            Err(e) => panic!("✗ Rust signature 1 verification failed: {:?}", e),
489        }
490
491        // Verify Elixir signature with Rust
492        if elixir_sig_1.len() == 96 {
493            let elixir_sig_1_array: [u8; 96] = elixir_sig_1.try_into().unwrap();
494            match verify(&expected_pk, &elixir_sig_1_array, &test_data_1, dst) {
495                Ok(_) => println!("✓ Elixir signature 1 verifies in Rust"),
496                Err(e) => panic!("✗ Elixir signature 1 verification failed: {:?}", e),
497            }
498        }
499
500        // Test 3: Signature Verification - Case 2
501        println!("\n--- Test 3: Signature Verification - Case 2 ---");
502        println!("Test data 2: {:?}", test_data_2);
503        println!("Elixir signature 2: {:?}", elixir_sig_2);
504
505        // Generate Rust signature for comparison
506        let rust_sig_2 = sign(&sk_bytes, &test_data_2, dst).expect("rust sign 2");
507        println!("Rust signature 2: {:?}", rust_sig_2.to_vec());
508
509        // Verify Rust signature
510        match verify(&expected_pk, &rust_sig_2, &test_data_2, dst) {
511            Ok(_) => println!("✓ Rust signature 2 verifies"),
512            Err(e) => panic!("✗ Rust signature 2 verification failed: {:?}", e),
513        }
514
515        // Verify Elixir signature with Rust
516        if elixir_sig_2.len() == 96 {
517            let elixir_sig_2_array: [u8; 96] = elixir_sig_2.try_into().unwrap();
518            match verify(&expected_pk, &elixir_sig_2_array, &test_data_2, dst) {
519                Ok(_) => println!("✓ Elixir signature 2 verifies in Rust"),
520                Err(e) => panic!("✗ Elixir signature 2 verification failed: {:?}", e),
521            }
522        }
523
524        println!("\n=== SIGNATURE COMPATIBILITY TEST COMPLETE ===");
525    }
526
527    #[test]
528    fn elixir_public_key_signature_verification() {
529        // Elixir-generated public key
530        let elixir_pk = vec![
531            169, 61, 121, 32, 15, 191, 174, 241, 143, 231, 124, 53, 186, 69, 28, 212, 233, 130, 22, 18, 34, 244, 13,
532            106, 212, 255, 255, 47, 184, 178, 49, 111, 90, 90, 184, 84, 230, 115, 5, 143, 205, 208, 136, 138, 2, 252,
533            27, 222,
534        ];
535
536        // Test cases with Elixir signatures and corresponding data
537        let test_cases = vec![
538            (
539                // data
540                vec![
541                    169, 61, 121, 32, 15, 191, 174, 241, 143, 231, 124, 53, 186, 69, 28, 212, 233, 130, 22, 18, 34,
542                    244, 13, 106, 212, 255, 255, 47, 184, 178, 49, 111, 90, 90, 184, 84, 230, 115, 5, 143, 205, 208,
543                    136, 138, 2, 252, 27, 222, 49,
544                ],
545                // elixir signature
546                vec![
547                    166, 193, 20, 132, 125, 87, 40, 182, 101, 225, 125, 220, 97, 93, 13, 2, 89, 220, 166, 6, 106, 203,
548                    96, 63, 122, 16, 226, 117, 143, 219, 5, 105, 180, 229, 65, 58, 238, 93, 230, 253, 208, 110, 35,
549                    219, 222, 176, 82, 112, 15, 149, 72, 148, 54, 88, 2, 94, 219, 26, 235, 98, 77, 202, 1, 83, 6, 38,
550                    39, 150, 236, 176, 141, 222, 93, 133, 66, 154, 226, 55, 214, 100, 183, 179, 167, 140, 140, 77, 117,
551                    11, 167, 219, 140, 144, 144, 160, 143, 128,
552                ],
553            ),
554            (
555                // data with "255" suffix
556                vec![
557                    169, 61, 121, 32, 15, 191, 174, 241, 143, 231, 124, 53, 186, 69, 28, 212, 233, 130, 22, 18, 34,
558                    244, 13, 106, 212, 255, 255, 47, 184, 178, 49, 111, 90, 90, 184, 84, 230, 115, 5, 143, 205, 208,
559                    136, 138, 2, 252, 27, 222, 50, 53, 53,
560                ],
561                // elixir signature
562                vec![
563                    141, 6, 181, 106, 49, 117, 193, 12, 249, 102, 71, 237, 125, 55, 25, 3, 14, 199, 113, 157, 49, 168,
564                    205, 89, 106, 76, 3, 37, 170, 124, 149, 45, 234, 206, 44, 177, 90, 0, 14, 111, 30, 9, 197, 189,
565                    201, 43, 86, 139, 22, 145, 182, 32, 77, 220, 35, 186, 5, 251, 37, 173, 187, 243, 110, 33, 57, 23,
566                    67, 58, 166, 74, 200, 145, 232, 5, 151, 244, 62, 216, 159, 43, 131, 43, 179, 105, 154, 33, 91, 88,
567                    143, 91, 40, 147, 129, 228, 37, 98,
568                ],
569            ),
570            (
571                // data with timestamp "1640995200"
572                vec![
573                    169, 61, 121, 32, 15, 191, 174, 241, 143, 231, 124, 53, 186, 69, 28, 212, 233, 130, 22, 18, 34,
574                    244, 13, 106, 212, 255, 255, 47, 184, 178, 49, 111, 90, 90, 184, 84, 230, 115, 5, 143, 205, 208,
575                    136, 138, 2, 252, 27, 222, 49, 54, 52, 48, 57, 57, 53, 50, 48, 48,
576                ],
577                // elixir signature
578                vec![
579                    137, 145, 8, 245, 3, 166, 187, 110, 172, 28, 115, 177, 226, 179, 239, 201, 245, 173, 213, 25, 211,
580                    84, 225, 194, 82, 30, 133, 105, 197, 97, 55, 185, 157, 83, 140, 89, 2, 3, 57, 7, 84, 242, 51, 161,
581                    247, 238, 16, 126, 18, 69, 208, 108, 184, 132, 63, 67, 219, 144, 108, 54, 50, 176, 128, 138, 121,
582                    191, 181, 168, 198, 229, 76, 246, 29, 36, 130, 95, 146, 213, 222, 230, 192, 179, 179, 198, 99, 209,
583                    120, 134, 194, 181, 239, 187, 42, 46, 136, 93,
584                ],
585            ),
586        ];
587
588        // Use the same DST
589        let dst = b"AMADEUS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_ANRCHALLENGE_";
590
591        println!("Testing Elixir signatures with Elixir public key");
592        println!("Elixir public key: {:?}", elixir_pk);
593
594        for (i, (data, elixir_sig)) in test_cases.iter().enumerate() {
595            println!("\nTesting case {}: data_len={}, sig_len={}", i, data.len(), elixir_sig.len());
596
597            // Try to verify the Elixir signature with the Elixir public key
598            match verify(&elixir_pk, elixir_sig, data, dst) {
599                Ok(_) => println!("Case {}: ✓ Elixir signature verifies with Elixir public key", i),
600                Err(e) => {
601                    println!("Case {}: ✗ Elixir signature failed verification: {:?}", i, e);
602                    println!("Case {}: This indicates potential issues with Elixir test data or DST mismatch", i);
603                }
604            }
605        }
606
607        println!("\nTest completed: Checked if Elixir signatures verify with Elixir public key");
608    }
609}