Skip to main content

atlas_c2pa_lib/
cose.rs

1//! # COSE Module
2//!
3//! This module provides functions for cryptographic operations on C2PA claims using the COSE
4//! (CBOR Object Signing and Encryption) standard. It enables signing and verification of claims
5//! to ensure their authenticity and integrity, with support for multiple hash algorithms.
6//!
7//! ## Supported Hash Algorithms
8//!
9//! The module supports the following hash algorithms as per C2PA specification:
10//! - SHA-256 (`sha256`)
11//! - SHA-384 (`sha384`)
12//! - SHA-512 (`sha512`)
13//!
14//! ## Functionality
15//!
16//! The module provides the following functions:
17//!
18//! - **sign_claim**: Signs a CBOR-encoded claim using SHA-384 (default)
19//! - **sign_claim_with_algorithm**: Signs a CBOR-encoded claim using a specified hash algorithm
20//! - **verify_signed_claim**: Verifies a signed claim using SHA-384 (default)
21//! - **verify_signed_claim_with_algorithm**: Verifies a signed claim using a specified hash algorithm
22//!
23//! ## Example: Signing a Claim with Default SHA-384
24//!
25//! ```rust,ignore
26//! use atlas_c2pa_lib::cbor::encode_claim_to_cbor;
27//! use atlas_c2pa_lib::cose::sign_claim;
28//! use openssl::pkey::PKey;
29//!
30//! // Create and encode a claim to CBOR
31//! let claim = create_claim(); // Your claim creation logic
32//! let cbor_data = encode_claim_to_cbor(&claim).unwrap();
33//!
34//! // Load a private key (key generation is the responsibility of the end user)
35//! let private_key_pem = std::fs::read("private_key.pem").unwrap();
36//! let private_key = PKey::private_key_from_pem(&private_key_pem).unwrap();
37//!
38//! // Sign the claim using default SHA-384
39//! let signed_data = sign_claim(&cbor_data, &private_key).unwrap();
40//! ```
41//!
42//! ## Example: Signing with Different Hash Algorithms
43//!
44//! ```rust,ignore
45//! use atlas_c2pa_lib::cbor::encode_claim_to_cbor;
46//! use atlas_c2pa_lib::cose::{sign_claim_with_algorithm, HashAlgorithm};
47//! use openssl::pkey::PKey;
48//!
49//! // Create and encode a claim to CBOR
50//! let claim = create_claim(); // Your claim creation logic
51//! let cbor_data = encode_claim_to_cbor(&claim).unwrap();
52//!
53//! // Load a private key (key generation is the responsibility of the end user)
54//! let private_key_pem = std::fs::read("private_key.pem").unwrap();
55//! let private_key = PKey::private_key_from_pem(&private_key_pem).unwrap();
56//!
57//! // Sign with SHA-384
58//! let signed_sha384 = sign_claim_with_algorithm(
59//!     &cbor_data,
60//!     &private_key,
61//!     HashAlgorithm::Sha384
62//! ).unwrap();
63//!
64//! // Sign with SHA-512
65//! let signed_sha512 = sign_claim_with_algorithm(
66//!     &cbor_data,
67//!     &private_key,
68//!     HashAlgorithm::Sha512
69//! ).unwrap();
70//! ```
71//!
72//! ## Example: Verifying a Signed Claim
73//!
74//! ```rust,ignore
75//! use atlas_c2pa_lib::cose::{verify_signed_claim, verify_signed_claim_with_algorithm, HashAlgorithm};
76//! use openssl::pkey::PKey;
77//!
78//! // Load a public key
79//! let public_key_pem = std::fs::read("public_key.pem").unwrap();
80//! let public_key = PKey::public_key_from_pem(&public_key_pem).unwrap();
81//!
82//! // Verify a claim signed with SHA-256 (default)
83//! let verification_result = verify_signed_claim(&signed_data, &public_key);
84//! assert!(verification_result.is_ok());
85//!
86//! // Verify a claim signed with SHA-384
87//! let verification_sha384 = verify_signed_claim_with_algorithm(
88//!     &signed_sha384,
89//!     &public_key,
90//!     HashAlgorithm::Sha384
91//! );
92//! assert!(verification_sha384.is_ok());
93//! ```
94//!
95//! ## Example: Complete Signing and Verification Workflow
96//!
97//! ```rust,ignore
98//! use atlas_c2pa_lib::claim::ClaimV2;
99//! use atlas_c2pa_lib::cbor::encode_claim_to_cbor;
100//! use atlas_c2pa_lib::cose::{sign_claim_with_algorithm, verify_signed_claim_with_algorithm, HashAlgorithm};
101//! use atlas_c2pa_lib::datetime_wrapper::OffsetDateTimeWrapper;
102//! use openssl::pkey::PKey;
103//! use time::OffsetDateTime;
104//!
105//! // Create a claim
106//! let claim = ClaimV2 {
107//!     instance_id: "xmp:iid:123456".to_string(),
108//!     created_assertions: vec![],
109//!     ingredients: vec![],
110//!     signature: None,
111//!     claim_generator_info: "atlas_c2pa_lib/0.1.4".to_string(),
112//!     created_at: OffsetDateTimeWrapper(OffsetDateTime::now_utc()),
113//! };
114//!
115//! // Encode to CBOR
116//! let cbor_data = encode_claim_to_cbor(&claim).unwrap();
117//!
118//! // Load pre-generated keys (key generation is the responsibility of the end user)
119//! let private_key_pem = std::fs::read("private_key.pem").unwrap();
120//! let private_key = PKey::private_key_from_pem(&private_key_pem).unwrap();
121//!
122//! let public_key_pem = std::fs::read("public_key.pem").unwrap();
123//! let public_key = PKey::public_key_from_pem(&public_key_pem).unwrap();
124//!
125//! // Try all supported algorithms
126//! let algorithms = vec![
127//!     HashAlgorithm::Sha256,
128//!     HashAlgorithm::Sha384,
129//!     HashAlgorithm::Sha512,
130//! ];
131//!
132//! for algo in algorithms {
133//!     // Sign the claim
134//!     let signed = sign_claim_with_algorithm(&cbor_data, &private_key, algo.clone()).unwrap();
135//!
136//!     // Verify the signature
137//!     let result = verify_signed_claim_with_algorithm(&signed, &public_key, algo);
138//!     assert!(result.is_ok(), "Verification failed for {:?}", algo);
139//! }
140//! ```
141//!
142//! ## Security Considerations
143//!
144//! - **Key Generation**: The generation and management of cryptographic key pairs is the
145//!   responsibility of the end user. This library does not provide key generation functionality.
146//!   Users should follow cryptographic best practices for key generation, storage, and rotation.
147//! - Private keys should be securely stored and never exposed
148//! - Public keys should be distributed through secure channels
149//! - The signature verification process ensures the claim hasn't been tampered with
150//! - COSE provides a standardized way to represent signed data in CBOR format
151//! - Choose the appropriate hash algorithm based on your security requirements:
152//!   - SHA-256: Good balance of security and performance
153//!   - SHA-384: Higher security with moderate performance impact
154//!   - SHA-512: Highest security but larger signatures
155
156use coset::{CborSerializable, CoseSign1, CoseSign1Builder, Header};
157use openssl::hash::MessageDigest;
158use openssl::pkey::PKey;
159use openssl::sign::{Signer, Verifier};
160use std::str::FromStr;
161
162/// Supported hash algorithms for COSE signing and verification
163#[derive(Debug, PartialEq, Clone)]
164pub enum HashAlgorithm {
165    /// SHA-256 hash algorithm (256-bit)
166    Sha256,
167    /// SHA-384 hash algorithm (384-bit)
168    Sha384,
169    /// SHA-512 hash algorithm (512-bit)
170    Sha512,
171}
172
173impl HashAlgorithm {
174    /// Convert the hash algorithm to OpenSSL MessageDigest
175    fn to_message_digest(&self) -> MessageDigest {
176        match self {
177            HashAlgorithm::Sha256 => MessageDigest::sha256(),
178            HashAlgorithm::Sha384 => MessageDigest::sha384(),
179            HashAlgorithm::Sha512 => MessageDigest::sha512(),
180        }
181    }
182
183    /// Get the string representation of the algorithm
184    pub fn as_str(&self) -> &'static str {
185        match self {
186            HashAlgorithm::Sha256 => "sha256",
187            HashAlgorithm::Sha384 => "sha384",
188            HashAlgorithm::Sha512 => "sha512",
189        }
190    }
191}
192// Implement the standard FromStr trait
193impl FromStr for HashAlgorithm {
194    type Err = String;
195
196    fn from_str(s: &str) -> Result<Self, Self::Err> {
197        match s {
198            "sha256" => Ok(HashAlgorithm::Sha256),
199            "sha384" => Ok(HashAlgorithm::Sha384),
200            "sha512" => Ok(HashAlgorithm::Sha512),
201            _ => Err(format!(
202                "Unsupported hash algorithm: {s}. Supported algorithms are: sha256, sha384, sha512"
203            )),
204        }
205    }
206}
207/// Signs a CBOR-encoded claim using the specified hash algorithm.
208///
209/// # Arguments
210///
211/// * `claim_cbor` - The CBOR-encoded claim data to sign
212/// * `private_key` - The private key used for signing
213/// * `algorithm` - The hash algorithm to use for signing
214///
215/// # Returns
216///
217/// * `Ok(Vec<u8>)` - The signed COSE structure as a byte array
218/// * `Err(String)` - Error message if signing fails
219///
220/// # Example
221///
222/// ```rust,ignore
223/// use atlas_c2pa_lib::cose::{sign_claim_with_algorithm, HashAlgorithm};
224/// use openssl::pkey::PKey;
225///
226/// let private_key = PKey::private_key_from_pem(&key_pem).unwrap();
227/// let signed = sign_claim_with_algorithm(
228///     &cbor_data,
229///     &private_key,
230///     HashAlgorithm::Sha384
231/// ).unwrap();
232/// ```
233pub fn sign_claim_with_algorithm(
234    claim_cbor: &[u8],
235    private_key: &PKey<openssl::pkey::Private>,
236    algorithm: HashAlgorithm,
237) -> Result<Vec<u8>, String> {
238    // Create a COSE Sign1 builder with the payload (claim_cbor)
239    let sign1_builder = CoseSign1Builder::new()
240        .payload(claim_cbor.to_vec()) // Set the payload to be signed
241        .protected(Header::default()); // Add a protected header
242
243    // Sign the payload using the provided private key and algorithm
244    let mut signer = Signer::new(algorithm.to_message_digest(), private_key).map_err(|e| {
245        format!(
246            "[COSE] Failed to create signer with {} algorithm: {} (check if private key is valid)",
247            algorithm.as_str(),
248            e
249        )
250    })?;
251
252    // Feed the payload into the signer
253    signer.update(claim_cbor).map_err(|e| {
254        format!(
255            "[COSE] Failed to update signer with payload: {} (payload size: {} bytes)",
256            e,
257            claim_cbor.len()
258        )
259    })?;
260
261    // Generate the signature
262    let signature = signer.sign_to_vec().map_err(|e| {
263        format!(
264            "[COSE] Failed to generate signature with {}: {}",
265            algorithm.as_str(),
266            e
267        )
268    })?;
269
270    // Add the signature to the COSE Sign1 structure
271    let sign1 = sign1_builder.signature(signature).build();
272
273    // Serialize the signed COSE structure to a byte array (CBOR format)
274    sign1
275        .to_vec()
276        .map_err(|e| format!("[COSE] Failed to serialize signed claim: {e}"))
277}
278
279/// Signs a CBOR-encoded claim using the default SHA-384 algorithm.
280///
281/// This function is provided for backward compatibility and convenience.
282/// It internally calls `sign_claim_with_algorithm` with SHA-384.
283///
284/// # Arguments
285///
286/// * `claim_cbor` - The CBOR-encoded claim data to sign
287/// * `private_key` - The private key used for signing
288///
289/// # Returns
290///
291/// * `Ok(Vec<u8>)` - The signed COSE structure as a byte array
292/// * `Err(String)` - Error message if signing fails
293///
294/// # Example
295///
296/// ```rust,ignore
297/// use atlas_c2pa_lib::cose::sign_claim;
298/// use openssl::pkey::PKey;
299///
300/// let private_key = PKey::private_key_from_pem(&key_pem).unwrap();
301/// let signed = sign_claim(&cbor_data, &private_key).unwrap();
302/// ```
303pub fn sign_claim(
304    claim_cbor: &[u8],
305    private_key: &PKey<openssl::pkey::Private>,
306) -> Result<Vec<u8>, String> {
307    sign_claim_with_algorithm(claim_cbor, private_key, HashAlgorithm::Sha384)
308}
309
310/// Verifies a signed claim using the specified hash algorithm.
311///
312/// # Arguments
313///
314/// * `signed_claim` - The signed COSE structure to verify
315/// * `public_key` - The public key used for verification
316/// * `algorithm` - The hash algorithm that was used for signing
317///
318/// # Returns
319///
320/// * `Ok(())` - If the signature is valid
321/// * `Err(String)` - Error message if verification fails
322///
323/// # Example
324///
325/// ```rust,ignore
326/// use atlas_c2pa_lib::cose::{verify_signed_claim_with_algorithm, HashAlgorithm};
327/// use openssl::pkey::PKey;
328///
329/// let public_key = PKey::public_key_from_pem(&key_pem).unwrap();
330/// let result = verify_signed_claim_with_algorithm(
331///     &signed_data,
332///     &public_key,
333///     HashAlgorithm::Sha384
334/// );
335/// assert!(result.is_ok());
336/// ```
337pub fn verify_signed_claim_with_algorithm(
338    signed_claim: &[u8],
339    public_key: &PKey<openssl::pkey::Public>,
340    algorithm: HashAlgorithm,
341) -> Result<(), String> {
342    // Parse the COSE-encoded signed claim
343    let sign1 = CoseSign1::from_slice(signed_claim)
344        .map_err(|e| format!("Failed to parse signed claim: {e}"))?;
345
346    // Get the payload (the signed data)
347    let payload = sign1
348        .payload
349        .as_ref()
350        .ok_or("No payload found in signed claim")?;
351
352    // Extract the signature from the COSE structure
353    let signature: &[u8] = &sign1.signature;
354
355    // Initialize the verifier with the public key and the specified algorithm
356    let mut verifier = Verifier::new(algorithm.to_message_digest(), public_key).map_err(|e| {
357        format!(
358            "Failed to create verifier with {} algorithm: {}",
359            algorithm.as_str(),
360            e
361        )
362    })?;
363
364    // Feed the payload (the data that was signed) into the verifier
365    verifier
366        .update(payload)
367        .map_err(|e| format!("Failed to update verifier with payload: {e}"))?;
368
369    // Verify the signature using the public key
370    if verifier
371        .verify(signature)
372        .map_err(|e| format!("Verification failed with {}: {}", algorithm.as_str(), e))?
373    {
374        Ok(()) // Signature is valid
375    } else {
376        Err(format!(
377            "Invalid signature for {} algorithm",
378            algorithm.as_str()
379        )) // Signature is invalid
380    }
381}
382
383/// Verifies a signed claim using the default SHA-384 algorithm.
384///
385/// This function is provided for backward compatibility and convenience.
386/// It internally calls `verify_signed_claim_with_algorithm` with SHA-384.
387///
388/// # Arguments
389///
390/// * `signed_claim` - The signed COSE structure to verify
391/// * `public_key` - The public key used for verification
392///
393/// # Returns
394///
395/// * `Ok(())` - If the signature is valid
396/// * `Err(String)` - Error message if verification fails
397///
398/// # Example
399///
400/// ```rust,ignore
401/// use atlas_c2pa_lib::cose::verify_signed_claim;
402/// use openssl::pkey::PKey;
403///
404/// let public_key = PKey::public_key_from_pem(&key_pem).unwrap();
405/// let result = verify_signed_claim(&signed_data, &public_key);
406/// assert!(result.is_ok());
407/// ```
408pub fn verify_signed_claim(
409    signed_claim: &[u8],
410    public_key: &PKey<openssl::pkey::Public>,
411) -> Result<(), String> {
412    verify_signed_claim_with_algorithm(signed_claim, public_key, HashAlgorithm::Sha384)
413}
414
415#[cfg(test)]
416mod tests {
417    use super::*;
418    use openssl::pkey::PKey;
419    use openssl::rsa::Rsa;
420
421    // Helper function to generate a test key pair
422    fn generate_test_keypair() -> (PKey<openssl::pkey::Private>, PKey<openssl::pkey::Public>) {
423        let rsa = Rsa::generate(2048).expect("Failed to generate RSA key");
424        let private_key = PKey::from_rsa(rsa.clone()).expect("Failed to create private key");
425        let public_key_rsa = rsa
426            .public_key_to_pem()
427            .expect("Failed to export public key");
428        let public_key =
429            PKey::public_key_from_pem(&public_key_rsa).expect("Failed to create public key");
430        (private_key, public_key)
431    }
432
433    #[test]
434    fn test_hash_algorithm_conversions() {
435        use std::str::FromStr;
436
437        // Test as_str()
438        assert_eq!(HashAlgorithm::Sha256.as_str(), "sha256");
439        assert_eq!(HashAlgorithm::Sha384.as_str(), "sha384");
440        assert_eq!(HashAlgorithm::Sha512.as_str(), "sha512");
441
442        // Test from_str() - valid cases
443        assert!(matches!(
444            HashAlgorithm::from_str("sha256").unwrap(),
445            HashAlgorithm::Sha256
446        ));
447        assert!(matches!(
448            HashAlgorithm::from_str("sha384").unwrap(),
449            HashAlgorithm::Sha384
450        ));
451        assert!(matches!(
452            HashAlgorithm::from_str("sha512").unwrap(),
453            HashAlgorithm::Sha512
454        ));
455
456        // Can also use the parse method that comes with FromStr
457        assert!(matches!(
458            "sha256".parse::<HashAlgorithm>().unwrap(),
459            HashAlgorithm::Sha256
460        ));
461
462        // Test from_str() - invalid cases
463        assert!(HashAlgorithm::from_str("sha1").is_err());
464        assert!(HashAlgorithm::from_str("md5").is_err());
465        assert!(HashAlgorithm::from_str("SHA256").is_err()); // Case sensitive
466        assert!(HashAlgorithm::from_str("").is_err());
467    }
468
469    #[test]
470    fn test_sign_and_verify_with_sha256() {
471        let (private_key, public_key) = generate_test_keypair();
472        let test_data = b"Test claim data for SHA-256";
473
474        // Sign with SHA-256
475        let signed = sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha256)
476            .expect("Failed to sign with SHA-256");
477
478        // Verify with SHA-256
479        assert!(
480            verify_signed_claim_with_algorithm(&signed, &public_key, HashAlgorithm::Sha256).is_ok(),
481            "Failed to verify SHA-256 signature"
482        );
483
484        // Verify with wrong algorithm should fail
485        assert!(
486            verify_signed_claim_with_algorithm(&signed, &public_key, HashAlgorithm::Sha384)
487                .is_err(),
488            "Verification should fail with wrong algorithm"
489        );
490    }
491
492    #[test]
493    fn test_sign_and_verify_with_sha384() {
494        let (private_key, public_key) = generate_test_keypair();
495        let test_data = b"Test claim data for SHA-384";
496
497        // Sign with SHA-384
498        let signed = sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha384)
499            .expect("Failed to sign with SHA-384");
500
501        // Verify with SHA-384
502        assert!(
503            verify_signed_claim_with_algorithm(&signed, &public_key, HashAlgorithm::Sha384).is_ok(),
504            "Failed to verify SHA-384 signature"
505        );
506
507        // Verify with wrong algorithm should fail
508        assert!(
509            verify_signed_claim_with_algorithm(&signed, &public_key, HashAlgorithm::Sha512)
510                .is_err(),
511            "Verification should fail with wrong algorithm"
512        );
513    }
514
515    #[test]
516    fn test_sign_and_verify_with_sha512() {
517        let (private_key, public_key) = generate_test_keypair();
518        let test_data = b"Test claim data for SHA-512";
519
520        // Sign with SHA-512
521        let signed = sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha512)
522            .expect("Failed to sign with SHA-512");
523
524        // Verify with SHA-512
525        assert!(
526            verify_signed_claim_with_algorithm(&signed, &public_key, HashAlgorithm::Sha512).is_ok(),
527            "Failed to verify SHA-512 signature"
528        );
529
530        // Verify with wrong algorithm should fail
531        assert!(
532            verify_signed_claim_with_algorithm(&signed, &public_key, HashAlgorithm::Sha256)
533                .is_err(),
534            "Verification should fail with wrong algorithm"
535        );
536    }
537
538    #[test]
539    fn test_default_functions_use_sha384() {
540        let (private_key, public_key) = generate_test_keypair();
541        let test_data = b"Test claim data for default algorithm";
542
543        // Sign with default function (should use SHA-384)
544        let signed_default =
545            sign_claim(test_data, &private_key).expect("Failed to sign with default algorithm");
546
547        // Sign explicitly with SHA-384
548        let signed_explicit =
549            sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha384)
550                .expect("Failed to sign with explicit SHA-384");
551
552        // Verify default signed data with SHA-384
553        assert!(
554            verify_signed_claim_with_algorithm(&signed_default, &public_key, HashAlgorithm::Sha384)
555                .is_ok(),
556            "Default sign_claim should use SHA-384"
557        );
558
559        // Verify default function can verify SHA-384 signatures
560        assert!(
561            verify_signed_claim(&signed_explicit, &public_key).is_ok(),
562            "Default verify_signed_claim should use SHA-384"
563        );
564
565        // Default verify should work with default sign
566        assert!(
567            verify_signed_claim(&signed_default, &public_key).is_ok(),
568            "Default functions should be compatible"
569        );
570    }
571
572    #[test]
573    fn test_signature_uniqueness() {
574        let (private_key, public_key) = generate_test_keypair();
575        let test_data = b"Test data for signature uniqueness";
576
577        // Sign the same data multiple times with the same algorithm
578        let sig1 = sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha384)
579            .expect("Failed to sign 1");
580        let sig2 = sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha384)
581            .expect("Failed to sign 2");
582
583        // Signatures might be different due to randomness in signing process
584        // But both should be valid
585        assert!(
586            verify_signed_claim_with_algorithm(&sig1, &public_key, HashAlgorithm::Sha384).is_ok(),
587            "First signature should be valid"
588        );
589        assert!(
590            verify_signed_claim_with_algorithm(&sig2, &public_key, HashAlgorithm::Sha384).is_ok(),
591            "Second signature should be valid"
592        );
593
594        // Sign with different algorithms
595        let sig_256 = sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha256)
596            .expect("Failed to sign with SHA-256");
597        let sig_384 = sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha384)
598            .expect("Failed to sign with SHA-384");
599        let sig_512 = sign_claim_with_algorithm(test_data, &private_key, HashAlgorithm::Sha512)
600            .expect("Failed to sign with SHA-512");
601
602        // Signatures with different algorithms should produce different results
603        assert_ne!(
604            sig_256, sig_384,
605            "SHA-256 and SHA-384 signatures should differ"
606        );
607        assert_ne!(
608            sig_384, sig_512,
609            "SHA-384 and SHA-512 signatures should differ"
610        );
611        assert_ne!(
612            sig_256, sig_512,
613            "SHA-256 and SHA-512 signatures should differ"
614        );
615    }
616
617    #[test]
618    fn test_empty_data_signing() {
619        let (private_key, public_key) = generate_test_keypair();
620        let empty_data = b"";
621
622        // Test all algorithms with empty data
623        for algo in [
624            HashAlgorithm::Sha256,
625            HashAlgorithm::Sha384,
626            HashAlgorithm::Sha512,
627        ] {
628            let signed = sign_claim_with_algorithm(empty_data, &private_key, algo.clone())
629                .unwrap_or_else(|_| panic!("Failed to sign empty data with {algo:?}"));
630
631            assert!(
632                verify_signed_claim_with_algorithm(&signed, &public_key, algo.clone()).is_ok(),
633                "Failed to verify empty data signature with {algo:?}"
634            );
635        }
636    }
637
638    #[test]
639    fn test_large_data_signing() {
640        let (private_key, public_key) = generate_test_keypair();
641        // Create 1MB of test data
642        let large_data = vec![0x42u8; 1024 * 1024];
643
644        // Test with SHA-384 (default)
645        let signed = sign_claim(&large_data, &private_key).expect("Failed to sign large data");
646
647        assert!(
648            verify_signed_claim(&signed, &public_key).is_ok(),
649            "Failed to verify large data signature"
650        );
651    }
652
653    #[test]
654    fn test_verification_with_wrong_key() {
655        let (private_key1, _) = generate_test_keypair();
656        let (_, public_key2) = generate_test_keypair();
657        let test_data = b"Test data for wrong key verification";
658
659        // Sign with key1
660        let signed = sign_claim_with_algorithm(test_data, &private_key1, HashAlgorithm::Sha384)
661            .expect("Failed to sign");
662
663        // Verify with key2 should fail
664        assert!(
665            verify_signed_claim_with_algorithm(&signed, &public_key2, HashAlgorithm::Sha384)
666                .is_err(),
667            "Verification should fail with wrong public key"
668        );
669    }
670}